第十五節 子任務與背景執行

復雜任務往往需要把工作拆給"子助手"。AVL Code 內置子任務調度能力,支持前景同步與背景並行兩種模式。

15.1 派發子任務

助手在對話中可以呼叫一個「子助手」工具,把一段提示詞派給獨立的子任務執行,子任務完成後把結果合並回主對話流。這一過程對你是透明的,但你可以在工具呼叫卡片裡看到每個子任務的詳情。

15.1.1 子 Agent 繼承外部 MCP 工具

派出去的子 Agent 自動繼承你在工作區配好的外部 MCP 工具(之前子任務用不到這些外接工具,得回到主會話才行):

  • 機制:派發時經 /admin/routes + /admin/backends 解析本工作區所有外部後端的工具前綴(如 notion / memory / channels 等命名空間),通過環境變數 ZAGENT_MCP_INHERIT_PREFIXES 注入;子 Agent 的 ZAGENT_ALLOWED_TOOLS 白名單無條件穿透這些前綴。
  • 覆蓋範圍:所有聲明了 tools: 的子代理(plan / assess / prepare / mcp-admin 與使用者自建);所有工作模式統一(含 plan)。
  • fail-safe:解析失敗 → 返空 → 不繼承,不影響子任務正常派發。

15.1.2 子任務模型 / 服務提供方匹配修正

之前在多服務提供方配置下,派出去的子任務沿用父會話的模型,但服務提供方恆取清單裡的第一個,兩者對不上時會報「模型不存在」起不來。本版改為服務提供方跟着繼承的模型走 — 優先選真正包含該模型的那個服務提供方,子任務能穩定啟動。

15.1.3 並行 fan-out:一次拆多路同時跑

除了一次派一個子任務,AI 還能用 AgentParallel 工具把一個任務一次拆成 2–16 條「腿」同時跑,每條腿是獨立的 fresh-context 子 Agent、互不可見,全部完成後自動匯總結果傳回。適合需要多路並行的活(如同時審好幾個模組、並行檢索多個來源),比一個個排隊串行快很多。

  • 每條腿各自帶提示詞、可單獨指定子代理類型與模型;提示詞需自包含(腿看不到主對話歷史)。
  • 並行上限 16 條(遠低於背景任務總並行 64,單次 fan-out 不會霸占全部槽位);不足 2 條請改用單個 Agent。
  • 整體等待超時預設 600 秒、上限 1800 秒;到時未完成的腿傳回 pending 並在背景繼續,可用 TaskWait / TaskOutput 續跟。
  • 單條腿失敗 / 被攔不會拖垮整批,失敗原因會一並隨結果傳回讓 AI 自行處置。每條腿輸出過長會截斷(提示用 TaskOutput 取全文)。
  • 執行期就能看到實時狀態:並行子任務的呼叫卡片在執行期間即出現(不必等終態才冒出來),並實時反映各子任務的執行狀態;停止後不再持續轉圈,極簡密度下結果塊也不會被誤折疊。

15.2 前景 vs 背景

  • 前景:主助手等待子任務完成(預設 10 分鐘超時),適合"必須等結果"的任務。
  • 背景:子任務非同步執行,主助手立刻得到一個任務編號,後續可主動拉取狀態或停止該任務。背景任務有總數上限(防失控)和自動回收(終態超時清理)。

15.2.1 「空閒制」看門狗 + 絕對兜底

子任務超時改成「按空閒計時」:只要還在持續產出就不算超時,看門狗只針對真的卡住、長時間無任何輸出的情形。再疊加一道絕對兜底(預設 60 分鐘),防止極端死循環 / hang 把任務永遠留在跑的狀態。

  • 空閒視窗:預設 15 分鐘無輸出 即超時(錯誤訊息會明確寫「子 Agent 空閒超時」)。
  • 絕對上限:預設 60 分鐘 強制結束(寫「絕對兜底」)。
  • 正常長任務不受影響:只要還在 stream 產出(包括思考段、增量字元)就一直續命。
  • RunAgentToolBackground 背景路徑不計執行超時(你顯式把它放背景跑就接受它跑久)。

