Files
Agent-System-Documents/docs/guides/squad-and-subagents.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

134 lines
8.6 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/`。
---
## 1. 一句话结论
系统有两条"委派"线,都发生在**普通 run 的执行过程中**(小队不是独立的一层):
- **子代理(sub-agent)**:模型在回合中调用 `delegate_task` 工具 → runmgr 派生
`Kind=subagent` 的子 run(任务树)。
- **小队(squad)**:三条触发路径 —— ① 设置 `runtime.entry=squad` 让**对话本身**
走 SquadRunner;② 模型调用 `squad_orchestrate` 工具;③ 演示消息含 `#squad`。
三者最终都汇入 `deps/agent-squad` 引擎。**成员执行统一走 agent-native 内核**
(legacy 的 agent-system 子 run 压缩路径已退役,`internal/squadadapter/adapter.go:289-292`
注释明示;后端缺席时 fail-closed,不静默降级)。
另有第三种"规划委派":`run_workflow` 触发的**规划子代理**(workflowPlannerID),
见[任务规划与执行](task-planning-and-execution.md)第 7 节。
---
## 2. 子代理:`delegate_task` 触发链
### 触发条件
主对话模型判断某段工作适合独立完成(如长调研、批量整理)时,在 ReAct 循环里发出
`delegate_task` 工具调用(工具名常量 `internal/runtime/llm.go:58`)。工具声明与门禁在
`internal/runtime/llm.go:267-315`(编排型工具随上下文声明;子代理 spec 由 prompts 包提供
tools/model/maxTurns 约束,**禁止子代理再持有派发类工具**,`internal/prompts/service.go`)。
### 调用链
| # | 步骤 | 位置 |
|---|---|---|
| 1 | 内核循环解析出 `delegate_task` 调用 → `runTool` 分派 | `internal/runtime/llm_direct.go:471` `runTool`,case 在 `:493` 起 |
| 2 | 后台模式:经 rc 端口断言调 `SpawnBackground`;前台:`SpawnAgent` | `llm_direct.go:502, 516` 附近 |
| 3 | runmgr 派生子 run | `internal/runmgr/runmgr.go:1348` `spawnSubagent` → `:1358` `startChild`(**深度检查**在此,注释 `:1356`) |
| 4 | 建 `Kind=subagent` 子 run,`ParentRunID` 指向父 run(**任务树**) | `runmgr.go` startChild 内;契约 `modules/core/model.go:188` |
| 5 | 发 `agent.spawn` 事件(前端 AgentTracker 可见) | `runmgr.go:1375` |
| 6 | 独立 ctx 执行(后台模式 detach) | `runmgr.go` startChild 内 |
| 7 | 子代理执行器 = `SubLLMRunner.Execute` —— 同样进入 `nruntime.Run` 内核循环(独立转录、按子代理 spec 收窄工具) | `internal/runtime/sub_loop.go:31` |
| 8 | 完成回填 `AgentResultPayload`;结果截断 32000 字 | `runmgr.go:1401` `finishChild`;`runmgr.go:1780` `capSubagentResult` |
### 后台子代理
- `delegate_task` 可请求后台模式 → `spawnBackground`(`runmgr.go:1475`),
**每会话上限 3 个**(`runmgr.go` 常量)。
- 后台子代理完成后不直接打扰当前回合,而是进通知队列:`deliverNotices`
(`runmgr.go:1560`)在会话空闲时以 system-reminder 消息启动一个新父回合汇报结果
(`startNoticeRun`,`runmgr.go:1616`)。
- 取消级联:子 run 的 ctx 派生自父 run,`CancelRun`(`runmgr.go:1222`)取消根 run
即级联取消全部子 run。
---
## 3. 小队:三条触发路径
### 路径 A:`squad_orchestrate` 工具调用(工具路径)
| # | 步骤 | 位置 |
|---|---|---|
| A1 | 模型在回合中调用 `squad_orchestrate goal=...` | 工具名常量 `internal/runtime/llm.go:62`;声明 `llm.go:268` 附近 |
| A2 | 声明门禁:settings `runtime.squad`(默认 true)开启才声明;**entry 已是 squad 时不再提供,避免套娃** | `internal/runtime/llm.go:467` `squadToolEnabled`;`:266` 注释 |
| A3 | runTool 分派 → `squadadapter.Orchestrate(rc, dataDir, goalText)` | `internal/runtime/llm_direct.go:479` → `internal/squadadapter/adapter.go:47` `Orchestrate` |
| A4 | 之后进入 agent-squad 引擎(规划→调度→质检) | 见[小队工作机制](squad-mechanics.md) |
### 路径 B:对话入口模式(entry=squad)
| # | 步骤 | 位置 |
|---|---|---|
| B1 | 灰度开关 `runtime.entry=squad`(默认 `direct`) | `internal/settings/settings.go:90` |
| B2 | runmgr 选 Runner:`selectConversationRunner` 检查 `runtime.entry=="squad"` → 返回 SquadRunner | `internal/runmgr/runmgr.go:918`(注入点 `:908` `SetSquadRunner`,装配 `internal/app/app.go:306`) |
| B3 | `SquadRunner.Execute`(`internal/runtime/squad_runner.go:26`)先做 **LLM 分诊**:`modelRouter.Route`(`:146`,轻量模型输出 JSON `direct|plan`,`parseRoute` :167)—— direct = 单步 assistant 直通(`deps/agent-squad/squad/router.go:55` `directAssistantPlan`);plan = 进 Leader 委派(`router.go:29` `applyRoute` 设 `metadata["mode"]="leader"`) | |
| B4 | plan 模式 → `squadadapter.RunEntry` → 引擎 leader 模式 | `internal/squadadapter/entry.go:42` `RunEntry`;`deps/agent-squad/squad/engine.go`(`RunOptions.Mode="leader"`,Options 注释 `engine.go:99-105`) |
### 路径 C:演示/脚本
用户消息含 `#squad` → MockRunner 走同样 `Orchestrate`(`internal/runtime/mock.go`)。
### 一条"不可达"的路径(设计预留)
意图识别绑定 `blueprint` 动作可路由到 `KindSquad`(`internal/route/route.go:96-97`),
但当前**双重不可达**:`server.go:664` 明确注释 `SquadReady 保持 false`(per-run entry
覆盖未就位);且系统默认意图目录为空(见[意图识别](intent-recognition.md) 4.5 节),
不装插件就没有意图可命中 blueprint。小队的实际启动走上面 A/B/C 三条路径;意图目录
对小队的现实作用是注入规划者/Leader 提示词的能力目录(`deps/agent-squad/adapters/intent/catalog.go`)。
---
## 4. 装配(启动期)
| 内容 | 位置 |
|---|---|
| runmgr 注入三个 runner(LLMRunner / SubLLMRunner / MockRunner),SquadRunner 经 `SetSquadRunner` | `internal/app/app.go:287-306` |
| 角色指令来源接提示词库(`agent.squad.<kind>`) | `app.go:176` → `squadadapter.RoleInstructions`(`adapter.go:303`) |
| agent-native 内核选项:模型路由、Tavily 搜索、解释器、MCP、技能根、Subagent 桥接、PermissionAsk、检查点 | `app.go:196` `squadadapter.AgentNativeOptions` |
| 小队后端解析器:**只注册 `"agent-native"` 内核工厂**(legacy `"agent-system"` SpawnSubagent 压缩路径已退役删除;工厂缺席解析 fail-closed) | `internal/squadadapter/adapter.go:292` `BuildBackends`(退役注释 `:289-291`);`entry.go:103` DefaultBackend="agent-native" |
| 内核工厂注册 | `deps/agent-squad/adapters/native/register.go:61` `RegisterFull` |
---
## 5. 两条线的对比小结
| | 子代理 delegate_task | 小队 squad |
|---|---|---|
| 触发者 | 主对话模型 | 设置开关 / 主对话模型 / 演示脚本 |
| 执行单位 | 一个子 run(`Kind=subagent`) | 一组角色化步骤(每步一个 agent-native 内核 agent) |
| 编排者 | 主对话(单层委派,子代理不可再委派) | agent-squad 引擎(DAG 调度 / Leader 循环) |
| 并发 | 后台最多 3/会话 | 无写集冲突的步骤可并行 |
| 深度 | 1 层(startChild 深度检查,`runmgr.go:1356-1358`;子代理 spec 禁派发工具) | 成员内核里的 `delegate_task` 经 `SubagentFactory`(`adapter.go:234`)桥回产品 `SpawnSubagent`,**深度恒为 1,子 run 不再嵌套小队** |
| 汇总 | `AgentResultPayload` 回父 run | 交付物聚合 `output.md` → 主 run 工件(见[小队工作机制](squad-mechanics.md)) |
---
## 6. 关键文件清单
| 职责 | 文件 |
|---|---|
| delegate_task 分派 | `internal/runtime/llm_direct.go:493` 起 |
| 子 run 派生与任务树 | `internal/runmgr/runmgr.go:1348-1475`、`1560-1616` |
| 子代理执行器 | `internal/runtime/sub_loop.go` |
| 工具声明与门禁 | `internal/runtime/llm.go:267-315, 466-467` |
| squad_orchestrate 分派 | `internal/runtime/llm_direct.go:479` |
| 小队适配器(工具路径 + 后端组装 + Subagent 桥接) | `internal/squadadapter/adapter.go`(Orchestrate :47、SubagentFactory :234、BuildBackends :292、builtinRoles :306) |
| 小队适配器(入口路径) | `internal/squadadapter/entry.go`(RunEntry :42、kernelRoles :177) |
| 对话入口 Runner + LLM 分诊 | `internal/runtime/squad_runner.go`(Execute :26、Route :146) |
| Runner 选择 | `internal/runmgr/runmgr.go:918` |
| 灰度开关 | `internal/settings/settings.go:90` |
| 装配 | `internal/app/app.go:176, 196, 287-306` |