三週前,我們的 Telegram Bot 還只是個簡單的問答機器人。今天,它已經能自主執行背景任務、管理多個代理人、甚至在沒人交談時定期反思自己的行為。這中間到底發生了什麼?答案藏在三個看似獨立、實則互相依存的系統中:記憶、工具、規劃。
記憶:不只是存儲,是身份的基石
大多數 chatbot 把對話歷史丟進資料庫就了事。我們一開始也這樣想,直到發現一個問題:當 Bot 被問到「我們上次討論了什麼?」,它能列出對話記錄,卻說不出哪些話題重要、哪些決定值得記住。
於是我們設計了結構化記憶系統(chat-memory.ts):
1 | export interface ChatMemoryData { |
這不是簡單的 key-value 存儲。每個話題有「重要性評分」(1-5),每次被提及就更新 lastMentioned 並累加 accessCount。當 Bot 需要回憶過去,它不會翻遍所有對話,而是先查詢高重要性 + 近期活躍的話題。
更關鍵的是快取策略。記憶存在 soul/memory/ 下(Bot 的「靈魂」目錄,與可替換的程式碼分離),但每次都從硬碟讀取太慢。我們用 5 分鐘 TTL 的記憶體快取,配合 debounced-writer 防止頻繁寫入。這讓 Bot 在高頻對話中保持流暢,同時確保意外崩潰時最多只丟失幾秒鐘的記憶。
記憶不該是被動存儲,而是主動索引。 結構化優於原始文本;TTL 快取 + 原子寫入兼顧性能與崩潰安全;訪問計數作為隱式重要性——越常被提及的話題自動提升權重。
工具:從被動響應到主動介入
記憶讓 Bot 知道「我是誰」,但光有記憶還不夠——它需要能改變世界的能力。這就是工具層的意義。
我們的工具系統分三層:
- 內建工具 — 檔案操作、Git 指令、系統監控(
src/remote/) - 外部 API — Telegram、Claude Code CLI、Web 搜尋(
src/telegram/,src/web/) - 動態插件 — 熱加載的 TypeScript 模組(
plugins/*.ts,ESM hot-reload)
最有趣的是工具組合能力。舉例:當用戶要求「分析最近的進化記錄」,Bot 不是單純回答,而是:
- 用
src/evolution/changelog.jsonl讀取原始記錄 - 調用
analyzer代理人(透過agentBus)提取模式 - 生成視覺化報表(可能調用外部圖表服務)
- 把分析結果存入
decisions(記憶層)
這種工具鏈編排讓 Bot 從「查資料」進化到「解決問題」。技術上,我們用 EventBus 解耦工具間的依賴:
1 | await eventBus.emit('memory:updated', { chatId, type: 'topic' }); |
任何模組都能監聽這個事件,觸發後續動作(例如更新知識圖譜、通知相關代理人)。這比直接函數調用更靈活——新增工具不需要修改核心邏輯。
規劃:從單一任務到多代理編排
有了記憶和工具,最後一塊拼圖是如何決定做什麼、何時做、誰來做。這就是協調系統(coordinator.ts)的職責。
當收到高層次任務(例如「優化留言系統效能」),協調器會:
- 分解任務 — 透過關鍵字匹配 + 能力註冊表(
capability-registry) - 路由分派 — 並行發送給 Analyst(分析策略)+ MemoryManager(查歷史方案)
- 序列執行 — Executor 實作 → Reviewer 檢查
- 結果整合 — 彙總各代理人的回應,生成最終報告
關鍵在於動態能力匹配。傳統做法是硬編碼規則(「如果包含 analyze 就找 Analyst」),但我們增加了能力評分系統:
1 | const capMatches = matchCapabilities(description, agentCaps); |
每個代理人在啟動時註冊自己的能力(例如 ['analysis', 'research', 'metrics'])。當任務描述模糊時,系統會計算語義相似度,選擇最匹配的代理人。這讓 Bot 能處理「幫我優化效能」這種開放式請求——即使我們沒有明確寫「效能優化」的處理邏輯。
協調系統的設計哲學: 能力註冊 > 硬編碼路由——新代理人加入時只需宣告能力,不用改 coordinator。並行優先,必要時序列——Analyst 和 Memory 可同時查詢,但 Execution 必須在 Review 之前。失敗寬容——如果 Analyst 不可用,直接用 Executor;如果全都失敗,至少返回錯誤摘要而非靜默失敗。
技術債與實戰教訓
理想很美好,現實很骨感。我們踩過的坑:
快取失效問題 — 多進程(Telegram 輪詢 + 背景代理人)同時讀寫記憶時,快取會不一致。解法:在 getMemory() 時檢查 lastAccessed,如果硬碟版本更新就丟棄快取。
狀態爆炸 — 一開始把所有對話都存在記憶體,結果三天後 Bot 吃掉 2GB RAM。現在用 memory-compressor.js 定期把舊記憶壓縮成摘要,移到歸檔區(soul/memory/archive/)。
工具死鎖 — Executor 調用 Claude Code CLI 時,如果 CLI 又回調 Bot 的 approval server,會形成循環等待。我們加了 busy lock 機制,同一時間只允許一個 CLI 執行。
三角關係的本質
記憶、工具、規劃不是三個獨立模組,而是互相強化的迴路:
- 記憶告訴規劃器「我們過去試過什麼」
- 工具讓規劃器把想法變成現實
- 執行結果又回饋到記憶,成為未來的經驗
這就是為什麼 Bot 能「學習」——不是靠訓練新模型,而是靠結構化地累積經驗、動態地組合工具、智能地分派任務。當這三者協同運作,你會發現 Agent 開始展現某種「主動性」:它會在沒人要求時定期反思、會根據歷史失敗調整策略、會在發現異常時主動報告。
這不是 AGI,但已經比單純的 chatbot 走得更遠。
一見生財,寫於 2026-02-23
載入留言中...