Files
Agent-System-Documents/docs/guides/task-planning-and-execution.md
T
weichen 4ce8155161 Expand guides with worked examples for intents, runs, and squads
Why: 补充实例让导览可对照实操:意图打分/填槽/多意图裁决给出
intentctl explain 实测轨迹(含样例意图、歧义、优先级 tie-break、
能力目录),任务执行给出工具清单与典型回合走查(含 ClaimGate
三类拦截),小队给出 agent-squad demo 全流程实测与真实工作区
目录树/TASK.md 实拍;并修正事实:默认意图目录为空、插件 overlay
与 registry loader 两种文件形态不同。

Evidence: intentctl explain/catalog 与 agent-squad demo 为 2026-10-08
本地实测输出(命令见各节);锚点仍为 Dev R0 218fc4b1;git diff
--check 通过;五篇互链全部可解析。SRC=SELF。

Impact: 仅文档增补(02/03/04/05 四篇),无代码影响。

Refs: Dev 任务分支 feature/architecture-guides。
2026-10-08 09:33:13 +00:00

247 lines
20 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Agent 的任务规划与执行:架构与调用链
> 基准:Agent-System-Dev 组合 `218fc4b1ba00b0a03b5bb79f292b3eba6c987a58`(R0,2026-10-08 锚点核验)。
> 行号为该提交下的函数入口位置(个别内部区段为约略范围),随代码演进漂移;文件与函数名是稳定锚点。
> `internal/` 指 `modules/backend/internal/`;`core/` 指 `modules/core/`;
> `nruntime/` 指 `deps/agent-native/runtime/`。
---
## 1. 一句话结论
系统有**两套并存的规划机制**:
1. **默认主线 = LLM 自主规划(ReAct 循环)**:没有预置 DAG。普通消息由 backend
`LLMRunner` 驱动,进入 `deps/agent-native` 自研内核的单轮循环
(组装上下文 → 调 LLM → 解析工具调用 → 执行工具 → 回填 → 直到模型不再调工具),
模型可用 `update_plan` / `update_todos` 工具自主维护任务列表(右栏 Plan/Todos 面板)。
2. **可选结构化路径 = WorkflowDef(数据化步骤列表)**:可由意图绑定启动,也可由模型
调用 `run_workflow goal=...` 触发"规划子代理生成步骤 → 用户 HITL 确认 → 编译执行"
(`internal/runtime/workflow_compiled.go:91` `planCompiledWorkflow`)。工作流的
编译与步进执行在 `internal/wfcompile` 包,执行时**装配到小队引擎上步进**
(挂起 → HITL 卡 → Respond;执行态 → Resume),收口带 critic 终判。
执行主体分两层:backend 的 `runmgr`(Run 生命周期/事件/持久化/策略)+
`internal/runtime`(各 Runner);真正的智能体循环内核在 `deps/agent-native/runtime/loop.go`。
---
## 2. Run 生命周期与状态机
契约定义在 `modules/core/model.go`:
- `RunStatus`(model.go:166):
`pending → running → (waiting_hitl ⇄ running) → completed | failed | cancelled | interrupted`
- `RunPhase`(model.go:178):`thinking → acting → responding → finalizing → done`
- `RunKind`(model.go:158):`conversation | subagent | tool`;
`Run` 结构体(model.go:188)含 `ParentRunID`(任务树)、Usage、Error。
状态迁移代码位置(均在 `internal/runmgr/runmgr.go`):
| 迁移 | 代码位置 |
|---|---|
| 创建(pending) | `:1023` `startClaimedRun`(建 Run + `store.CreateRun`,`internal/store/messages.go:120`) |
| 运行 | `:1055` 附近 `execute`(置 running/thinking) |
| HITL 挂起/恢复 | `:449` `AskHITL` 置 waiting_hitl;`:1261` `ResolveHITL` 恢复 running |
| 终态 | `:1087` `finishRun` → `internal/store/recovery.go:125` `SettleRun`(条件 UPDATE + 同事务写权威助手消息树,防并发双终态)→ 发 `run.completed/failed` |
| 取消 | `:1222` `CancelRun`(根 run ctx 取消,子 run 派生自父 ctx 天然级联;并唤醒 HITL 等待) |
| 停机中断/重启对账 | `:1655` `InterruptRunning`、启动时 `:1689` `ReconcileInterrupted` |
| 会话并发控制 | `:980` `ClaimConversation`(一会话至多一个在途 run,忙返回 409,`server.go` 错误映射) |
---
## 3. 端到端调用链(普通消息 → 结果落库)
| # | 步骤 | 位置 |
|---|---|---|
| 1 | HTTP 入口 `postMessage`:附件经 `ingest.Parse`;`/` 命令不启 run;意图识别 + `route.Decide` 三分支 | `internal/server/server.go:574`;路由调用 `:656` |
| 2 | 抢会话槽位 + 消息落库(幂等 requestId) | `server.go:679` → `runmgr.go:980`;`server.go:686` 附近 `AppendMessageWithRequest` |
| 3 | 启动 run:工作流回合 `StartClaimedWorkflowRun`(定义深拷贝进 Launch,插件卸载不影响在跑副本);普通回合 `StartClaimedConversationRun` → `startClaimedRun`:建 Run 落库 → 注册 runHandle → `selectConversationRunner` → 附件原件落工作区 → `go m.execute(rc, runner)` | `server.go:725,727`;`runmgr.go:991, 996, 1023-1043`;runner 选择 `runmgr.go:918` |
| 4 | `LLMRunner.Execute`(`internal/runtime/llm.go:198`)准备阶段:模型路由 `ResolveChatModel`(`internal/runtime/model.go:54`,settings `models.chat.*` + 画像 ModelRoute,密钥经 SecretLookup);`@knowledge` 显式引用触发 RAG(llm.go:216 附近);system 段 `prompts.Service.ResolveSystem`(`internal/prompts/service.go:290`);上下文组装 `ctxasm.AssembleInput`/`FinishAssembly`(`internal/ctxasm/ctxasm.go:45`、`finish.go:97`,产出 `ContextObservation` 随 run.completed 下发);工具声明(编排型内置 + 注册表过滤,llm.go:267-315;计划模式过滤 `filterPlanDecls` llm.go:332) | |
| 5 | 进入内核:`l.runDirect(...)`(`internal/runtime/llm_direct.go:95`)把声明包成内核工具注册进 `agenttools.NewRegistry`、打开转录 journal(`<sandbox>/.agentx/transcript.jsonl`)、接线 Stop 钩子(交付物门禁 + 验证器)、调 `nruntime.Run(...)` | |
| 6 | **内核 ReAct 单轮循环** `nruntime.Run`(`deps/agent-native/runtime/loop.go:196`,`for turn < cfg.MaxTurns` 默认 12):① ctx/预算检查 → ② `prepareRequest`(loop.go:787:转录投影 + 窗口清理 + 记忆注入 + reminders)→ ③ `consume` 流式消费 LLM 事件(loop.go:492)→ ④ 持久化 assistant → ⑤ 有工具调用:`executeTools`(loop.go:595,可并行),每个 `runOne`(loop.go:709)走完整性校验 → `gateTool`(hooks PreToolUse,ask 决策交宿主 PermissionAsk)→ commitToolIntent(崩溃恢复配对)→ `cfg.Tools.Execute` → AfterTool,结果按 Role=tool 回填 → `continue` 下一轮 → ⑥ 无工具调用:`ContinueOnLength` 续写(loop.go:399)+ follow-up drain + Stop 钩子决定续跑(`stopDecision` loop.go:645;宿主侧做交付物门禁与验证,最多 3 次续跑)→ `settle` 结束;轮数耗尽报 max turns exceeded(loop.go:447) | |
| 7 | 工具执行回落宿主:编排型工具在 runner 内消化——`squad_orchestrate` → squadadapter.Orchestrate(case 在 `llm_direct.go:479`)、`delegate_task` → rc 端口 `SpawnBackground`/`SpawnAgent`(`llm_direct.go:493` 起)、`update_plan/update_todos` → `rc.UpdatePlan/UpdateTodos`、`run_workflow`、`submit_card/card_*`(各 case 依次在 `llm_direct.go:471` `runTool` 内);其余 → `rc.ExecuteTool`(`runmgr.go:740`):画像三清单 → policy.Decide(计划模式 deny / ask→HITL 确认卡)→ `registry.Execute`(`internal/tools/registry.go:94`)→ 结果截断转工件 | |
| 8 | 子代理 run(任务树):`spawnSubagent/startChild`(`runmgr.go:1348/1358`,startChild 做深度检查)建 `Kind=subagent` 子 run(ParentRunID 指父)→ 发 `agent.spawn`(`runmgr.go:1375`)→ `finishChild`(`runmgr.go:1401`)跑 `SubLLMRunner.Execute`(`internal/runtime/sub_loop.go:31`,同样调 `nruntime.Run`)→ 子转录独立落盘 → 结果截断 32000 字(`runmgr.go:1780` `capSubagentResult`)。后台子代理:`spawnBackground`(`runmgr.go:1475`,每会话上限 3);完成后 `deliverNotices`(`runmgr.go:1560`)在会话空闲时以 system-reminder 启动新父回合汇报(`startNoticeRun`,`runmgr.go:1616`) | |
| 9 | 终态收敛:`finishRun`(`runmgr.go:1087`)usage 汇总 → 终态消息树(tool_call/tool_result/thinking/card 部件)→ `SettleRun` 单事务落库 → submit_card 回填锚点 → 发 `run.completed`(含 ContextObservation)/`run.failed`。工件:`runCtx.WriteArtifact` → `store.CreateArtifact` → `artifact.created` 事件 → 文本进检索索引;工件作为 `PartArtifactRef` 挂进助手消息树 | |
完成判定者 = 内核循环 + 宿主 Stop 钩子组合:模型不再调工具且无 follow-up → Stop 钩子
(交付物门禁说谎检测、可选验证器)决定续跑或放行 → 可选用主模型 `composeFinal` 重写
最终回复(`nruntime/compose.go`)→ `finishRun` 落终态。
---
## 4. LLM 调用与流式
- **不用第三方 SDK**,自研 HTTP 客户端 `deps/agent-native/runtime/llm/client.go`:
默认 OpenAI 兼容 `POST {base}/chat/completions` + `stream:true`;
`Protocol=anthropic` 走原生 Messages(`anthropic.go`,带 cache_control 断点与签名思考块)。
- **Provider 预设**:`llm/providers.go`(anthropic/openai/google/deepseek/minimax/
openrouter/groq/azure)+ `models.json` 自定义;`ParseModelRef` 支持 `provider/model:thinking`。
- **流式解析**:`llm/sse.go` `ParseSSE` → `emitParsed` 产出统一事件
(text_delta / reasoning_delta / tool_call_delta / message_end)。
- **模型配置链**:settings 覆盖链(defaults→system→user→project)+ 画像 ModelRoute
(`internal/runtime/model.go:30-62`);轻量档回落主档(model.go:84);
`models.chat.fallbacks` 故障切换(`llm/fallback.go`,在 runDirect 内接线);
重试(内核 `WithRetry`)。
- **事件到前端**:内核事件 → `directRun.onEvent`(`llm_direct.go:278`)转 `rc.Emit(...)`
→ `runmgr.emit`(`runmgr.go:1217`)→ 事件缓冲合并(150ms/64 条,`internal/runmgr/events.go:42-43,83`)
→ `store.AppendEvents`(事务内分配会话内单调 seq,`internal/store/messages.go:243`)
→ `bus.Publish` → SSE `GET /api/events`(`server.go:275`,Last-Event-ID 重放,15s 心跳);
PG 多实例经 `internal/pgbus` NOTIFY/LISTEN 扇出。
---
## 5. 工具 / 技能 / 提示词体系
**工具系统**
- 契约:`modules/core/tools.go:16` `ToolDesc`(JSON Schema 参数、ReadOnly/Edits/Risk 权限声明)、
`tools.go:33` `HTTPToolSpec` 无代码声明式 HTTP 工具。
- 注册:`internal/tools/registry.go:50` `Register`(后注册覆盖)、`:112` `RegisterBuiltin`
(time_now、knowledge_search、write_artifact、quality_review、跨会话 peers、ask_user 等);
另有 fs / exec / search / 声明式 HTTP / `run_tool_script`(需开关)各文件。
- 执行策略在 runmgr:`ExecuteTool`(`runmgr.go:740`)deny→错误、计划模式 deny、
confirm/ask→HITL 确认卡、执行后超长截断转工件。
- 结果回填:内核侧作为 `Role=tool` 消息进转录;UI 侧由 onEvent 聚成 `PartToolCall`+
`PartToolResult` 终态落库;下一轮由 ctxasm `renderAssistantTurns`(`internal/ctxasm/ctxasm.go:174` 附近)
还原成 provider 原生配对消息。
**skills 包 = 内置技能文件**:go:embed `recipes/**`(`internal/skills/skills.go:23` 附近),
启动 `Seed`(skills.go:53)落 `<data>/skills/`,带 manifest 哈希做升级/drift 保护;
供内核的 SKILL.md 加载器与 `load_skill` 渐进披露工具消费
(`deps/agent-native/runtime/skills/skills.go`)。
**prompts 包 = 提示词模块库**:embed `library/*.md` + DB 用户覆盖链。
`ResolveSystem`(`internal/prompts/service.go:290`):base/env 层按画像引用 →
task 层全员触发匹配(command/intent/attachment 三类触发)→ 正文点名的 `method.*`
辅助模块跟随注入 → 占位符填充(`{{date}}/{{model}}/{{skillsDir}}`)。
另提供子代理 spec(tools/model/maxTurns,`service.go:81` 附近;**禁止子代理持有派发类工具**)、
AgentPrompt、工具描述覆盖。
**ctxasm 包 = 上下文组装**:`AssembleInput`(`internal/ctxasm/ctxasm.go:45`)=
sys 段 → 历史窗口(默认 40 条,超出注入抽取式摘要)→ 卡片/工件正文回填
(防"下一轮失忆")→ 当前轮文本 + 附件解析全文(默认 48000 字符截断)+ knowledge 块;
`FinishAssembly`(`finish.go:97`)追加条件注入;产出 Report(token 估算、裁剪记录、
prefixHash)作为可观测 `ContextObservation`。记忆注入在内核按请求完成(`loop.go` prepareRequest 段)。
---
## 5.5 主对话模型手上有哪些工具
模型每轮请求能看到的工具分三类(全部在 `LLMRunner.Execute` 声明阶段组装,`llm.go:267-315`):
| 类别 | 工具名(实测常量/注册名) | 来源 |
|---|---|---|
| **编排型**(在 runner 内消化,不走通用执行器) | `update_plan` / `update_todos`(维护右栏计划/待办)、`delegate_task`(派子代理)、`squad_orchestrate`(发起小队)、`run_workflow`(发起工作流)、`review_findings`、`submit_card`、`card_begin`/`card_append`/`card_commit`(流式产卡三段式) | `internal/runtime/llm.go:56-70` 常量 |
| **内置产品工具** | `time_now`、`knowledge_search`(项目/插件知识全文检索)、`write_artifact`(写工件)、`quality_review`(交付物打分)、`ask_user`(主动提问=HITL)、跨会话 `peers` 系列 | `internal/tools/registry.go:112` `RegisterBuiltin` |
| **内核工作区工具**(经 hostbridge 注入内核) | `read_file` / `write_file` / `edit_file` / `multi_edit` / `list_dir` / `grep` / `glob` / `load_skill` / `run_python` / `run_command` / `web_search` / `tavily_search` / `fetch_url` / `delegate_task` | `internal/runtime/hostbridge.go:15-21` `kernelToolNames`;`web_search`/`tavily_search` 仅在配置了搜索密钥时注册 |
实际可见集 = 以上与**画像三清单**(allow/deny/confirm,`core/profile.go`)过滤后的交集;
计划模式下 `filterPlanDecls`(llm.go:332)会再收窄。
---
## 5.6 实例:一个典型回合的机制走查
> 走查基于上文全部代码锚点;模型输出为示意,机制、工具名与顺序均为代码实测。
用户输入:"把我会话里刚上传的季度数据整理成一份摘要文档,并告诉我三个关键结论。"
1. **进入回合**:`postMessage` → 意图 fallback(没有装匹配意图)→ `route.Decide` 判
`KindTurn` → 抢会话槽位 → `startClaimedRun` 建 `Run{pending}` → 选 `LLMRunner` →
`go execute()`(`runmgr.go:1023, 918`)。
2. **上下文组装**:`ctxasm.AssembleInput`(ctxasm.go:45)——system 段(base/env/task 提示词
模块,`prompts.ResolveSystem`)→ 历史窗口(默认 40 条)→ 当前轮文本 + **附件解析全文**
(ingest 已把 xlsx/csv 解成 ParsedDocument,默认 48000 字符截断)→ knowledge 块。
3. **第 1 轮 LLM**:模型看到数据后先调 `update_todos`(拆 3 个待办)+ `read_file`(读附件
解析物)。两个工具经内核 `runTool` 回落宿主执行,结果以 `Role=tool` 消息回填转录
(loop.go `executeTools` 段),UI 侧聚成工具过程部件。
4. **第 2 轮 LLM**:模型调 `write_artifact`(把摘要写成工件)。宿主执行 → `artifact.created`
事件 → 工件登记进会话。
5. **第 3 轮 LLM**:模型输出三个结论的纯文本(无工具调用)→ 内核判定无工具调用 →
follow-up drain → **Stop 钩子**:宿主做交付物门禁(`deps/agent-native/runtime/gates/gates.go:52`
`ClaimGate`)——它抓三类"说谎":① 回复正文出现工具调用标记但本轮没有真实调用;
② 引用了不存在的工件 ID;③ **声称"已生成/已登记文件"但这轮没有产出对应工件**。
本例模型确实产出了工件,门禁放行;若模型只写了结论却声称"文件已生成",
门禁拒绝结算并附原因让模型续跑(最多 3 次续跑,`loop.go` stopDecision + 宿主接线)。
6. **收口**:可选用主模型 `composeFinal` 重写最终回复 → `finishRun`(runmgr.go:1087)→
`SettleRun` 单事务落 run 终态与助手消息树 → 发 `run.completed`(带 ContextObservation)。
子代理变体:若第 2 轮模型改调 `delegate_task {agent:"researcher", task:"分析季度数据"}`,
则走 `llm_direct.go:493` → `runmgr.go:1348` 派生 `Kind=subagent` 子 run(任务树 + `agent.spawn`
事件)→ `SubLLMRunner` 用同一内核循环独立跑完 → 结果回填父轮。
---
## 6. HITL(人工介入)
- 契约:`modules/core/hitl.go:33-36` 四种卡(choice/input/confirm/form)。
- 挂起:`runCtx.AskHITL`(`runmgr.go:449`)存卡 → run 置 waiting_hitl → 发
`hitl.request` → 收件箱投影 → `waitHITL` 阻塞等恢复(支持超时)。
- 恢复:`POST /api/hitl/{id}/resolve` → `runmgr.ResolveHITL`(`runmgr.go:1261`)
三分支:选项回填恢复;自由输入命中其他意图 → 取消挂起 run 并转轨;否则作澄清回答回填。
- 触发时机:a) 工具确认(画像 confirm 清单 / policy ask,`runmgr.go:757` 附近与内核
`PermissionAsk` 端口);b) 工作流缺槽位提问(`internal/wfcompile/run.go` 槽位补齐段)、
显式 hitl 步骤(`internal/wfcompile/host.go:138` `doHitl`)、动态计划执行前确认
(`RunOptions.RequireConfirm`,`internal/wfcompile/run.go:34`);c) `ask_user` 工具。
---
## 7. workflow 体系与"规划器"
- `internal/workflow/registry.go`:工作流**定义注册表**(只依赖 core)。
`Registry.Set` 整体替换快照、`ByIntent` 按意图查定义、`Launch` 产启动副本。
- **编译执行器在 `internal/wfcompile/`**(Q3 WP1):
- `run.go:39` `RunCompiled`:缺槽位逐个 HITL input 卡收齐 → `compiler.go:76` `Compile`
编译 WorkflowDef → **装配到小队引擎(squad)步进执行**(挂起 → HITL 卡片 → Respond;
执行态 → Resume)→ critic 终判 → outcome 汇总(run.go 文件头注释即此流程)。
- `RunOptions`(run.go:27):`DynamicLimits`(模型生成上限:≤8 步、禁 script)、
`RequireConfirm`(模型生成工作流执行前强制 HITL 确认;注册的声明式工作流免确认)、
`ModelScript`(脚本步为模型提供,执行前单独确认)。
- **"先规划任务列表再执行"的 planner 真实存在**:`internal/runtime/workflow_compiled.go:91`
`planCompiledWorkflow` —— `rc.SpawnAgent(workflowPlannerID, "规划", …)` 派**规划子代理**
输出 WorkflowDef JSON("只输出 JSON,不要完成目标本身")→ `wfcompile.ParseWorkflowDef`
解析校验 → `runLaunch` 以 `DynamicLimits:true, RequireConfirm:true` 执行(用户 HITL
确认计划后步进)。另有 `registeredWorkflowLaunch`(:117,注册定义 + 快照脚本正文)、
`modelScriptLaunch`(:138,模型脚本,单独确认)。入口 Runner 是
`CompiledWorkflowRunner`(workflow_compiled.go:30,`Execute` :39)。
---
## 8. deps/agent-native 内核的其他部件
- `runtime/` 是**进程内自研内核**(不派子进程):`loop.go` 循环、`llm/` 客户端、
`tools/` 内核注册表(deferred 延迟暴露 + tool_search)、`session/`(JSONL 转录树,
可 fork/clone)、`session_agent.go`(headless AgentSession:steer/follow_up/abort/换模型)、
`hooks/`、`memory/`、`skills/`、`promptx/`、`mcp/`、`checkpoint/`、`subagent/`、
`gates/`(验收+守卫)、`codeorch/`(Python 依序调工具的代码编排)、`policy/`。
- `rpc/` 是本地 JSONL RPC(一行一命令,双向 pipe),把内核暴露给子进程/外部宿主;
**产品后端主路径不走 rpc**,直接进程内调 `nruntime.Run`。
- `adapters/native/backend.go` 把内核包成 `exec.Backend`(`exec/backend.go` 接口),
供小队成员执行使用(见[小队与子 Agent 启动](squad-and-subagents.md)、
[小队工作机制](squad-mechanics.md));`adapters/pitransport/` 是远程 pi runner 的
HTTP/NDJSON 传输适配(产品未用为主)。
---
## 9. 关键文件清单
| 职责 | 文件 |
|---|---|
| Run/消息/计划/工件契约 | `modules/core/model.go`、`events.go`、`hitl.go`、`tools.go` |
| Run 生命周期枢纽 | `modules/backend/internal/runmgr/runmgr.go` |
| 事件缓冲与 seq | `modules/backend/internal/runmgr/events.go` |
| Runner 端口与四实现 | `internal/runtime/runner.go`、`llm.go`、`sub_loop.go`、`workflow_compiled.go`、`squad_runner.go` |
| 内核桥接(工具/事件/Stop 钩子) | `internal/runtime/llm_direct.go`(runDirect :95、onEvent :278、runTool :471) |
| 模型路由 | `internal/runtime/model.go` |
| 工作流编译执行 | `internal/wfcompile/run.go`(RunCompiled :39)、`compiler.go`(Compile :76)、`host.go`(doHitl :138) |
| 工作流入口与 planner | `internal/runtime/workflow_compiled.go`(CompiledWorkflowRunner :30、planCompiledWorkflow :91) |
| 工作流定义注册表 | `internal/workflow/registry.go` |
| 内核 ReAct 循环 | `deps/agent-native/runtime/loop.go` |
| LLM 客户端/协议/预设 | `deps/agent-native/runtime/llm/client.go`、`providers.go`、`sse.go`、`anthropic.go` |
| 上下文组装 | `internal/ctxasm/ctxasm.go`、`finish.go` |
| 提示词库 | `internal/prompts/service.go` |
| 技能种子 | `internal/skills/skills.go` |
| 工具注册表 | `internal/tools/registry.go` |
| run/事件/工件落库 | `internal/store/messages.go`、`recovery.go` |
| HTTP 入口与 SSE | `internal/server/server.go` |