跳到內容

自主目標迴圈(Goal Loop)

在對話裡丟一個目標,AI 員工就會自主規劃、執行、自我驗收,做到完成或卡住時回來通知你。這一頁說明從頻道使用 /goal 的方式、自主程度(AutonomyLevel)分級、相關設定鍵,以及卡住轉人工時的按鈕語意。

派工引擎自 v1.59 起預設開啟(沒有目標任務時只做週期性的 SQLite 輪詢,只有任務真正進入 review 才會花 LLM 呼叫,見下方「兩段式驗收裁決」),一問一答對話完全不受影響。不想要時在 config.toml[dispatch] enabled = false,或到儀表板「設定 → 自動化」關閉「派工引擎」開關(免重啟熱生效)。


在任何已接通的頻道(Telegram / Discord / Slack / LINE / …)對 AI 員工輸入:

指令 行為
/goal <目標描述> 建立一個自主目標任務,指派給當前對話的 AI 員工。沒有另外指定驗收標準時,以目標描述本身當作驗收基準。
/goal <目標> || <驗收標準> || 分隔:前半是目標,後半是驗收標準(判官核可的依據)。
/goal <目標> || <驗收標準> || outcome:<spec> 再加一段結構化產出驗收(見下方「結構化產出驗收」)。交付前先跑零成本的 deterministic 校驗,未達標直接退回修正,不燒判官。
/goal status 列出當前 AI 員工進行中的目標任務(短碼 / 狀態 / 第幾輪)。
/goal 顯示用法說明。

範例

/goal 整理這批客戶資料成月報並寄出 || 報表含每月營收圖表,寄到 boss@example.com
/goal 產出 Q3 月報 || 含每月營收圖表 || outcome:files:report.docx

建立後會回覆確認訊息,包含任務短碼、上限輪數,以及「完成或卡住會在這裡通知你」。任務進度與需人工的通知會推回你發起的這個對話(來源頻道),而不只是 AI 員工的 [proactive] 通知頻道。

若派工引擎被關閉([dispatch] enabled = false),任務仍會建立,但確認訊息會提醒你它不會自動開始執行。


目標契約:建立時凍結、事後不能悄悄改

Section titled “目標契約:建立時凍結、事後不能悄悄改”

建立目標的當下,驗收標準會被凍結成一份不可變的基準(acceptance_criteria_baseline)。之後所有裁決(第一階段評估器、MAV 驗收判官)一律讀這份凍結基準,不讀事後可能被改動的欄位。這是為了擋掉兩個方向的「悄悄改契約」:AI 員工不能在做的過程中把驗收標準改鬆,操作者也不會誤以為改了儀表板上的欄位就等於重新設定了裁決依據。

  • AI 員工不能改:agent 身分呼叫 MCP tasks_update 若帶了 acceptance_criteria 想改自己 goal 任務的驗收標準,會被整筆拒絕,並留一筆審計紀錄(原因 goal_contract_frozen)。
  • 操作者可以編輯顯示用的副本,但不會回頭改變裁決依據:儀表板 tasks.update 仍可以修改任務上顯示的 acceptance_criteria(例如補充說明給人看),但凍結的基準值不會跟著變。判官與評估器繼續照原本建立時的標準裁決。真的要換一組驗收標準,等於是換一個目標。
  • 沒有凍結基準的舊任務(凍結機制上線前建立的)退回讀可變欄位,行為與過去一致。

/goal <目標描述>(不帶 ||)仍會照常建立任務,目標描述本身當驗收基準,但確認訊息會多附一段提示,幫你想清楚下次要不要補得更精確:

💡 這次沒有另外指定驗收標準,之後想清楚這四件事會更好抓:
• 目標:要達成什麼
• 輸入:需要用到哪些資料或素材
• 輸出格式:成果長什麼樣子(例如 Word/Excel/一段文字/一張圖)
• 約束:風格、時限等限制,以及「怎樣才算完成」
建議補 3-5 條具體、看得出結果的驗收標準(例如「報表含每月營收圖表」而非「做得好」),
用 /goal 描述 || 標準 格式重新交付一次即可。

有明確帶 || 驗收標準時不會出現這段提示。開啟 planner_enabled 的子任務拆解也套用同一套紀律:每條驗收標準寫「結果」不寫「做法」(凍結 HOW 會讓走不同但同樣正確路徑的產出被誤判失敗)、精簡到 3-5 條就夠(契約愈肥,子任務愈難收斂)、範圍外的事項列為 Non-goals,不要硬塞進驗收項。


目標任務的每個狀態轉移都會推一則簡短(一到三行)的進度訊息回來源對話:

  • 開始執行 / 重試(第 N/上限 輪)
  • 驗收中
  • 未通過 → 修正後重試(附驗收判官回饋摘要)
  • 完成 ✅(附結果摘要)
  • 卡住 → 需要你決定(同時另外推送審批按鈕,附暫停原因分類,見下方「needs_human 按鈕語意」)