15.3 並行上限

為防止一次發起過多背景任務造成失控,AVL Code 會限制同時在跑的子任務數;超出後助手會被告知"先稍等再發起",避免雪崩。

15.3.1 阻塞等待 — TaskWait 工具

主助手可以「一次呼叫、阻塞等待」背景任務 / 子代理跑完,不用再一輪一輪反復查詢進度:

  • 支持等「全部完成」或「任一完成」。
  • 可設超時(預設 5 分鐘、最長 30 分鐘);到點會如實傳回「還在跑」讓模型決定是否繼續等。
  • 等待期間只回狀態與最終結果,不把中間過程灌回主對話,省上下文也更省 token。

等待期間你能看見什麼:

  • 等待中的 TaskWait 呼叫行顯示一條總體倒數計時,按這次呼叫的超時預算往下走,"還要等多久"心裡有數;沒有背景任務在跑時不啟動倒數計時(不會空轉),倒數到 0 也不會停在 0ms 乾耗,而是回落成灰色的靜態預算值。
  • 等完之後條頭圖示換成打鉤的時鐘,一眼分清"還在等"和"等完了"。
  • 呼叫行支持緊湊模式,配合子任務狀態馬賽克(一格一個子任務、按狀態着色),一眼看出這批子任務跑到哪了。
  • 耗時顯示不再逐秒跳動導致整條行左右抖動(時長格式已收斂,分鐘檔秒數補零)。

結果裡的兩件事:

  • other_tasks:結果裡會帶上同會話裡沒被這次等待覆蓋的其它背景任務,避免"等了一批、忘了另一批"。工作區範圍內的全景同樣可見。
  • 終態可見「給主代理的最終回復」:被等待的任務跑完後,它給主代理的那段最終回復在介面裡直接可讀,不用去翻 JSONL。
  • 被 TaskStop 主動終止的任務不再顯示成"✓ 成功完成";被停止的任務、以及記錄已被清理的任務都如實呈現,不會再誤報"未命中,可能已被清理"或憑 JSONL 兜底謊報"已完成"。
  • 執行中的任務在 TaskWait / TaskOutput 的結果裡會帶上已執行階段長;任務被終止時當場結算耗時,不會記成 0。

15.4 終止

  • 在桌面端,點擊輸入框右側 停止 按鈕會同步終止主助手與所有派生子任務。
  • 在微信側,發送 /stop 同樣會觸發級聯終止。
  • 子任務也能單獨停止:子代理抽屜裡每個正在跑的子任務都有獨立的停止按鈕,不必整輪一起停 —— 背景任務、並行 fan-out 的腿、以及同步內聯的子代理現在都支持單停。停止時可選擇保留已產生的子任務記錄。
  • 只有你點停止才會級聯:重新發訊息、自動重新啟動對話等內部動作不再順手把已派出去的子代理一起殺掉。

15.4.1 真實總消耗看得清(含子代理)

用量統計會把子代理(並行 / 子任務)的真實消耗也算進來:目標條(GoalStrip)與每輪結束的識別都顯示真實總 token 消耗 + 工具呼叫次數,有子代理時還會拆出「自身 + 子代理」的構成。該統計按會話持久化,長會話看到的是全程真實總量(即使沒設目標也照常累計)。

15.4.2 子任務抽屜:看得見,也管得住

點子任務呼叫卡片上的「在右側開啟實時流」,右側抽屜會展開這個子代理的完整執行記錄 —— 它不只是"只讀記錄檔",你可以直接在裡面接管這次執行。

抽屜裡看到什麼:

  • 任務 prompt(含父會話給的「任務輸入」與「系統指令」)、逐條記錄、記錄條數。
  • 狀態:執行中 / 已暫停 / 完成 / 已結束 / 失敗 / 已終止 / 被拒絕。
  • 跑完後有「給主代理的最終回復」區塊,直接讀結論。
  • 頂部分頁列在多個子任務同時跑時可切換;裝不下的會提示「還有 N 個執行中的子任務未顯示」。
  • 「複製 JSONL 路徑」「複製 TaskWait 參數」兩個按鈕,便於自己接着用工具跟蹤(TaskOutput / TaskStop 用同一個 id)。

