計與驗證方法)
oh-my-openagentsenpi-task 中 task_send 的 always-steer 契約設(shè)計與驗證方法【免費下載鏈接】oh-my-openagentOmO: Just type mass ulw keyword with your prompt. Now you are the master of graph engineering.項目地址: https://gitcode.com/gh_mirrors/oh/oh-my-openagent本文基于倉庫內(nèi)的驗證證據(jù)文檔 .omo/evidence/20260727-senpi-task-always-steer/README.md 展開。它記錄了 oh-my-openagent 的senpi-task包一次公共 API 行為變更task_send工具從可選投遞模式收斂為無條件 steer轉(zhuǎn)向注入契約。讀完本文你將理解這次變更動機(jī)的契約層含義、deliver_as參數(shù)被移除后的源碼級實現(xiàn)路徑以及如何用 RED/GREEN 焦點測試加真實任務(wù)表面real task surface兩階段場景對一項工具行為變更做可復(fù)現(xiàn)的驗證。1. 變更范圍為什么這是一次 HEAVY 級改動證據(jù)文檔的 Scope 一節(jié)給出了改動的定級與上下文Tier: HEAVY——定級理由寫得很明確public task_send schema and task-session message delivery semantics change即公共的task_send參數(shù) schema 與任務(wù)會話的消息投遞語義同時發(fā)生了變化。對 Agent 工具而言schema 是模型可見的公共 API語義變更直接影響模型側(cè)的調(diào)用行為因此歸為最高驗證強(qiáng)度檔。分支fix/senpi-task-always-steer基線origin/dev的f2ae25041。文檔隨后列出了四條成功判據(jù)Success criteria這也是整個驗證方案的驗收標(biāo)準(zhǔn)task_send不再暴露deliver_as屬性且普通的子任務(wù)消息無條件請求 steer運行中子任務(wù)running-child與已完成的駐留子任務(wù)finished-resident兩種行為能通過真實任務(wù)工具表面而非 mock工作且不依賴任何投遞選項焦點測試、senpi 兼容性、typecheck、build 與改動文件的診斷全部干凈評審者批準(zhǔn)并按倉庫要求的 merge commit 方式合并 PR。后兩條屬于工程流程要求前兩條才是技術(shù)核心它們把schema 收斂與語義統(tǒng)一拆成了兩個獨立可驗證的斷言——參數(shù)消失是靜態(tài)契約無條件 steer 是動態(tài)行為。證據(jù)文檔把兩類斷言分別映射到了不同的驗證場景見第 4、5 節(jié)這個拆分方式本身對做工具 API 收斂的團(tuán)隊有參考價值。2. 背景senpi-task 的任務(wù)工具面在深入變更之前需要先理解task_send所處的位置。senpi-task包是omo-senpi的任務(wù)引擎一個持久化任務(wù)狀態(tài)機(jī) 記錄存儲 兩種子進(jìn)程 runner 轉(zhuǎn)向steering引擎 工具面。包的完整解剖可見 packages/senpi-task/AGENTS.md其工具面由 4 個任務(wù)工具構(gòu)成工具工廠函數(shù)位置taskcreateTaskTooltools/task/tool.tstask_sendcreateTaskSendTooltools/control/send.tstask_cancelcreateTaskCancelTooltools/control/cancel.tstask_outputcreateTaskOutputTooltools/output/output.ts分工規(guī)則是task只負(fù)責(zé)派生轉(zhuǎn)向、駐留會話復(fù)活、團(tuán)隊消息與 shutdown 審批統(tǒng)一走task_send子任務(wù)輸出讀取走task_output。task_send的變更之所以被定為 HEAVY正是因為它同時承載轉(zhuǎn)向消息投遞這一核心語義。3. 核心變更deliver_as從公共 schema 中消失3.1 公共參數(shù) schema投遞選項不復(fù)存在變更后的task_send公共參數(shù)定義在 send-schema.tsTypeBox schemaexport const TaskSendParams Type.Object({ to: Recipient, message: Type.Optional(Type.Union([PlainMessage, StructuredMessage])), team_run_id: Type.Optional(Type.String({ description: Team run id for lead-to-member messages or shutdown messages. })), summary: Summary, all_scope: Type.Optional( Type.Boolean({ description: Allow messaging a child owned by another session. Off by default. }), ), })對照證據(jù)文檔的成功判據(jù) 1——task_sendexposes nodeliver_asproperty——當(dāng)前 schema 中只有to、message、team_run_id、summary、all_scope五個屬性deliver_as確實不存在。其中message是普通字符串 | 結(jié)構(gòu)化 shutdown 對象的聯(lián)合類型shutdown_request/shutdown_response結(jié)構(gòu)化消息走的是完全不同的 shutdown 路由與投遞模式無關(guān)。3.2 運行時行為普通消息硬編碼為 steerschema 去掉參數(shù)只是模型看不見真正的語義變更發(fā)生在執(zhí)行路徑。send.ts 中runTaskSend處理普通字符串消息時向 steering 引擎的調(diào)用把投遞模式寫死為steerif (typeof params.message string) { const outcome await manager.sendToTask({ idOrName: params.to, message: params.message, deliverAs: steer, ...(callerSessionId ! undefined ? { callerSessionId } : {}), ...(params.all_scope true ? { allScope: true } : {}), }) ... }也就是說工具層永遠(yuǎn)以 steer 身份向引擎發(fā)消息調(diào)用方模型沒有任何選擇空間。同時工具描述send.ts 的DESCRIPTION常量也同步改口Plain-text messages always steer a running child immediately.并聲明了復(fù)活語義——發(fā)往已停泊的 in-process 或 detached RPC 子任務(wù)帶轉(zhuǎn)錄和已記錄的啟動規(guī)格的普通文本消息會在符合條件的 finished 運行之后恢復(fù)該會話而 killed/cancelled/lost 的子任務(wù)永不復(fù)活。3.3 內(nèi)部引擎仍保留兩種投遞模式——收斂的邊界一個值得注意的細(xì)節(jié)deliverAs并沒有從系統(tǒng)內(nèi)部被刪除它只是從公共面退到了內(nèi)部面。steering 引擎的類型定義steering/types.ts保留了完整的雙模式export type SendDelivery steer | followUp export type SendInput { readonly idOrName: string readonly message: string readonly deliverAs?: SendDelivery readonly callerSessionId?: string readonly allScope?: boolean } // The SEND DEFAULT is followUp: codexs followup_task routes a send to a running child as a // follow-up prompt, not an interrupting steer. steer is opt-in for polite mid-turn injection. export const DEFAULT_SEND_DELIVERY: SendDelivery followUp從源碼結(jié)構(gòu)看這次變更的準(zhǔn)確含義是引擎級默認(rèn)仍是followUp兼容 codex 風(fēng)格的追補(bǔ)提示語義但task_send這個工具面把禮貌的回合內(nèi)注入固定為唯一路徑。引擎內(nèi)部followUp分支仍然存在服務(wù)于不經(jīng)由task_send的調(diào)用路徑例如測試或其他內(nèi)部路由。在 steering/engine.ts 的steerRunning中可以驗證兩條分支if (deliverAs steer) await handle.steer(message) else await handle.followUp(message)steer走h(yuǎn)andle.steer()打斷當(dāng)前回合、立即注入followUp走h(yuǎn)andle.followUp()排隊為追補(bǔ)提示。工具面永遠(yuǎn)命中前者這正是always-steer契約的動態(tài)含義。此外引擎對pending狀態(tài)的子任務(wù)會把消息寫入持久化的pending_steering隊列engine.ts 的enqueuePending每條隊列項落盤deliver_as字段子任務(wù)真正啟動時notifyStartedengine.ts按持久化順序逐條排空deliver_as steer的條目同樣調(diào)用handle.steer()。由于工具面現(xiàn)在只產(chǎn)生steer消息預(yù)發(fā)射排隊prelaunch steering這條路徑上的投遞模式也事實上統(tǒng)一了。4. 驗證場景一RED/GREEN 焦點測試證據(jù)文檔的 Planned exact scenarios 第一部分給出了精確的焦點測試命令bun test packages/senpi-task/src/tools/control/send-always-steer.test.ts packages/senpi-task/src/tools/control/renderers.test.ts判據(jù)寫明PASS 是實現(xiàn)完成后退出碼為 0RED 的形態(tài)是生產(chǎn)代碼修改前斷言失敗因為deliver_as仍存在、默認(rèn)發(fā)送以followUp抵達(dá)引擎、或渲染器打印deliver:。這正是經(jīng)典的 TDD 證據(jù)鏈先寫測試釘住目標(biāo)契約測試在舊代碼上必然紅再動生產(chǎn)代碼轉(zhuǎn)綠。對應(yīng)的測試文件 send-always-steer.test.ts 包含三個測試逐一映射成功判據(jù) 1 的三個側(cè)面靜態(tài)契約createTaskSendTool返回的parameters.properties鍵列表中不含deliver_as工具description中不出現(xiàn)deliver_as、followUp、interrupt任何一個詞——把模型可見面的收斂做到了字符串級斷言動態(tài)行為用記錄型SendManager假件調(diào)用runTaskSend(manager, { to: st_1, message: new direction }, parent-1)斷言到達(dá)引擎的SendInput恰好是{ idOrName: st_1, message: new direction, deliverAs: steer, callerSessionId: parent-1 }——調(diào)用方?jīng)]傳任何投遞選項引擎收到的卻必須是deliverAs: steer渲染面renderTaskSendCall渲染出的行包含task_send to:st_1與消息摘要但不包含deliver:token——終端 UI 上同樣不能泄露已移除的選項。渲染實現(xiàn)印證了這一點renderers.ts 的taskSendCallLine只拼接task_send、to:目標(biāo)與消息摘錄三段代碼路徑上根本不存在投遞 token 的分支。5. 驗證場景二真實任務(wù)表面real task surface證據(jù)文檔強(qiáng)調(diào)第二類場景必須through the real task surface——即通過task/task_send/task_output工具鏈的真實調(diào)用來驗證覆蓋兩種子任務(wù)狀態(tài)5.1 運行中子任務(wù)running childtask({ prompt: Report WORKING, wait for a steering instruction, then return the exact steered text., subagent_type: explore, run_in_background: true, name: always-steer-running }) task_send({ to: always-steer-running, message: STEER-PROBE-RUNNING }) task_output({ name: always-steer-running, mode: full })PASS 判據(jù)send 調(diào)用不包含任何投遞選項結(jié)果報告 steer 投遞steer delivery子任務(wù)轉(zhuǎn)錄中包含STEER-PROBE-RUNNING。這個場景驗證的是steer 真的注入進(jìn)了運行中的回合子任務(wù)被指示先報告WORKING并等待轉(zhuǎn)向指令隨后task_send注入探針文本若投遞模式仍是followUp追補(bǔ)提示探針會出現(xiàn)在下一個回合而非打斷等待——轉(zhuǎn)錄中子任務(wù)返回被轉(zhuǎn)向的確切文本這一行為只有 steer 語義才能解釋。5.2 已完成但駐留的子任務(wù)finished residenttask({ prompt: Return RESIDENT-FIRST and stop., subagent_type: explore, name: always-steer-resident }) task_send({ to: always-steer-resident, message: Return RESIDENT-REVIVED and stop. }) task_output({ name: always-steer-resident, mode: full })PASS 判據(jù)第二次調(diào)用不含投遞選項復(fù)活同一個 task id / session轉(zhuǎn)錄包含RESIDENT-REVIVED。這條對應(yīng)工具描述中的復(fù)活語義子任務(wù) finished 后其會話仍是駐留態(tài)residenttask_send應(yīng)觸發(fā)復(fù)活而非拒絕或新建。從源碼看引擎sendToTask的主分支先經(jīng)messageability判定engine.ts非 steer 模式走reviveTerminal路徑engine.ts結(jié)果 kind 為revived并攜帶新的run_epoch。5.3 配套讀取面task_output 的模式兩個場景都以task_output({ ..., mode: full })收尾。output.ts 的 schema 定義mode為status | tail | full三值聯(lián)合缺省statusstatus返回記錄快照加最終響應(yīng)tail返回轉(zhuǎn)錄末尾tail_lines行缺省 60full返回整個轉(zhuǎn)錄有上限帶頭尾省略標(biāo)記。驗證場景選full是為了讓STEER-PROBE-RUNNING/RESIDENT-REVIVED探針在完整轉(zhuǎn)錄中可被機(jī)器校驗。6. 變更的邊界與不改動的部分從 send.ts 的完整執(zhí)行路徑可以界定這次always-steer到底改了什么、沒改什么改普通字符串消息到子任務(wù)child路徑的投遞模式——從可選deliver_as變?yōu)橛簿幋asteer不改結(jié)構(gòu)化 shutdown 消息shutdown_request/shutdown_response仍由routeStructuredMessage獨立路由send.ts與投遞模式無關(guān)不改子任務(wù)未找到時的團(tuán)隊兜底路由——當(dāng)manager.sendToTask返回not_found且配置了teamRouting時普通消息會作為團(tuán)隊郵件經(jīng)runTeamSend投遞send.ts工具描述同時聲明Team messages always steer into the recipients running turn即團(tuán)隊消息的 steer 語義是既有行為本次未動不改跨會話尋址規(guī)則——指向其他會話擁有的子任務(wù)默認(rèn)拒絕需顯式all_scope: true不改一次性 agent如 plan-reviewer在所有狀態(tài)下一律拒絕task_send。這一邊界劃分的意義在于always-steer 契約只作用于父 → 子的普通消息投遞團(tuán)隊的 mailbox 投遞、shutdown 握手、跨會話權(quán)限等鄰接語義全部保持原狀變更面因此可以被兩條焦點測試文件send-always-steer.test.tsrenderers.test.ts與兩個真實表面場景完整覆蓋。7. 證據(jù)文檔的其他約定文檔末尾還有兩個工程性約定值得留意Observations / Why this is enough / Cleanup receipts 三節(jié)標(biāo)注 Pending該文件是隨 PR 推進(jìn)填充的活證據(jù)文件——觀察記錄、充分性論證與清理回執(zhí)在驗證執(zhí)行后回填。閱讀此類證據(jù)文檔時Pending 意味著對應(yīng)證據(jù)尚未落盤不應(yīng)視為已完成。Omitted 一節(jié)明確聲明脫敏原則Secret-bearing environment values, provider credentials, and raw private logs will not be copied into evidence.攜帶密鑰的環(huán)境變量、provider 憑據(jù)與原始私有日志不進(jìn)入證據(jù)文件。這與包級文檔 packages/senpi-task/AGENTS.md 中記錄的 QA 規(guī)范持久化記錄帶 redaction、安全測試一脈相承。8. 小結(jié)與延伸閱讀回到證據(jù)文檔的四條成功判據(jù)本文梳理的倉庫證據(jù)與它們的對應(yīng)關(guān)系是判據(jù) 1 由 send-schema.ts無deliver_as send.ts硬編碼deliverAs: steer send-always-steer.test.ts三層斷言共同支撐判據(jù) 2 由 running-child 與 finished-resident 兩個真實表面場景支撐底層分別落在引擎的steerRunning與reviveTerminal分支判據(jù) 3 對應(yīng)的包級 QA 入口為tsgo --noEmit -p packages/senpi-task/tsconfig.json與bun test packages/senpi-task見 packages/senpi-task/AGENTS.md 的 QA 一節(jié)判據(jù) 4 屬于評審流程不在倉庫靜態(tài)證據(jù)范圍內(nèi)。對于維護(hù) Agent 工具面的團(tuán)隊這篇證據(jù)文檔示范了一個可復(fù)用的方法當(dāng)一次變更同時觸及模型可見 schema與運行時投遞語義時用 RED/GREEN 焦點測試釘住契約的靜態(tài)與動態(tài)側(cè)面再用不帶 mock 的真實工具鏈場景驗證端到端行為并把脫敏與清理約束顯式寫進(jìn)證據(jù)文件本身。延伸閱讀均在當(dāng)前倉庫內(nèi)packages/senpi-task/AGENTS.mdsenpi-task 包完整解剖含狀態(tài)機(jī)、存儲、runner、生命周期、steering 與團(tuán)隊運行時packages/senpi-task/src/steering/engine.ts轉(zhuǎn)向引擎實現(xiàn)sendToTask/steerRunning/enqueuePending/notifyStarted全鏈路packages/senpi-task/src/steering/types.tsSendDelivery、SendInput與SendOutcome的完整類型面packages/senpi-task/src/tools/output/output.tstask_output的 status/tail/full 三種讀取模式?!久赓M下載鏈接】oh-my-openagentOmO: Just type mass ulw keyword with your prompt. Now you are the master of graph engineering.項目地址: https://gitcode.com/gh_mirrors/oh/oh-my-openagent創(chuàng)作聲明:本文部分內(nèi)容由AI輔助生成(AIGC),僅供參考