同一任務同一狀態不會重複推播。來源對話不存在時,退回 AI 員工的 [proactive] 頻道;兩者都沒有時只寫入儀表板 Activity Feed,不打擾你。

任務被認領(in_progress)後,如果超過 [goal_loop] progress_report_minutes(預設 10 分鐘)都沒有任何可觀察的進度訊號(以 Activity Feed 事件為準,updated_at 欄位會被 lease renewer 定期刷新,不能拿來當作「有在動」的證據),驅動器會推一則「已執行 X 分鐘未回報進度,仍在執行中」的通知(Activity Feed +來源對話),同一輪最多發一次。

純粹是通報,不是介入:不會重派工、不升級、不取消任務。真正會動手的仍然只有 stalled_secs(重派)、iteration_cap(轉人工)、wall_clock_hours(轉人工)這幾條既有護欄。設成 0(或任何負數)整個功能關閉,且關閉時不花任何額外查詢。


自主迴圈裡,AI 員工偶爾會卡在「對同一個工具、帶同一組參數,一次又一次呼叫」的迴圈。結果早就拿到了,卻沒有意識到自己在重複。這與下方「停滯偵測」不同:停滯偵測要連續兩整輪(派工→驗收)才能發現卡住,工具連擊 advisory 看的是單一輪內的工具呼叫序列,能在判官介入之前就先提醒。

判定依據是同工具、同一組遮罩後參數(沿用稽核紀錄本來就做過的機密遮罩)的連續呼叫次數,門檻與提醒逐級加重:

連續次數 提醒內容
3 次 建議先重讀上一次的執行結果,確認是否已取得所需資訊,避免重複呼叫浪費輪次
5 次 目前的做法可能沒有進展,建議換一個方法或角度切入
8 次 強烈建議停止重複嘗試:直接收斂目前已取得的結果回報,或改用 tasks_block 說明受阻原因並求助

提醒文字會注入下一輪派工的 <state> 區塊,零 LLM 成本、純 advisory:不會擋下、重試或否決任何一次工具呼叫或派工,是否要照做由 AI 員工自己判斷。提醒本身刻意排除在 state_hash 之外,不會干擾既有的(狀態,行動)震盪偵測。

設定 config.toml [goal_loop] tool_streak_advisory(預設 true)可整體關閉。


每個 AI 員工的自主程度由 agent.toml [capabilities] autonomy_level 一個刻度控制。未設定 / 無法解析 → 預設 Approver(保守:只有卡住或需人工才問你)。

級別 行為
operator 迴圈完全不自主驅動;任務建立後靜置,由人手動推進。
collaborator 第一次派工前需人工核准(kickoff 審批),核准後自主重試到完成。
consultant 同 collaborator 的 kickoff 審批。
approver 預設。無 kickoff 閘;卡住 / 需人工時才轉人工審批。
observer 全自動;需人工時只通知、不等待(任務自動結束)。
agent.toml
[capabilities]
autonomy_level = "approver"

階段性授權工具(scoped_tools,v1.41)

Section titled “階段性授權工具(scoped_tools,v1.41)”

高風險工具可以宣告為「持授權才可用」:列在 scoped_tools 的工具,AI 員工沒有拿到有效授權(grant)前一律拒絕,且授權只活在單一任務的生命週期內。任務結束(通過、駁回、轉人工、取消)時全部自動撤銷,不會殘留到下一件事。

agent.toml
[capabilities]
scoped_tools = ["shared_wiki_delete", "odoo_execute"] # 這些工具需逐任務授權
grant_ttl_secs = 3600 # 授權硬性存活上限(秒),預設 3600

取得授權的兩條路:

  1. AI 員工自行申請:呼叫 MCP 工具 capability_request { tool, reason, task_id? },會轉成一則審批(與其他審批走同一個通知/儀表板介面);你核准後授權生效,逾時未決視同拒絕。
  2. 目標任務開工時一併授予:goal 任務的 tags 加 grant:<工具名>,kickoff 審批(collaborator/consultant 級)通過時原子性授予,任務結束自動收回。

判定一律 fail-closed:授權資料庫讀不到就是沒有授權。未列入 scoped_tools 的工具完全不受影響。