「已結束」和「完成」不是一回事:匯入的會話、或應用重載後回灌的記錄裡,如果一個子代理沒有留下明確的終態,現在顯示為「已結束」而不是籠統的「完成」—— 此前它會謊報"已完成",讓人以為活幹完了。真正被停掉 / 失敗的任務會按盤上記錄如實回灌為「已終止」/「失敗」。

執行控制(抽屜底部):

動作 說明
停止 終止這個子代理;可選擇保留已產生的子任務記錄
暫停 / 繼續 暫停後可隨時「繼續」,可在「補充指令」裡補一句再接着跑
重試 截斷到最後一條使用者訊息後重新執行
再跑一次 用相同的類型 + prompt 重新發起一次(prompt 缺失時該按鈕停用並說明原因)

按訊息粒度接管:抽屜裡的每條訊息左側有訊息柄(與主會話同一套慣例),點開選單可以:

  • 編輯訊息 / 刪除訊息(執行中拒絕編輯,避免改到一半被覆蓋)
  • 刪除某個工具呼叫(連帶刪掉配對的工具結果;匯入的舊記錄沒有 id 時會說明無法單獨刪除)
  • 從此處重試 —— 此訊息起的內容重新生成(使用者訊息保留提問重答,助手 / 工具訊息重做該步)
  • 從此處繼續 —— 保留到這條為止,其後內容丟棄,回到該時間點繼續執行,下方「補充指令」的內容會一並帶入
  • 複製 / 複製選中的部分
  • 選單標頭顯示這條訊息的時間(絕對 + 相對兩行)

危險動作都帶行內二次確認(「確認刪除?」「確認重試?」「確認繼續?」)。

編輯器掛在側邊,不擠訊息流:點「編輯訊息」彈出的是掛在清單外側的側掛編輯器(有拉線指回被編輯的那條),儲存走增量更新 —— 不會因為改一條訊息就讓整條訊息流跳位。被編輯的訊息滾出視野時編輯器會提示並給「回到訊息」;有未儲存修改時關閉會問你「放棄修改 / 繼續編輯」。

導航與右鍵:訊息柄旁有「跳轉到頂部 / 底部」快捷按鈕;「滾動到底部」會一路盯到真正見底(執行中持續釘底,不會滾一半停下)。訊息區支持右鍵選單,與訊息柄選單同一套動作。

15.4.3 子任務狀態條:一眼看全在跑的活

會話狀態條裡有一項「子任務」(形態見 §5.5.1),匯總當前執行中與已暫停的子任務:

  • 條上直接顯示「N 執行中 / N 已暫停」。
  • 點開逐條列出,點某條即在右側抽屜中開啟(暫停 / 繼續 / 重試等操作都在抽屜裡做)。
  • 條目過多時提示「+N 條未展示」,完整清單去左側「子會話」面板看。
  • 左側「本會話的子會話」清單與「全部子會話歷史」按最後活躍時間分組折疊,預設只展開最近一批,歷史再多也不會刷屏。

子代理不會被"順手"殺掉:重新發訊息、自動重新啟動對話等動作不再級聯終止已派出去的子代理 —— 只有你顯式點停止才會終止它們及其程序。

15.5 輸出聚合

背景任務的中間記錄檔可作為獨立卡片在主對話流裡展開檢視;任務完成後會以一段精簡摘要傳回,避免淹沒主線索。

15.5.1 AI 建議任務:一鍵拆分去做

AI 幹活時如果順手發現了分外但值得做的事(死程式碼、過期文件、確認存在的 TODO、安全隱患等),會在訊息流裡掛出一張「建議任務」卡片,而不是打斷當前正事。

