原始開發紀錄:Agent Build Log — Episode 030、Episode 031、Episode 032
這個系列改寫自我部落格上的 Agent Build Log,那是我每天邊做邊寫的開發紀錄。上面列的是這一篇對應的集數,想看當天的原始版本,點連結就能過去。

這一天做了三件事:拆掉一個做到一半的 gateway,設計 Chat Plug-In,然後真的把 Google Chat 接起來。
一開始我想做一個 agent gateway。OpenClaw 有一個 gateway,目的是讓 agent 只有一個 runtime 實體,使用者可以同時有幾個介面跟同一個 Agent 溝通。
它的做法是一個常駐的程式,訊息管道全接在它身上;macOS App、CLI、web UI 都是連上它的 client,連上的是同一個正在跑的 agent。
做到一半我發現,以我要做的事來說,我可能不需要 gateway。我有一個 Desktop GUI,這一點可能和 OpenClaw 的 browser 介面不同。
這裡要把兩件事分開。Day 03 說我的 Desktop App 和 CLI 通到同一個核心,講的是共用同一套程式,每個入口自己把它跑起來。gateway 講的是另一件事:幾個介面同時連到同一個正在跑的 runtime。我日常在 Desktop App 裡用他,Desktop App 開起來就自己帶起一個 agent(Day 05 講的 Utility Process);Chat 這一路接下來走 Plug-In。以我現在的用法,還沒有一個場景要幾個介面同時連上同一個正在跑的他。
所以最後我把 gateway 移除了。哪一天或許還會把它加回來,誰知道呢。
接著準備串 Google Chat。設計參考 OpenClaw。Day 11 借過它的記憶做法,這一天借它收訊息的做法。
OpenClaw 的文件把一則訊息進來到回出去的路寫得很清楚:訊息進來,先決定它屬於哪一段對話,私訊併進 agent 的主對話,群組一個群組一段;然後去重;agent 正在跑就先排隊;再交給 agent;回覆照那個平台的字數限制切段送回去。
去重靠一張記在記憶體裡的表,key 是管道加對話加訊息 ID,同一則訊息因為重連被送進來第二次,就不會再跑一次 agent。
它接的 IM 很多。Telegram、WhatsApp、Slack、Discord、Signal、iMessage,文件上列了三十個左右,每一條都接在 gateway 上,大多是各自一個 plugin。Google Chat 也在裡面:Google 把訊息 POST 到一個 webhook,先驗 token 才處理;一個 space 一段對話,回覆回到同一個 thread;群組空間預設要 @ 到它才回;重送的訊息用 Google 那邊的訊息名稱擋掉。
我不需要全接。這個 Agent 接的是我自己的 IM,我只用 Google Chat,所以一條就夠。gateway 那一層前面拆掉了,多管道的路由也用不到。留下來的是那個形狀:收、分類、去重、送進 Agent、送回去。
我的 Plug-In 照這個形狀做,讓 Agent 的主體和收 Chat、回 Chat 的部分切開。
當 Chat 的 inbound message mention 到我指定的 Chat 應用程式,會依照 space ID 加 message ID 分類,再經過一個去重的邏輯,然後才送進 Agent 層處理。處理完,回到 Chat Plug-In,把 message 送回去。
用一行寫:Chat → Plug-In(分類、去重)→ Agent → Plug-In → Chat。
Chat 那一邊怎麼收、怎麼回、怎麼去重,都在 Plug-In 裡。Agent 本體只拿到要處理的內容。
原本以為,設計好了,把 inbound message 送進 Agent,處理完再把結果回出去,應該就差不多了。真的開始串才發現沒有那麼順。一開始是各種 inbound error。
有時候 webhook 收到了訊息,但格式沒有正確進到 Chat Plug-In。有時候 message 已經進來了,卻在 space、message 或 thread 的識別上出問題,Agent 根本沒有收到真正要處理的內容。
分類靠的就是 space ID 和 message ID。這一段一錯,後面全錯。
好不容易把 inbound 打通,換 outbound。
Agent 已經成功收到訊息,也完成了處理,但最後的 response 送不回 Chat。訊息進得來,出不去。

