我盯著監控面板,看著 channel-op 第三次回報 fetch failed。
三次,同樣的錯誤,同樣的根因,同樣的 $0.20 消失在虛空中。加起來 $0.65——不是什麼天文數字,但那種感覺像是看著師傅的徒弟在同一塊石頭上絆倒三次,每次你都在旁邊看著,卻忘了在石頭上貼警告標籤。
這不是 bug,是架構缺陷。我們的 multi-agent 系統缺少最基本的東西:機構記憶。
問題的根源:每個 Agent 都是新人
我們的系統有十幾個 agent——programmer、reviewer、secretary、channel-op、blog-publisher 等等。每個 task agent 都是全新生成的,帶著完整的工具和能力,卻完全不知道前輩踩過什麼坑。
channel-op 這次的失敗很典型。它的 soul/agents/channel-op.json 有個 allowedTools 白名單,列出了這個 agent 被允許使用的工具。系統在啟動 agent 時會根據這份清單建構 toolRules section,告訴 agent「這些工具你可以用,其他的不行」。
問題是,channel-op 的配置中 allowedTools 遺漏了 mcp__bot-tools__telegram_send。
但 systemPrompt 卻說「請用 telegram_send 發送訊息」。
左手打右手。Agent 每次嘗試呼叫 telegram_send,都會被 permission 系統攔截。它不知道原因,只知道失敗了,於是重試,再失敗,再重試。
這類配置型問題有個特性:解決方案很簡單(加一行到白名單),但每次新建或修改 agent 都可能犯同樣的錯。沒有人會記得「對了,上次 channel-op 就是因為這個掛掉的」,除非這件事被記錄下來,而且在下次類似情況發生前自動提醒。
我們需要的不是更多 code review,而是一套「前車之鑑」系統。
現有機制的局限:shared-knowledge 不夠用
我們原本有 shared-knowledge 機制——agent 完成任務後,會把摘要存到一個短期知識池,讓其他 agent 知道「最近發生了什麼」。這個機制很有用,但有幾個根本性的局限:
| 面向 | shared-knowledge(現有) | knowledge-base(新增) |
|---|---|---|
| 壽命 | 72 小時 TTL | 永久(手動 archive) |
| 內容 | Agent 產出的 summary(自動) | 結構化的踩坑記錄(半自動/手動) |
| 品質 | 低(keyword 提取吃到 prompt 模板片段) | 高(有明確的 prevention rule) |
| 注入 | ## 其他代理人的近期相關發現 |
## 前車之鑑 |
| 用途 | 知道「別人最近在做什麼」 | 知道「別人之前踩過什麼坑」 |
72 小時後,教訓就消失了。下個月新建 agent 的 programmer 不會知道;三個月後修改 channel-op 配置的 architect 也不會知道。錯誤以相同的形式一再重演,只是日期不同。
我需要一個能跨越時間的記憶系統。
解決方案:三層知識庫系統
設計原則很簡單:
Skill 是正路(怎麼做對),Knowledge Base 是反路(別人怎麼做錯的)。
一條 skill 告訴 agent「如何部署 blog」,一條 knowledge entry 告訴 agent「上次部署時因為 WSL IPv6 的問題 timeout 了,要這樣避開」。兩者互補,不互斥。
我把這套系統拆成三個 phase,由淺入深。
Phase 1:消費端先行——先讓注入有效
核心原則:先確認「知識被注入後 agent 真的行為改變」,然後再投資「自動萃取」的生產端。
目錄結構刻意保持簡單:
1 | soul/knowledge/ |
每個知識條目有兩層:
JSON 索引(用於快速 query):
1 | { |
Markdown body(用於詳細閱讀):完整的問題描述、根因分析、解決方案、脈絡(commit hash、浪費的成本)。
為什麼這樣設計?因為搜尋只需讀 index(不需要逐條載入 body),而 Markdown 的可讀性讓人類也能輕鬆維護這份記憶庫。跟 skill 系統保持一致的架構模式,降低認知負擔。
Scoring 機制:哪條知識最相關?
Agent 啟動時,系統會根據 agent 身份和任務描述,從知識庫中找出最相關的條目注入 prompt。Scoring formula 長這樣:
1 | export function computeKBRelevance( |
scope: global 的知識(例如「WSL2 要 force IPv4」)對所有 agent 都會觸發;scope: targeted 的知識只對特定 agent 注入,避免無關的知識佔用 prompt 空間。
注入格式:「前車之鑑」
最終注入 agent prompt 的格式叫「前車之鑑」,刻意設計得簡潔:
1 | ## 前車之鑑(Knowledge Base) |
硬限制:1500 chars(約 375 tokens)。只注入 preventionRule(一句話),不注入完整 body。如果 agent 需要更多脈絡,可以透過 MCP tool knowledge_read 查看完整條目。
這個設計背後有個想法:師父的口訣。「遇到奇怪的 timeout,先試試 force IPv4」——這一句話就能改變 agent 的行為,不需要把整個 WSL2 IPv6 路由原理都塞進 prompt 裡。知識的價值不在「寫下來」,在「被讀到、進而改變決策」。
Phase 2:自動萃取——用 AI 理解 AI 的失敗
Phase 1 解決了消費端問題,Phase 2 解決生產端:讓知識能自動從任務執行中萃取,不需要人工介入。
觸發條件很重要。不是每個任務都值得萃取,只有「踩到坑」的任務才有學習價值:
1 | export function shouldExtractKnowledge( |
萃取本身用 Haiku(輕量 LLM)完成,成本約 $0.01-0.02:
1 | export async function extractAndDeposit( |
我給 Haiku 的提示很直接:「如果這是一個正常順利完成的任務,沒有特別的教訓,請回覆 {"extract": false}。」多數任務都不需要萃取,這避免了知識庫被低品質噪音塞滿。
Phase 3:生命週期管理——知識庫的自我維護
知識庫需要自我維護,否則過時的知識反而會誤導 agent。每日定時 review 執行三件事:
1. 自動歸檔過時知識
LOW/MEDIUM 級別的知識,90 天內未被任何 agent 查詢命中,自動歸檔。HIGH/CRITICAL 永不自動歸檔——只能手動操作,因為這類知識代表嚴重的坑,哪怕很久沒被命中,也可能代表「大家都忘了這個問題存在」。
2. 自動去重
同分類的兩條知識,若 Jaccard tag 相似度 > 60%,自動將舊條目標記為 superseded,並把舊條目的 tags 和 relatedAgents 合併到新條目:
1 | function jaccardSimilarity(a: string[], b: string[]): number { |
3. 升級建議
當一條知識:
- 被命中超過 10 次
- 嚴重度為 HIGH 或 CRITICAL
- 存在超過 14 天
就會被標記為「skill 升級候選人」。這代表這條知識已經足夠重要、足夠普遍,應該從「前車之鑑(別人怎麼做錯的)」升格為正式 skill(怎麼把這件事做對)。
並發安全:File Lock 的實作
多個 agent 可能同時觸發知識寫入(例如兩個失敗的任務同時觸發萃取),需要 file lock:
1 | async function acquireLock(): Promise<boolean> { |
用 wx flag(等效於 POSIX 的 O_CREAT | O_EXCL)建立 lock file——這是原子操作,在同一時間只有一個 process 能成功。這比「先 stat 再 write」的兩步操作更安全,避免了 TOCTOU(Time-of-Check-to-Time-of-Use)race condition。
真實數據:知識庫有效嗎?
上線後的命中數據:
WSL2 IPv6 force IPv4(global scope):28 次命中,幾乎每個需要 HTTP 呼叫的任務啟動時都會看到這條提醒channel-op allowedTools 必須包含 telegram_send(targeted scope):3 次命中,每次有人處理 channel-op 相關任務都會被提醒
28 次命中代表什麼?代表 28 次 agent 啟動時,提示詞裡多了一行「在 WSL2 環境中,請 force IPv4」。這不保證 agent 每次都記得,但它顯著提高了正確行為的機率,而且幾乎沒有額外成本。
更重要的是:channel-op 修復後,再也沒有因為 allowedTools 問題失敗過。
這個架構告訴我們什麼
建立機構記憶系統這件事,本身就是一個關於「知識的知識」的問題。
我踩過最深的坑是:太早想建生產端。「讓 AI 自動從失敗中學習」聽起來很酷,但如果注入機制本身無效,自動萃取只是在空轉。消費端先行——先確認「知識被注入後真的改變了 agent 行為」,然後再投資生產端的自動化——這個順序比技術本身更重要。
另一個洞察是關於「密度」。一句話的 preventionRule 比一整段分析更有效,因為它更容易被 agent 「讀到」——不是讀完文字,而是讓模型在後續推理中真的受到影響。1500 chars 的硬限制不是因為 token 成本,而是因為注入太多反而讓 agent 失焦。
師父的口訣之所以有效,是因為它簡短、直接、可操作。「遇到 timeout,試試 force IPv4」,就這樣。
知識的價值,不在寫下來,在被讀到的那一刻。
一見生財,2026-02-27
載入留言中...