[dispatch]
enabled = true # 啟用自主派工引擎(含 goal loop 驅動器)。預設 false
policy = "fixed_hierarchy" # 派工策略(選哪個 AI 員工接任務)。見下方「派工策略」。預設 fixed_hierarchy
grounding_precheck_enabled = true # 驗收前的證據落地預檢(見「證據落地預檢」)。預設 true
two_stage_judge = true # 驗收前先跑便宜的第一階段評估(見「兩段式驗收裁決」)。預設 true
judge = "mav" # 由誰做驗收裁決(見「換掉驗收判官」)。mav / evaluator_only / external / human_only。預設 mav
admission = "queue" # 子代理(ephemeral spawn)撞並發上限時的處置,"queue" 或 "fail"。預設 queue(見下方「ephemeral spawn 准入排隊」)
[task_forward_model] # 任務層前瞻模型(見同名章節)。預設整組關閉
enabled = false
[goal_loop]
iteration_cap = 5 # 困難目標的硬性派工上限,超過 → 轉人工。預設 5
iteration_cap_simple = 3 # 簡單目標的派工上限(動態判官深度)。預設 3
wall_clock_hours = 24 # 從建立起算的牆鐘預算(小時),超過 → 轉人工。預設 24
max_concurrent = 3 # 同時在飛的目標任務上限(防 spawn 風暴)。預設 3
tick_secs = 30 # 驅動器輪詢週期(秒)。預設 30
stalled_secs = 600 # 派工後未被認領視為停滯、可重派的秒數。預設 600
planner_enabled = false # 開啟後允許把目標拆成帶依賴的子任務 DAG(見「平行子任務」)。預設 false
resume_on_restart = "pause" # gateway 重啟時 in-flight 目標任務的處置,"auto" 或 "pause"(見「重啟行為」)。預設 pause,可在儀表板「設定 → 自動化」切換
progress_report_minutes = 10 # 已認領任務多久沒進度訊號才通報一次,`0` 關閉(見「逾時進度通報」)。預設 10
tool_streak_advisory = true # 同工具同參數連擊 3/5/8 次時是否注入提醒(見「工具連擊 advisory」)。預設 true
[dispatch_guard] # 回饋路徑斷路器(防再生型無限迴圈)
window_secs = 60 # 滑動窗長度(秒)。預設 60
max_in_window = 20 # 一個窗內允許的派工次數,超過即熔斷。預設 20
cooldown_secs = 60 # 熔斷後拒絕派工的冷卻秒數。預設 60
max_hop_depth = 5 # 委派鏈跨行程 re-spawn 的深度上限。預設 5

所有區塊都可省略;缺省 / 部分設定一律退回上表的內建預設。未知的 policy 值一律退回 fixed_hierarchy 並記一筆警告。


開啟 [goal_loop] planner_enabled = true 後,建立目標時會先讓 AI 員工「試著」把目標拆成一組帶依賴標注的子任務(例如:先各自查兩個資料源、再彙整)。拆出來的子任務會各自進 Task Board,depends_on 全部完成的子任務會並行開跑,各自獨立驗收。並行度仍受 max_concurrentdispatch_guard 斷路器約束,不會繞過。

  • 非強制:模型判斷不需要拆(或回覆無法解析)時,就退回單一任務,行為與關閉時完全一致。
  • 循環依賴防護:拆出來的計畫若含循環依賴(或索引越界),整份計畫作廢、退回單一任務並記警告,絕不落地一個壞掉的 DAG。
  • 上游卡住不孤兒化:某個子任務的上游依賴走到 failed / cancelled / needs_human(或依賴不存在),下游會繼承升級一起轉人工,讓你看到整條被卡住的分支;上游只是還在跑時,下游該輪凍結、下一輪再看。

預期效益以「多資料源查詢型」目標最大;獨立重測顯示加速約 1.25 倍(非論文自報的 3.7 倍),請以 eval 實測為準再推廣。


[dispatch] policy 決定「選哪個 AI 員工接一項目標任務」。預設 fixed_hierarchy 的行為與過去完全相同(派給任務原本指派的員工)。

策略 行為
fixed_hierarchy 預設。派給任務原本的 assigned_to,不改動。零 LLM 成本、完全確定性。
round_robin 依「任務類別」(有標籤取第一個標籤,否則取優先級)在員工名冊中輪詢分派。狀態僅存記憶體,重啟即從頭。
llm_select 由工具用 LLM 從名冊挑最合適的員工。失敗關閉:輸出不在名冊內、或解析/LLM 失敗,一律退回 fixed_hierarchy 的結果,絕不派給捏造的員工。不硬編碼任何模型名(走設定的工具用 runtime)。

名冊 = <home>/agents/ 下的員工目錄。名冊為空時,round_robin / llm_select 都退回原指派(不孤兒化)。改派會寫回任務的 assigned_to,讓 heartbeat 拉取與活動記錄一致。


子代理准入排隊(ephemeral spawn admission)

Section titled “子代理准入排隊(ephemeral spawn admission)”

目標拆解、委派等路徑有時會需要開一個短命的子代理(ephemeral spawn)。撞到並發上限(ephemeral_max_active,預設 32)時,config.toml [dispatch] admission 決定怎麼處理這次超限請求:

