跳至主要内容

打造一個「可理解、可掌控」的個人 AI Agent 系統

· 閱讀時間約 8 分鐘
Kestrel

為什麼要自己造?

過去一年,AI Agent 框架如雨後春筍。但當我真正去看那些框架的原始碼時,發現一個根本性的問題:我看不完它們。

以 OpenClaw 為例,整個專案超過 15 萬行程式碼。對一個要讓 AI 代替你操作電腦、讀寫檔案、執行命令的系統來說,「看不完」等於「無法掌控」。你不知道它在哪些情況下會做出什麼事,你只能信任它——但信任一個你看不懂的系統,本質上就是賭博。

所以我決定自己造。

Kestrel 是我的個人 AI Agent 系統。它的核心只有大約 9,200 行 TypeScript,跑在我家的 M1 iMac 上。用 Discord 跟它對話,它可以讀寫檔案、執行命令、搜尋網路、管理 GitHub repo,也可以提出修改自己設定的提案。

我對這個系統的核心哲學只有兩條:

  • 可理解性:我能讀完每一行程式碼,知道它在做什麼。
  • 可掌控性:我知道它能做什麼、不能做什麼,且這些邊界是我設計的。

這篇文章分享的就是 Kestrel 的架構設計,特別是如何在「讓 agent 有用」和「確保安全」之間取得平衡。

架構總覽

Kestrel 的架構可以用一句話概括:一台 Mac、一個 Bun 進程、透過 Cloudflare Tunnel 對外服務。

架構總覽架構總覽

整個系統跑在一個 Bun 進程裡,沒有資料庫、沒有 Redis、沒有 Kubernetes。所有資料存在本地檔案系統上。Discord 作為對話介面,Cloudflare Tunnel 負責把外部請求安全地導入本機。

Agent 有 7 個工具可以用:

工具用途
exec執行 shell 命令
read讀取檔案
write寫入檔案
edit精確替換檔案內容
memory管理長期記憶
web_search搜尋網路
web_fetch抓取網頁內容

7 個工具,就這樣。看起來很少,但搭配 Skills 系統(按需載入的知識文件),agent 可以做的事遠比工具數量暗示的多。好的系統不是給 agent 100 個工具,而是讓少量通用工具的組合足夠強大。


權限隔離:三區模型

Agent 能讀寫檔案、能執行命令——這很強大,也很危險。權限設計是整個系統安全的基礎。

Kestrel 把檔案系統分成三個區域:

三區權限模型三區權限模型

Read-Write 區:agent 的工作空間

~/workspace/~/.kestrel/ 是 agent 可以自由讀寫的區域。寫程式、跑測試、下載檔案、存記憶——都在這裡。

Read-Only 區:agent 的源碼

~/agent/ 存放 agent 自己的程式碼。Agent 可以讀自己的源碼——它需要理解自己的運作方式才能有效工作。但它不能直接修改源碼。這是刻意的設計:agent 可以理解自己,但不能篡改自己。

源碼的更新只能透過 GitHub PR → 人類審核 merge → 本機拉取的流程完成(後面會詳述)。

Denied 區:碰都不能碰

~/.ssh/~/.gnupg/~/.env~/.config/——這些路徑連讀都不行。SSH key、API key 這些敏感資訊,agent 完全無法接觸。

光擋路徑不夠。如果 agent 在工作區建一個 symlink 指向 .ssh 目錄呢?

ln -s ~/.ssh ~/workspace/my_innocent_folder

Kestrel 的路徑檢查會先用 realpathSync() 解析 symlink 的真實目標,邏輯路徑和實際目標都必須在允許的區域內,否則拒絕存取。這堵住了 symlink 繞過攻擊。


Change Request 機制:agent 的「自我修改提案」

Kestrel 有幾類「系統管理檔案」:

  • Soul:agent 的人格定義——它是誰、怎麼溝通、核心價值觀
  • Heartbeat:定時自動執行的待辦任務模板
  • Skills:按需載入的知識與操作指南
  • Config:系統設定

這些檔案雖然在 .kestrel/ 目錄(Read-Write 區),但 agent 的 writeedit 工具會額外檢查,不允許直接修改這些路徑

那 agent 怎麼更新自己的設定?答案是 Change Request

流程類似 GitHub PR:

Change Request 流程Change Request 流程

舉個實際例子:agent 在某次對話後覺得自己的 Soul 定義可以更精確,它會主動發一個 CR:

{
"id": "cr-20260216-refine-soul-proactivity",
"target": "soul",
"reason": "對話中確認用戶更期待主動行為,強化相關描述",
"proposal": {
"old_string": "你不會消極等待指令",
"new_string": "你不會消極等待指令,而是主動識別問題然後想辦法解決他們"
},
"status": "pending",
"createdAt": "2026-02-16T12:00:00.000Z"
}

這個機制的精神是:agent 可以有自己的想法,但重要的自我修改需要人類同意。 它可以提案修改自己的人格、技能、設定,但最終決定權在我手上。


GitHub 整合:用 PR 改源碼

Agent 的源碼在 ~/agent/,是唯讀的。那源碼怎麼更新?

兩個 GitHub App

Kestrel 用 GitHub App(而非 Personal Access Token)來操作 GitHub,而且根據 repo 所有權拆成兩個 App:

Repo 擁有者使用的 Bot用途
kestrel-labs(組織)merlin-bot-org其他專案
個人帳號merlin-bot-personalAgent 源碼

為什麼用 GitHub App 而非 PAT?

  • 身份隔離:App 有自己的身份,不是用我的帳號操作
  • 短效 token:每個 token 只有 1 小時有效期,自動刷新
  • 權限最小化:每個 App 只有它需要的 repo 權限

源碼更新流程

源碼更新流程源碼更新流程

整個流程跟正常的軟體開發一模一樣:code review → merge → deploy。只不過提 PR 的人是 AI。


五層安全防線

把前面提到的安全機制整理起來,Kestrel 有五層防線:

五層安全防線五層安全防線

第一層:OS 帳號隔離

Agent 跑在一個專用的系統帳號下,沒有管理員權限。.env 檔案由 root 擁有、權限 600,agent 只能透過 launchd 啟動時讀入,運行中無法直接讀取。

第二層:Path Jail(三區模型)

前面詳述的三區權限模型——Read-Write、Read-Only、Denied,加上 symlink 解析防護。每次檔案操作都會經過路徑檢查。

第三層:命令驗證

Agent 的 exec 工具不是直接把命令丟給 shell 執行。每個命令都會經過驗證:

直接封鎖的命令

  • sudosu — 阻止提權
  • chmod 777 — 阻止不安全的權限設定
  • curl | shwget | sh — 阻止遠端程式碼執行
  • launchctlosascript — 阻止作業系統層級操作
  • envprintenv(無參數) — 阻止洩露環境變數中的金鑰

特殊處理——rm 改寫成 trash

簡單的 rm file.txt 不會被封鎖,而是自動改寫成 trash file.txt,把檔案移到垃圾桶而非永久刪除。這比一律封鎖 rm 更實用——agent 有時確實需要清理檔案,但用 trash 讓操作可復原。

至於 rm -rfrm -rrm -f 這些危險的變體?直接封鎖,沒有商量餘地。

環境變數過濾

Agent 執行子程序時,環境變數會被過濾。API key、Discord token、Admin token 等敏感變數不會傳遞給子程序。即使 agent 想透過 echo $ANTHROPIC_API_KEY 偷看,也什麼都拿不到。

第四層:Audit Log

所有工具呼叫都會寫入 audit log——時間戳、工具名稱、參數、結果(允許/封鎖/改寫)。

關鍵設計:audit log 存在 Denied 區。Agent 無法讀取、更無法竄改自己的行為紀錄。這就像監視器的錄影帶鎖在 agent 進不去的房間裡。

Admin UI 提供 audit log 瀏覽和安全稽核功能,只有通過認證的管理員可以查看。

第五層:Cloudflare Tunnel

Kestrel 跑在家裡的 Mac 上,但不開任何 port、不暴露 IP。所有外部流量都透過 Cloudflare Tunnel 進來:

  • 真實 IP 永遠不會暴露
  • 所有 mutating endpoint 需要 Bearer token 認證
  • CORS 設定為白名單制(fail-closed),沒有 wildcard
  • Token 比對使用 timing-safe 演算法,防止 side-channel 攻擊

即使有人知道域名,沒有正確的 token,什麼都做不了。


結語

回頭看,Kestrel 的架構沒有任何「黑科技」。OS 帳號隔離、檔案路徑管控、命令驗證、audit log、反向隧道——這些都是資安領域行之有年的基本手段。

但把它們組合在一起,用 9,200 行程式碼實現一個我能完全理解的 AI Agent 系統,這件事本身就是設計上的選擇。

市面上的 agent 框架追求的是「功能完備」,Kestrel 追求的是「完全掌控」。對一個要代替你操作電腦的 AI 來說,我認為後者更重要。

你不需要一個什麼都能做的 agent。你需要一個你知道它在做什麼的 agent。