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。
134 lines
8.6 KiB
Markdown
134 lines
8.6 KiB
Markdown
# 何时启动小队或子 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` |
|