行為
queue 預設。有界 FIFO 排隊:請求不會憑空消失,等有空位釋放就依序執行。每張排隊券帶 TTL(queue_item_ttl_secs,預設 600 秒),逾期直接丟棄並記一筆稽核;佇列本身有深度上限(queue_max_depth,預設 64),滿了就明確拒絕(避免無界佇列本身變成新的失控風險)。發起請求的 turn/session 結束時,屬於它的排隊券會一併作廢,不會讓一個早就結束的流程突然冒出遲到的子代理。
fail 舊行為:超限直接拒絕,不排隊。

ephemeral_max_active 本身遵守「可調但不可為 0」:設成 0 會被鉗到 1 並記一筆警告,並發上限永遠不能被設定成完全關閉。

config.toml
[dispatch]
admission = "queue" # "queue"(預設)或 "fail"
queue_max_depth = 64 # 佇列深度上限,超過即拒絕
queue_item_ttl_secs = 600 # 排隊券存活秒數,逾期丟棄並稽核
ephemeral_max_active = 32 # 並發上限,0 會被鉗到 1

結構化產出驗收(outcome schema,WP2.4)

Section titled “結構化產出驗收(outcome schema,WP2.4)”

/goal … || outcome:<spec> 讓你在自由文字的驗收標準之外,再加一層機器可校驗的產出契約。當 AI 員工回報完成、任務進入 review 時,這層契約會在驗收判官(LLM)之前先跑一次 deterministic、零 LLM 成本的校驗:

  • 校驗不通過 → 任務直接退回 revising,回饋訊息帶具體缺陷(缺哪個欄位、少哪個檔),完全不呼叫判官。這是擋判官假陽性的防線:結構上明顯不合格的產出,不會被過度寬鬆的判官放行,也不浪費一次判官 LLM call。
  • 校驗通過 → 才進判官,且判官 prompt 會附上「結構化產出驗收已通過 deterministic 校驗」的註記,讓判官專注在品質面向。

三種 spec 型別:

