Compare commits

..
Author SHA1 Message Date
weichen a47754b494 Add EXT-1 inproc command handler proposal; align tag name to v0.2.0
Why: EXT-1 申请卡按 compat-policy §6 四要素成文,作为 surface 变更的提案记录;
compat-policy §5 的 v0.2.0-experimental 与 V1.2 定稿批复的 v0.2.0 口径不一致,统一为后者。
Evidence: 新增 docs/proposals/2026-10-05-ext-1-inproc-command-handler.md;§5 tag 命令改为 v0.2.0 并注明对齐来源。
Impact: 仅文档;下游与基座发版材料(12 号核对清单)按 v0.2.0 口径。
Refs: 2026-10-01-extension-compat-policy.md §5/§6;10-实施推进方案 V1.2 D2 批复。
2026-10-05 11:39:13 +00:00
6 changed files with 1 additions and 398 deletions
-19
View File
@@ -1,19 +0,0 @@
# Agent-System-Documents 项目事实
## 职责与边界
本仓保存系统架构方案、使用指南、验证报告与实施记录。目录入口见 [README](README.md)。
通用开发规范由 Dev 仓固定的 Development-Guidelines 提供;本仓不复制通用规则,
不承载业务实现、凭证、运行数据或部署制品。
方案、已实现、已验证、已合入与已部署分别记录;历史报告只证明其指定版本和环境。
系统级记录关联组件 PR 与 Dev 组合 PR,不以文档状态代替运行事实。
## 验证
纯文档修改执行 `git diff --check`,核对受影响相对链接、引用来源、版本及规则一致性。
报告中的通过断言关联真实命令、被测对象和证据来源;未验证事项写 NOT_RUN。
修改运行提示词或有效策略时,按行为影响追加对应系统验证,不能按普通文字编辑验收。
本仓没有可编译程序或独立业务测试套件。跨仓固定组合在 Dev 仓运行 `make test`;
过程记录中的命令和环境须按当次任务核实,不能机械重放旧运维命令。
+1 -1
View File
@@ -85,7 +85,7 @@ AGENT_SYSTEM_CONFIG=/etc/agent-system.json agent-system serve # 环境变量
- **长回复折叠**:终态后超过 800 字的回复自动收成「📄 长回复 · N 字(点击展开)」摘要行——流式期间全文可见,历史消息同样适用。
- **长结果进右侧卡片(默认关)**:设置 → 界面 →「长结果进右侧卡片」开启后,报告/纪要/方案/对比/文档草稿等**产物型**长内容进右栏收件箱(result 卡,可置顶/收纳);长正文优先流式三段式——`card_begin` 建卡(收件箱即时出现 active 卡)→ `card_append` 逐段追加(卡片实时刷新,事件按 250ms 窗口节流)→ `card_commit` 收尾(转 done 并锚定到对话);一次性短产物走 `submit_card`。中栏保留结论摘要 + 锚点部件(`📄 标题 · N 字 ▸ 展开全文 · ↗ 收件箱`)——锚点可无跳转展开全文,右栏收起时阅读不残废;解释型长回答不进卡。下一轮对话上下文自动回填卡片全文(「🧩 上下文构成」可见 card(n) 条目);run 异常终止时未收尾的流式卡自动置 done/failed 并标注「未正常收尾」,内容不丢。
- **上下文构成观测**:每条助手消息尾部「🧩 上下文构成」折叠芯片——该轮模型实际看到什么(系统提示/历史条数/附件/知识命中)与裁剪记录(历史超窗/工具结果超限),输入规模可解释。
- **附件**:📎 上传 md/txt/docx/pdf/xlsx/pptx/图片——文档经内置解析器(确定性代码解析:md/txt 直读、docx/xlsx/pptx 解 OOXML、pdf 提取文本层)转为中间表示注入对话;解析结果与警告显式可见(不静默)。
- **附件**:📎 上传 md/txt/docx/pdf/图片——文档经内置解析器(确定性代码解析:md/txt 直读、docx 解 OOXML、pdf 提取文本层)转为中间表示注入对话;解析结果与警告显式可见(不静默)。
- **输入增强**:`/` 唤起命令菜单(/new /mock /model /profile /help);`@knowledge` 在消息中触发知识检索注入。
### 3.3 mock 演示指令(无需模型)
@@ -24,8 +24,6 @@
`modules/backend/pkg/extension/surface_test.go` 用 AST 列出 `pkg/extension` 的导出类型与函数,与 `surface.txt` 比较。CI 通过 `make test`(含 `go test ./...`)执行。未更新快照的导出变更会失败。
导出**接口**同时记录其方法(`Host.ParseDocument` 形态)——只加方法、不改顶层名的变更同样触发快照失败(2026-10-06 EXT-5 起执行)。
依赖边界:`make test-boundaries` 拒绝
- `pkg/extension` 导入 `agent-system-backend/internal`
@@ -184,8 +184,6 @@ flowchart TB
每阶段单独任务。依赖:R0 先做。R1 在三个库内并行。R1 之后做 D1。D1 之后 N1、Q1、I1 可以同时做。它们完成后再做 N2、Q2、I2。N3 在 N2 之后。Q3 在 Q2、N1、I1 之后。
N1 / Q1 / I1 的实施方案见 [2026-10-07-agent-stack-n1q1i1-plans.md](2026-10-07-agent-stack-n1q1i1-plans.md)。
### R0 组合一致性
Dev 根 `go.work`。检查脚本对比 Backend `deps/` 与 Dev 指针。`lint-boundaries` 加上文第 3 节的依赖断言。核对 Releases 导出是否跟随 `go.work`。
@@ -1,51 +0,0 @@
# EXT-5:宿主句柄开放内置文档解析(含 xlsx/pptx 内置解析器)
状态:**实施中(2026-10-06)**;owner:weichen;主任务:Dev 仓 `feature/ext-5-host-parse`。
规范依据:[扩展面兼容与演进](2026-10-01-extension-compat-policy.md)(§6 扩展点申请模板);前序 [EXT-1](2026-10-05-ext-1-inproc-command-handler.md)(Host 最小集)。相关:EXT-4(下游注册 inproc 解析器,备选、未提交)——方向相反,本申请不依赖它。
撰写日期:2026-10-06。
## 1. 申请四要素(compat-policy §6)
- **场景**:Agent-System-RDM 的 F1 上传主链(`rdm.upload`,PR #103)需要解析用户上传的文档;基座 `internal/ingest` 有内置解析器但没有经 `extension.Host` 开放——下游拿到的只有 `ArtifactGet/ArtifactContent`(原件字节),要解析就得自带一份解析代码(RDM 现状:自带 md/txt/docx 解析器,pdf/pptx/xlsx 一律回「不支持」)。MS1(W4,11-01)验收第 4 条要求「报价单 Excel + 图文混排 PPT 解析完整性核对」,当前两条路都走不通:句柄没有解析方法,基座自身也没有 xlsx/pptx 解析器。
- **希望符号**:`pkg/extension` 的 `Host` 新增一个方法:
```go
// ParseDocument 用基座内置解析器把文档字节解析为 ParsedDocument(EXT-5)。
ParseDocument(ctx context.Context, filename, mime string, content []byte) (core.ParsedDocument, error)
```
同时 `internal/ingest` 新增 `builtin:xlsx`(xlsx/xlsm)与 `builtin:pptx`(pptx/pptm/ppsx)两个内置解析器——这两个是基座能力补齐,不依赖 EXT-5,上传/附件路径同步受益。
- **为何现有注册点组合不出来**:`ParsersDescription` 只是注册表描述(设置页/文档用),没有执行入口;`PluginParserDecl.Tool` 仅接受内置名或 sidecar 工具名(core/plugin_v2.go 注释明示);Host 最小集里与文档相关的只有工件只读两个方法。下游唯一替代是复制基座解析代码进自己仓库——违背「引擎在基座」的接缝表裁定。
- **兼容**:只加符号。实验窗口内给 `Host` 接口加方法会破坏第三方 fake 的编译(compat-policy §1 允许,须改快照并在提案里写一句);`SurfaceVersion` 保持 `2.0.0-experimental`。本次同时把契约快照升级为**记录导出接口的方法**(此前只记顶层名,加方法这类变更检测不到)。
## 2. 接口语义
- 分派按文件扩展名,与 `ingest.Parse` 完全一致(同一实现,`hostcaps.parse` 委托)。
- 返回 error 的只有两种情况:`ctx` 已取消;输入超过 100 MiB(与上传端点 `io.LimitReader(file, 100<<20)` 同口径)。除此之外的「不可解析」都返回带 `Warnings` 的文档(显式失败,不静默),`Meta.Confidence` 相应降低。
- 纯计算:不读写 owner 数据、不走存储。要解析工件原件,先调 `ArtifactContent`(owner 作用域由它保证),把字节传进来——不另造句柄、不新增凭证面。
- 参数为位置参数(filename/mime/content),与 Host 既有方法风格一致;不引入 options 结构。
## 3. 新解析器格式与限制
**xlsx/xlsm(builtin:xlsx)**:工作表顺序取 `workbook.xml` 的 sheets 列表(不按文件名),target 经 rels 解析(相对/绝对路径);共享字符串含富文本 run(排除注音 `rPh`);单元格覆盖 `t=inlineStr/str/b/e/n`(布尔输出 TRUE/FALSE、错误值原样)、公式取缓存值(无缓存输出 `=公式`)、日期按内置格式 ID 与含 y/m/d/h/s 记号的自定义格式转 ISO(1900/1904 双日期系统);稀疏列按单元格引用就位,去行尾空与全空行;隐藏工作表标题标「(隐藏)」;每个工作表产出一个 heading 块 + 一个 table 块,空表跳过。
**pptx/pptm/ppsx(builtin:pptx)**:幻灯片顺序取 `presentation.xml` 的 `sldIdLst`;标题占位符(title/ctrTitle)产「幻灯片 N:标题」heading 块;正文形状每形状一个 para 块(`a:br` 换行);组合形状递归;`mc:AlternateContent` 只取 Choice 分支;graphicFrame 表格产 table 块(合并单元格位置留空以保持列对齐);备注页正文产「备注:…」;跳过页码/日期/页脚占位符。图片、SmartArt 不解析(无 OCR/图形理解,属远期档)。
**旧二进制格式 doc/xls/ppt**:不做解析,Warnings 给可操作提示(请转存 docx/xlsx/pptx 后重传)。
**上限**(包级变量,测试可覆盖):单元格总数 200000、列数 1024、zip 内单部件解压 64 MiB(zip 炸弹防护,docx 一并受益);超限保留已解析部分 + Warnings + `Confidence` 降 0.6,原件不受影响。
## 4. 验证
- 手工构造 OOXML zip 的单元测试:xlsx(表序/富文本/公式/日期/稀疏列/隐藏表)、pptx(sldIdLst 序/标题/组合/AlternateContent/表格/备注)、失败路径(损坏 zip/缺 workbook.xml/部件超限/超上限)。
- openpyxl 与 python-pptx 现场生成真实文件的轮测(依赖缺失时 t.Skip;CI 已装这两个包)。
- `internal/app` 走真实 `applyCompiled → cmdbus.Dispatch → inprocHost` 链路:alice 解析自己的 xlsx 工件成功、bob 同 ID 被拒(owner 作用域不因新方法放宽);ctx 取消与超限两条 error 路径。
- 上传端点 e2e:xlsx 上传返回 `parser=builtin:xlsx` 且无 Warnings,知识检索命中单元格文本。
- surface 快照按「顶层名 + 接口方法」新口径重建并通过。
## 5. 不在本文范围
- sidecar `/host/v1` 的 HTTP 解析路由(sidecar 形态可继续读字节走自己的解析;出现真实需求再申请)。
- MinerU 交叉验证、OCR、SmartArt、旧 OLE 格式解析。
- 上传超过 100 MiB 被静默截断的问题、`kindOf` 与 `artifactKindOf` 口径不一致——已登记为 EXT-5 盘点发现的缺陷,另行修复。
- 下游把上传链路切到 Host 解析、RDM pin 升级到 v0.2.0:RDM 仓自己的任务卡。
@@ -1,323 +0,0 @@
# N1 / Q1 / I1 实施方案:内核 harness、图执行、命令化识别
状态:**proposed(2026-10-07)**;owner:weichen;主任务:Dev 仓 `feature/agent-stack-n1q1i1-plans`。
上游:[2026-10-02-agent-stack-completion.md](2026-10-02-agent-stack-completion.md) §5 分期(下称「总提案」,行号以该文件为准);审计:[2026-10-02-agent-stack-audit.md](../records/2026-10-02-agent-stack-audit.md)。
规范依据:Development-Guidelines(workflows/delivery.md、standards/collaboration.md、standards/agent-execution.md、standards/verification.md);本文是项目级消费,不复制其正文。
本文把总提案的 N1、Q1、I1 三个阶段落成可启动的实施方案:现状基线、工作包分解、文件级落点、测试与验收、风险。本文不实施代码;三个阶段的实现各走各的任务(总提案「每阶段单独任务」)。
---
## 1. 共同基线与三任务协调
### 1.1 现状基线(撰写日实测)
| 仓 | pin | 说明 |
|---|---|---|
| Agent-Native | `3a110a728cf520cfbbbafc0c4c75f14bf78bec3f` | durable-rewind-3 与 permission-hooks(PR #173)之后 |
| Agent-Squad | `5a57636cc0b8426a86a72bbbdd5b7f30e4fcac88` | 同上 |
| Agent-Intent | `65aaebca1ea69ab7d48eda51d33ef1d4eea5a0bf` | agent-stack-d1(R2)之后 |
| Backend | `e7fb276ee9b3af51329a0c6df27517184b26edee` | v0.1.0-4 |
| Core | `0b5c7747a3165bf0d0f2219204b7b70ec3ed7645` | v0.1.0 |
两个要点:
- **审计基线已旧**。审计写于 native `c6d867c` / squad `590fd86`;R1/R2 之后多条审计项在当前 pin 已修(native 侧 N1/N7/N8/N21/N22/N30/X4,squad 侧 Q1–Q5/Q7/Q14–Q16/Q18/Q19)。本文逐节标注「已修 / 仍开放」,实施时不得以审计原文为唯一依据,须以当前代码为准。
- **D1 的借力点已成**:三库核心互不依赖(native `go.mod` 只依赖 error-code-system 与 logging-system)、Options 已按端口拆分、跨库类型只出现在 adapters 子模块。N1/Q1/I1 都假设这个地基,不再返工。
### 1.2 分支与合入协调
按 AGENTS.md「同批互不依赖的子任务用同一 R0 的兄弟分支」:
- 三个任务分支:`feature/agent-stack-n1`、`feature/agent-stack-q1`、`feature/agent-stack-i1`,从同一权威 `origin/main`(同一 R0)切出,互不嵌套。
- 每个任务的 PR 链固定为三段、按序合入:**库仓 PR → Backend 仓 PR(bump 嵌套 pin + 适配)→ Dev 仓 gitlink PR(bump 顶层 pin 与 modules/backend)**。R0 已落地的检查脚本核对 Backend `deps/` 与 Dev 指针一致,不允许只推一层。
- 三任务并行时的冲突面:N1 与 I1 都动 Backend(N1 在 `internal/app`、`internal/runtime`、`internal/runmgr`;I1 在 `internal/server`、`internal/intentgate`、`internal/runmgr`),`internal/runmgr` 是唯一重叠文件,**后合入者按 `make task-sync` 并入最新 main 后解决**;Dev 仓 gitlink pin 同理,后到者 sync 后重指。
- Q1 若涉及 `modules/backend/internal/squadadapter` 消费方适配,改动限制在「驱动循环跟上新引擎 API」范围内;编排收敛(WorkflowDef 编译、internal/workflow 退役)是 Q3,不在 Q1。
### 1.3 共同验收纪律
- 每阶段都带 E 评测(总提案 §5 E 节):Native 的对照用脚本化模型输出,不把模型自报当通过。
- 证据按 AGENTS.md「证据与验证门禁」:完成声明的 precheck / task-sync / 测试三段输出由 `make evidence` 写入任务卡;测试通过的权威源是 CI(PR HEAD 上 `make test`+`make build`),本地输出是 SELF 级辅助线索。
- 各阶段验收条款在本文 §2.6 / §3.6 / §4.6 给出机器可核对的形式,与总提案对应条目逐条映射。
---
## 2. N1 内核 harness
总提案原文(§5 N1):「AgentSpec、HookBus、PermissionPolicy、三类 guardrail。CompletionGate 与提醒从 Backend 下沉。`adapters/native` 透传钩子。Backend 改为消费,A/B 对照直连行为。」
### 2.1 现状与 gap
| 需求项 | 现状(证据) | gap |
|---|---|---|
| AgentSpec | **不存在**(全库无此类型)。最接近的是 `deps/agent-native/runtime/subagent/subagent.go:21-33` 的委派 `Request`(Goal/Inputs/WriteScope/NonGoals/DoneCriteria/Deliverables + Deadline/MaxToolCalls);backend 子代理画像是 `prompts.SubagentSpec` + `fallbackSubagentSpec`(`modules/backend/internal/runtime/sub_loop.go:18-27`) | 类型、文件格式、插件校验入口全缺 |
| HookBus | 已有:`runtime/hooks/hooks.go:56-107`(On/Emit,决策优先级 block/deny>ask>modify>allow)。事件枚举 hooks.go:9-20 有 SessionStart/UserPromptSubmit/PreToolUse/PermissionRequest/PostToolUse/PostToolUseFailure/Stop/SubagentStart/SubagentStop/PreCompact。**实际发射点只有 3 处**:`runtime/loop.go:512`(Stop)、`:532`(PreToolUse)、`:557`(PostToolUse/Failure) | 缺 PostToolBatch、SessionEnd 两个事件;6 个已枚举事件无发射点;无「已在续跑中」防递归标志(hooks.go:33-43);**Ask 决策被当拒绝处理**(loop.go:534-540),无 ask→宿主审批→放行回路 |
| PermissionPolicy | 以函数式存在:`runtime/policy/policy.go` 三档 Mode(:9-13)、Rule(:30-34)、Decide(:55-93,deny>计划禁写>本会话放行>ask>auto_edit>allow)、TraitsFor(:113-128)、ClassifyCommand(:145-158)、Visible(:96-101)。backend 直接消费(`internal/runmgr/runmgr.go:681-685`) | 无 Policy 命名类型、无规则序列化形态;会话放行记忆在 backend 内存 map(runmgr.go:265-286),未下沉 |
| 三类 guardrail | **不存在**(输入/输出/工具三层全无,grep 无命中);内核只有 `RepeatGuard` 布尔(loop.go:93-95) | 三层端口与执行点全缺 |
| CompletionGate 下沉 | 在 backend 直连路径三层串联:`deliverableGate`(`internal/runtime/llm.go:477-510`,上限 `maxDeliverableGuards=2`)→ `checkDeliverables`(`runtime/verify.go:48-84`,工件存在+非空+`quality.ScoreBytes`)→ `runVerifier`(verify.go:86-122,开关 `runtime.verify`,提示词 `aux.verify.final`)。经自建 hooks 总线挂一个 Stop 处理器注册进内核(`internal/runtime/llm_direct.go:162-172`) | 依赖 `core.ArtifactRef`、`rc.Artifact`、`quality.ScoreBytes`、提示词库——未端口化;**子代理(sub_loop.go:98-112 无 Stop/Hooks)与小队步骤(app.go:202-217 未注入 Stop)都无此门禁**;小队只有事后 `claimsFalseDeliverable` 判失败(`runtime/squad_runner.go:90-92`) |
| 提醒下沉 | backend 侧 `directRun.reminders`(verify.go:223-250,连续 3 个工具轮未更新计划/待办则提醒,`reminderToolGap=3`、`maxChecklistReminders=3`,用库侧 `reminders.Bus` 节流去重)。内核有通用 `Reminders func(turn)` 回调(loop.go:90-92) | `rc.Todos()/Plan()` 与 `core.TodoList/PlanSnapshot` 是产品接口,未端口化;小队/子代理路径未注入 |
| adapters/native 透传 | **库侧管道已修**:`GateOptions`(`adapters/native/backend.go:70-80`,含 BeforeTool/AfterTool/Stop/Reminders/RepeatGuard/Hooks/Ask)、透传(:370-372)、`gateBus`(:464-496);squad 侧映射(`deps/agent-squad/adapters/native/register.go:39-49,92-96`) | **装配未接通(N31 剩余部分)**:backend 组合根只注入 `RepeatGuard:true` 与 plan 模式 BeforeTool(`internal/app/app.go:207-217`);直连与子代理绕过 `exec.Backend` 直接调 `nruntime.Run`(llm_direct.go:173、sub_loop.go:98) |
| Backend 改为消费 | `adapters/native.Backend` 只实现旧门面 `exec.Backend`(backend.go:130/140),未实现 `ContractBackend`;`conformance.Suite`(conformance/fake.go:106,三条用例)无真实实现调用方 | ContractBackend 实现与 conformance 接入 |
### 2.2 工作包分解
**WP1 AgentSpec 与插件校验**(新类型,库侧 `runtime/subagent` 或新 `runtime/agentspec` 包)
- 定义 `AgentSpec{Name, Description, Prompt, Tools, DisallowedTools, Model, PermissionMode, Skills, MaxTurns, MemoryScope, Effort, Background, WorktreeIsolation, OutputSchema}`,字段对齐总提案 §2.1 子代理行(Claude subagents frontmatter 集 + `outputSchema`)。
- 文件格式:Markdown frontmatter(`agent-*.md`),加载器在库侧;backend 现有 `prompts.SubagentSpec` 迁移为读 AgentSpec 文件,`fallbackSubagentSpec` 保留为内置兜底。
- **校验红线**:插件清单里的 AgentSpec **不得声明 hooks、MCP、权限模式**(总提案 §2.1 权限行),校验在插件装载期 fail-closed。
- 测试:字段 round-trip、缺省值、插件声明 hooks/权限模式被拒、backend 旧画像到 AgentSpec 的等价映射。
**WP2 HookBus 补全与 ask 回路**
- 事件枚举加 `PostToolBatch`、`SessionEnd`(对齐总提案 §2.1 事件名单);`Call` 增加「已在续跑中」标志位,hook 触发续跑时下游可见、禁止递归触发 Stop 系事件。
- 发射点补齐:`SessionStart`(Run 入口)、`UserPromptSubmit`(用户消息入列)、`PermissionRequest`(gateTool ask 时)、`SubagentStart/SubagentStop`(委派创建/收口)、`PreCompact`(compact 前)、`SessionEnd`(Run 出口)、`PostToolBatch`(工具批结束)。
- **ask 回路(本阶段最关键的行为变化)**:`gateTool` 收到 ask 不再当拒绝(loop.go:534-540 现状),改为发 `PermissionRequest` 并等待宿主决策端口;宿主默认实现 = backend HITL 确认卡(runmgr.go:384-424 现有 AskHITL/waitHITL 机制接为端口实现)。`deny` 语义不变;`block` 不变。
- 风险与兜底:ask 从「拒绝」变「挂起审批」是行为变化;用 A/B 对照(WP6)证明「无 hook 注册时行为与现状逐字节一致」,把行为变化严格限制在「注册了审批端点」的场景。
- 测试:每个新发射点一例;ask→批准→执行、ask→拒绝→配对结果两条回路;续跑标志防递归;与既有 `ab_gate_test.go` 对照不回归。
**WP3 PermissionPolicy 命名类型与会话放行下沉**
- 在 `runtime/policy` 增加 `Policy` 结构体:`{Mode, Rules []Rule, SessionAllow AllowStore}`,`Decide` 收为方法;`AllowStore` 是端口(Get/Add/List,`key = 工具+参数模式`),库内给内存实现,backend 把 `sessionAllows`(runmgr.go:265-286)替换为该端口的实现(本会话内存即可,持久化形态留给 N3 的 Memory/RunState)。
- `Visible`、模式切换(含 `ExitPlanMode`,runmgr.go:257-263)改走 `Policy`。
- 测试:规则优先级矩阵在结构体形态下全等复现现有 `policy_test.go` 四例 + backend `perm_test.go`;会话放行跨调用存活。
**WP4 三类 guardrail 端口**
- 端口定义:`InputGuardrail`(只作用于入口智能体的用户输入)、`OutputGuardrail`(只作用于给出最终结果的那个智能体)、`ToolGuardrail`(每次工具调用)。形态 = `func(ctx, payload) (allow / deny / modify + 原因)`,与 HookBus 决策词汇一致。
- 执行点:输入在 UserPromptSubmit 处、输出在 Stop 判定前、工具在 PreToolUse 链内。guardrail 与 hook 的关系写清:guardrail 是类型化语义层,hook 是通用事件层,guardrail 以 hook 实现注册。
- 测试:三层各自 deny/modify/allow 各一例;只入口、只最终输出的作用域限定各一例。
**WP5 CompletionGate 与提醒下沉**
- 下沉的不是实现而是**组合点**:库侧新增「输出 guardrail 注册位 + 提醒源注册位」,产品依赖端口化:
- 工件存在/非空/质量分:端口 `ArtifactVerifier`(backend 用 `rc.Artifact` + `quality.ScoreBytes` 实现,verify.go:48-84 逻辑迁入 adapters 侧实现);
- 独立验证器:端口 `FinalVerifier`(backend 用 `aux.verify.final` 提示词 + 独立模型调用实现,verify.go:86-122 迁入);
- 计划/待办提醒源:端口 `ProgressProbe`(backend 用 `rc.Todos()/Plan()` 实现,verify.go:223-250 迁入);`maxDeliverableGuards=2`、`maxVerifyContinuations=3`、`reminderToolGap=3`、`maxChecklistReminders=3` 等阈值随端口配置走,默认值不变。
- 迁移后 `deliverableGate`/`checkDeliverables`/`runVerifier`/`directRun.reminders` 在 backend 只剩端口实现与装配,判定逻辑(正则族、续跑上限、节流)进库。
- 测试:库内脚本化夹具复现 backend `llm_test.go`/`llm_verify_test.go` 的全部门禁用例(GuardTwiceThenNotice、验证器拒绝后通过、垃圾失败关闭、提醒节流等),确保下沉后逐条等价。
**WP6 Backend 装配接通与 A/B 对照(N31 收口)**
- `internal/app/app.go`:直连、子代理(sub_loop.go:98-112)、小队(squadadapter `AgentNativeOptions`,app.go:207-217)三条路径注入同一套 `Hooks/Stop/Reminders/Ask`;小队经 squad→native 两级 GateOptions 透传(管道已在,只装配)。
- 直连与子代理改为经 `adapters/native` 消费(不再裸调 `nruntime.Run`);`adapters/native.Backend` 实现 `ContractBackend`(补 Descriptor),`conformance.Suite` 挂到 native 后端跑(现有三条用例起步)。
- **A/B 对照(提案验收核心)**:同一组脚本化模型输出(含停止、提醒、工具拒绝、HITL 分支),分别在「旧直连装配」与「新下沉装配」下跑,断言停止原因、提醒次数、工具拒绝序列一致。`runtime/ab_gate_test.go` 是库内雏形,本 WP 把它扩成 backend 侧对照。
- 小队路径行为变化明示:小队步骤首次获得 Stop 门禁与提醒——这是本阶段有意的行为拉齐,写进 PR 影响段。
### 2.3 涉及仓与落点汇总
- Agent-Native:`runtime/hooks`、`runtime/policy`、`runtime/loop.go`、`runtime/subagent`(或新 agentspec 包)、新 `runtime/guardrails`(或并入 hooks)、`adapters/native`、`conformance`。
- Backend:`internal/app/app.go`、`internal/runtime/{llm.go,llm_direct.go,verify.go,sub_loop.go,squad_runner.go}`、`internal/runmgr/runmgr.go`(AskHITL 接端口、sessionAllows 替换)、`internal/squadadapter`(透传配置)。
- Dev:两层 pin。
### 2.4 实施顺序
WP2(ask 回路)与 WP3(Policy 类型)先行(其余都依赖决策与权限语义)→ WP1 AgentSpec、WP4 guardrail 端口 → WP5 下沉 → WP6 装配与 A/B。每 WP 单独提交,库内测试绿了再进下一 WP。
### 2.5 风险
- ask 语义变化是最大风险点:必须有 A/B 对照证明默认行为不变(见 WP2/WP6)。
- 下沉搬移正则族与阈值时容易丢行为:WP5 的「逐条等价」测试先行(先在库内写好对现网行为的刻画,再搬)。
- 小队首次上门禁可能暴露存量提示词的虚假交付习惯:`claimsFalseDeliverable` 与 Stop 门禁并存期要观察误报率,必要时给小队入口单独的宽松阈值(配置项,不硬编码)。
### 2.6 N1 验收(对照总提案 §5 N1 验收逐条)
| 总提案条款 | 机器可核对的验收 |
|---|---|
| 同一组脚本化模型输出,下沉前后停止原因、提醒次数、工具拒绝一致 | backend 侧 A/B 对照测试在 CI 通过;对照夹具覆盖 deliverableGate 命中/未命中、verifier 拒绝/通过/垃圾、提醒触发/节流、工具拒绝 |
| 插件里的 AgentSpec 不能带钩子和权限模式 | 插件装载测试:声明 hooks/permissionMode 的 AgentSpec 被拒且错误可定位到字段 |
## 3. Q1 图执行
总提案原文(§5 Q1):「superstep、三档检查点、attempt id、interrupt / resume。`Needs` 驱动依赖。读写集。可组合 Termination。Store 接口。platform 的 SQLite 成为 Store 的一个实现,不再单独驱动一套运行。」
### 3.1 现状与 gap
| 需求项 | 现状(证据) | gap |
|---|---|---|
| superstep | 无。执行是「线性步骤数组 + 状态机扫描」:`flowDriver.advance`(`deps/agent-squad/squad/workflow.go:22-101`)一个大循环扫到挂起/终态;leader 是独立大循环(leader.go:178-372);`WorkflowPlan.CurrentStepIndex`(models.go:124)是从未写入的死字段 | 整个推进模型 |
| 三档检查点 | 无 checkpoint 类型。最接近的是「步骤完成即 `st.save()` 全量落盘」(workflow.go:367-371,同步单档)+ 挂起时 pending.json。原子写已修(`AtomicWrite`,workspace.go:307-349) | 检查点类型与退出时/异步/同步三档全缺 |
| attempt id | 只有计数器 `Attempts`(models.go:93-94)与不变的 `ExecutionID = runID+"-"+stepID`(workflow.go:286);恢复靠「running + Attempts>=2 + HITL 模式」启发式(engine.go:879-925),无产物校验 | attempt id、幂等键、「副作用次数=1」的判定基础 |
| interrupt/resume 同一对原语 | 挂起原语面向人:`hitlGate`/pending.json + `Respond`/`Resume`(engine.go:481,928);事件唤醒是旁路 `WaitCondition` + `SweepWakeups`(engine.go:1080-1163);`SweepExpired`(engine.go:1107-1130)**无调用方,是死代码** | 统一原语;SweepExpired 接线 |
| Needs 驱动依赖 | 字段已声明(models.go:90)但**调度完全不读**(全仓唯一引用即声明行);调度只看「相邻 Parallel 标记」(workflow.go:44-72) | DAG、拓扑排序全缺 |
| 读写集 | 无 Reads/Writes 字段;「写同一工件必须串行」无判定;并行安全靠 `st.mu` 串行化写回 | 字段与调度判定全缺 |
| 可组合 Termination | 无该类型。四个停止条件硬编码分散:leader 轮次(leader.go:207/371,达上限直接 leaderFail 无降级,Q23 仍开放)、修复预算(engine.go:36-37 默认 1,workflow.go:583-596 扣减,engine.go:698 人工 retry 加预算无上限,Q26 仍开放)、唤醒上限(hitl.go:68 `MaxWakeupFires=5`)、StepTimeout/HITL 超时(engine.go:32-33、hitl.go:23-30、hitl.go:119-137) | 类型与 AND/OR 组合全缺 |
| Store 接口 | squad 核心无 Store 端口;运行状态由引擎直接管文件(runstate.go:52-76 一次写六文件);`store.FileStore` 只管 roles/blueprints;`store/eventsink.go` 是事件 jsonl 装饰器 | 接口不存在 |
| platform 收编 | `adapters/sqlite`(包名 platform)是**独立第二套运行系统**:23 张表无 run/step/checkpoint 表(db.go:51-149);task 队列自有一套 Claim/FinishTask/RecoverOrphans(queue.go:194-348);`RunInProcess` 绕过 Executor 端口直驱 agent-native(runtime.go:201-229,X6 仍开放);与 squad 文件态 runs 双真相源并列挂载(cmd/agent-squad/server/server.go:31-41,94-96) | 双系统合一 + schema 扩展 + X6 |
| 顺带瑕疵(Q1 会踩到) | 并行组 `wg.Wait()` 等全部结束才收口(workflow.go:172);黑板是快照不是增量(blackboard.go:12-38);动态调整直接 splice Steps(workflow.go:510-534)——与「图结构不变」的恢复要求冲突;产品侧消费方是 squadadapter 的外层 Run/Resume 循环(`modules/backend/internal/squadadapter/adapter.go:92-117`、entry.go:115-139) | 在设计中显式裁定,见 §3.2 WP1/WP4 |
已修不再返工(R1/R2 已落):原子写、授权/预算落盘、批准键写入(Q1)、中断步骤挂起分流(Q4 部分)、跨进程锁(Q5)、Respond 幂等(Q7)、WAL(Q19)、事件 seq 续号(Q18)。
### 3.2 工作包分解
**WP1 图模型与 Needs 调度**
- `StepSpec` 增补 `Writes []string`(写集,工件路径)与 `Reads []string`(读集,可空=不声明);`Needs` 从死字段激活为依赖声明。
- 编译期(`planAndExecute` 入口)把 Steps 编译成图:节点=步骤,边=Needs;拓扑分层(Kahn),同层可并行。**兼容映射**:无 Needs 的旧蓝图按「相邻 Parallel」规则自动分层(相邻且 Parallel=true 的步骤入同层),行为不变量由现有测试保证。
- **动态调整裁定**(与恢复语义冲突的解决):`AllowDynamicAdjustment` 步骤的 splice(workflow.go:510-534)改为「追加节点生成新图版本」——图带 `version`,已完成的 superstep 结果按节点 id+图版本键保存;恢复时要求图版本与检查点一致,不一致则拒绝恢复并提示重新规划(对齐总提案 §2.2「恢复要求图结构不变」)。
- 调度隔离:同层内**写同一工件(Writes 交集非空)的步骤强制串行**;只读步骤并行。交集判定在分层时做,拒绝或串行化在调度时做(默认串行化 + 事件提示,不拒绝)。
- 测试:Needs 分层顺序、环检测拒绝、旧蓝图兼容映射等价、写集交集串行化、图版本不一致拒绝恢复。
**WP2 superstep 推进**
- `advance` 重写为 superstep 循环:每 superstep = 「取当前可执行层 → 层内调度(写集串行)→ 层结束做检查点」。leader 循环映射为「leader 节点 + 动态追加成员节点」的图形态(委派=追加,close=终止边),leader.json 的状态并入检查点。
- `CurrentStepIndex` 死字段删除或改作「当前 superstep 序号」。
- 并行组 fail-fast 裁定:保持 cancel 传播(现状),superstep 边界统一收口失败,不再 `wg.Wait()` 空等——失败即终止本 superstep 剩余节点。
- 测试:多蓝图(standard/router/leader)在新循环下现有 engine_test/leader_test 全绿;层边界事件序列断言。
**WP3 attempt id 与幂等**
- 每次执行生成 `AttemptID = runID/stepID/N`(N=Attempts+1),随步骤状态落盘;执行事件、产物、副作用记录都挂 AttemptID。
- 恢复判定升级:`recoverInterrupted`(engine.go:879-925)从启发式改为「attempt 记录 + 产物校验」——步骤标 running 且其 attempt 无完成记录时,按 HITL 模式分流(现状三分支保留),选择 retry 时新 attempt 执行前校验该步骤已登记副作用(产物文件存在性)并在事件流标注「第 N 次尝试」。
- 验收硬指标「副作用次数=1」的判定基础:已完成(有完成记录)的 attempt 永不重跑;瞬态重试(MaxTransientStepInvocations=2,workflow.go:260)共享同一 AttemptID 的「子尝试」标记,不计为新 attempt。
- 测试:杀进程于每个 superstep 边界(见 §3.6);retry/skip/abort 三分支;瞬态重试不增 AttemptID。
**WP4 Store 端口与三档检查点**
- 新端口 `Store`(squad 包内):`SaveRun/LoadRun/SaveStep/LoadStep/SaveAttempt/AppendEvent/LoadEvents/SaveCheckpoint/LoadCheckpoint/ListRuns`,以 run(thread)为键。
- 现状文件态收编为 `FileStore`(runs/ 目录六文件 + pending.json + leader.json 的全部读写走 Store 接口);检查点三档:`sync`(superstep 边界同步写,默认)、`async`(批量刷盘)、`on_exit`(进程退出时)——档位在 RunOptions 声明,默认 sync。
- 黑板裁定:Q1 只把黑板快照纳入检查点载荷;「可回放增量」留给 Q2 拓扑(总提案 §2.2 把黑板增量列在拓扑行)。
- 测试:三档各自的一致性;检查点以 run 为键的隔离;FileStore 与现状文件布局的读取兼容(老 runs 目录可恢复)。
**WP5 SQLiteStore 与 platform 收编**
- `adapters/sqlite` schema 增加 `run / step / attempt / checkpoint / event` 五表;`SQLiteStore` 实现 Store 端口。
- **收编裁定**(双真相源合一):platform 的 task 队列不再自行驱动执行——`Claim` 到的任务翻译为 squad run 并交给引擎(Store=SQLiteStore),`RunInProcess` 改走 `Executor` 端口(修 X6);platform 的 wakeup 表与 squad 的 WaitCondition 合并为一处(保留 squad 语义,platform 表改为投影或下线,迁移期双写只读旧表)。
- 过渡:platform 现有 23 表中非运行类(workspace/member/issue/skill/autopilot/webhook/chat/project 等)不动;本 WP 只合一「运行」这一层。
- 测试:同一蓝图分别在 FileStore 与 SQLiteStore 上跑通 conformance(见 §3.6);孤儿恢复(RecoverOrphans)在合并后语义不变;server 并列挂载改为单引擎。
**WP6 可组合 Termination**
- 新类型 `Termination = And(...)|Or(...)|MaxTurns(n)|RepairBudget(n)|MaxWakeups(n)|Timeout(d)|StepTimeout(d)`;四处硬编码收编为默认组合:`And(MaxLeaderTurns(8), RepairBudget(1), MaxWakeups(5))` + 步骤级 StepTimeout(默认 10min,语义不变:重试共享截止,Q24 维持现状并写进文档)。
- 顺手关账:leader 达上限的降级收口(Q23:不再裸 leaderFail,走一次收口质检或挂起人工);人工 retry 加预算设上限(Q26:默认累计不超过初始值的 2 倍,可配);`SweepExpired` 接入 serve 周期循环(与 SweepWakeups 并列),补 Q6 另一半。
- 测试:AND/OR 组合矩阵;三个关账项各一例回归。
**WP7 interrupt/resume 统一原语**
- 把「HITL 挂起(pending.json)」与「事件等待(WaitCondition/SweepWakeups)」统一为 `Interrupt{Kind: human|event|expired, Key, Payload}` / `Resume(Key, Response)` 一对原语;pending.json 与 wakeup 成为 interrupt 的两种持久化形态。
- 对外 API(Respond/Resume/steer)语义不变,内部改走统一原语。
- 测试:两种 interrupt 的挂起/恢复/过期三态;API 兼容(现有 server_test、engine_test 绿)。
### 3.3 涉及仓与落点汇总
- Agent-Squad:`squad/{workflow.go,engine.go,leader.go,models.go,runstate.go,blackboard.go,hitl.go}`(重写面大)、新 `squad/{graph.go,superstep.go,store.go,termination.go,interrupt.go}`、`adapters/sqlite`(schema+Store)、`store/`(FileStore 归位)、`cmd/agent-squad/server`(挂载合一、SweepExpired 接线)。
- Backend:`internal/squadadapter`(驱动循环跟上引擎 API;入口/步进循环适配)。
- Dev:两层 pin。
### 3.4 实施顺序
WP1 图模型 → WP2 superstep(旧行为兼容先绿)→ WP3 attempt id → WP4 Store+检查点 → WP7 统一原语 → WP6 Termination → WP5 SQLite 收编排最后(风险最高,前面 WP 已各自可独立交付)。
### 3.5 风险
- **advance 重写是心脏手术**:WP2 必须先建立「旧蓝图兼容等价」的测试网(现有 engine/leader/recovery/r2_regression 套件就是网底),重写中任何一绿变红即停。
- 动态调整与图版本:产品端目前依赖动态调整的蓝图(如有)在恢复时会被要求重新规划——行为变化写进 PR 影响段,并把「图版本不一致」的报错做成可操作提示。
- platform 收编(WP5)触及双系统合一与 X6,体量接近一个小阶段:允许 WP5 在 Q1 任务内再拆子 PR 序列(Store 实现先行、驱动合一随后),但不允许只交一半——「不再单独驱动一套运行」是验收条款。
- 消费方只有 squadadapter 一处(backend)与 squad CLI/serve 一处,适配面可控。
### 3.6 Q1 验收(对照总提案 §5 Q1 验收逐条)
| 总提案条款 | 机器可核对的验收 |
|---|---|
| 在每个 superstep 边界杀进程再恢复,已成功步骤的副作用次数为 1 | 新测试:脚本化蓝图(≥3 个 superstep),逐边界 kill -9 后 Resume,断言每个已完成步骤的产物写入次数=1、事件流无重复完成记录 |
| 两个写同一工件的步骤不会并行 | 调度测试:写集交集步骤的执行区间不重叠(事件时间戳断言) |
| 文件 run 与 SQLite 不再各记一份步骤状态 | conformance 套件在 FileStore 与 SQLiteStore 双实现上通过;server 单引擎挂载;platform 侧无独立 run 状态表残留(schema 断言) |
## 4. I1 命令化识别
总提案原文(§5 I1):「命令列表与 ActionRef。中文匹配的词界或最短词长。同分时可按 priority 决胜。Catalog 白名单与 top-k。Backend 唯一 Router 消费 ActionRef。」验收点名审计 I1、I2、I3、I5 有回归(I1/I2 在当前 pin 已修并有测试,I3/I5 开放,见下)。
### 4.1 现状与 gap
| 需求项 | 现状(证据) | gap |
|---|---|---|
| 命令列表 | **不存在**。`Result = {Input, Branch, Top *Candidate, Candidates, SnapshotRevision}`(`deps/agent-intent/intent/result.go:44-50`),输出是单 Top 意图;`ActionRef`/`Start`/`SetSlot` 全仓零代码命中 | 命令类型、序列化、校验全缺 |
| ActionRef 统一 | **四种动作键并存**:(a) `IntentSpec.Handler`(spec.go:81,squadintent Runner 消费);(b) `Metadata["blueprint"]`(adapters/intent/gate.go:123-131 → engine.go:228-237);(c) `CommandSpec.ID`(agent-command,backend server.go:653-654 把意图 ID 当命令 ID);(d) `WorkflowDef.IntentID`(core plugin.go:60,workflow/registry.go:40-42 索引) | 统一表示与迁移映射 |
| Backend 唯一 Router | **不存在**。消费分散三处:chat 路由内联 if-else(`internal/server/server.go:647-660`,命令优先于工作流写死)、HITL 自由输入转轨(`internal/runmgr/runmgr.go:1203-1248`)、装配(`internal/app/app.go:300-301,316,381`)。squad 的蓝图意图链 backend **完全没接**(squadadapter/entry.go:98-110 不设 IntentGate/IntentCatalog/IntentRunner;全 backend grep `squadintent` 零命中) | Router 组件与优先级裁定 |
| 中文词界/最短词长(I3) | 纯子串 `strings.Contains`(registry.go:142/147/154);无词界设施、无最短词长告警(grep 零命中);NFKC 已落地(fold.go:11-28)但**两个宿主都没开**(backend intentgate gate.go:57 只设 TopN;squadintent gate.go:37-44 只设 Guard/Protected 域);语料中文负例只有一条靠 excludes_any 硬回避(testdata/corpus.json 第 6 行 + specs/base.json:10) | 词界设施、lint 告警、负例语料、宿主开 NFKC |
| 同分 priority 决胜(I5) | `Priority` 字段已在(spec.go:74)且是排序第二键(registry.go:327-335、recognizer.go:172-183);但裁决要求分差 ≥ margin(recognizer.go:92),`AmbiguityMargin <= 0` 强制回 0.1(registry.go:61-63)——**同分恒 ambiguous,priority 不参与决胜** | 裁决逻辑调整 |
| Catalog 白名单与 top-k | Metadata 白名单已有(fold.go:61-64,只放 blueprint/guidance 两键);但 `CatalogSpec.Handler` 仍进 JSON 视图(catalog.go:20-30);目录全量输出无 top-k、无相关度(catalog.go:34-62) | top-k 与相关度参数;Handler 是否进目录的裁定 |
| eval | `intentctl eval` 单轮(cmd/intentctl/main.go:229-292,recognizer 以 nil expectation/classifier 构建于 main.go:254);语料 10 条(testdata/corpus.json);输出只有 PASS/FAIL 计数(main.go:285-290) | 多轮夹具格式与逐轮命令期望(precision/recall 指标输出属 I2,I1 先扩格式) |
| 命令层前置概念 | 否定只认 excludes_any(recognizer.go:66 注释明说「不要写入」交给命令层 Cancel——命令层尚不存在,审计 I12);ExpectationProvider 接口在(expectation.go:7-19)但宿主侧零实现;L2 Classifier 同理(classifier.go:9-15,全部 NewRecognizer 传 nil) | I1 只需命令层与宿主编排;L2 实现、语义端口在 I2 |
另外注意:overlay 解析 `DisallowUnknownFields`(loader.go:21-24、51)——spec 加字段不做显式注册会硬失败旧配置;backend 插件读的是**裸 spec 数组**(internal/plugins/host.go:216-222),不是 `{intents, disable}` overlayDoc——两种载体的兼容都要照顾。
### 4.2 工作包分解
**WP1 命令模型(库侧核心)**
- 新类型:`Command` 联合 `{Kind, ...}`——`Start{ActionRef, Slots}` / `SetSlot{Name, Value}` / `ChangeSlot{Name, Value}` / `Cancel{Scope}` / `Clarify{Question, Missing}` / `Chitchat{}` / `Handoff{Target}`;`ActionRef{Kind: command|workflow|blueprint|handler, ID string}`。
- `Result` 增加 `Commands []Command`(Branch/Top 保留作诊断与兼容,逐步退役由 Router 封装);从候选到命令的展开规则:Top 命中 → Start(带 ActionRef 推导);槽位缺失 → Clarify(或 ExpectationProvider 命中时 SetSlot/ChangeSlot);多候选 → Clarify 消歧或 Ambiguous 保持(见 WP4/WP5 裁决);否定/取消语义 → Cancel(命令层首次承认否定,收编 recognizer.go:66 的注释承诺)。
- 命令的校验与现有候选校验同级(ID 集合、槽位作用域);序列化进 schema 包并给 JSON 样例(对齐 D1 的「三库发布 JSON Schema 样例」做法)。
- 测试:每种命令的展开路径;校验拒绝;round-trip。
**WP2 ActionRef 推导与四种动作键迁移**
- spec 增加显式字段承载 ActionRef(`action: {kind, id}`),`DisallowUnknownFields` 下注册;推导规则(兼容期):`action` 缺省时按旧键推导——`Handler` → `{handler, <值>}`、`Metadata["blueprint"]` → `{blueprint, <值>}`、命中 backend 命令总线 → `{command, <ID>}`、命中 workflow 索引 → `{workflow, <IntentID>}`。
- backend 插件装载(host.go:216-222 裸数组)与库 overlay 两种载体都接受新字段;旧字段在 I1 不删除(退役留给 I2 之后的发版任务)。
- 测试:四种旧键的推导等价于现状路由结果(对照 server.go:647-660 现行为刻画测试先行);新字段优先于旧键。
**WP3 Backend 唯一 Router**
- 新组件(建议 `internal/route`,或收进 `internal/server` 的独立文件):输入 = 命令列表 + 会话状态,输出 = 路由决策(command 分发 / workflow 启动 / squad 入口 / 普通回合 / 澄清回问)。
- 收编两处现有消费:server.go:647-660 的内联 if-else 与 runmgr.go:1203-1248 的 HITL 自由输入转轨,统一改调 Router;slash 命令(server.go:741-758)保持旁路不过在优先级文档里写明。
- **优先级明文**(提案 §6 要求 I1 写明):`command > workflow > blueprint > 普通回合`;`PromptTrigger`(技能触发)与 Intent 同命中时的裁定:**技能触发是提示词装配、不参与路由决胜;若 Intent 产出 Start/SetSlot 类命令则路由优先,纯提示词命中不产生命令**——写进 Router 文档与测试。
- squad 蓝图链(`{blueprint, key}`)经 ActionRef 到达 Router 后,Router 调 squad 入口;**backend 接入 squadintent 适配器(entry.go:98-110 的缺口)在本 WP 一并补上**——这是「blueprint 经 ActionRef 到 Router」验收的必要条件,范围可控(装配 + 映射,不改 squad 库)。
- 测试:命令优先于工作流(现有 `TestCommandBusBeatsWorkflow` 迁移)、workflow 经 ActionRef 启动、blueprint 经 ActionRef 进 squad 入口、HITL 自由输入转轨走同一路由、fallback 进普通回合。
**WP4 中文词界与最短词长(I3)**
- `MatchSpec` 增加 `boundary`(词界模式:CJK 按「非汉字或首尾」为界,拉丁按词字符边界)与 `min_len`(词条最短长度,低于则 lint 告警)——二选一还是并存由实施期原型定,但 lint 告警(`intentctl lint`)必须覆盖「短词条 + 无词界」组合。
- 两宿主开启 NFKC:backend `intentgate.Rebuild`(gate.go:57)与 squadintent(gate.go:37-44)的 Options 显式开 `NFKC:true`,并补开启前后对照测试。
- 语料补负例:中文子串误命中系列(如「配置」误中「配置项」、「写入」误中「不要写入」的否定场景交给 Cancel 的正例对照)。
- 测试:词界开/关矩阵、最短词长告警、新负例全过、既有 10 条语料不回归。
**WP5 同分 priority 决胜(I5)**
- 裁决调整:前两名置信度同分(差 < margin)时,若 `Priority` 不同则高优先者判 `matched`;priority 也相同才 ambiguous。`AmbiguityMargin` 语义不变(仍防分数过近误判),margin 强制下限 0.1 保留。
- 测试:同分高 priority 命中、同分同 priority 标歧、margin 边界三例;既有漏斗测试不回归。
**WP6 eval 格式扩展**
- 语料格式 v2:用例带 `turns: [{input, expect: {commands: [...]}}]`,逐轮期望命令列表;`intentctl eval` 读 v2 时构建 ExpectationProvider 夹具实现(库内给测试用 Provider),逐轮比对命令序列;v1 单轮格式兼容读取并自动映射为单 turn。
- 指标输出在本阶段只加「逐轮命令匹配率」明细(precision/recall 的正式定义与输出属 I2,不在 I1 提前承诺)。
- 测试:v2 多轮夹具端到端(含 SetSlot+Start 组合用例——验收条款的载体)、v1 兼容、误期望 FAIL。
### 4.3 涉及仓与落点汇总
- Agent-Intent:`intent/{result.go,recognizer.go,registry.go,spec.go,loader.go,catalog.go}`、新 `intent/command.go`、`schema/`、`cmd/intentctl`、`testdata/`。
- Backend:`internal/server/server.go`、`internal/server/`(新 Router)、`internal/intentgate/`(Recognize 返回命令、NFKC 开启)、`internal/runmgr/runmgr.go`(HITL 转轨改调 Router)、`internal/squadadapter/entry.go`(接 squadintent)、`internal/plugins/host.go`(spec 新字段)。
- Agent-Squad:`adapters/intent` 只在映射 ActionRef 时动(blueprint 键推导),核心不动。
- Dev:两层 pin。
### 4.4 实施顺序
WP1 命令模型 → WP2 ActionRef 迁移(刻画测试先行)→ WP5 决胜 + WP4 词界(库内独立,可并行)→ WP3 Router(依赖 WP1/WP2)→ WP6 eval(收尾,验收载体)。
### 4.5 风险
- **路由行为变化面**:Router 收编两处消费 + squad 蓝图链接入,是产品可见行为。对策:WP2 的刻画测试先把现路由行为钉死,Router 切换后逐条等价;蓝图链接入属新增能力,单独一组测试,不改动有路由。
- overlay 严格解析:spec 新字段必须同步注册进 loader 与 backend 裸数组装载两处,漏一边即硬失败——测试覆盖两种载体。
- NFKC 默认开启是行为变化:只动两个宿主的显式配置,库默认不变(库保持 fail-closed 哲学,宿主显式选择)。
- 命令列表与「单 Top」并存期,`Result.Top` 的消费方(squadintent gate 等)保持可用;退役由后续发版任务评估,不在 I1。
### 4.6 I1 验收(对照总提案 §5 I1 验收逐条)
| 总提案条款 | 机器可核对的验收 |
|---|---|
| 审计 I1、I2、I3、I5 有回归 | I1(分类器篡改拒绝,recognizer_test.go:188/238/269 现存)与 I2(作用域校验器,slots_catalog_test.go:70 现存)保持绿;I3 新增词界/负例测试;I5 新增决胜测试——四个测试名写进任务卡 |
| 一条用户话可以产出 SetSlot 加 Start | eval v2 多轮夹具含该用例并通过 |
| 命令 id、工作流 IntentID、小队 blueprint 都通过 ActionRef 到达 Router | Router 测试三条各一;插件清单字段名不变的 diff 断言(装载测试比对字段名清单) |
| 插件清单不改字段名 | 插件装载兼容测试:现有插件 intents/workflows 清单零修改装载通过 |
## 5. 三任务之外的说明
- N2/Q2/I2 及更后的阶段不在本文范围;本文的 WP 划分假定 N1/Q1/I1 全部合入后才启动它们。
- 三任务的验收都依赖「脚本化模型 + 库内夹具」,不依赖真实模型密钥;真实任务 A/B(总提案 E 节「少量真实任务加人工复核」)在各阶段验收时由所有者安排。
- 本文档本身不修任何代码;如评审要求改设计,在本文档上修订后再启动对应任务。
## 6. 验收(本文)
- 三个阶段的实施方案各自满足:工作包可独立派生任务卡、验收条款可机器核对、与总提案逐条映射(§2.6 / §3.6 / §4.6 的表)。
- 现状陈述均有 file:line 证据(撰写日 pin 见 §1.1);评审重放 = 按表中证据抽查。
- 三任务协调规则(§1.2)与 AGENTS.md 分支层级一致。