ACP 執行緒綁定代理
概覽
本計畫定義 OpenClaw 如何在支援執行緒的頻道(優先 Discord)中支援 ACP 程式碼代理,具備正式環境等級的生命週期與復原能力。
相關文件:
目標使用者體驗:
- 使用者建立或聚焦一個 ACP session 到執行緒中
- 該執行緒中的使用者訊息路由至已綁定的 ACP session
- 代理輸出以相同的執行緒身分串流回覆
- session 可設為持久或一次性,並提供明確的清理控制
決策摘要
長期建議採用混合架構:
- OpenClaw 核心掌管 ACP 控制平面的關注點
- session 身分與中繼資料
- 執行緒綁定與路由決策
- 投遞不變量與重複抑制
- 生命週期清理與復原語義
- ACP 執行時後端可抽換
- 第一個後端為 acpx 支援的外掛服務
- 執行時負責 ACP 傳輸、排隊、取消、重連
OpenClaw 不應在核心中重新實作 ACP 傳輸內部機制。 OpenClaw 不應僅依賴純外掛攔截路徑進行路由。
終極架構(聖杯)
將 ACP 視為 OpenClaw 的一級控制平面,搭配可抽換的執行時轉接器。
不可妥協的不變量:
- 每個 ACP 執行緒綁定都參照有效的 ACP session 記錄
- 每個 ACP session 都有明確的生命週期狀態(
creating、idle、running、cancelling、closed、error) - 每次 ACP 執行都有明確的執行狀態(
queued、running、completed、failed、cancelled) - spawn、bind 和初始入隊為原子操作
- 指令重試為冪等的(不產生重複的執行或 Discord 輸出)
- 綁定執行緒的頻道輸出是 ACP 執行事件的投影,絕非即興的副作用
長期擁有權模型:
AcpSessionManager是唯一的 ACP 寫入者與協調者- manager 初期位於 gateway 行程中;之後可移至獨立的 sidecar,使用相同介面
- 對每個 ACP session key,manager 擁有一個記憶體中的 actor(序列化的指令執行)
- 轉接器(
acpx、未來的後端)僅作為傳輸/執行時實作
長期持久化模型:
- 將 ACP 控制平面狀態移至 OpenClaw 狀態目錄下的專屬 SQLite 儲存區(WAL 模式)
- 遷移期間保留
SessionEntry.acp作為相容投影,非事實來源 - ACP 事件以僅附加方式儲存,支援重播、崩潰復原與確定性投遞
投遞策略(通往聖杯的橋樑)
- 短期橋接
- 保留現有的執行緒綁定機制與既有 ACP 設定介面
- 修復中繼資料缺口的 bug,透過單一核心 ACP 分支路由 ACP 回合
- 立即加入冪等鍵與預設拒絕的路由檢查
- 長期轉換
- 將 ACP 事實來源移至控制平面 DB + actors
- 使綁定執行緒的投遞成為純粹的事件投影
- 移除依賴投機式 session-entry 中繼資料的舊有回退行為
為何不採用純外掛模式
目前的外掛掛鉤不足以在不修改核心的情況下完成端對端 ACP session 路由。
- 從執行緒綁定的入站路由在核心分派中先解析為 session key
- 訊息掛鉤是發射後不管的,無法短路主回覆路徑
- 外掛指令適合控制操作,但無法取代核心的逐回合分派流程
結論:
- ACP 執行時可以外掛化
- ACP 路由分支必須存在於核心
可重用的既有基礎
已實作且應維持為正規的部分:
- 執行緒綁定目標支援
subagent和acp - 入站執行緒路由覆寫在正常分派前透過綁定解析
- 透過 webhook 在回覆投遞中實現出站執行緒身分
/focus和/unfocus流程支援 ACP 目標- 持久綁定儲存區,啟動時可恢復
- 在歸檔、刪除、unfocus、重置、刪除時的解綁生命週期
本計畫擴展而非取代該基礎。
架構
邊界模型
核心(必須在 OpenClaw 核心中):
- 回覆管線中的 ACP session 模式分派分支
- 投遞仲裁以避免父頻道加執行緒的重複
- ACP 控制平面持久化(遷移期間搭配
SessionEntry.acp相容投影) - 與 session 重置/刪除相關的生命週期解綁和執行時分離語義
外掛後端(acpx 實作):
- ACP 執行時 worker 監督
- acpx 行程呼叫與事件解析
- ACP 指令處理器(
/acp ...)與操作者 UX - 後端專屬的設定預設值與診斷
執行時擁有權模型
- 一個 gateway 行程擁有 ACP 協調狀態
- ACP 執行在 acpx 後端透過被監督的子行程運行
- 行程策略為每個活躍 ACP session key 維持長期存活,非逐訊息
這避免了每次提示的啟動成本,並使取消和重連語義更可靠。
核心執行時合約
新增核心 ACP 執行時合約,使路由程式碼不依賴 CLI 細節,並可在不改變分派邏輯的情況下切換後端:
export type AcpRuntimePromptMode = "prompt" | "steer";
export type AcpRuntimeHandle = {
sessionKey: string;
backend: string;
runtimeSessionName: string;
};
export type AcpRuntimeEvent =
| { type: "text_delta"; stream: "output" | "thought"; text: string }
| { type: "tool_call"; name: string; argumentsText: string }
| { type: "done"; usage?: Record<string, number> }
| { type: "error"; code: string; message: string; retryable?: boolean };
export interface AcpRuntime {
ensureSession(input: {
sessionKey: string;
agent: string;
mode: "persistent" | "oneshot";
cwd?: string;
env?: Record<string, string>;
idempotencyKey: string;
}): Promise<AcpRuntimeHandle>;
submit(input: {
handle: AcpRuntimeHandle;
text: string;
mode: AcpRuntimePromptMode;
idempotencyKey: string;
}): Promise<{ runtimeRunId: string }>;
stream(input: {
handle: AcpRuntimeHandle;
runtimeRunId: string;
onEvent: (event: AcpRuntimeEvent) => Promise<void> | void;
signal?: AbortSignal;
}): Promise<void>;
cancel(input: {
handle: AcpRuntimeHandle;
runtimeRunId?: string;
reason?: string;
idempotencyKey: string;
}): Promise<void>;
close(input: { handle: AcpRuntimeHandle; reason: string; idempotencyKey: string }): Promise<void>;
health?(): Promise<{ ok: boolean; details?: string }>;
}
實作細節:
- 第一個後端:以外掛服務形式發布的
AcpxRuntime - 核心透過登錄檔解析執行時,並在沒有可用 ACP 執行時後端時回報明確的操作者錯誤
控制平面資料模型與持久化
長期事實來源是專屬的 ACP SQLite 資料庫(WAL 模式),用於交易式更新與崩潰安全復原:
acp_sessionssession_key(pk)、backend、agent、mode、cwd、state、created_at、updated_at、last_error
acp_runsrun_id(pk)、session_key(fk)、state、requester_message_id、idempotency_key、started_at、ended_at、error_code、error_message
acp_bindingsbinding_key(pk)、thread_id、channel_id、account_id、session_key(fk)、expires_at、bound_at
acp_eventsevent_id(pk)、run_id(fk)、seq、kind、payload_json、created_at
acp_delivery_checkpointrun_id(pk/fk)、last_event_seq、last_discord_message_id、updated_at
acp_idempotencyscope、idempotency_key、result_json、created_at,唯一約束(scope, idempotency_key)
export type AcpSessionMeta = {
backend: string;
agent: string;
runtimeSessionName: string;
mode: "persistent" | "oneshot";
cwd?: string;
state: "idle" | "running" | "error";
lastActivityAt: number;
lastError?: string;
};
儲存規則:
- 遷移期間保留
SessionEntry.acp作為相容投影 - 行程 ID 和 socket 僅存於記憶體
- 持久的生命週期和執行狀態存放在 ACP DB 中,非一般 session JSON
- 若執行時擁有者異常終止,gateway 從 ACP DB 重新載入並從檢查點恢復
路由與投遞
入站:
- 保持現有的執行緒綁定查詢作為第一個路由步驟
- 若綁定目標為 ACP session,路由至 ACP 執行時分支而非
getReplyFromConfig - 明確的
/acp steer指令使用mode: "steer"
出站:
- ACP 事件串流正規化為 OpenClaw 回覆區塊
- 投遞目標透過既有的綁定目的地路徑解析
- 當該 session 回合有活躍的綁定執行緒時,抑制父頻道的完成訊息
串流策略:
- 以合併視窗串流部分輸出
- 可設定的最小間隔和最大區塊位元組以符合 Discord 速率限制
- 完成或失敗時一律送出最終訊息
狀態機與交易邊界
Session 狀態機:
creating -> idle -> running -> idlerunning -> cancelling -> idle | erroridle -> closederror -> idle | closed
執行狀態機:
queued -> running -> completedrunning -> failed | cancelledqueued -> cancelled
必要的交易邊界:
- spawn 交易
- 建立 ACP session 列
- 建立/更新 ACP 執行緒綁定列
- 入隊初始執行列
- close 交易
- 標記 session 為 closed
- 刪除/過期綁定列
- 寫入最終 close 事件
- cancel 交易
- 使用冪等鍵標記目標執行為 cancelling/cancelled
不允許在這些邊界中出現部分成功。
逐 session actor 模型
AcpSessionManager 為每個 ACP session key 運行一個 actor:
- actor 信箱序列化
submit、cancel、close和stream副作用 - actor 擁有該 session 的執行時 handle 載入和執行時轉接器行程生命週期
- actor 在任何 Discord 投遞前按序(
seq)寫入執行事件 - actor 在成功的出站傳送後更新投遞檢查點
這消除了跨回合的競爭條件,防止重複或亂序的執行緒輸出。
冪等性與投遞投影
所有外部 ACP 操作必須攜帶冪等鍵:
- spawn 冪等鍵
- prompt/steer 冪等鍵
- cancel 冪等鍵
- close 冪等鍵
投遞規則:
- Discord 訊息從
acp_events加上acp_delivery_checkpoint衍生 - 重試從檢查點恢復,不重新傳送已投遞的區塊
- 最終回覆的送出由投影邏輯保證每次執行恰好一次
復原與自我修復
Gateway 啟動時:
- 載入非終態的 ACP session(
creating、idle、running、cancelling、error) - 在首次入站事件時惰性重建 actor,或在設定上限內積極重建
- 調和任何遺漏心跳的
running執行,標記為failed或透過轉接器復原
入站 Discord 執行緒訊息時:
- 若綁定存在但 ACP session 遺失,以明確的過時綁定訊息預設拒絕
- 可選的在操作者安全驗證後自動解綁過時綁定
- 絕不靜默將過時 ACP 綁定路由至一般 LLM 路徑
生命週期與安全
支援的操作:
- 取消當前執行:
/acp cancel - 解綁執行緒:
/unfocus - 關閉 ACP session:
/acp close - 依有效 TTL 自動關閉閒置 session
TTL 策略:
- 有效 TTL 取以下的最小值
- 全域/session TTL
- Discord 執行緒綁定 TTL
- ACP 執行時擁有者 TTL
安全控制:
- 依名稱白名單 ACP 代理
- 限制 ACP session 的工作區根目錄
- 環境變數白名單傳遞
- 每帳號及全域的最大並行 ACP session 數
- 執行時崩潰的有界重啟退避
設定介面
核心鍵:
acp.enabledacp.dispatch.enabled(獨立的 ACP 路由開關)acp.backend(預設acpx)acp.defaultAgentacp.allowedAgents[]acp.maxConcurrentSessionsacp.stream.coalesceIdleMsacp.stream.maxChunkCharsacp.runtime.ttlMinutesacp.controlPlane.store(預設sqlite)acp.controlPlane.storePathacp.controlPlane.recovery.eagerActorsacp.controlPlane.recovery.reconcileRunningAfterMsacp.controlPlane.checkpoint.flushEveryEventsacp.controlPlane.checkpoint.flushEveryMsacp.idempotency.ttlHourschannels.discord.threadBindings.spawnAcpSessions
外掛/後端鍵(acpx 外掛區段):
- 後端指令/路徑覆寫
- 後端環境變數白名單
- 後端逐代理預設
- 後端啟動/停止逾時
- 後端每 session 最大進行中執行數
實作規格
控制平面模組(新增)
在核心中新增專屬的 ACP 控制平面模組:
src/acp/control-plane/manager.ts- 擁有 ACP actor、生命週期轉換、指令序列化
src/acp/control-plane/store.ts- SQLite schema 管理、交易、查詢輔助
src/acp/control-plane/events.ts- 帶型別的 ACP 事件定義與序列化
src/acp/control-plane/checkpoint.ts- 持久投遞檢查點與重播游標
src/acp/control-plane/idempotency.ts- 冪等鍵預留與回應重播
src/acp/control-plane/recovery.ts- 啟動時的調和與 actor 重載計畫
相容橋接模組:
src/acp/runtime/session-meta.ts- 暫時保留用於向
SessionEntry.acp投影 - 遷移轉換後必須停止作為事實來源
- 暫時保留用於向
必須強制的不變量(必須在程式碼中強制)
- ACP session 建立與執行緒綁定為原子操作(單一交易)
- 每個 ACP session actor 同時最多一個活躍執行
- 事件
seq在每次執行中嚴格遞增 - 投遞檢查點永不超過最後提交的事件
- 冪等重播對重複的指令鍵回傳先前的成功 payload
- 過時/遺失的 ACP 中繼資料無法路由至一般非 ACP 回覆路徑
核心接觸點
需修改的核心檔案:
src/auto-reply/reply/dispatch-from-config.ts- ACP 分支呼叫
AcpSessionManager.submit和事件投影投遞 - 移除繞過控制平面不變量的直接 ACP 回退
- ACP 分支呼叫
src/auto-reply/reply/inbound-context.ts(或最近的正規化上下文邊界)- 為 ACP 控制平面公開正規化的路由鍵和冪等種子
src/config/sessions/types.ts- 保留
SessionEntry.acp為僅投影的相容欄位
- 保留
src/gateway/server-methods/sessions.ts- 重置/刪除/歸檔必須呼叫 ACP manager 的 close/unbind 交易路徑
src/infra/outbound/bound-delivery-router.ts- 對 ACP 綁定 session 回合強制預設拒絕的目的地行為
src/discord/monitor/thread-bindings.ts- 新增連接到控制平面查詢的 ACP 過時綁定驗證輔助函式
src/auto-reply/reply/commands-acp.ts- 透過 ACP manager API 路由 spawn/cancel/close/steer
src/agents/acp-spawn.ts- 停止即興的中繼資料寫入;呼叫 ACP manager 的 spawn 交易
src/plugin-sdk/**及外掛執行時橋接- 乾淨地公開 ACP 後端登錄與健康語義
明確不替換的核心檔案:
src/discord/monitor/message-handler.preflight.ts- 保持執行緒綁定覆寫行為作為正規的 session-key 解析器
ACP 執行時登錄檔 API
新增核心登錄檔模組:
src/acp/runtime/registry.ts
必要 API:
export type AcpRuntimeBackend = {
id: string;
runtime: AcpRuntime;
healthy?: () => boolean;
};
export function registerAcpRuntimeBackend(backend: AcpRuntimeBackend): void;
export function unregisterAcpRuntimeBackend(id: string): void;
export function getAcpRuntimeBackend(id?: string): AcpRuntimeBackend | null;
export function requireAcpRuntimeBackend(id?: string): AcpRuntimeBackend;
行為:
requireAcpRuntimeBackend在後端不可用時拋出帶型別的 ACP 後端遺失錯誤- 外掛服務在
start時登錄後端,在stop時取消登錄 - 執行時查詢為唯讀且行程內部的
acpx 執行時外掛合約(實作細節)
對於第一個正式後端(extensions/acpx),OpenClaw 和 acpx 透過嚴格的指令合約連接:
- 後端 id:
acpx - 外掛服務 id:
acpx-runtime - 執行時 handle 編碼:
runtimeSessionName = acpx:v1:<base64url(json)> - 編碼的 payload 欄位:
name(acpx 具名 session;使用 OpenClaw 的sessionKey)agent(acpx 代理指令)cwd(session 工作區根目錄)mode(persistent | oneshot)
指令對應:
- 確保 session:
acpx --format json --json-strict --cwd <cwd> <agent> sessions ensure --name <name>
- prompt 回合:
acpx --format json --json-strict --cwd <cwd> <agent> prompt --session <name> --file -
- 取消:
acpx --format json --json-strict --cwd <cwd> <agent> cancel --session <name>
- 關閉:
acpx --format json --json-strict --cwd <cwd> <agent> sessions close <name>
串流:
- OpenClaw 從
acpx --format json --json-strict消費 ndjson 事件 text=>text_delta/outputthought=>text_delta/thoughttool_call=>tool_calldone=>doneerror=>error
Session schema 修補
在 src/config/sessions/types.ts 中修補 SessionEntry:
type SessionAcpMeta = {
backend: string;
agent: string;
runtimeSessionName: string;
mode: "persistent" | "oneshot";
cwd?: string;
state: "idle" | "running" | "error";
lastActivityAt: number;
lastError?: string;
};
持久化欄位:
SessionEntry.acp?: SessionAcpMeta
遷移規則:
- 階段 A:雙寫(
acp投影 + ACP SQLite 事實來源) - 階段 B:主要從 ACP SQLite 讀取,回退讀取舊有
SessionEntry.acp - 階段 C:遷移指令從有效的舊有項目回填遺漏的 ACP 列
- 階段 D:移除回退讀取,僅保留投影作為 UX 用途
- 舊有欄位(
cliSessionIds、claudeCliSessionId)保持不變
錯誤合約
新增穩定的 ACP 錯誤碼與面向使用者的訊息:
ACP_BACKEND_MISSING- 訊息:
ACP runtime backend is not configured. Install and enable the acpx runtime plugin.
- 訊息:
ACP_BACKEND_UNAVAILABLE- 訊息:
ACP runtime backend is currently unavailable. Try again in a moment.
- 訊息:
ACP_SESSION_INIT_FAILED- 訊息:
Could not initialize ACP session runtime.
- 訊息:
ACP_TURN_FAILED- 訊息:
ACP turn failed before completion.
- 訊息:
規則:
- 在執行緒中回傳可操作的使用者安全訊息
- 詳細的後端/系統錯誤僅記錄於執行時日誌
- 當明確選擇了 ACP 路由時,絕不靜默回退至一般 LLM 路徑
重複投遞仲裁
ACP 綁定回合的單一路由規則:
- 若目標 ACP session 和請求者上下文存在活躍的執行緒綁定,僅投遞至該綁定執行緒
- 同一回合不同時傳送至父頻道
- 若綁定目的地選擇模糊,以明確錯誤預設拒絕(不隱式回退至父頻道)
- 若不存在活躍綁定,使用正常的 session 目的地行為
可觀測性與營運就緒
必要指標:
- 依後端和錯誤碼分類的 ACP spawn 成功/失敗次數
- ACP 執行延遲百分位數(排隊等待、執行時回合時間、投遞投影時間)
- ACP actor 重啟次數與重啟原因
- 過時綁定偵測次數
- 冪等重播命中率
- Discord 投遞重試與速率限制計數器
必要日誌:
- 以
sessionKey、runId、backend、threadId、idempotencyKey為鍵的結構化日誌 - session 和執行狀態機的明確狀態轉換日誌
- 帶脫敏安全參數和退出摘要的轉接器指令日誌
必要診斷:
/acp sessions包含狀態、活躍執行、最後錯誤和綁定狀態/acp doctor(或等效指令)驗證後端登錄、儲存區健康和過時綁定
設定優先順序與有效值
ACP 啟用優先順序:
- 帳號覆寫:
channels.discord.accounts.<id>.threadBindings.spawnAcpSessions - 頻道覆寫:
channels.discord.threadBindings.spawnAcpSessions - 全域 ACP 開關:
acp.enabled - 分派開關:
acp.dispatch.enabled - 後端可用性:
acp.backend的已登錄後端
自動啟用行為:
- 當 ACP 已設定(
acp.enabled=true、acp.dispatch.enabled=true或acp.backend=acpx)時,外掛自動啟用將plugins.entries.acpx.enabled=true標記為啟用,除非被拒絕清單或明確停用
TTL 有效值:
min(session ttl, discord thread binding ttl, acp runtime ttl)
測試對照
單元測試:
src/acp/runtime/registry.test.ts(新增)src/auto-reply/reply/dispatch-from-config.acp.test.ts(新增)src/infra/outbound/bound-delivery-router.test.ts(擴展 ACP 預設拒絕案例)src/config/sessions/types.test.ts或最近的 session-store 測試(ACP 中繼資料持久化)
整合測試:
src/discord/monitor/reply-delivery.test.ts(綁定 ACP 投遞目標行為)src/discord/monitor/message-handler.preflight*.test.ts(綁定 ACP session-key 路由延續性)- acpx 外掛執行時測試在後端套件中(服務登錄/啟動/停止 + 事件正規化)
Gateway 端對端測試:
src/gateway/server.sessions.gateway-server-sessions-a.e2e.test.ts(擴展 ACP 重置/刪除生命週期覆蓋)- ACP 執行緒回合來回端對端測試:spawn、訊息、串流、取消、unfocus、重啟復原
上線防護
新增獨立的 ACP 分派開關:
acp.dispatch.enabled初次發布時預設為false- 停用時:
- ACP spawn/focus 控制指令仍可綁定 session
- ACP 分派路徑不會啟動
- 使用者收到明確訊息說明 ACP 分派已被策略停用
- 經金絲雀驗證後,可在後續版本中將預設翻轉為
true
指令與 UX 計畫
新指令
/acp spawn <agent-id> [--mode persistent|oneshot] [--thread auto|here|off]/acp cancel [session]/acp steer <instruction>/acp close [session]/acp sessions
既有指令相容性
/focus <sessionKey>繼續支援 ACP 目標/unfocus保持現行語義/session idle和/session max-age取代舊有的 TTL 覆寫
分階段上線
第 0 階段:ADR 與 schema 凍結
- 發布 ACP 控制平面擁有權與轉接器邊界的 ADR
- 凍結 DB schema(
acp_sessions、acp_runs、acp_bindings、acp_events、acp_delivery_checkpoint、acp_idempotency) - 定義穩定的 ACP 錯誤碼、事件合約和狀態轉換防護
第 1 階段:核心控制平面基礎
- 實作
AcpSessionManager和逐 session 的 actor 執行時 - 實作 ACP SQLite 儲存區和交易輔助
- 實作冪等儲存區和重播輔助
- 實作事件附加 + 投遞檢查點模組
- 將 spawn/cancel/close API 連接至 manager,提供交易保證
第 2 階段:核心路由與生命週期整合
- 從分派管線將執行緒綁定的 ACP 回合路由至 ACP manager
- 當 ACP 綁定/session 不變量失敗時強制預設拒絕路由
- 將重置/刪除/歸檔/unfocus 生命週期與 ACP close/unbind 交易整合
- 新增過時綁定偵測和選用的自動解綁策略
第 3 階段:acpx 後端轉接器/外掛
- 依執行時合約實作
acpx轉接器(ensureSession、submit、stream、cancel、close) - 新增後端健康檢查和啟動/拆卸登錄
- 將 acpx ndjson 事件正規化為 ACP 執行時事件
- 強制後端逾時、行程監督和重啟/退避策略
第 4 階段:投遞投影與頻道 UX(優先 Discord)
- 實作帶有檢查點恢復的事件驅動頻道投影(優先 Discord)
- 以速率限制感知的刷新策略合併串流區塊
- 保證每次執行恰好一次的最終完成訊息
- 發布
/acp spawn、/acp cancel、/acp steer、/acp close、/acp sessions
第 5 階段:遷移與轉換
- 引入向
SessionEntry.acp投影加 ACP SQLite 事實來源的雙寫 - 新增舊有 ACP 中繼資料列的遷移工具
- 將讀取路徑翻轉為 ACP SQLite 優先
- 移除依賴遺漏
SessionEntry.acp的舊有回退路由
第 6 階段:穩固化、SLO 與規模限制
- 強制並行限制(全域/帳號/session)、佇列策略和逾時預算
- 新增完整的遙測、儀表板和告警閾值
- 混沌測試崩潰復原和重複投遞抑制
- 發布後端故障、DB 損壞和過時綁定修復的運維手冊
完整實作檢查清單
- 核心控制平面模組和測試
- DB 遷移和回滾計畫
- ACP manager API 跨分派和指令的整合
- 外掛執行時橋接中的轉接器登錄介面
- acpx 轉接器實作和測試
- 支援執行緒的頻道投遞投影邏輯搭配檢查點重播(優先 Discord)
- 重置/刪除/歸檔/unfocus 的生命週期掛鉤
- 過時綁定偵測器和面向操作者的診斷
- 所有新 ACP 鍵的設定驗證和優先順序測試
- 營運文件和故障排除手冊
測試計畫
單元測試:
- ACP DB 交易邊界(spawn/bind/入隊原子性、cancel、close)
- session 和執行的 ACP 狀態機轉換防護
- 所有 ACP 指令的冪等預留/重播語義
- 逐 session actor 的序列化和佇列排序
- acpx 事件解析器和區塊合併器
- 執行時監督器重啟和退避策略
- 設定優先順序和有效 TTL 計算
- 核心 ACP 路由分支選擇和後端/session 無效時的預設拒絕行為
整合測試:
- 假 ACP 轉接器行程用於確定性的串流和取消行為
- ACP manager + 分派整合搭配交易式持久化
- 執行緒綁定入站路由至 ACP session key
- 執行緒綁定出站投遞抑制父頻道重複
- 檢查點重播在投遞失敗後恢復並從最後事件繼續
- 外掛服務登錄和 ACP 執行時後端的拆卸
Gateway 端對端測試:
- 建立帶執行緒的 ACP,進行多回合 prompt,unfocus
- 使用持久化 ACP DB 和綁定的 gateway 重啟,然後繼續同一 session
- 在多個執行緒中的並行 ACP session 無串擾
- 重複指令重試(相同冪等鍵)不產生重複的執行或回覆
- 過時綁定場景產生明確錯誤和選用的自動清理行為
風險與緩解
- 轉換期間的重複投遞
- 緩解:單一目的地解析器和冪等事件檢查點
- 負載下的執行時行程頻繁變動
- 緩解:每個 session 長期存活的擁有者 + 並行上限 + 退避
- 外掛缺失或設定錯誤
- 緩解:明確的操作者錯誤和預設拒絕的 ACP 路由(不隱式回退至一般 session 路徑)
- subagent 和 ACP 開關之間的設定混淆
- 緩解:明確的 ACP 鍵和包含有效策略來源的指令回饋
- 控制平面儲存區損壞或遷移 bug
- 緩解:WAL 模式、備份/還原掛鉤、遷移冒煙測試和唯讀回退診斷
- Actor 死鎖或信箱飢餓
- 緩解:看門狗計時器、actor 健康探測和帶拒絕遙測的有界信箱深度
驗收檢查清單
- ACP session spawn 可在支援的頻道轉接器中建立或綁定執行緒(目前為 Discord)
- 所有執行緒訊息僅路由至已綁定的 ACP session
- ACP 輸出以串流或批次方式出現在相同的執行緒身分中
- 綁定回合不在父頻道產生重複輸出
- spawn+bind+初始入隊在持久儲存中為原子操作
- ACP 指令重試為冪等的,不重複產生執行或輸出
- cancel、close、unfocus、archive、reset 和 delete 執行確定性的清理
- 崩潰重啟保留對應關係並恢復多回合的延續性
- 並行的執行緒綁定 ACP session 獨立運作
- ACP 後端遺失狀態產生清晰可操作的錯誤
- 過時綁定被偵測並明確呈現(搭配選用的安全自動清理)
- 控制平面指標和診斷可供操作者使用
- 新的單元、整合和端對端覆蓋通過
附錄:當前實作的針對性重構(狀態)
以下是非阻塞的後續工作,用於在目前功能集落地後保持 ACP 路徑的可維護性。
1) 集中 ACP 分派策略評估(已完成)
- 透過
src/acp/policy.ts中的共用 ACP 策略輔助實作 - 分派、ACP 指令生命週期處理器和 ACP spawn 路徑現在使用共用策略邏輯
2) 依子指令領域分割 ACP 指令處理器(已完成)
src/auto-reply/reply/commands-acp.ts現在是精簡的路由器- 子指令行為分割為:
src/auto-reply/reply/commands-acp/lifecycle.tssrc/auto-reply/reply/commands-acp/runtime-options.tssrc/auto-reply/reply/commands-acp/diagnostics.ts- 共用輔助在
src/auto-reply/reply/commands-acp/shared.ts
3) 依職責分割 ACP session manager(已完成)
- manager 分割為:
src/acp/control-plane/manager.ts(公開外觀 + 單例)src/acp/control-plane/manager.core.ts(manager 實作)src/acp/control-plane/manager.types.ts(manager 型別/依賴)src/acp/control-plane/manager.utils.ts(正規化 + 輔助函式)
4) 選用的 acpx 執行時轉接器清理
extensions/acpx/src/runtime.ts可分割為:- 行程執行/監督
- ndjson 事件解析/正規化
- 執行時 API 介面(
submit、cancel、close等) - 提升可測試性並讓後端行為更容易稽核