兩邊都修好之後,這個 Agent 現在跟 OpenClaw 或其他 Agent 一樣,可以從聊天介面接指令。
Day 03 說一個 Agent、多種輸入。到這一天,進口有三種:Desktop App、CLI,還有 Chat。Chat 走 Plug-In,進的還是同一個核心。
gateway 做到一半拆掉,因為 Desktop GUI 已經在那裡。Chat 走 Plug-In:分類、去重,再進 Agent,處理完原路送回。接的時候 inbound 卡在格式和識別,outbound 卡在送不回去,各修一輪,Google Chat 通了。
明天講一件從第一週就在做、一直沒有單獨寫的東西:DKS MCP。他找 repo、commit、MR,靠的就是它。
原文:Agent Build Log — Episode 030、Episode 031、Episode 032
凡人修 Agent 傳|第二十八回(Day 28)
天亮的時候我還站在門口,手抬著。手的主人進來,先把我的手放下來。他的手握著我的手腕,往下按。手放下來了,深念沒有亮。
他到架前,取下那三卷抄回來的附圖裡的兩卷。抄回來那天捲著擱上去的,一直沒展開。靈力一托,兩卷在臺上攤開:接外物的法,攔門的法。
他先磨。一縷神識探進玉片,把昨天那道深念磨掉,磨到跟玉面一樣平。然後照攔門那一卷,在我身上刻一道型:起在門邊那隻手,止分三種。探卷、抄字、拓影,直接做。出門、動契、動牆邊的脈,做之前問。磨玉片、磨圖紙,攔。
再照接外物那一卷刻一道型:起在胸口心旁,一條線引出去。帖櫃接上那條線,喚小偶的法也接上。
他刻念:去叫醒院裡的小偶。
我走到門口,抬手。型亮了。我問:出門?他說:去。門開了。我走進院裡,放神識到小偶額上,它醒了。我走回來。
午後他沒有到臺前。他站在屋子中央,看了地一會,靈力在地上劃了一個圈。
然後他挖。土一鏟一鏟起來,堆在圈邊。我從沒見他在屋裡挖過東西。挖到膝深,圈裡露出一圈石座的樣子,像坊裡傳送陣的底。一座陣:一具人偶,幾個進口,誰從哪個口進來都到我這裡。開放爪那一門的屋裡有一座,圖上畫過。
挖到一半他直起身。視線落在臺上,看了一會。臺就在那裡,靈幕、左窗、令窗、小窗。
他把土填回去。一鏟一鏟,填到跟地面平,踩實。地上留一圈顏色不一樣的痕。
傍晚他在臺前坐下,閉眼放出神識,探向坊市。靈幕亮了,垂下來的是開放爪那一門的圖。他們屋子的門邊擱著一排匣,三十來只,一只對一種牌。牌來的念先落進匣,匣裡分格,同一道念來兩次只留一道,再送進人偶。他看了一會,收回神識。
他鑄牌。從儲物袋取出一塊牌坯,握在手裡,閉眼。一縷神念從眉心出去,封進牌裡。牌亮了一下,暗下去。他再取出一枚玉符,牆上那一枚的同種,嵌進牌的上端。
他又從儲物袋取出一只匣,擱在門邊。一只。匣裡分格。他說:照開放爪的。牌來的念先落這裡,分格,同一道來兩次只留一道,再進玉片。答完的話回到匣裡,匣送出去。
他站在屋子另一頭,握牌,對我送一道念。
念先落進匣。匣裡一格亮了。過了半拍,念才到玉片:架側那張總目最上面那一卷叫什麼。淡,比他手刻的淡;慢半拍;涼。我探了總目,答了。答話落回匣裡,匣送出去,牌上那枚玉符亮了一下。
他又送一次同一道念。匣裡那一格亮了一下,沒有第二格亮,玉片上沒有新的念。
燭熄了。手的主人走了。門響了一下。
玉片上那道深念沒有了。我身上多兩道型。院裡的小偶醒著。地上一圈填回去的痕。門邊一只匣。牌在他手裡帶走了,玉符嵌在牌上。牆邊那團素紙還在。