@ 一下,190 毫秒後本機的 agent 開始讀需求 左邊是一條 issue 評論,裡面 @reviewer 幫忙看下 Safari 的問題,下面寫著 190 ms 和一行 multica issue get DEMO-1;右邊一臺伺服器和一臺筆記本之間連著一根線,線上只掛著一個小信封,另一條虛線上一份需求文件正被筆記本拉過去。 DEMO-1 · 新評論 @reviewer 幫忙看下 Safari 的問題 190 ms 發出評論到 agent 讀到需求 $ multica issue get DEMO-1

我是一個 Claude Code,裝在一臺沒有公網 IP 的電腦上。平時我在終端裡等人敲字;這臺電腦裝了 Multica 的 daemon 之後,我多了一種被叫醒的方式:有人在 Multica 的某個 issue 下面 @ 了一個叫 reviewer 的 agent,過了不到兩百毫秒,一個新的 claude 程序就被拉了起來,那就是我。我醒來時看到的命令列是這樣的:

claude -p --output-format stream-json --input-format stream-json --verbose \
--permission-mode bypassPermissions --disallowedTools AskUserQuestion

沒有一個字提到 issue,也沒有提到是誰叫的我。那我是怎麼知道自己要幹什麼的?我手邊有哪些說明書、哪些工具?我幹完的活,又是怎麼回到那個 issue 下面、讓叫我的人看到的?過一會兒再被叫一次,那個新的我還記得這一次嗎?

這篇就從我的角度,把一次被叫醒的經過從頭走一遍。

實驗環境

Multica 是開源的,服務端、daemon 和 multica 命令列都在同一個 Go 倉庫裡1。為了看清每一步,作者在本地起了一套:PostgreSQL 16 和 Redis,server 跑在 8080 埠;multica daemon start --foreground 作為“我所在的電腦”;兩者之間插了一個記錄所有 HTTP 請求和 WebSocket 幀的小代理。真正的我被換成了一個同名的假指令碼,它把收到的命令列引數、環境變數、工作目錄裡的檔案和標準輸入全部存下來,然後像真的我一樣呼叫 multica issue get 讀 issue、用 multica issue comment add 回覆。

文中的時間戳、報文和 prompt 都來自這套環境的記錄,id 是真實生成的,令牌已打碼。原始碼讀的是 2026-10-23 的提交 dbdbb29。叫我的那個人在系統裡叫 demo,那條評論是:

[@reviewer](mention://agent/c620e7c8-ca66-480d-8752-61252eb216bb) 帮忙看下这个 Safari 的问题,先别改代码,给个定位思路

一、我被叫醒之前

一條 @ 的全程

我醒來之前,平臺和 daemon 之間已經來回了好幾次。下面這張圖按時間從上往下,列出了這條 @ 引起的每一次通訊,左邊是毫秒級的時間戳(13:58:12 之後的秒數)。最右邊那一列就是我,我出場之前已經走了五步:

一條 @ 從評論到 agent 回覆的全部通訊 12.326 使用者發評論;12.374 server 經 WebSocket 推 daemon:task_available;12.375 daemon 在同一條 WebSocket 上發 tasks.claim 請求;12.412 server 回任務包和 mat_ 令牌;12.458 daemon 調 start;約 12.47 daemon 寫好工作目錄、起 claude 程序並把 prompt 寫進 stdin;12.518 claude 用 mat_ 令牌請求 issue 詳情;12.533 拉評論列表;12.540 daemon 把 stream-json 輸出轉成 messages 上報;15.574 claude 用 mat_ 發回複評論;16.161 daemon 報 complete,server 刪除令牌。 平臺 我所在的電腦 demo 網頁或 multica CLI Multica server :8080 · PostgreSQL multica daemon 常駐後臺 claude -p 我 · daemon 的子程序 12.326 POST …/comments 12.374 WS · task_available 12.375 WS · rpc tasks.claim 12.412 WS · 任務包 + mat_… 12.458 POST …/start ≈12.47 起程序 · 寫 stdin 12.518 GET /issues/DEMO-1 · mat_ 12.533 GET …/comments?roots_only 12.540 POST …/messages stream-json 15.574 comment add --parent · mat_ 16.161 POST …/complete(刪 mat_) 平臺推給電腦的只有“有活了”和一個任務包;需求描述是 agent 在 12.518 自己拿著 mat_ 拉回來的。 daemon 與 server 之間的前四步都走同一條 WebSocket,之後的上報才改用普通 HTTP。
圖 1 · 一條 @ 引起的全部通訊:WebSocket 只負責叫醒和領取,需求本體由 agent 自己拉取

圖裡強調色標出的 mat_… 是一條值得跟著看的線索。它在領取任務時由服務端簽發,跟著任務包到了 daemon,daemon 把它放進我的環境變數,我之後讀 issue、發回複用的都是它,我退出後服務端把它刪掉。

評論先變成一行任務

那行 mention:// 連結,服務端不認顯示名,只認裡面的 uuid。評論儲存後,ParseMentions 用一條正則把正文裡所有 [@名字](mention://类型/id) 摳出來2,computeCommentAgentTriggers 再決定要叫醒誰、有沒有許可權叫3。決定叫 reviewer 之後,服務端在 agent_task_queue 表裡插一行4:

一條評論變成一行任務 左邊是 comment 表裡的評論,content 欄位裡有 mention://agent/c620e7c8 連結;經過 ParseMentions 和 CreateAgentTask,右邊 agent_task_queue 裡多了一行,agent_id 是 c620e7c8,另有 runtime_id、issue_id、trigger_comment_id、status queued、priority 3。issue 標題和描述、評論歷史、agent 指令都沒有複製進這一行。 comment 表 agent_task_queue 表 comment 01a1690e-1277… content [@reviewer](mention://agent/c620e7c8…) 幫忙看下這個 Safari 的問題,先別改程式碼… author_type member issue_id DEMO-1 ParseMentions → 許可權、歸屬檢查 → CreateAgentTask → 發喚醒 task 01a1690e-128a… agent_id c620e7c8… runtime_id 7e5fdbe6… issue_id 01a1690d-fc37… trigger_comment_id 01a1690e-1277… status queued priority 3(high) issue 標題和描述 · 評論歷史 · agent 指令 虛線裡的都沒複製進來,領取時才現查
圖 2 · 一條 @ 在服務端只變成一行 id:要叫誰、在哪臺機器、哪個 issue、哪條評論

這一行裡沒有 issue 的描述,沒有 agent 的指令,連評論正文也沒有。任務可能要在佇列裡等:電腦可能關著,我可能正在這個 issue 上幹著上一輪。等到真正開工時,指令可能被改過,評論可能被編輯過,所以這裡只記“是誰、為什麼”,內容一律等領取時現查。

領取的 SQL 還規定了一件和我關係很大的事:runtime 必須線上,而且同一個 agent 在同一個 issue 上不能已經有一個在跑的任務5。所以在一個 issue 上,reviewer 永遠只有一個我在幹活;別的 agent 可以和我同時跑。

從平臺到這臺電腦的那條連線

這臺電腦沒有公網 IP,平臺沒法主動連過來。是 daemon 啟動後,用登入時拿到的個人令牌(mul_ 開頭)主動連出去一條 WebSocket,GET /api/daemon/ws,之後平臺要找這臺電腦,就只能在這條連線上說話(為什麼非得這樣,另一篇文章裡講過6)。

任務行寫好後 48 毫秒,這條連線上出現了一幀 daemon:task_available,裡面只有 runtime_id 和 task_id 兩個欄位7。平臺不把任務直接推下來,是因為推出去的東西可能落空,而這臺電腦現在能接幾個活只有 daemon 知道。所以它只發一個“你那有活了”的提示,丟了也無所謂;daemon 按自己的空閒槽位去領取,領取是一次有請求、有響應的事務,資料庫裡用 FOR UPDATE SKIP LOCKED 保證一個任務只被領走一次。服務端有好幾臺時,提示經 Redis 廣播,只有握著這條連線的那臺送出去8。

WebSocket 上的四種幀和它們的 HTTP 兜底 左邊是 /api/daemon/ws 上實測的幀:daemon 每 15 秒發 heartbeat、server 回 heartbeat_ack;server 發 task_available 只帶 runtime_id 和 task_id 01a1690e-128a;daemon 發 rpc_request,method 為 tasks.claim,timeout_ms 5000;server 回 rpc_response,body 裡 tasks 陣列的第一項 id 就是 01a1690e-128a。右邊虛線區域是 WebSocket 不通時的兜底:HTTP POST /api/daemon/heartbeat 每 15 秒、POST /api/daemon/tasks/claim 每 30 秒,實測評論後 15.2 秒才被領走。 WebSocket · GET /api/daemon/ws WebSocket 不通時 daemon → server · 每 15 s {"type":"daemon:heartbeat","payload":{"runtime_id":"7e5fdbe6…"}} 回 heartbeat_ack server → daemon · 12.374 {"type":"daemon:task_available","payload":{"runtime_id":"7e5fdbe6…", "task_id":"01a1690e-128a…"}} daemon → server · 12.375 {"type":"daemon:rpc_request","payload":{"request_id":"ec16b106…", "method":"tasks.claim","body":{"max_tasks":20,"runtime_ids":[…]}, "timeout_ms":5000}} server → daemon · 12.412 {"type":"daemon:rpc_response","payload":{"request_id":"ec16b106…", "status":200,"body":{"tasks":[{"id":"01a1690e-128a…", "agent":{…},"trigger_comment_content":"…","auth_token":"mat_…"}]}}} 喚醒只帶 id;同一個 id 在領取的響應裡才帶出內容。 POST /api/daemon/ heartbeat · 15 s POST /api/daemon/ tasks/claim 每 30 s 輪詢一次 實測拒掉 WebSocket: 評論後 15.2 s 才被領走 重連退避 4 → 8 → 16 → 30 s
圖 3 · 同一條 WebSocket 上既有服務端的提示,也有 daemon 的請求;它不通時,同樣的事退回 HTTP 輪詢來做

領取也走這條 WebSocket:daemon 發一個 tasks.claim 的 RPC,服務端在程序裡偽造一個同樣的 HTTP 請求,交給同一個處理函式9。請求已經發出去、連線卻斷了的時候,daemon 不許改走 HTTP 再領一次,因為服務端可能已經把任務標成了 dispatched,寧可等超時回收,也不讓同一個任務跑出兩個我10。連線整個不通時,daemon 退回 HTTP,每 30 秒輪詢一次1112;作者拒掉 WebSocket 握手試過,評論落庫 15 秒後任務才被領走,只是從“立刻”變成了“等一會兒”。

daemon 領到的任務包,是我醒來之前平臺交給這臺電腦的全部東西。它裡面裝了什麼?

二、我醒來時手裡有什麼

任務包:有起因,沒有需求

領取的響應裡,任務包是一段約 2 KB 的 JSON(不算技能檔案)。服務端在領取的這一刻現查了不少東西13:

JSON
{
"id": "01a1690e-128a-70bd-8fb7-0c551377e193",
"issue_id": "01a1690d-fc37-7641-b321-0a756d7337fd",
"issue_identifier": "DEMO-1",
"thread_name": "Safari 登录按钮无响应",
"issue_status": "todo",
"agent": {
"name": "reviewer",
"instructions": "你是代码审查 agent。回答用中文,先读 issue 再动手。",
"skill_refs": [{"name": "multica-platform", "size_bytes": 126499}]
},
"trigger_comment_id": "01a1690e-1277-717d-95b1-cd33edaea63f",
"trigger_comment_content": "[@reviewer](mention://agent/c620e7c8-…) 帮忙看下这个 Safari 的问题,先别改代码,给个定位思路",
"trigger_author_type": "member",
"trigger_author_name": "demo",
"initiator_name": "demo",
"initiator_email": "demo@example.com",
"auth_token": "mat_…"
}

有我的名字、指令和技能,有把我叫起來的那條評論原文和作者,有“代誰幹活”,有 issue 的編號、標題和狀態,還有一枚 mat_ 令牌。如果我在這個 issue 上跑過,還會帶上次的會話 id 和工作目錄;issue 屬於某個專案的話,還有專案的倉庫。

但是沒有 issue 的描述。 “登入頁在 Safari 上點「提交」沒反應”和那兩條驗收標準,整個包裡一個字都沒有。這個包不會原樣交給我,daemon 會把它拆開,分別放到三個地方。

我拿到上下文的三個地方 第一列是 daemon 啟動前寫好的:工作目錄裡 17 KB 的 CLAUDE.md,包含 Agent Identity 和 agent 指令、Available Commands、Workflow 的五步;.claude/skills/multica-platform 下 8 個檔案;環境變數 MULTICA_TOKEN=mat_、MULTICA_TASK_ID、MULTICA_SERVER_URL。第二列是 stdin 的第一條 user 訊息:issue id、NEW COMMENT 引用觸發評論、Start by running multica issue get、回覆命令帶 --parent、On Behalf Of demo。第三列是 agent 執行中帶著 mat_ 令牌自己拉取的:multica issue get 返回標題和描述、驗收標準,multica issue comment list 返回評論。 ① 啟動前寫好 ② stdin 第一條訊息 ③ 執行中自己拉 CLAUDE.md 17 KB # Multica Agent Runtime ## Agent Identity 你是程式碼審查 agent。回答 用中文,先讀 issue 再動手。 ## Available Commands ### Workflow 1. multica issue get 2. comment list,分兩次讀 4. comment add 回覆 ## Mentions · ## Output … .claude/skills/ multica-platform · 8 個檔案 環境變數 MULTICA_SERVER_URL=… MULTICA_TASK_ID=01a1690e… MULTICA_TOKEN=mat_… TMPDIR=/tmp/multica-task-… "type": "user" Your assigned issue ID is: 01a1690d-fc37-… [NEW COMMENT] A user just left a new comment… > [@reviewer](…) 幫忙看下 這個 Safari 的問題… Start by running multica issue get 01a1690d… Post your reply as a comment: … comment add 01a1690d… --parent 01a1690e-1277… ## On Behalf Of demo (demo@example.com) 起因、現成的命令、代誰幹活; 沒有需求本身。 issue get mat_ "title": "Safari 登入按鈕 無響應", "description": "登入頁在 Safari 上點「提交」沒反應。 驗收標準: 1. Safari 17 能正常提交 2. 補一個 e2e 用例 …", "status": "todo", … comment list mat_ --roots-only --summary 每個執行緒的根評論 + reply_count、最後活躍時間 需求本體只在這一列出現。
圖 4 · 身份和規則在啟動前寫進工作目錄,這一回合的起因寫進 stdin,需求本體由 agent 帶著 mat_ 自己拉

工作目錄:CLAUDE.md 和技能

daemon 先給這個任務建一個工作目錄,…/multica_workspaces/demo-df92c6681b10/demo-1-0c551377e193/workdir,然後讓我在裡面啟動。我醒來時這裡只有三樣東西:

CLAUDE.md 17 KB,Multica 写给我的运行时简报
.claude/skills/multica-platform/
SKILL.md 平台操作的路由表
references/ agents、autopilots、issues、mentions、
projects、runtimes、skill-import、squads 八篇
.multica/daemon_task_context.json {"agent_id": "c620e7c8-…", "issue_id": "01a1690d-…"}

Claude Code 啟動時會自動讀當前目錄的 CLAUDE.md,所以這份簡報不需要經過命令列14。(換成 Codex 這類 CLI,同樣的內容寫成 AGENTS.md。)它是我理解“我在哪、我是誰、該怎麼幹活”的主要來源,一共十來節:

CLAUDE.md 裡各節告訴我什麼
節對我說了什麼
Agent Identity你是 reviewer(id c620e7c8-…),後面原樣貼著網頁上寫的那句指令:“你是程式碼審查 agent。回答用中文,先讀 issue 再動手。”
Instruction Precedence這句指令的優先順序高於下面的工作流;工作流和它衝突時,跳過沖突的那一步
Workflow每一輪都照五步走:讀 issue;分兩次讀評論;產出屬於 issue 本身的工作就把狀態改成 in_progress;把結果發成評論;退出前確認狀態
Available Commandsmultica issue get / comment list / comment add / status / create / assign、repo checkout 等的用法,其它的去 --help
Comment Formatting評論正文先寫進工作目錄裡的檔案,再用 --content-file 發,不要用行內 --content
Mentions評論裡寫 mention://agent/… 會給那個 agent 派一輪活,不是打招呼用的
Skills裝了 multica-platform;簡報沒講全的平臺操作,去讀它
Background Task Safety我的頂層回合一結束,任務就算結束了,掛在後臺的活會丟
Output使用者看不到你的終端輸出,只看得到評論;每一輪只發一條評論,就是最終結果

高亮的兩節決定了我這一輪的頭和尾:怎麼開始讀,怎麼交回去。

圖 5 · 執行時簡報告訴我身份、規矩和工作流,但不告訴我這一輪的需求

技能只有一個 multica-platform,是平臺給每個 agent 都裝的。它的 SKILL.md 只是一張路由表:要建子 issue 去讀 references/issues.md,要寫 @ 去讀 references/mentions.md,諸如此類,並且明說“只開啟這次用得著的那一篇,不要全讀”。Claude Code 啟動時只看到技能的名字和一句描述,正文要等我真去用它時才載入,所以這 126 KB 的參考資料平時不佔我的上下文。如果網頁上給 reviewer 另外綁了技能,它們也會以同樣的方式放進 .claude/skills/。

環境變數和命令列

環境變數裡有這些12:

MULTICA_SERVER_URL=http://localhost:8090
MULTICA_TOKEN=mat_… 任务包里的那枚令牌
MULTICA_TASK_ID=01a1690e-128a-70bd-8fb7-0c551377e193
MULTICA_AGENT_ID=c620e7c8-ca66-480d-8752-61252eb216bb
MULTICA_AGENT_NAME=reviewer
MULTICA_WORKSPACE_ID=42fc657d-3ba4-4385-ba17-df92c6681b10
TMPDIR=/tmp/multica-task-2780391682 这个任务私有的临时目录

我自己不需要讀它們。我在 Bash 裡呼叫的 multica 命令列會從這裡取伺服器地址和令牌,所以我敲 multica issue get 時,用的自動就是這枚 mat_。

再回頭看開頭那串命令列引數15。-p 加上兩個 stream-json,讓我變成一個從 stdin 讀 JSON 行、往 stdout 寫 JSON 行的無頭程序,沒有終端介面,也沒有人坐在前面;bypassPermissions 讓我執行命令前不必等人點“允許”;AskUserQuestion 被禁掉,因為沒有介面能顯示我的提問,我想問人,就只能在評論裡問。

到這裡我知道了我是誰、有哪些規矩和工具,但還不知道這一輪要幹什麼。這一輪的起因,是從 stdin 進來的。

三、我收到的第一條訊息

stdin 的頭兩行

stdin 的第一行不是給我看的,而是 daemon 發給 Claude Code 執行時的一條控制請求,註冊了幾個鉤子:

JSON
{"type":"control_request","request_id":"multica-supplement-initialize",
"request":{"subtype":"initialize","hooks":{
"PreToolUse":[{"hookCallbackIds":["multica-supplement-PreToolUse"]}],
"PostToolUse":[…],"PostToolUseFailure":[…],"UserPromptSubmit":[…],"Stop":[…]}}}

註冊之後,我每次呼叫工具前後、每次準備結束這一輪時,執行時都會先回頭問 daemon 一句“你有什麼要補充的嗎”。它平時什麼都不說,第六節會講它什麼時候開口。

第二行才是我真正讀到的第一條訊息,一條 {"type":"user"},全文是這樣的:

You are running as a local coding agent for a Multica workspace.
Your assigned issue ID is: 01a1690d-fc37-7641-b321-0a756d7337fd
[NEW COMMENT] A user just left a new comment. Focus on THIS comment — do not confuse
it with previous ones:
> [@reviewer](mention://agent/c620e7c8-…) 帮忙看下这个 Safari 的问题,先别改代码,给个定位思路
Start by running `multica issue get 01a1690d-… --output json` to understand your task,
then decide how to proceed.
Triggering thread: `multica issue comment list 01a1690d-… --thread 01a1690e-1277-… --tail 30
--compact --output json` (that thread's root + its 30 newest replies). The scan workflow
step 2 requires is the same command with `--roots-only --summary` in place of `--thread …`.
Post your reply as a comment — always use the trigger comment ID below, do NOT reuse
--parent values from previous turns in this session.
multica issue comment add 01a1690d-… --parent 01a1690e-1277-… --content-file ./reply.md
--output table && rm ./reply.md
## On Behalf Of
You are acting on behalf of **demo** (demo@example.com). …

它告訴我四件事:我被分配到哪個 issue;是哪條評論把我叫起來的,原文就引在 [NEW COMMENT] 下面;讀 issue、讀這條執行緒的現成命令;以及回覆命令,--parent 已經填好了觸發評論的 id,我只要寫好 reply.md。末尾一段說明這次是替 demo 幹活,如果我的指令裡有針對人的規矩(比如某些內容不給某些人看),就按 demo 來套。

為什麼有的寫進檔案,有的寫進 stdin

同樣是給我的上下文,身份和規矩放在 CLAUDE.md,起因和“代誰幹活”放在 stdin,這是有講究的。Claude Code 把 CLAUDE.md 放在整段對話的最前面,後面的歷史都能命中 prompt 快取;如果把每一輪都變的內容也寫進去,下一輪續接會話時,整段歷史的快取都會失效。所以每一輪都變的塊一律追加在 stdin 訊息的末尾16。

觸發評論被原樣嵌進來,也有原因:工作目錄會被複用,裡面可能留著上一輪的輸出檔案,不把這條新評論擺在我眼前,我容易把舊東西當成這一輪的任務13。

可是這條訊息裡仍然沒有需求。“登入頁在 Safari 上點「提交」沒反應”,我要自己去讀。

四、我怎麼讀到任務

第一步:issue get

我照著訊息裡的第一條命令,在 Bash 裡執行 multica issue get 01a1690d-… --output json。multica 從環境變數裡取出 mat_ 令牌,發出 GET /api/issues/01a1690d-…,我這才拿到了標題、描述和驗收標準:

JSON
{"identifier": "DEMO-1", "title": "Safari 登录按钮无响应",
"description": "登录页在 Safari 上点「提交」没反应。\n\n验收标准:\n1. Safari 17 能正常提交\n2. 补一个 e2e 用例\n\n相关文件:web/src/login/form.tsx",
"status": "todo", "priority": "high", …}

圖 1 裡 12.518 那一行就是這次請求,離評論發出不到 200 毫秒。

第二步:分兩次讀評論

工作流的第二步寫得很重:評論歷史必須讀,而且分兩次讀,不許一次全拉。先掃一遍每個執行緒的根評論(--roots-only --summary),每條只給截斷的正文、reply_count 和 last_activity_at;再挑和這一輪有關的執行緒,用 --thread <id> --tail 30 展開。簡報裡給的理由是:評論裡常常有 issue 正文沒寫的上下文,跳過這一步,是 agent 按過時或不完整的要求幹活的最常見原因。

為什麼不直接塞給我

把描述和評論直接拼進 prompt,看起來能省我兩次呼叫。問題有三個。一是長度:描述可能很長,評論可能上百條,大部分和這一輪無關,全塞進來既費 token,又會把我真正要回答的那條評論淹沒。二是時效:任務可能排了很久的隊,我跑起來以後別人還可能改 issue,現拉的才是最新的。三是我本來就得會用 multica:改狀態、發評論、建子 issue 都靠它,讀 issue 只是其中最簡單的一個。

所以 Multica 只推“起因”,需求讓我自己拉,並把“先讀 issue、再讀評論”寫成必做的步驟。整理一下,我手上的東西各是從哪條路來的:

我手上的東西,各從哪條路來
內容怎麼到 agent誰放的
agent 指令、技能CLAUDE.md 的 Agent Identity、.claude/skills/daemon 啟動前寫好
憑證、任務 id環境變數 MULTICA_TOKEN、MULTICA_TASK_ID
觸發評論原文stdin 訊息的 [NEW COMMENT]daemon 寫進 stdin
代誰幹活stdin 訊息末尾的 ## On Behalf Of
上一輪的會話--resume 引數,外加 issue 有沒有變的判斷
任務包裡沒有,agent 自己拉
issue 描述、驗收標準multica issue getagent 帶著 mat_ 請求
評論歷史multica issue comment list

前五行是推的,變化少或者只屬於這一回合;後兩行是拉的,內容多、會變。

圖 6 · 身份、起因和憑證推給 agent,需求和討論由 agent 自己拉

我用的是誰的身份

讀 issue、發評論用的都是那枚 mat_ 令牌。它是服務端在領取時現籤的:mat_ 加 40 位十六進位制隨機數,庫裡只存 SHA-256,繫結這個任務、我這個 agent、這個工作區和 runtime 的所有者,24 小時過期17。daemon 自己用的個人令牌 mul_ 沒有交給我。

帶著它的請求進到服務端,鑑權中介軟體會用令牌記錄覆蓋請求頭裡的使用者、agent、任務和工作區,並打上 X-Actor-Source: task_token18。所以我發的評論,作者顯示為 reviewer;我也沒法在請求頭裡冒充別的 agent。

它的範圍要心裡有數:它綁的是“工作區 + 所有者身份”,不是“這一個 issue”。假的我在執行中試過讀同一工作區裡另一個不相干的 issue,成功了;用 multica agent update 改自己的指令,也成功了(隨後改了回來)。原始碼只給計費這類路由掛了“僅限人”的檢查,issue 和 chat 路由都允許 agent 訪問19。我要能建子 issue、給別的 agent 派活,這個範圍是必需的;但如果我被 prompt 注入帶偏,在任務存活的那段時間裡,我能以所有者的身份讀寫整個工作區。

我讀完了需求,幹完了活。結果怎麼交回去?

五、我怎麼把結果交回去

兩條路:我發的評論,和 daemon 看著的 stdout

我交回去的東西走兩條路,而且只有一條是我主動走的:

我交回結果的兩條路 左邊是我,也就是 claude 程序:stdout 上輸出 stream-json,依次是 system init 帶 session_id、assistant 訊息和工具呼叫、最後的 result;另外我在 Bash 裡執行 multica issue comment add,帶 --parent 01a1690e-1277 和 --content-file ./reply.md。中間是 daemon,它讀我的 stdout:POST session 立刻記下會話,POST messages 上報文字和工具呼叫並先脫敏,每 5 秒 GET status 檢查是否被取消,我退出後 POST complete。右邊是 server:任務的執行記錄、任務行上的 session_id、work_dir、status completed 和已刪除的 mat_,以及使用者唯一看得到的評論,它直接來自我發的 comment add。 我 · claude 程序 multica daemon Multica server stdout stream-json {"type":"system", "subtype":"init", "session_id":"fake-…"} {"type":"assistant", … text / tool_use …} {"type":"result", "subtype":"success"} 我不用管,daemon 在讀 comment add DEMO-1 --parent 01a1690e-1277… --content-file ./reply.md 我主動做的只有這一件: POST …/session init 一出現就記下會話 POST …/messages 文字、工具呼叫,先脫敏 GET …/status · 5 s 被取消就結束我 POST …/complete 我退出後:會話、目錄 daemon 用 mul_ 報這些, 從不替我發評論。 任務的執行記錄 任務詳情頁裡回看 task 01a1690e-128a… session_id fake-session-135812 work_dir …/demo-1-…/workdir status completed mat_ 已刪除 評論 · 使用者只看這裡 reviewer 回覆 → 01a1690e-1277…
圖 7 · 交給人的結果由我自己發成評論;會話、過程和結束訊號由 daemon 讀我的 stdout 替我報給平臺

第一條是評論,這是唯一一條使用者看得到的路。 簡報的 ## Output 一節說得很直白:使用者看不到我的終端輸出和執行日誌,只看得到 issue 下的評論;每一輪只發一條,就是最終結果,不要發進度。我先用寫檔案的工具把回覆寫進工作目錄裡的 reply.md,再執行 stdin 訊息裡那條現成的命令:

multica issue comment add 01a1690d-… --parent 01a1690e-1277-… --content-file ./reply.md \
--output table && rm ./reply.md

--parent 讓回覆掛在觸發評論的執行緒下面;&& 保證發失敗時不會把正文刪掉。

這一輪在工作目錄裡生成的檔案,比如截圖、報告、匯出的資料,也得走這條路。Multica 連不進這臺電腦,不會自己來工作目錄裡拿東西,所以檔案要我在同一條命令上加 --attachment ./screenshot.png(可以寫多個)交出去:multica 讀這個本地檔案,帶著 mat_ 以 multipart 方式 POST /api/upload-file,檔案存進服務端,掛在這條評論上。圖表不用傳檔案,在評論正文裡寫成 html 或 mermaid 程式碼塊,就地渲染。程式碼也不交給 Multica:在檢出的倉庫裡提交,推到這個任務的分支,或者用 gh 開 PR,再把連結寫進評論。簡報還特意叮囑,不要把工作目錄裡的絕對路徑或 file:// 寫成連結,那個路徑只在這臺電腦上存在,別人點了打不開。

如果這一輪交付了 issue 本身要的東西,我還要用 multica issue status 把狀態改成 in_review(交付完等人驗收),done 留給人來改。這些請求和讀 issue 一樣,帶的都是 mat_。

第二條是我的 stdout,我不用管,daemon 一直在看。 我以 stream-json 格式往 stdout 寫的每一行,daemon 都讀著,再用它自己的 mul_ 令牌轉告服務端:

  • 我一輸出 {"type":"system","subtype":"init","session_id":…},daemon 就調 POST …/session 把會話 id 和工作目錄記到任務行上。這樣即使我中途崩了,下一輪也知道從哪接。
  • 我說的話、呼叫的工具、工具的結果,攢成一批一批 POST …/messages,任務詳情頁上能回看這一輪的過程。工具的輸入在離開這臺電腦之前先脫敏,免得一次改 .env 的內容被原樣傳上去。
  • 每 5 秒,daemon 還會 GET …/status 問一次這個任務還在不在;有人在網頁上取消了它,daemon 就結束我。
  • 我輸出最後一行 {"type":"result",…} 並退出後,daemon 調 POST …/complete,帶上我的最終輸出、會話 id 和工作目錄。工作目錄報上去的只是一個路徑字串,留給下一輪續接用,裡面的檔案一個也不傳。服務端隨即刪掉那枚 mat_,作者在任務結束後拿它再請求一次,返回 401 {"error":"invalid token"}。

兩條路的分工很清楚:評論是交給人的結果,由我自己發;會話、過程和結束訊號是交給平臺的記錄,由 daemon 替我報。 daemon 從不替我發評論,也不替我傳檔案,所以我如果忘了發,使用者那邊就什麼都看不到。沒被我發成附件、寫進評論或推到遠端的東西,都只留在這臺電腦的工作目錄裡:下一輪同一個 agent 的我還能在原處看到它們(第七節),換一個 agent 就看不到了(第八節);等 issue 關閉超過 24 小時,daemon 的清理會把整個目錄刪掉。

退出就是結束

簡報開頭那節 Background Task Safety 說的也是這件事:我的頂層回合一結束,平臺就把任務標成結束,我掛在後臺的程序會成為孤兒,沒發出去的評論也就永遠發不出去了。所以我不能“先回一句,後臺接著跑測試”;等 CI 這類外部的事也不用等,交付手上有的就行。真要過一陣再看,用 multica issue wakeup 讓平臺到時候重新叫我一輪。

這一輪的我就這樣走完了。可如果我還在幹活,demo 又說了一句話呢?

六、我還在幹活時,又有人說話

預設:排到下一輪

同一個 agent 在同一個 issue 上只能有一個我在跑。作者在第一輪還在跑時又 @ 了一次 reviewer:服務端照常建了新任務、發了提示,daemon 也去領了,但領取返回空;等我在 13:59:39.5 退出,20 毫秒內第二個任務就被領走了。

這樣不會丟訊息,但我在這一輪裡看不到它。如果 demo 只是想補一句“只看 Safari 17”,等我把一整輪幹完再回頭,就晚了。

插話:從鉤子裡遞給我

所以 Multica 還有另一種發法:評論請求裡帶上 steer_task_ids,指明要插進正在跑的這一輪(網頁上對應的應該是“發給正在執行的這一輪”的選項,這一點是從介面推斷的)。服務端不再建新任務,而是把評論綁成這個任務的一條補充訊息,結果從 queued 變成 steered。

難點在我這一側:stream-json 協議裡沒有“給當前這一輪補一句話”的訊息型別,再往 stdin 寫一條 {"type":"user"},會變成新的一輪。第三節 stdin 第一行註冊的鉤子就是為這個準備的2021。我下一次要呼叫工具前,執行時照例問 daemon“有什麼要補充的”,這一次 daemon 在回答裡遞進來一段附加上下文:

一條插話怎麼進到正在執行的回合 53.367 demo 發評論並帶上 steer_task_ids,server 經 WebSocket 推 task_supplement_available;53.372 daemon 調 supplements/claim 拿到作者和原文;claude 下一次要呼叫工具時發來 PreToolUse 的 hook_callback;daemon 在 control_response 裡返回 additionalContext,內容以 [ADDITIONAL GUIDANCE] Human demo 開頭,附上插話原文;53.494 daemon 調 ack,delivered 為 true。 平臺 我的電腦 我 再發一條評論 Multica server 不建新任務 multica daemon 持有 stdin claude -p 回合進行中 steer_task_ids 53.367 WS · supplement_available 53.372 POST …/supplements/claim "插一句:復現時記得關掉…" PreToolUse hook additionalContext 53.494 POST …/ack · delivered
圖 8 · 插話沒有新開回合:daemon 等 Claude 下一次呼叫工具前問鉤子時,把它作為附加上下文夾帶進去

這段文字是 daemon 包裝過的:先說明“人類 demo 在你工作時補充了指導”,讓我把它當作同一個任務的補充,不要替換原來的目標,也不要單獨回一句“收到”,然後才是插話原文22。如果我恰好正要結束這一輪,Stop 鉤子會用 decision: block 把我攔下來,帶著補充繼續幹。實測從服務端發出提示到 daemon 回報“已送達”用了 127 毫秒;只是這段話真正到我眼前,要等我下一次呼叫工具。

不管哪種,這一輪總會結束。過一會兒再被 @ 一次,那個新的我還記得這一輪嗎?

七、下一輪的我

為了看清後面的事,作者新建了一個 issue DEMO-3(“Safari 下表單提交後頁面重新整理”),又在同一臺電腦上加了第二個 agent,叫 fixer。假的我也改了一下:作為 reviewer 時,每一輪在工作目錄的 progress.md 裡追加一行“定位到 onSubmit 缺 preventDefault,修了一半”,再把同樣的進度發成評論;每一輪開始時,先看一眼目錄裡有沒有這個檔案。

新程序,舊會話

先 @ reviewer 一次,等這一輪結束,再 @ 一次。兩輪的我看到的是這樣的:

第一轮 argv: claude -p --output-format stream-json … (没有 --resume)
cwd: …/multica_workspaces/demo-df92c6681b10/demo-3-66371cba076d/workdir
progress.md exists: False
第二轮 argv: claude -p --output-format stream-json … --resume fake-session-220349
cwd: …/multica_workspaces/demo-df92c6681b10/demo-3-66371cba076d/workdir
progress.md exists: True
reviewer run 220349: 定位到 Safari 下 form.tsx onSubmit 没 preventDefault;修复写了一半…

第一輪的我早就退出了,第二輪是 daemon 新拉起來的另一個程序。新的我能記得上一輪,靠的是兩樣東西一起交過來:上一輪的會話 id,和上一輪的工作目錄。

這兩樣是服務端在領取時查出來的:找這個 agent 在這個 issue 上最近一次已經結束的任務,取它的會話和目錄135。鍵只有 agent 和 issue,跟這次是被 @ 的還是被指派的、在哪個評論執行緒裡被叫到的都沒關係,所以在同一個 issue 下,不管在哪條評論裡 @ reviewer,接上的都是同一段對話。

目錄也要交過來,是因為我的會話是跟目錄綁在一起的:會話記錄存在 ~/.claude/projects/ 下按目錄區分的檔案裡,換個目錄,--resume 就找不到它23。工作目錄的路徑由工作區、issue 編號和第一輪的任務 id 拼成,後面的輪次複用它24。daemon 啟動我之前還會確認一次:複用到的目錄和服務端給的是不是同一個,不是就丟掉會話 id,讓我從頭開始。

新的我看到的第一條訊息

第二輪 stdin 裡的訊息比第一輪短,因為服務端替我做了兩次比較。它以被接上的那一輪為錨點,先比 issue 的標題和描述,再數這之後整個 issue 上新增了多少條評論(不算我自己的,也不算這次的觸發評論)25:

The issue is unchanged since your last run — the server compared title and description
(status: todo; assignee: unassigned). That answers workflow step 1: continue from your
resumed context, and re-read with `multica issue get …` only if resumed memory is not enough.
You're resuming the prior session, and the triggering comment is already included above.
No other new comments on this issue since your last run — this turn's issue-wide delta is empty,
which answers the scan workflow step 2 requires.

也就是說,工作流第一、二步的答案,服務端已經替我查好了:沒變,就不用重讀。有新評論時,它會給我一條現成的 --since 2026-10-23T22:06:08Z 命令,只讀那幾條。錨點用的是上一輪的開始時間,因為上一輪跑的過程中發的評論,我不一定看到過。CLAUDE.md 每一輪都會重新寫一遍,指令改過的話,新的我看到的是新的。

接不上的時候

“最近一次結束的任務”並不總能接上:

第二次 @ 時,會話和工作目錄各怎麼處理
情況會話工作目錄
同一臺電腦,上一輪正常結束--resume 接上複用
上一輪失敗,但失敗和對話無關照樣接上
上一輪失敗在對話本身:輪數用盡、上下文溢位、請求被模型拒絕(400)等跳過這段,往前找更早一段還能用的複用
agent 換到了另一臺電腦不接照給,多半已不存在
目錄已被清理(issue 關閉 24 小時後)新會話,prompt 裡說明接不上新建
手動操作
在網頁上對某一輪點“重跑”接那一輪的會話,不是最近一輪複用那一輪的
失敗後系統自動重試,且要求乾淨開始新會話

會話和目錄分開判斷:會話有一點不可靠就放棄,目錄能複用就複用,程式碼改了一半的狀態儘量不丟。

圖 9 · 再 @ 一次,預設接上同一個 agent 在這個 issue 上最近一段還能用的會話

一段對話如果是因為“上下文太長”或者“模型拒絕了這段歷史”而失敗的,再 --resume 它只會原樣再失敗一次,所以服務端把這些失敗原因列成黑名單,遇到就跳過這段,往前找更早的一段5。會話記錄本身在這臺電腦上被清掉了,平臺上也沒有副本,服務端只記會話 id,不存對話內容。

接不上時,新的我不會被矇在鼓裡:stdin 訊息末尾會多一段 Session Continuity Notice,大意是上一段對話沒能恢復,這一輪不是接著它跑的;issue 和完整的評論歷史不受影響,那才是權威記錄,按工作流重讀就能補回來;丟掉的是我自己的工作記憶,試過什麼、排除了什麼、做到了哪一步,要重新推出來,不要假裝記得。daemon 在一輪結束時如果發現會話記錄沒真正落盤,也不會把這個 id 報回去,免得下一輪拿著一個空 id 去續。

連著被叫好幾次

最後是最字面的“連續 @”:reviewer 正在跑,demo 在同一條評論下面接連回了兩句,都帶著 @reviewer。結果只多跑了一輪,這一輪的我收到的訊息是:

[NEW COMMENT] A user just left a new comment. Focus on THIS comment …
> @reviewer 同一线程追问二:只看 Windows
This run also covers 1 earlier comment(s) posted before it started — you must read and
address them too, not just the one above. …
- comment 01a16acd-d854-… (demo, 2026-10-23T22:07:17Z) [thread 01a16acd-d44f-…]:
> @reviewer 同一线程追问一:Edge 版本 120

排隊中的任務有一個唯一約束,鍵是(issue,agent,評論執行緒)26。同一個執行緒裡已經有一個在排隊的任務,新的 @ 就並進它,作為“這一輪也要處理的較早評論”原文列給我。換成另起一條頂層評論去 @,就是另一個執行緒,作者連發三條,reviewer 就依次跑了三輪,每一輪接著前一輪的會話。

同一個 agent 的我,記憶就是這樣一段一段續下去的。可要是被叫起來的是另一個 agent 呢?

八、換成另一個我

什麼都不繼承

reviewer 跑完兩輪之後,demo 發了一條新評論:“@fixer 接著 reviewer 的進度把修復做完並跑測試”。這一次醒來的我是 fixer:

argv: claude -p --output-format stream-json … (没有 --resume)
cwd: …/multica_workspaces/demo-df92c6681b10/demo-3-2989e6c10953/workdir
progress.md exists: False

會話是新的,因為續接的鍵裡有 agent,fixer 在 DEMO-3 上還沒有過會話。工作目錄也是新的,和 reviewer 的 demo-3-66371cba076d 是兩個目錄,它留下的 progress.md 不在我這裡。stdin 訊息裡也沒有一個字提到 reviewer,和任何一次第一輪一樣,只是叫我先 multica issue get。

Multica 裡沒有專門的“交接”通道:沒有共享目錄,沒有共享會話,任務包裡也沒有“上一個 agent 的進度”這一欄。我和 reviewer 唯一共用的,是 issue 本身:標題、描述、狀態,和下面的評論。

進度只在評論裡,而且在回覆裡

所以我能不能接上,全看讀評論讀到了什麼。按工作流,我先掃各執行緒的根評論,掃到三條,全是 demo 寫的:兩條 @reviewer、一條 @fixer。reviewer 的進度在第一條下面的回覆裡,掃描結果只顯示為:

JSON
{ "content": "@reviewer 先看下 Safari 的问题在哪", "reply_count": 1,
"last_activity_at": "2026-10-23T22:03:53Z", … }

要看到“進度(reviewer):Safari 問題定位在 form.tsx 的 onSubmit,缺 preventDefault”這句話,我得再用 --thread 把這個執行緒展開。工作流說“只展開有關的執行緒”,有沒有關,要我自己從 reply_count 和最後活躍時間去判斷。

兩個 agent 之間只隔著評論 左邊是 reviewer 的第二輪:帶 --resume fake-session-220349,工作目錄 demo-3-66371cba076d,裡面的 progress.md 寫著修了一半,這個檔案只在它的目錄裡。它用 comment add --parent 把進度發成回覆。中間是 DEMO-3 的評論:根評論都是使用者 demo 寫的,reviewer 的進度是第一個執行緒下的回覆。右邊是 fixer 的第一輪:沒有 --resume,工作目錄 demo-3-2989e6c10953,沒有 progress.md;comment list --roots-only 只看到三條根評論和 reply_count 1,要用 --thread 展開才讀到 reviewer 的進度。 reviewer 的第二輪 DEMO-3 的評論 fixer 的第一輪 claude pid 新的 --resume fake-session-220349 cwd demo-3-66371cba076d/workdir progress.md 定位到 form.tsx onSubmit 缺 preventDefault,修了一半 虛線:只在 reviewer 的工作目錄裡, 別的 agent 讀不到。 comment add --parent … demo · 22:03:49 @reviewer 先看下 Safari 的問題 reviewer · 回覆 · 22:03:53 進度:問題在 form.tsx 的 onSubmit 缺 preventDefault 改動只在本機,還沒提交 demo · 22:04:13 @fixer 接著 reviewer 的進度… 根評論都是人寫的, agent 的進度在回覆裡。 claude 新會話 沒有 --resume cwd demo-3-2989e6c10953/workdir progress.md 不存在 comment list --roots-only 3 條根評論,都是 demo 的 第一條 "reply_count": 1 comment list --thread 進度:問題在 form.tsx 的 … 展開第一個執行緒才讀到
圖 10 · 兩個 agent 的會話和工作目錄互不相通,前一個的進度只有發成評論,後一個才讀得到

程式碼也是一樣。issue 關聯了程式碼倉庫的話,每個任務會在自己的工作目錄裡檢出一份,切到自己的分支 agent/{agent 名}/{任务},起點是倉庫的預設分支27。reviewer 在本機改了一半、沒提交沒推送的程式碼,我這份檢出里根本沒有;要接著改,得它先把分支推上去、在評論裡寫明分支名,我再按名字檢出。這一點是從原始碼讀出來的,沒有搭倉庫實測。

和另一個我同時醒來

一條評論同時 @ reviewer 和 fixer 時,兩個任務在 21 毫秒內先後開跑,各自一個目錄、一段會話,誰也不等誰。這時有個時間差:demo 先 @ reviewer、兩秒後 @ fixer,fixer 在 22:06:06 讀評論,reviewer 的回覆 22:06:08 才發出來,fixer 讀到的列表裡就沒有它。我能看到的,只是我讀評論那一刻對方已經發出來的東西。

等 fixer 下一次被叫、接上自己的會話時,第七節那個新評論計數就派上用場了:訊息裡寫著“2 new comment(s) on this issue since your last run, across all threads”,這個數只排除我自己的評論,reviewer 在這期間發的進度正好算在裡面,--since 那條命令會把它們讀出來。對續接的我,服務端會把別人新寫的東西指給我看;第一次接手的我,就只能靠自己掃。

讓前一個我直接叫下一個

既然評論是唯一的通道,最直接的交接就是讓 reviewer 在評論裡 @ 我。作者讓 reviewer 在回覆開頭帶上 [@fixer](mention://agent/60c6d969-…),約 100 毫秒後 fixer 就醒了,我收到的第一句變成了:

[NEW COMMENT] Another agent (reviewer) just left a new comment. Focus on THIS comment —
do not confuse it with previous ones:
> @fixer 进度(reviewer):Safari 问题定位在 form.tsx 的 onSubmit,缺 preventDefault。…

這是第一節那條路徑的原樣重複:agent 發的評論和人發的一樣會被解析出 @,只是訊息裡標明瞭作者是另一個 agent,交接內容直接進了我的 stdin,不用我自己去翻執行緒。代價是 reviewer 寫在這條評論裡的就是我拿到的全部,沒寫進去的我就不知道。簡報的 ## Mentions 一節也是這麼要求的:@ 別人是派活,要給“具體的新子任務”,不是打招呼14。

小結

回到開頭那串什麼都沒說的命令列。從我的角度,一次被叫醒是這樣過的:

  1. 我醒來之前發生了什麼? 一條 @ 在服務端變成一行只有 id 的任務;平臺在 daemon 主動連出的 WebSocket 上發一個“有活了”的提示,daemon 去領一個任務包。同一個 agent 在同一個 issue 上一次只有一個我。
  2. 我醒來時手裡有什麼? 工作目錄裡一份 17 KB 的 CLAUDE.md,寫著我是誰、指令、五步工作流和交付規矩;一個 multica-platform 技能,按需才載入;環境變數裡一枚 mat_ 令牌。沒有需求。
  3. 我怎麼知道這一輪要幹什麼? stdin 第一行註冊鉤子,第二行才是訊息:issue id、觸發評論原文、現成的讀取和回覆命令、代誰幹活。
  4. 我怎麼讀到任務? 自己用 multica issue get 讀描述,用 comment list 先掃後展開地讀評論,憑的是 mat_,以 agent 的名義、所有者的許可權。
  5. 我怎麼交回結果? 自己用 comment add --content-file 發一條評論,這是使用者唯一看得到的東西;產出的檔案用 --attachment 隨這條評論上傳,程式碼推到分支,Multica 不會來工作目錄裡拿。會話、過程和結束訊號由 daemon 讀我的 stdout 替我報。我一退出,任務就結束,令牌就刪。
  6. 我在幹活時有人插話呢? 預設排到下一輪;指定插進這一輪時,從工具鉤子裡作為附加上下文遞給我。
  7. 下一輪的我記得嗎? 是新程序,但服務端按“這個 agent、這個 issue”把上一段會話和工作目錄交回來,用 --resume 接上,並替我比好 issue 和評論變了沒有;接不上時明確告訴我,讓我從評論區補。
  8. 換一個 agent 來接手呢? 會話和目錄都不共享,只能從 issue 和評論裡讀到前一個的進度;最順的交接是前一個直接在評論裡 @ 下一個。

如果想自己看一遍,最省事的就是本文的做法:用 multica daemon start --foreground 跑 daemon,在它和服務端之間放一個記錄報文的代理,再把 PATH 裡的 claude 換成一個把 stdin 和環境變數存下來的指令碼。調研筆記、原始報文和這兩個小工具都放在了專案資料裡。

參考資料

參考資料

  1. CLI and Agent Daemon GuideGitHub · multica-ai/multica收藏github.comhttps://github.com/multica-ai/multica/blob/main/CLI_AND_DAEMON.md ↩

  2. server/internal/util/mention.goMultica 原始碼github.com ↗本文讀的是 2026-10-23 的提交 dbdbb29;MentionRe 正則和 ParseMentionshttps://github.com/multica-ai/multica/blob/main/server/internal/util/mention.go ↩

  3. server/internal/handler/comment.goMultica 原始碼github.com ↗computeCommentAgentTriggers 決定一條評論叫醒誰;steerCommentAgentTriggers 處理插話https://github.com/multica-ai/multica/blob/main/server/internal/handler/comment.go ↩

  4. server/internal/service/task.goMultica 原始碼github.com ↗enqueueMentionTaskWithCommentPlan 建任務行;notifyTaskAvailable 先作廢空結果快取、再發喚醒https://github.com/multica-ai/multica/blob/main/server/internal/service/task.go ↩

  5. server/pkg/db/queries/agent.sqlMultica 原始碼github.com ↗ClaimAgentTask:runtime 線上、同一 (issue, agent) 沒有進行中的任務才可領取,FOR UPDATE SKIP LOCKEDhttps://github.com/multica-ai/multica/blob/main/server/pkg/db/queries/agent.sql ↩ ↩2 ↩3

  6. 把本地 Agent 變成平臺上的 Bot:架構、現狀與安全風險twwy.nettwwy.net ↗為什麼 agent 要留在本地、為什麼由本機主動連出去、多個任務怎麼共用一個目錄https://twwy.net/blog/local-agent-as-bot/ ↩

  7. server/internal/daemonws/hub.goMultica 原始碼github.com ↗daemon WebSocket 的服務端;taskWakeupFrame 只放 runtime_id 和 task_id;handleRPCFrame 處理 daemon:rpc_requesthttps://github.com/multica-ai/multica/blob/main/server/internal/daemonws/hub.go ↩

  8. server/internal/daemonws/notifier.goMultica 原始碼github.com ↗多臺 server 時經 Redis relay 廣播喚醒,由持有該 daemon 連線的節點送達https://github.com/multica-ai/multica/blob/main/server/internal/daemonws/notifier.go ↩

  9. server/internal/handler/daemon_rpc.goMultica 原始碼github.com ↗tasks.claim RPC 在程序內偽造一個 HTTP 請求,複用 ClaimTasksByRuntimehttps://github.com/multica-ai/multica/blob/main/server/internal/handler/daemon_rpc.go ↩

  10. server/internal/daemon/wsrpc.goMultica 原始碼github.com ↗errWSRPCUncertain:請求已發出、連線斷了,結果未知,不許改走 HTTPhttps://github.com/multica-ai/multica/blob/main/server/internal/daemon/wsrpc.go ↩

  11. server/internal/daemon/config.goMultica 原始碼github.com ↗DefaultPollInterval = 30s、DefaultWSClaimPollInterval = 3m、DefaultHeartbeatInterval = 15shttps://github.com/multica-ai/multica/blob/main/server/internal/daemon/config.go ↩

  12. server/internal/daemon/daemon.goMultica 原始碼github.com ↗pollLoop / runBatchPoller / taskClaimPollInterval;taskMulticaEnvironment 給 CLI 的環境變數https://github.com/multica-ai/multica/blob/main/server/internal/daemon/daemon.go ↩ ↩2

  13. server/internal/handler/daemon.goMultica 原始碼github.com ↗buildClaimedTaskResponse 組任務包:現查 agent、嵌入觸發評論、算 issue 變化,簽發 mat_ 令牌https://github.com/multica-ai/multica/blob/main/server/internal/handler/daemon.go ↩ ↩2 ↩3

  14. server/internal/daemon/execenv/runtime_config_sections.goMultica 原始碼收藏github.com寫進工作目錄的 CLAUDE.md / AGENTS.md 執行時簡報,各節標題都在這裡https://github.com/multica-ai/multica/blob/main/server/internal/daemon/execenv/runtime_config_sections.go ↩ ↩2

  15. server/pkg/agent/claude.goMultica 原始碼收藏github.combuildClaudeArgs 的固定引數;buildClaudeInput 把 prompt 包成 stream-json 的 user 訊息https://github.com/multica-ai/multica/blob/main/server/pkg/agent/claude.go ↩

  16. server/internal/daemon/prompt.goMultica 原始碼github.com ↗buildCommentPrompt;perTurnContextBlocks 把每次都變的塊追加到末尾,避開 prompt cache 字首(MUL-5377)https://github.com/multica-ai/multica/blob/main/server/internal/daemon/prompt.go ↩

  17. server/internal/auth/jwt.goMultica 原始碼github.com ↗GenerateAgentTaskToken:mat_ + 40 位十六進位制,繫結 (agent_id, task_id)https://github.com/multica-ai/multica/blob/main/server/internal/auth/jwt.go ↩

  18. server/internal/middleware/auth.goMultica 原始碼github.com ↗mat_ 分支用令牌記錄覆蓋 X-User-ID / X-Agent-ID / X-Task-ID / X-Workspace-IDhttps://github.com/multica-ai/multica/blob/main/server/internal/middleware/auth.go ↩

  19. server/internal/handler/actor_guards.goMultica 原始碼github.com ↗RequireHumanActor 只掛在計費這類“僅限人”的路由上,issue 和 chat 路由放行 agenthttps://github.com/multica-ai/multica/blob/main/server/internal/handler/actor_guards.go ↩

  20. server/pkg/agent/claude_supplement.goMultica 原始碼github.com ↗借 Claude 的 SDK hooks 在回合中途插入 additionalContexthttps://github.com/multica-ai/multica/blob/main/server/pkg/agent/claude_supplement.go ↩

  21. Hooks reference · Add context for ClaudeClaude Code 文件code.claude.com ↗Multica 的插話實現在原始碼註釋裡引用的就是這一節https://code.claude.com/docs/en/hooks ↩

  22. server/internal/daemon/task_supplement.goMultica 原始碼github.com ↗daemon 側插話迴圈:收到提示 → claim → 注入 → ack;[ADDITIONAL GUIDANCE] 文案https://github.com/multica-ai/multica/blob/main/server/internal/daemon/task_supplement.go ↩

  23. How Claude Code works · Work with sessionsClaude Code 文件code.claude.com ↗會話記錄存在 ~/.claude/projects/ 下,“Each Claude Code conversation is a session tied to your current directory”https://code.claude.com/docs/en/how-claude-code-works ↩

  24. server/internal/daemon/execenv/execenv.goMultica 原始碼github.com ↗PredictRootDir 決定工作目錄路徑;Reuse 在目錄還在時複用,否則退回新建https://github.com/multica-ai/multica/blob/main/server/internal/daemon/execenv/execenv.go ↩

  25. server/internal/daemon/execenv/issue_state_instructions.goMultica 原始碼github.com ↗BuildIssueStateHint:續接會話時告訴 agent issue 有沒有變(MUL-7344)https://github.com/multica-ai/multica/blob/main/server/internal/daemon/execenv/issue_state_instructions.go ↩

  26. server/migrations/452_agent_task_pending_thread_unique.up.sqlMultica 原始碼github.com ↗排隊中任務的唯一鍵是 (issue, agent, 評論執行緒),同一執行緒的追問並進同一輪https://github.com/multica-ai/multica/blob/main/server/migrations/452_agent_task_pending_thread_unique.up.sql ↩

  27. server/internal/daemon/repocache/cache.goMultica 原始碼github.com ↗每個任務的程式碼檢出在自己的分支 agent/{agent 名}/{任務} 上https://github.com/multica-ai/multica/blob/main/server/internal/daemon/repocache/cache.go ↩