diff --git a/docs/guides/intent-recognition.md b/docs/guides/intent-recognition.md index 11ff777..7c59938 100644 --- a/docs/guides/intent-recognition.md +++ b/docs/guides/intent-recognition.md @@ -83,6 +83,121 @@ clarify / chitchat / handoff`;规则漏斗只产出 start/set_slot/clarify(` (`intent/catalog.go:36,68`)把启用意图渲染为 Markdown,供小队规划者/Leader 提示词注入 (`deps/agent-squad/adapters/intent/catalog.go` 消费)。 +## 4.5 内置与默认意图:默认是空目录 + +**系统默认不内置任何意图**:backend 组合根 `intentgate.New()`(`app.go:309`)起步是空注册表, +`g.Match` 在空闸门下直接透传(未启用 = 行为回退,所有消息走普通回合)。意图只能经两条 +overlay 路径进入,且**两种路径的文件形态不同**: + +| 路径 | 文件形态 | 装载方 | +|---|---|---| +| 插件 overlay(产品路径) | **裸数组** `[ {…IntentSpec…}, … ]` | `plugins/host.go:216-220` 按元素透传 → `gate.Rebuild`(gate.go:104)逐个 unmarshal 成 IntentSpec | +| registry/loader(库路径:intentctl、BuildFS) | 包装对象 `{"intents":[…], "disable":[…]}` | `intent/loader.go:21` `overlayDoc`,严格 JSON(未知字段报错) | + +仓库自带两份样例意图(即"默认能看到的意图"上限——装载对应插件/配置后生效): + +- `plugins/example/intents/demo.json`(插件形态):`demo.show`(命中"示例/演示",无槽位)、 + `demo.brief`(命中"简报",必填槽位 `topic`,抽取正则 `主题[::]\s*(\S+)`,缺槽问"主题是什么?")。 +- `deps/agent-intent/examples/specs/demo.json`(loader 形态):`demo.run.status_query` + (命中"进度/状态"、排除"配置",槽位 `run_id`,base=0.8/threshold=0.7,handler=`taskrun.query_status`)、 + `demo.run.restart`(需同时含"重启"+"任务",`requires_confirm=true`,risk=medium)。 + +--- + +## 4.6 实测:一句话怎么被打分、填槽、命中 + +以下全部为 `intentctl explain` 在样例配置上的**真实输出**(2026-10-08 实测;例 5/6 的配置目录 += 两份样例意图合并)。打分公式:`score = base + (正则命中 ? pattern_bonus : 0)`(封顶 1.0), +前两名分差 < `margin`(默认 0.1,`intent/registry.go:55`)判 ambiguous。 + +**例 1|命中 + 抽到槽位**(输入"查一下任务 run_42 的进度"): + +``` +分支:matched +Top:demo.run.status_query 置信度=0.8000 风险=low 确认=false handler=taskrun.query_status + 槽位 run_id="run" +候选(1): + 0.8000 demo.run.status_query terms=进度 +裁决轨迹: + margin=0.1000 + 守卫:未评估 + 规则 命中 demo.run.status_query 0.8000 进度 + 规则 排除 demo.run.restart no_positive_match +``` + +要点:`terms=进度` 命中词表加 0 分(base 已含);`restart` 因 `includes_all:["任务"]` 缺"重启" +被排除(`missing_includes_all`)。槽位 `run_id="run"` 是真实结果——槽位正则 +`(?:run|任务)[:# ]*([A-Za-z0-9-]+)` 的字符类不含下划线,捕获在 `run` 后被 `_` 截断: +**规则识别完全确定性,槽位 pattern 的质量决定抽取质量**(写 `[A-Za-z0-9_-]+` 即可取到 `run_42`)。 + +**例 2|命中但缺必填槽位**(输入"写一份简报",`demo.brief` 装载时): + +``` +分支:matched +Top:demo.brief 置信度=0.7000 + 缺失必填槽位:topic +裁决轨迹: + 规则 命中 demo.brief 0.7000 简报 + 规则 排除 demo.run.restart missing_includes_all:任务 + 规则 排除 demo.run.status_query no_positive_match + 规则 排除 demo.show no_positive_match +``` + +`Missing=[topic]` 连同 spec 的 `question:"主题是什么?"` 一起随 route 进入工作流 run, +由 HITL 输入卡向用户澄清(见调用链第 10 步)。 + +**例 3|槽位填上**(输入"主题:核能 写一份简报"):同一意图 matched, +`槽位 topic="核能"`,Missing 为空——正则 `主题[::]\s*(\S+)` 命中,直接启动。 + +**例 4|什么都不像**(输入"随便聊聊今天的天气"): + +``` +分支:fallback +裁决轨迹: + 规则 排除 demo.run.restart missing_includes_all:任务 + 规则 排除 demo.run.status_query no_positive_match +``` + +无命中 → fallback;若装载了语义慢通道(`runtime.intentSemantic`,默认关)会先尝试 embedding +重排,仍不达阈才 fallback。gate 把 fallback 折叠后 `route.Decide` 判 `KindTurn` → 普通对话回合。 + +**例 5|能力目录实测**(`intentctl catalog`,这段 Markdown 就是注入小队规划者/Leader +提示词的内容,`squad/engine.go:44` 注释明示用途): + +``` +# 系统能力目录(意图快照 revision=1) + +以下能力可直接调用,无需派智能体执行。子任务命中其一时,输出携带 intent_id 的步骤并填齐必填槽位; +必填槽位信息不足时不要编造,改为向用户澄清(优先使用各槽位的提问建议)。 + +## demo.brief — 写一份简报。必填槽位 topic,没写就问「主题是什么?」。 +- 风险:low;执行前需人工确认:否 +- 槽位 topic(必填,topic) + - 提问建议:主题是什么? +``` + +--- + +## 4.7 一句话里有多个意图怎么办 + +意图层的契约是**一条输入只产出一个 Decision**(top-1),不做多意图拆分。多个候选的 +裁决顺序固定为: + +1. **分差裁决**:唯一过阈,或第一名领先 ≥ margin → 直接 matched。 +2. **同分且 priority 不同 → 高优先级直接胜出**(I5,不标歧)。实测("帮我生成一份报告", + 两个意图 base 都是 0.7、词表都命中"报告"):template 设 `priority:10` 后 + `分支:matched,Top:demo.report.template 置信度=0.7000`。 +3. **分差 < margin(含同分同优先级)→ ambiguous**,先走可选语义重排 `semanticRerank` + (`recognizer.go:292`,默认关)→ 再走 Classifier L2 重打分(backend 未接线)→ 都没有则 + **保持 ambiguous**。实测同配置去掉 priority 后: + `分支:ambiguous,候选(2):0.7000 demo.report.generate / 0.7000 demo.report.template`。 +4. ambiguous 经 gate 折叠 → `KindTurn` → **交给普通 LLM 回合**:模型在一次 ReAct 循环里 + 自行处理多个请求(这也是多数"一句话多个事"的实际归宿——意图层只接管高确定性的 + 命令/工作流类请求)。 +5. 多轮场景:Stage 1 挂起预期(`ExpectationProvider`)让澄清中的输入优先回到挂起意图; + HITL 自由输入命中其他意图时会**取消挂起 run 并转轨**(`runmgr.go:1261` `ResolveHITL`), + 相当于允许用户中途改道。 + --- ## 5. 端到端调用链:用户输入一条消息后发生了什么 diff --git a/docs/guides/squad-and-subagents.md b/docs/guides/squad-and-subagents.md index 3196710..61cf7b6 100644 --- a/docs/guides/squad-and-subagents.md +++ b/docs/guides/squad-and-subagents.md @@ -84,8 +84,10 @@ tools/model/maxTurns 约束,**禁止子代理再持有派发类工具**,`int ### 一条"不可达"的路径(设计预留) 意图识别绑定 `blueprint` 动作可路由到 `KindSquad`(`internal/route/route.go:96-97`), -但当前 `server.go:664` 明确注释 `SquadReady 保持 false`(per-run entry 覆盖未就位), -**该路径暂不可达**——小队的意图目录注入另走 `deps/agent-squad/adapters/intent/catalog.go`。 +但当前**双重不可达**:`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`)。 --- diff --git a/docs/guides/squad-mechanics.md b/docs/guides/squad-mechanics.md index dbb33c0..b0acd4d 100644 --- a/docs/guides/squad-mechanics.md +++ b/docs/guides/squad-mechanics.md @@ -91,6 +91,90 @@ blackboard.json / pending.json / runstate.json / usage.json` --- +## 4.5 实测:离线小队演示(真实输出) + +`deps/agent-squad` 自带离线演示(`cmd/agent-squad demo -keep`,stub 脚本成员 + 规划脚本 + +自动应答 HITL,无需 LLM/网络),以下为其 2026-10-08 实测输出摘录。**引擎机制与产品完全 +同一套**,差异只有三处:成员后端是脚本(产品是 agent-native 内核)、规划器是 MockPlanner +(产品默认 RulePlanner)、HITL 自动应答(产品走 HITL 卡片)。 + +``` +═══ agent-squad 离线演示(stub 智能体 + mock 规划器 + HITL)═══ + +[1] 注册角色(手动注册,backend=demo 脚本智能体) + + researcher 调研专员 + writer 撰写专员 + reviewer 审核专员 + +[2] 发起运行:调研便携式超声诊断仪并产出报告 + ✔ step_01_research researcher 市场与技术调研,回填事实与缺口 + step_02_draft writer 汇总调研产出,撰写报告初稿 + step_03_review reviewer 一致性终审并产出终版 + +[3] ⏸ HITL 挂起(gap):发现 1 个阻塞缺口,需要人工补充信息后才能继续: + - [gap_1] 目标年产量未提供,影响成本测算(owner=agent) + → 自动回应:choice="resolved" answer="年产量 10 万套" + ✔ step_01_research ✔ step_02_draft ✔ step_03_review + +[4] 运行终态:completed + 步骤 3 个(完成 3/跳过 0/失败 0)。质检得分 100。 + 交付物: /runs/run_20261008T090457.882598947/output.md + 黑板字段: {"annual_volume":"100000","budget":"15万","device_class":"二类", + "review":"passed","target_market":"中国"} +``` + +对应真实落盘的工作区(`runs//`)——与第 4 节调用链逐项对应: + +```text +runs/run_20261008T090457.882598947/ +├── context/goal.md, context/index.json # 9. 目标与材料快照 +├── tasks/01_researcher/ # 9. 每步一个子工作区 +│ ├── TASK.md # 步骤任务书(角色、期望产出、资源权威等级、黑板快照、产出要求) +│ ├── AGENTS.md # 角色指令 +│ ├── context/ # 快照进来的目标/材料 +│ ├── notes.md fields.json gaps.json # 成员产出(黑板来源) +│ └── execution.log +├── tasks/02_writer/{TASK.md,AGENTS.md,context/,output.md,...} +├── tasks/03_reviewer/{TASK.md,AGENTS.md,context/,output.md,fields.json,...} +├── blackboard.json # 11. 黑板(字段在步骤间累积) +├── workflow.json squad.json runstate.json # 7. 拆解结果与运行态(磁盘即权威) +├── checkpoints/{001,002,latest}.json # 11. superstep 检查点 +├── output.md manifest.json # 14. 聚合交付物 + sha256 清单 +└── .lock # 6. acquire 的 flock 文件锁 +``` + +`tasks/01_researcher/TASK.md` 实拍(节选): + +```markdown +# 步骤任务:市场与技术调研,回填事实与缺口 +- 角色:调研专员(researcher) +- 期望产出:[notes.md fields.json gaps.json] +## 任务上下文资源与权威等级(已快照到本目录,只引用本地路径) +| 资源路径 | 权威等级 | 用途说明 | +| `context/goal.md` | **authoritative** | 运行目标 | 不可推翻 | +## 黑板快照(共享状态) +## 产出要求 +1. 按期望产出文件名落盘到本子目录; +2. 结构化事实回填 `fields.json`(字符串键值); +3. 发现信息缺口且无法假设时写 `gaps.json`([{"item":"…","blocking":true}])。 +``` + +产品的默认拆解(RulePlanner 四阶段骨架,`squad/planner_rule.go:79` 起)与演示计划同构, +真实代码里的步骤与交付约定: + +```go +// 通用四阶段骨架:梳理 → 专项 → 成文 → 终审 +{"step_01_organizer", "梳理目标背景与输入材料,产出事实与缺口", "organizer"}, +{"step_02_specialist", "围绕目标开展专业分析与推演", "specialist"}, +{"step_03_writer", "汇总各角色产出,撰写交付物初稿", "writer"}, +{"step_04_leader", "一致性终审与交付把关", "leader"}, +// 非末步 targets = ["notes.md","fields.json"](事实经黑板传给后步);末步 = ["output.md"] +``` + +注册表缺角色时 `Registry.Derive` 现场派生(名为"organizer 专员"等)并置 +`NeedsConfirmation` → `awaiting_confirmation` 挂起等用户确认编制。意图目录注入规划器/ +Leader 的形态见[意图识别](intent-recognition.md) 4.6 节例 5 的 catalog 实测输出。 + +--- + ## 5. 小队如何"协作" - **黑板(Blackboard)**是协作中枢:Facts(已确认事实)/Gaps(信息缺口)/ diff --git a/docs/guides/task-planning-and-execution.md b/docs/guides/task-planning-and-execution.md index 0ae08ef..785ccfc 100644 --- a/docs/guides/task-planning-and-execution.md +++ b/docs/guides/task-planning-and-execution.md @@ -125,6 +125,53 @@ prefixHash)作为可观测 `ContextObservation`。记忆注入在内核按请 --- +## 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)。