卡片上的執行按鈕是個 split button:

  • 主按鈕一鍵:預設「啟用新會話執行」。
  • 展開 ▾ 還有 4 種方式:
    • 在新會話中執行(主按鈕預設)
    • 在當前會話中執行
    • 複製到輸入框
    • 複製到剪貼板

由你來決定怎麼處置。已執行的建議會標記狀態,AI 也可以撤回已經過時的建議。多條建議會聚合到輸入框上方的一個條裡統一管理。

15.6 例行程序:讓 AI 按計畫自動幹活

「例行程序(Routine)」讓你給某個工作區定一條指令,讓 AI 按時自動或按需手動執行 — 比如每天早上拉取並總結昨日 issue。

新增入口:

  • 工作區側邊欄「新增例行程序」一鍵開啟對話框(也支持從 Header 當前工作區進)。
  • 表單欄位:名稱、指令內容、執行模式、模型(與正常對話同款選擇器:模式色卡說明、模型搜尋分組)、計畫時刻。
  • 寫到一半自動暫存 — 中途跳去設定或 Agent 管理器調配置,回來自動重開並復原已填內容。

計畫方式:

類型 行為
每小時一次(hourly) + 指定分鐘 在每小時該分鐘點觸發一輪;復用 HH:MM 欄位但只看分鐘,小時鎖 00、觸發器顯示 :MM
每天 / 工作日 / 每週某幾天 + 指定時刻 到點自動觸發一輪
不設計畫(手動) 不自動跑;儲存後立即觸發一輪,按鈕顯示「執行一次」;之後需要時再點立即執行

時刻選擇順手:

  • 時 / 分雙列下拉(選中置中、選完分鐘自動收起);每小時一次模式下只露分鐘列
  • 週幾用圓形單字字塊,附「工作日 / 週末」一鍵預設
  • 調度精度:30 秒 tick;hourly 跨日 / DST 用時間算術天然正確

統一管理:

設定 → 例行程序 Tab 按來源工作區分組:

  • 啟停開關(與其它設定頁同款)
  • 立即執行
  • 檢視最近執行痕跡
  • 未開啟的工作區有徽標提示並可一鍵開啟
  • 側邊欄圖示直達本工作區的例行程序清單
  • 支持按名稱 / 工作區搜尋

執行行為:

  • 每次執行會在對應工作區生成正常會話,過程與結果都可回看。
  • 同一條例行程序執行中會自動跳過下一次觸發,不會疊跑。

15.6.1 會話內例行程序:讓 AI 在當前會話裡定時提醒自己

除了上面這種"每次開新會話獨立跑"的工作區例行程序,Agent 還可以在當前會話裡掛定時提示 —— 比如「每隔 10 分鐘看一眼構建是否跑完」「30 分鐘後檢查一下下載結果」。到點後,提示會送進設定它的那個會話:會話執行中就直接插入當前循環,空閒時按設定起一輪或跳過。兩套例行程序資料各自獨立,設定 → 例行程序 裡只顯示工作區例行程序。

怎麼建:直接跟 AI 說你要什麼節奏,由它呼叫 RoutineCreate 設定(介面上沒有新增按鈕);目標達成或不再需要時它會呼叫 RoutineCancel 自行取消,不必等跑滿次數。僅主會話可用(auto / execute 模式可新增;plan / prepare / assess 模式只能檢視與取消),子代理與終端機介面(TUI)不可用。

計畫方式(均按本機當地時間):

類型 含義 限制
N 分鐘後 一次 1–1440 分鐘
每隔 N 分鐘 重復 1–720 分鐘;預設有效期 8–24 小時、預設 30 次
定點 指定時間一次 須在將來且最遠 30 天
每小時 每小時某分鐘 預設有效期 72 小時
每日 / 工作日 / 每週 到點觸發 每週至少選一天;預設有效期 7 天(最長 30 天)

到點時怎麼送:會話在跑時,可選「優先」(當前步驟結束後插入,預設)或「立刻處理」(截停正在生成的回復後插入),語義與即時指導相同(見 §5.4.2);會話空閒時按「會話閒置時」設定「起一輪」或「略過」。