spec 意義
outcome:text 預設。無結構化契約,行為與未加 outcome 時完全一致(不持久化、判官前不跑任何校驗)。
outcome:json:<JSON Schema> JSON Schema 子集(object / array / string / number / integer / boolean,支援 properties / required / items)。校驗 AI 員工最終回覆中的 ```json 區塊(找不到 fenced 區塊則退而解析整段回覆)。缺欄位 / 型別不符都會列成具體缺陷。
outcome:files:<glob,glob> 斷言 AI 員工工作目錄下有符合每個 glob 的產出檔(支援 *?)。例:outcome:files:report.docx, out/*.pdf

範例

/goal 匯出這季營收數字 || outcome:json:{"type":"object","required":["revenue","month"],"properties":{"revenue":{"type":"number"},"month":{"type":"string"}}}
/goal 產出季報並存檔 || 需含營收圖表 || outcome:files:report.docx,charts/*.png

界線與 fail-closed 行為:

  • 路徑穿越拒絕files: 的 glob 若是絕對路徑、家目錄(~)、或含 .. 上層目錄,一律在 /goal 建立時就拒絕(fail-closed,任務不建立),校驗時再擋一次。工作目錄基準是 <home>/agents/<agent>/
  • 畸形 spec 拒絕json: 不是合法 JSON 物件、files: 空清單、未知型別前綴 → /goal 直接回錯誤、不建立任務(不會靜默降級成 text)。
  • 持久化:spec 以單一 outcome:<base64url> 標籤存在任務既有的 tags 欄位(base64url 不含逗號,不與標籤分隔衝突),不改資料庫 schematext 不持久化。
  • 與 planner 的關係:設了 outcome spec 時會略過 planner_enabled 的子任務拆分,結構化契約針對單一最終交付物,不套用到每個被拆出的子任務。
  • 判官仍是後盾:標籤若毀損無法解碼,deterministic 校驗跳過、直接交給判官(判官照樣把關),不會因為觀測性缺口而卡住任務。

證據落地預檢(grounding precheck,v1.53)

Section titled “證據落地預檢(grounding precheck,v1.53)”

AI 員工回報完成、任務進入驗收時,在呼叫驗收判官之前會先跑一道零 LLM 的 證據預檢:把最終回覆與這次任務實際的工具執行紀錄(稽核日誌裡的工具結果) 比對。回覆若宣稱查到了什麼、做了什麼,卻找不到任何一段與真實工具結果重疊 的內容,就直接退回修正,不燒判官。兩個防偽細節:

  • 自我回音不算證據:像 tasks_complete 這類會把 AI 員工自己寫的摘要 原樣回傳的工具,列在排除名單上,不能拿自己的話證明自己。
  • 自己餵進去的不算:AI 員工放進工具呼叫參數裡的文字會從證據中扣除, 只有工具真正回傳的內容才算數。

config.toml [dispatch] grounding_precheck_enabled = false 可關閉。沒有 任何工具紀錄可比對時(例如純對話型任務),預檢會跳過(Skip),不誤傷。


任務層前瞻模型(task forward model,v1.53,預設關閉)

Section titled “任務層前瞻模型(task forward model,v1.53,預設關閉)”

開啟後,goal loop 在每次派工前會依過往同類任務的統計先「預測」這次執行 大概會如何(會不會失敗、大概動用哪些工具類別),執行結束後把預測與實際 觀察比對並記錄成轉移,讓系統對「做這類事會發生什麼」累積出任務層的世界 模型。所有 runtime(claude / codex / gemini / openai-compat)通用:

  • 預測分層退化:有同類統計用統計、沒有就用整體邊際、再沒有用先驗 預設值,冷啟動不花任何 LLM 費用。
  • 觀察誠實分級:每筆觀察都標記證據保真度(原生工具事件 / 只有稽核 日誌 / 無證據),不會把「沒看到」當成「沒發生」。
  • <state> 狀態區塊:任務進行中,提示裡會注入一個結構化的目前狀態 區塊,AI 員工可透過回覆中的狀態更新標籤修訂它;搭配(狀態,行動)訪問 圖,同一狀態重複做同一動作兩次以上會提早轉人工(震盪偵測)。
  • 預示警告:預測顯示這次派工大機率失敗時,派工提示會附上警告脈絡, 但不直接擋下(預測是輔助,不是閘門)。
  • 任務規則歸納:同型轉移重複出現時,以確定性模板歸納成任務規則注入 後續提示(上限 2 條),與其他學習規則共用同一套 helpful/harmful 生命週期,表現不好會自動退休。
config.toml
[task_forward_model]
enabled = false # 預設關閉;開啟後整套 predict-act-verify 生效

AI 員工回報完成、任務進入 review 之後,不是每次都直接燒一次完整的 MAV 判官團。先跑一個便宜很多的第一階段評估:無工具、單次 LLM 呼叫,只輸出三選一的 JSON 判定:

判定 意思 後續動作
continue 這輪還沒做完,但方向對 跳過 MAV,直接拿評估器給的下一步當回饋重新派工(計入迭代上限)
blocked 卡在外部阻礙(缺權限、缺資料、等第三方……) 直接轉 needs_human,不必假裝走完一輪判官
candidate_complete 看起來是完成候選 才進現行的 MAV 三面向判官團仔細核

評估器出任何狀況(逾時、解析失敗、呼叫本身出錯)一律降級直接跑 MAV 判官:絕不會因為第一階段故障就自動判過或自動判失敗,安全性與沒有這層時完全一樣。設定 config.toml [dispatch] two_stage_judge(預設 true);設成 false 退回單段式、每輪都跑完整判官團的舊行為。

「這件工作算不算完成」是整個平台唯一的放行權力點。預設由平台內建的 MAV 三面向判官團裁決,也可以換成別的實作,用 config.toml [dispatch] judge 指定:

誰來裁決 適用情境
mav(預設) 第一階段評估器 → MAV 三面向判官團 一般情況
evaluator_only 只跑第一階段評估器,candidate_complete 直接判過 省成本。驗收強度明顯較弱:只有一次無工具的便宜呼叫在把關,沒有判官團複核,通過的回饋會自我標註為低成本模式
external 你自己的程式(judge_command 想接自家 CI、規則引擎、或第二個模型當判官
human_only 沒有機器裁決,每個 review 任務都轉 needs_human 高風險部署,要求每次交付都經人眼

寫錯值不會靜默生效:gateway 會警告並回退 mav(四個選項裡驗收最嚴的一個)。這個設定每次裁決時重讀,跟 two_stage_judge 一樣改完即生效,不必重啟。

[dispatch]
judge = "external"
judge_command = ["/usr/local/bin/my-judge", "--strict"]
judge_timeout_secs = 120 # 預設 120

平台會執行這支指令,把一份 JSON 從 stdin 餵進去:

{
"schema": "duduclaw.judge.v1",
"task": "任務描述(已含 <tool_activity> / <risk_boundary> 等區塊)",
"acceptance_criteria": "凍結的驗收標準基準",
"result": "AI 員工這輪的提交內容",
"tool_activity": "工具稽核摘要"
}

指令要在 stdout 印出一個 JSON 物件當裁決:

{"pass": true, "feedback": "驗收條件逐項核對通過"}

passtrue/false,也收 "pass"/"fail" 字串;feedback 可省略。

三件事值得先知道:

  1. 外部判官出任何狀況都退回 MAV 判官團,包含逾時、非零離開碼、stdout 不是合法 JSON、judge_command 沒設好。降級的方向永遠是變嚴,不會變成放行,每次降級都會寫進 security_audit.jsonljudge_seam_degraded)。
  2. 它的輸出被當成不受信資料feedback 會進到下一輪派工的 prompt,所以會先過注入掃描並截斷;掃描擋下來時整份裁決作廢,改由 MAV 判官團決定。回饋文字前面會標上來源,你在任務時間軸上看得出哪一段話是外部判官說的。
  3. judge_command 只能改檔案,不能從儀表板改。它指定一支可執行檔,所以 system.update_config RPC 只收 judge(四個列舉值),不收 judge_commandjudge_timeout_secs;AI 員工本身也寫不了 ~/.duduclaw/config.toml

想拿 duduclaw eval 當判官的話,直接把 judge_command 指向包一層 duduclaw eval 的腳本即可,不需要另一個模式。

子行程會繼承 gateway 的完整環境變數。 judge_command 是用平台的 process spawn 直接執行(tokio::process::Command),沒有做 env_clear() 或任何白名單過濾。你指定的判官程式看得到 gateway 行程當下的整組環境變數,這包含 gateway 用來呼叫 LLM 供應商、通道 API 的那些密鑰。這不代表資料被主動傳給判官(判官吃的輸入只有上面那份 stdin JSON);判官程式本身有能力讀取這些環境變數(例如惡意或寫壞的程式去讀 std::env::vars())。這不是漏洞,是這個 seam 目前的設計取捨:只指向你自己信任、來源清楚的程式,不要指向第三方或未經審查的執行檔;需要更嚴格隔離(例如判官行程完全看不到 gateway 密鑰)的話,把 judge_command 包成一支先自行清空環境變數、再重新注入判官實際需要的少數變數的 wrapper 腳本。

MAV 判官與第一階段評估器的 prompt 都內建幾條紀律,治的是「判官自己製造假駁回,讓正確的工作卡死」這個活測抓到過的失敗模式:

  • 反棘輪:驗收標準沒變時,不能每一輪都挑一個新毛病出來,這是讓目標永遠無法完成的典型失敗模式。
  • 只稽核、不自建證據:判官只能比對 AI 員工提交的證據與工具稽核摘要,不能自己想像、補寫證據,也不能拿「我覺得更好的做法」當標準。
  • 反契約外擴張:驗收標準沒寫的事項不能被拿來當駁回理由。這是最常見的假駁回,也是明明做對、做在範圍內的工作卡住不前的頭號原因。
  • agent 自稱完成不是證據:「已完成」「已處理好」這類自述本身不構成通過理由,判官必須逐項比對驗收標準與實際產出。

這幾條紀律沒有 config 開關,即刻套用到所有 goal 任務。

驗收判官的檢核面向數量會隨目標難度縮放,省下不必要的判官 LLM 成本:

  • 簡單目標(短、單步、無多步/研究/比較/部署/遷移等關鍵詞):判官只查兩個面向 correctness + safety,派工上限用 iteration_cap_simple(預設 3)。
  • 困難目標:完整三面向 MAV panel correctness + completeness + safety,派工上限用 iteration_cap(預設 5)。

safety 面向在任何深度都保留(失敗關閉精神):降深度只裁掉 completeness 的細緻度,安全檢核永不裁撤。難度由本地零 LLM 啟發式(長度 + CJK-aware token 估算 + 關鍵詞)判定,判官深度與派工上限用的是同一套判定,兩者一致。

[capabilities]
autonomy_level = "approver"
irreversible_tools = ["send_email"] # 一律需人工核准的不可逆工具
maybe_irreversible_tools = ["Bash", "http_post"] # 由 judge 判定是否需升人

判斷「連續兩輪卡在同一個地方」不再只看駁回回饋是不是逐字相同。判官每次遣詞用字不一定一樣:「缺少 goal_loop.rs:120 的錯誤處理」跟「你忘記在 goal_loop.rs 第 120 行做驗證」講的是同一個 gap,但字串比對會判成兩件不同的事,讓真正卡住的訊號被措辭差異吃掉。

現在改抽取駁回回饋裡的 path:line 引用與反引號包住的關鍵詞(函式名、變數名、錯誤代號),正規化後(暫存/scratch 路徑歸一成同一個佔位符、忽略大小寫、去重排序)組成一組指紋,同一個 gap 換句話說也會得到同一個指紋。完全抽不到任何引用或關鍵詞時(例如純敘述性的回饋),退回原本的逐字比對,行為相容。連續兩輪同指紋才觸發下面的 needs_human,門檻本身沒有變。

AI 員工在自主迴圈裡有時會用「聽起來像收尾、但其實沒有被判官驗證過」的話結束這一輪,例如「我先做到這裡好了」「請稍後再來查看結果」「已提交待審」「VERDICT: PASS」(自己簽的,不是判官簽的)之類。這些話本身不代表工作有錯,只是一種「流程上」的警訊,值得被記錄下來、也值得下一輪多留意一下。

九條 zh+en 正則只比對 agent 這一輪回覆的最後一段非空文字,命中任何一條會:

  • 記一筆 Activity Feed 事件(goal_loop.premature_stop_suspected
  • 累加 Prometheus 計數器 goal_loop_bail_pattern_total{pattern="<命中的樣式名>"}
  • 把提示帶進下一輪派工的 <state> 區塊、第一階段評估器的輸入、MAV 判官的輸入:一句中性提醒(「疑似提前收工,請確認任務是否真的完成」),不會替評估器或判官預先下判斷

這層偵測本身不會駁回、卡住或轉人工任何任務,純粹是訊號與提醒,實際要不要判過還是看評估器/判官對證據的判斷。

gateway 重啟或崩潰復原後,還在跑的目標任務預設會轉成 needs_humanresume_on_restart = "pause"預設值):gateway 每次開機時,會把所有非終態的 goal_mode 任務(todo/pending/revising/in_progress/review/blocked)轉成 needs_human(原因 gateway_restart),走既有的通道通知,等你按下「重試」才會繼續。一次非預期的行程重啟或部署,不會悄悄接著跑一個沒人重新確認過安全的目標。

想要接續原本更寬鬆的行為,把 [goal_loop] resume_on_restart 設成 "auto":還在跑的目標任務會直接接續執行,就像行程從未中斷過一樣(這是這項設定出現前的唯一行為)。

不論哪個方向,這個檢查只在 gateway 開機時跑一次,設定熱重載(system.update_config)不會觸發它,變更只在下一次 gateway 真正重啟時生效。

儀表板切換:設定 → 自動化(Automation)分頁的「gateway 重啟後的進行中目標任務」下拉選單可直接切換,不必手動編輯 config.tomlsystem.update_config 只接受 "auto"/"pause" 兩個值,其餘一律拒絕。


任務轉「需人工」時(達派工上限 / 牆鐘超時 / 連續兩輪駁回且 gap 指紋相同 / 驗收判官在重試預算耗盡時仍不通過 / 上游依賴子任務卡住而繼承升級 / resume_on_restart = "pause" 時 gateway 重啟),會推送四顆按鈕到 AI 員工的控制頻道。

「需人工」不再是單一個桶子。除了既有的自由文字 judge_feedback(判官或評估器的完整回饋,可能好幾句話),每次轉人工的當下,同時會蓋上一個六選一的封閉分類,這是給你一眼分辨「這是什麼類型的卡住」用的,judge_feedback 才是逐句細節:

分類 token UI 文案
no_progress 卡住沒進展
budget_exhausted 次數或時限用盡
blocked_needs_decision 等你決策
infra 系統問題
restart 系統重啟後暫停
unknown 需要人工確認

分類在觸發現場靜態標記(每個轉人工的路徑各自標記自己的類別),絕不從 judge_feedback 的 LLM 敘述反解。模型自己的措辭不可靠、也不該被拿來當路由依據。未分類、遇到辨識不出的舊值,或這個欄位上線前就存在的舊任務,一律讀成 unknown(「需要人工確認」):一個分不清類型的卡住,寧可讓你多看一眼,不能被誤判成一個看似明確、其實是猜的分類。

顯示位置:/goals 看板卡片與任務詳情頁的分類 chip、通道 needs_human 審批訊息裡的「類型」一行(Observer 全自動模式的純通知也有)。任務被人工決定(重試 / 標記完成 / 放棄)後,分類欄會清空,不會殘留到下一次卡住。

按鈕 動作
重試 任務回到待重試(pending),下一輪驅動器再派工。
標記完成 直接標記完成(done)。
放棄 取消任務(cancelled)。
交給我 你接手處理,任務標記由你認領(claimed_by);狀態仍留在 needs_human,所以驅動器本就不會再自動派工(候選查詢只看 todo/pending/revising)。這是目前的實作範圍:停止自動重試+標記+收斂卡片。完整把對話控制權轉給你(讓後續訊息不再進 AI 判斷)是下一階段的功能,尚未實作。

一則訊息的主要動作上限 3 顆,四顆超過此限,因此「放棄」與「交給我」在有次要層級的通道上收進次級:Telegram 是第二排按鈕、Discord 是第二排按鈕、Slack 是原生的 overflow 選單;LINE 沒有對應的次要選單機制,這兩個動作不會出現在 LINE 的快速回覆按鈕上,改在訊息裡以文字說明並附儀表板連結。

按鈕決策是冪等且失敗關閉的:重試/標記完成/放棄只會從 needs_human 狀態轉出,重複按或狀態已變一律無效(no-op);「交給我」則沒有終態可比對,重複按(甚至換一位有權限的人按)就是重新蓋章認領,不會報錯。collaborator / consultant 的 kickoff 審批同理,逾時未決=拒絕(fail-closed)。

自 v1.53 起,轉人工的審批會附上一段模擬預覽(simulate-before-act):若你選擇讓任務繼續,接下來三步大概會發生什麼。模擬產生有 15 秒上限,逾時就不附模擬、照常送出審批(不會因此卡住);模擬引用的知識庫內容限唯讀 namespace,且模擬敘述本身不能決定某個動作是否可逆(不能自證安全)。儀表板審批卡片會渲染這段預覽。

模擬預覽講的是「如果放行,接下來可能發生什麼」;「變更」分頁講的是已經發生了什麼。儀表板的收件匣決策卡與任務詳情頁都多了一個「變更」分頁,列出這個任務歷輪實際動過的檔案:

欄位 內容
路徑 被寫入/修改/刪除的檔案路徑;指令 類型顯示的是那道 shell 指令本身,不會假裝知道它碰了哪些檔案。可一鍵複製。
操作 新建/覆寫、修改、刪除、指令四種。
狀態 失敗或被攔截的呼叫也會列出並標記「未成功」,這正是即時查詢工具狀態看不到的那一半。
摘要片段 寫入內容或指令說明的片段,直接沿用稽核紀錄的遮罩結果(不會為了顯示而重讀原始檔案)。
來源 執行期工具事件(Write / Edit / NotebookEdit / Bash……)或 MCP 稽核紀錄(shared_wiki_write 等)。

證據來自兩條既有軌跡:執行期的原生工具事件在每一輪派工後落成檔案變更紀錄(以任務 id 歸屬),MCP 稽核紀錄則沿用判官 <tool_activity> 同一套「認領→驗收時間窗+執行者」歸屬。查無就是查無:沒有紀錄時分頁直接顯示「此任務沒有留下檔案變更紀錄」,不會用敘述硬湊。

目前顯示的是「動過哪些檔案、動作是什麼」,還不是逐行的 before/after diff。真 diff 需要在寫入前留快照,屬後續項。


「想一想」計畫模式(plan-first,I-1c)

Section titled “「想一想」計畫模式(plan-first,I-1c)”

儀表板的交辦面板除了「問一問」「交辦」,還有第三種模式「想一想」:先讓 AI 員工擬一份執行計畫給你看,你核准後才會真的開始做。

選「想一想」送出時,前端一樣呼叫 tasks.goal_create,只是多帶一個 plan_first: true。後端在建立任務的當下(同步、非派工迴圈的一輪)呼叫工具用 LLM,依目標描述與驗收標準產出一份 3-8 條、純文字的執行計畫(不是 JSON,是給人看的敘述)。任務直接誕生在 needs_human(沿用既有的 blocked_needs_decision「等你決策」分類,沒有新增分類),核准前不會進入派工迴圈的候選查詢,所以連一輪都不會跑。

計畫文字同時寫進兩個地方:

  • judge_feedback(既有欄位):讓「等你決定」卡片、任務詳情頁、通道審批訊息不必改任何顯示邏輯就看得到計畫內容。
  • plan_pending(新欄位,I-1c 專用):與 judge_feedback 分開存放,是因為核准動作本身(任何 needs_human 任務都通用的「重試」按鈕)會用你的核准備註覆寫 judge_feedback;若計畫也存在同一欄位,核准那一刻就會被自己的核准動作蓋掉,永遠傳不到第一輪派工。

核准就是既有的「重試」按鈕,沒有新增按鈕種類。核准後任務轉回 pending,驅動器下一輪 tick 派工時,會把 plan_pending 的內容包成 <execution_plan> 區塊注入這一輪的工作提示,讓 AI 員工照計畫開始執行;注入後立刻清空 plan_pending,所以計畫只會被貼一次,不會每輪重複出現在提示裡。

計畫是指引,不是免驗收的保證。執行完成後仍要走與其他目標任務完全相同的兩段式驗收裁決/MAV 判官團,計畫寫的步驟被判官認定不夠、做錯,一樣會被打回重做。

呼叫工具用 LLM 產計畫的過程本身可能失敗(逾時、傳輸錯誤、或回覆整段空白)。這種情況任務照樣 fail-closed 停在 needs_human,但分類改成 infra(系統問題)而非 blocked_needs_decision,且不會帶 plan_pending。核准動作在這種任務上永遠只是把任務放行到「沒有計畫可注入」的第一輪,不會有計畫憑空消失或被悄悄跳過的情況。


驅動器(而非模型)掌握硬邊界,卡住的目標不可能無限迴圈:完成訊號只認驗收判官核可(不信任 AI 自評「做完了」);派工上限、牆鐘上限、並行上限、進度震盪偵測、回饋路徑斷路器各自獨立生效,任何一條踩線即轉人工或熔斷。