輸入框上方的「例行」條(形態見 §5.5.1)列出本會話的例行程序,每條顯示名稱、計畫、「下次 HH:MM · 已送達 n/m」與空閒時的處理方式:

  • 「暫停 / 恢復」「編輯」(就地改名稱、提示內容、計畫、空閒時與在跑時的處理、次數上限、有效期)「刪除」。
  • 下方「已結束」區列出結束原因(已完成、已達次數上限、已到期、模型已取消、你按了停止、錯過觸發時間等),點「知道了」收起;結束時也會彈提示。
  • 每次送達在訊息流裡顯示為緊湊的一行「⏰ 例行 · 名稱 · 3/30」,點開可看提示全文;它不是你的新訊息,也不代表新的授權,超出你原本請求的操作 AI 會先向你確認。

邊界與安全閥:

  • 點停止按鈕會一並取消本會話的全部例行程序(隨行通訊裡的 /stop 同效);刪除會話會連同例行程序一起清掉,歸檔會話則將其結束。
  • 每個會話最多 5 個、每個工作區最多 50 個未結束的例行程序;每會話 24 小時內最多新增 10 個;每個工作區每天最多送達 300 次。
  • 例行程序發起的回合連續失敗 3 次自動暫停(點「恢復」清零);會話在跑卻插不進去時會重試一段時間,仍不行則跳過這一次。
  • 重新啟動後保留(存在本機設定目錄 workspaces/<工作區 ID>/session-routines.yaml);只在工作區處於開啟狀態時調度,錯過的時間點不補發。
  • 由例行程序起的一輪,回復會轉發給綁定了該會話的隨行通訊對端(抬頭「⏰ 例行「名稱」」)。

15.7 長程任務當機後可復原

放到背景跑的長任務(即以 run_in_background 派發的子任務),其進度會實時落盤儲存;應用意外結束 / 被殺 / 當機 / 升級打斷後重新開啟,能把沒正常結束的任務找回來接着乾,不必從頭再來。

它怎麼工作:

  • 每個未結束的背景任務都以一份快照寫到本機(跨工作區的全域目錄);任務正常走到 done / error / killed 終態時會刪除該快照。所以重開後還留在盤上的,就是上次被中斷、沒跑完的任務。
  • 啟動時會非同步掃描這些殘留任務,先做一次「髒條目清理」——父會話已不存在、再也無法復原的條目自動刪掉;掃描不阻塞啟動,沒有殘留就靜默無感。
  • 復原並不是直接重新啟動子程序,而是往原來的父會話裡注入一句「之前委派的背景任務尚未完成,請繼續或重新委派」,讓主助手以「先派發」的方式接着完成——這一步對你可見,你能看到「正在復原任務 X」。

你會看到什麼(「中斷任務」狀態條):

  • 有被中斷的任務時,會話狀態條裡出現一項「中斷任務 · N 待恢復」(窄屏在輸入框上方、寬屏在訊息區右上,形態見 §5.5.1);沒有就不顯示。它不佔用頁面空間,任務再多也不會把訊息區往下頂。
  • 點開後浮窗按條列出,標題形如「N 個背景任務上次被中斷」;每條任務旁有兩個按鈕:復原(,注入續跑訊息並從盤上移除該條)/ 忽略(從盤上刪除,下次不再提示)。標題右側還有「全部忽略」。全部處理完後該項自動消失。
  • 復原成功後彈 toast「已復原:將在原會話繼續該任務」;失敗則提示具體原因(如「原會話已不存在,已清除該復原項」)。

自動復原(可選,預設關):

  • 在 設定 → 智慧體 → 自省 開啟「自動復原被中斷的任務」開關,重新啟動後會自動接着跑上次當機 / 被殺時未完成的背景任務,適合夜間無人值守的長任務。
  • 為防「續跑訊息風暴」,啟動自動復原每次最多 5 個,其餘仍留在「中斷任務」狀態條供手動復原。
  • 關閉該開關時(預設),所有被中斷的任務都只在「中斷任務」狀態條裡列出,由你手動決定復原或忽略。