Files
Agent-Intent/docs/records/2026-09-29-agent-intent-design.md
weichen b450b03638 Initialize the repository with pinned submodules, project entry documents, and the design record
Details (original message):
Initialize the repository with pinned submodules, project entry documents, and the design record.
2026-09-29 15:20:38 +00:00

283 lines
22 KiB
Markdown
Raw Permalink 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-Intent 通用意图识别模块设计方案
状态:待审;owner:weichen;主任务:本文件(仓库初始化前为唯一任务记录)。
授权与交付终点:本方案获批后进入实现——初始化仓库、按阶段交付代码与文档、推送到批准远端。
并行基线:新仓库,无并行基线;生态固定组合见「submodule 与版本固定」。
## 问题与证据
- Agent-Squad 当前"意图→路由"的唯一决策点是 `BlueprintRegistry.Match`(关键词 contains 计分,`squad/blueprint.go:82`)与 `RoleRegistry.Recommend`(`squad/registry.go:85`);入口层 CLI 一级命令硬编码 switch(`cli/cli.go:32`)。新增一个"自然语言系统功能命令"需要改多处 Go 源码。
- Agent-Native 定位为纯执行内核(README),仓内无任何意图/路由/命令解析代码;"用户输入→决定做什么"全部留在消费方。
- Agent-System 有成熟的四级漏斗意图层(`internal/intent/`),但它是 internal 包、33 种 IntentType 与词表全部硬编码在 Go 源文件(`recognizer_matchers_*.go` 共千余行)、与 Agent-System 会话模型强耦合,不可直接复用。
- 生态既有事实:Agent-Native 与 Agent-Squad 均为 stdlib-only Go 模块(go 1.27),以 `deps/<name>` submodule + gitlink + `require v0.0.0` + `replace` 接线;Error-Code-System 与 Logging-System 已定义库级轻接入范式(Agent-Native 先例:错误码走"常量取规范字符串 + ErrorCode()/ErrorFields() 鸭子方法 + catalogcheck 对齐测试",日志走"可选 `*logsys.Logger` 注入,nil→Nop")。
结论:缺的是一个**机制与内容分离、配置驱动、可被两个消费方以既有 submodule 范式接入**的独立意图识别模块。
## 目标与边界
**目标**(对应用户要求的三属性):
1. **可定义**:意图以声明式 `IntentSpec`(JSON)或 Go 代码定义,含匹配规则、槽位、置信度参数、风险等级、宿主 handler 键与路由元数据。
2. **可配置**:内置(Go 注册)为基线,JSON 配置可新增/覆盖/禁用;合并结果为不可变快照,带 revision;启动校验 fail-closed;提供 check/explain 诊断。
3. **可扩展**:流水线各阶段(守卫、挂起预期、规则、分句、L2 分类)都是接口,宿主可注入实现;模块本身不含任何业务意图内容。
**非目标(v1 明确不做)**:
- 不内置 LLM 客户端(L2 只暴露接口,由宿主用 `agent-native/runtime/llm` 等自行实现)。
- 不做动作执行器(识别结果交宿主;IntentSpec 只携带 handler 键与元数据)。
- 不含 Agent-System 的领域内容(docgov/task/kb 词表、33 种 IntentType 一律不迁移;实现全新编写,机制层借鉴其漏斗结构)。
- 不提供 HTTP 服务、文件监听热更新(Reload 显式 API 即可)。
- 消费方(Squad/Native)的接入改造不在本仓交付范围,只保证其可行并给出预告。
**硬边界(将写入根 AGENTS.md)**:
1. 只做识别机制,不做领域内容;所有意图内容宿主注册。
2. 依赖方向唯一:`agent-intent` 可选接入 error-code-system / logging-system(轻接入范式);**禁止**依赖 agent-native、agent-squad、agent-system 及任何宿主类型。
3. 配置/spec 校验失败 fail-closed:拒绝生成新快照,保留上一个 applied 快照并暴露原因。
4. "无匹配"不是错误(fallback 是正常识别结果);系统错误(配置损坏等)与识别结果严格分离。
5. 零第三方依赖(仅标准库 + 两个生态子模块)。
6. 版本以消费方 gitlink 为权威;禁止 `submodule update --remote`。
## 总体设计
### 定位与依赖方向
```
Agent-Squad ──┐ ┌──> deps/development-guidelines (dev-only)
├─ deps/agent-intent ─────┤
Agent-Native ─┘ (module agent-intent) ├──> deps/error-code-system (build-required, 仅 catalogcheck 对齐 + 规范码字符串)
└──> deps/logging-system (build-required, 可选注入)
```
关键约束:**不得依赖 agent-native**。否则 Agent-Native 接入本模块时形成仓级循环(agent-native → deps/agent-intent → deps/agent-native)。因此 LLM 分类器、挂起预期的会话状态等一律以本模块定义的窄接口表达,宿主提供实现(模块契约规范:"进程内调用方可定义更窄的需求接口,通过适配器连接")。
### 核心概念与识别流水线
识别入口:`Recognizer.Recognize(ctx, input, snap) (Result, error)`。四级漏斗(借鉴 Agent-System 结构,机制化、去内容化):
```
Stage 0 Guard 守卫域规则(宿主声明 protected domain,优先级最高;命中→GuardHit,可携带"识别即拒绝"语义)
Stage 1 Expect 会话挂起预期(宿主可选注入 PendingExpectation;命中→ExpectationHit,优先于一般规则)
Stage 2 Rules 规则匹配:编译后的 matcher(关键词/正则/排除词)对快照内全部启用 spec 计分
Stage 3 Adjudicate 置信度裁决:threshold 之上取最高分→Matched;多候选分数接近且都过阈→Ambiguous;全不过→Fallback
(可选) L2 宿主对 Ambiguous 追加分类器重打分(接口 Classifier,宿主自担延迟与成本,默认不接)
```
确定性要求:同一输入 + 同一快照 → 逐字节相同的结果(排序键:置信度降序 → priority 降序 → spec 注册序 → id 字典序)。识别无随机性、无时间依赖,可脱离外部 I/O 完整单测。
### IntentSpec(可定义的核心)
```json
{
"id": "squad.run.status_query",
"domain": "squad",
"version": 1,
"description": "查询任务运行进度",
"enabled": true,
"priority": 100,
"matcher": {
"includes_any": ["进度", "跑到哪了"],
"includes_all": ["查询", "任务"],
"excludes_any": ["怎么配置"],
"patterns": ["(?:查询|看).{0,6}(?:进度|状态)"],
"case_sensitive": false
},
"slots": [
{"key": "run_id", "required": false, "pattern": "(?:run|任务)[:# ]?([A-Za-z0-9-]+)", "group": 1}
],
"confidence": {"base": 0.8, "pattern_bonus": 0.15, "threshold": 0.6},
"risk": "low",
"requires_confirm": false,
"handler": "taskrun.query_status",
"metadata": {"channel": "dialogue"}
}
```
- `id` 全局唯一,格式 `^[a-z][a-z0-9-]{1,31}\.[a-z][a-z0-9_]{1,63}$`(与错误码同构,域名.记忆名)。
- `handler`、`metadata` 模块不解释,仅供宿主路由/执行。
- `risk`(low/medium/high)、`requires_confirm` 透传给宿主做授权判断。
- 校验规则:id 冲突、正则编译失败、未知字段、空 matcher、槽位 group 越界 → 定位到字段路径的 `intent.invalid_spec` 错误(含来源文件与 spec id)。
- 安全上限:单 spec 正则数、长度、快照内 spec 总数有默认上限(可由宿主在构建 Registry 时调整),防止配置膨胀拖垮识别。
### 注册与配置合并(可配置)
双源合并,遵循配置规范的"单一解析管道 + 快照":
1. **内置基线**:宿主(或本模块 examples)用 `RegistryBuilder.Register(spec...)` 注册 Go 定义的 spec。本模块自身不内置任何业务 spec。
2. **JSON overlay**:`loader.Load(ctx, fs.FS)` 支持 `embed.FS` 与真实目录(config root 由宿主显式给出,不从 cwd 猜测)。overlay 语义:新增(新 id)/ 覆盖(同 id 整体替换,version 必须递增)/ 禁用(同 id `enabled:false`)。
3. **保护域**:宿主构建时声明 protected domain 列表(典型:guard 域)。overlay 对保护域内 spec 的覆盖与禁用 → `intent.spec_protected` 错误,整批加载原子失败(不产生半套快照)。
4. **快照**:合并产物编译为不可变 `Snapshot`(含全部编译后 matcher),原子指针发布;携带 `revision` 单调递增与来源摘要(每个 spec 来自哪个源、哪次覆盖)。
5. **Reload**:显式 `Reload(ctx)`——校验→构建新快照→原子替换;失败保留旧 applied 快照并返回带原因的错误。运行中识别请求绑定取快照时刻的版本,不受并发替换影响。
6. **诊断**:`Snapshot.Explain(input)` 输出各阶段裁决轨迹(每 spec 得分、命中词、排除原因);`Snapshot.Conflicts()` 输出 includes 词重叠/正则遮蔽报告(诊断信息,不阻断加载;确定性排序依据保证同名冲突不产生歧义)。
### 扩展点(可扩展)
| 接口 | 阶段 | 默认实现 | 宿主典型实现 |
|---|---|---|---|
| `GuardSource` | Stage 0 | 无(宿主注册 guard 域 spec) | Agent-System 式安全红线词表(内容宿主给) |
| `ExpectationProvider` | Stage 1 | 无(不启用该阶段) | 会话系统提供挂起预期(多轮澄清) |
| `Classifier` | 可选 L2 | 无 | 基于 `runtime/llm.Client` 的重打分适配器(在宿主仓实现) |
| `Segmenter` | 前置分句 | 规则分句(分隔符表可配) | LLM 分句(宿主实现) |
| `Recognizer` | 整体 | 漏斗实现 | —(接口本身即消费面) |
新增机制不改核心:流水线阶段间以中间结果传递,宿主扩展实现接口并在 Options 装配(组合根模式,同 Squad 的 Planner/Backend 注入惯例)。
### 结果契约
```go
type Result struct {
Input string // 原文(宿主决定是否记录日志,默认不落原始输入)
Branch Branch // GuardHit / ExpectationHit / Matched / Ambiguous / Fallback
Top *Candidate // GuardHit/Matched/ExpectationHit 非空;Ambiguous/Fallback 为 nil
Candidates []Candidate // 排序后的 top-N(含分数与命中原因)
Slots map[string]string
SnapshotRev int64 // 所用快照 revision,用于审计与复现
}
type Candidate struct {
ID string
Domain string
Confidence float64
Risk string
RequiresConfirm bool
Handler string
Metadata map[string]string
Reason string // 命中解释(哪些词/哪个模式)
}
```
Query 语义(模块契约四类通信):纯函数式 Query——无副作用、无 I/O、无超时依赖(预算由宿主在调用侧控制);错误仅表示系统级失败(配置损坏等),不表示"没识别出"。
### 错误码与日志接入
**错误码(Pattern B,同 Agent-Native 先例,不 import errcode 于核心包)**:
- 核心包定义错误常量,字符串值即规范码;错误类型实现 `ErrorCode() string` + `ErrorFields() map[string]any`(logsys 鸭子识别,自动展开 code 与字段)。
- `catalogcheck/` 对齐测试断言本仓常量与错误码库 `intent` 域种子码逐个相等(防双权威)。
- 上游变更(见下节):Error-Code-System 新增 `intent` 种子域,最小集:
- `intent.invalid_spec`(http 400 / error / retry never / side_effects none)— spec 定义或校验失败,字段含 spec id、字段路径、来源。
- `intent.spec_protected`(http 400 / error / never / none)— 覆盖/禁用保护域。
- `intent.load_failed`(http 500 / error / never / none)— 配置读取/解析失败,含来源路径与原因。
每条按六要素登记(http/severity/retry/side_effects/中文 summary/hint),走上游 PR + `WRITE_CATALOG=1` 重导目录。
**日志(可选注入,同 Agent-Native 惯例)**:
- `Config.Log *logsys.Logger`,nil → `logsys.Nop()`;使用时 `.Module("intent")`。
- 事件:`intent recognized`(info:branch、top intent id、confidence、snapshot_rev、duration_ms)、`intent snapshot reloaded`(info:新旧 revision、spec 计数)、`intent config rejected`(error:整批拒绝原因)。
- 原始输入默认不写入日志(槽位值可能敏感);确需排障时宿主自行脱敏后以字段携带。trace 走 `logsys.WithContext(ctx)`,沿用宿主 `trace_id`。
### 并发、快照与安全
- Registry 持有 `atomic.Pointer[Snapshot]`;识别路径全程无锁读。
- 编译产物(regexp、分词表)在快照构建期生成;识别期零编译。
- 正则全部 `RE2` 语义(Go regexp 天然线性时间),另加输入长度上限与单次识别的 spec 评分预算。
- `-race` 下并发识别 + Reload 的隔离测试为必测项。
## 仓库结构与生态接入
### 目录布局
```
agent-intent/
├── go.mod # module agent-intent, go 1.27; require+replace 两个生态库
├── AGENTS.md # 短入口 + 硬边界 + 指向 deps/development-guidelines/core/baseline.md
├── PROJECT.md # 项目事实:依赖组合表、验证命令(GOWORK=off go vet && go test)、任务权威
├── README.md # 定位、spec 编写指南、消费方接线(require v0.0.0 + replace)
├── policy/guidelines-adoption.md # 采纳记录(固定 gitlink、登记 docs/records/ 豁免)
├── docs/records/ # 过程记录(本方案为第一篇)
├── deps/ # 三个 submodule(见下)
├── intent/ # 核心包:spec/registry/loader/recognizer/slots/result/errors + testdata/
├── conformance/ # 消费方行为验收套件(Recognizer 公开契约的正反例场景)
├── catalogcheck/ # 与错误码库 intent 域的对齐断言(仅测试)
├── examples/ # 最小接入示例:embed 配置 + guard 域 + 槽位抽取端到端样例
├── scripts/submodule-consistency.sh
└── .gitea/workflows/ci.yml # vet + test(-race) + 一致性脚本
```
后期(v0.2)追加 `cmd/intentctl`(lint 配置冲突、跑评测集、explain 单条输入——即配置规范要求的只读 check/explain 诊断)。
### submodule 与版本固定
- `.gitmodules`:`deps/development-guidelines`(dev-only,不进 go.mod)、`deps/error-code-system`、`deps/logging-system`(build-required)。
- **pin 与生态当前组合一致**:guidelines `26b562d2`、error-code-system `95a03b75`、logging-system `2c5716f`(实现时以 `git submodule status` 实测为准)。理由:消费方的 `submodule-consistency.sh` 校验嵌套副本 gitlink 与本仓一致,Agent-Intent 作为中间层必须与上下游同组合,否则 Squad/Native 接入时一致性校验失败。
- go.mod:`require error-code-system v0.0.0` / `require logging-system v0.0.0` + 两条 `replace => ./deps/...`;无 go.sum、测试带 `GOWORK=off`(同 Squad/Native)。
- 远端:`ssh://git@git-ssh.u1680832.nyat.app:31411/weichen/Agent-Intent.git`;初始化流程按 adoption 工作流第 2 节(远端需先建仓,Gitea API token 在 `~/.secrets/gitea`,实现阶段处理)。
### Error-Code-System 上游变更
新增 `intent` 种子域(`errcode/codes.go` + `domains/intent.go` + 目录 JSON + docs 映射表)。这是唯一跨仓前置项;不落地则本仓 catalogcheck 无法编写。计划先于 Phase 1 提交该 PR 并固定新 gitlink。
### 消费方接入预告(不在本仓交付)
- **Agent-Squad**:`SquadEngine.planAndExecute`(`squad/engine.go:175`)处以 `Recognizer` 前置/替换 `Blueprints.Match`;CLI `cmdRun` 与 HTTP `handleCreateRun` 入口用 guard 域拦截"系统功能命令"。接线清单:submodule + replace + PROJECT.md 依赖表 + submodule-consistency 扩路径 + 采纳记录补行。
- **Agent-Native**:可选。典型位置是 `contract.Resolver` 决策前置(intent → 所需 Capability → Backend),须遵守"缺能力即拒绝"边界。
## 实施计划
| 阶段 | 内容 | 出口(验收证据) |
|---|---|---|
| P0 仓库初始化 | 建远端仓、clone、挂三个 submodule 至既定 pin、AGENTS/PROJECT/policy/CI/一致性脚本、首次提交推送 | 全新 clone `submodule update --init --recursive` 后 CI 绿;PROJECT.md 验证命令通过 |
| P1 错误码上游 | Error-Code-System 加 `intent` 域 PR | 上游合入,目录 JSON 与 docs 更新,本仓 pin 新 gitlink |
| P2 核心 v0.1.0 | intent 包全量(spec/校验/registry/loader/recognizer/slots/guard/expectation 接口/result/errors/logsys 接入)+ 单测 + testdata 评测集 + conformance + examples + catalogcheck | `GOWORK=off go vet ./... && go test -count=1 -race ./...` 全绿;零第三方依赖检查通过 |
| P3 v0.2 增量 | cmd/intentctl(lint/explain/评测)、Segmenter 规则实现、Reload+revision 报告 | intentctl 在 examples 配置上输出冲突报告与 explain 轨迹 |
| P4 消费方(独立仓) | Squad 接入 PR;Native 视需要 | 各自仓 conformance + 记录文档 |
## 兼容、迁移与恢复
- 新仓无历史兼容负担;公开 API 从 v0.1.0 起视为契约,变更走"扩展→兼容→迁移→移除"。
- spec 定义的兼容:spec `version` 字段递增用于宿主审计;id 一旦被消费方依赖即不改语义(改名=新 id+旧 id 禁用)。
- 回退:消费方以普通 PR 恢复旧 gitlink 即回退本模块;本仓配置快照失败保留上一个 applied,不需要专门恢复程序。
## 验收
| 需求/验收条件 | 负责模块/不变量 | 验证方法 | 结果 |
|---|---|---|---|
| 同输入+同快照→同结果(确定性) | intent/recognizer | 表驱动单测(含同分并列场景) | 待执行 |
| guard 保护域不可被 overlay 禁用/覆盖 | intent/loader | 负例单测:整批原子失败、错误码 `intent.spec_protected` | 待执行 |
| 配置错误定位到字段与来源 | intent/spec 校验 | 未知键/坏正则/空 matcher/槽位越界负例 | 待执行 |
| 合并语义(新增/覆盖/禁用/version 递增) | intent/registry+loader | Go+JSON 双源合并表测 | 待执行 |
| 无匹配=Fallback 正常返回,非错误 | intent/result | 单测断言 Branch 与 nil error | 待执行 |
| Reload 失败保留旧快照;并发识别不受影响 | intent/registry | `-race` 并发测试 | 待执行 |
| 硬负例语料(否定写+只读、排除词、近义混淆) | testdata 评测集 | 表测逐条断言期望分支 | 待执行 |
| 错误码与上游 intent 域逐个相等 | catalogcheck | 对齐测试 | 待执行 |
| 零第三方依赖 | go.mod | `go list -m all` 仅标准库+两个生态模块 | 待执行 |
| 消费方可按生态范式接入 | conformance+examples | 示例端到端跑通(embed 配置→识别→槽位) | 待执行 |
| 用户/系统条件 | 检查层级 | 预期结果 | 必需环境 |
|---|---|---|---|
| 系统功能命令被 guard 拦截(原例) | examples 端到端 | GuardHit + 宿主 handler 键 | 本地 Go 1.27 |
| 普通目标文本走 Fallback(变体) | examples 端到端 | Fallback 分支,不误触 | 同上 |
| 歪曲写法/否定式(边界) | 硬负例语料 | 不误命中高斯 intent | 同上 |
| 正常行为保持(Squad 接入后) | Squad conformance | 原蓝图匹配行为由宿主基线 spec 复现 | P4 |
## 模块设计卡
| 维度 | 内容 |
|---|---|
| owner、目的、非职责 | weichen;通用意图识别机制(定义/合并/识别/槽位/诊断);不做领域内容、动作执行、LLM 客户端、HTTP 服务 |
| 隔离范围与失效行为 | L1 库级逻辑隔离;无 I/O 依赖(配置加载除外);识别路径纯内存,失败影响=单次返回错误,不崩溃宿主 |
| 数据与不变量 | 快照唯一写者=Reload;spec 只在快照构建期编译;revision 单调;同输入同快照同结果 |
| 入口 | Query:Recognize、Explain、Conflicts;Command:Reload、Register(构建期);无 Event/Stream |
| 契约 | 见「结果契约」;错误=系统级失败(intent.* 码),无匹配=正常分支 |
| 依赖 | 公开端口:Classifier/ExpectationProvider/Segmenter(宿主实现);必需:无;可选:logsys、errcode(仅 catalogcheck) |
| 独立测试 | 纯函数识别 + testdata 语料,无外部 I/O;时间不参与裁决;conformance 供消费方复用 |
| 简洁性 | 调用方只需 Recognize(ctx, input, snap);快照/合并/编译复杂性全部隐藏在 Loader+Registry |
| 并发/副作用 | 识别只读;Reload 原子指针替换;识别期零编译零分配热点 |
| 故障控制与定位 | 配置失败 fail-closed 保留旧快照;定位信号=错误码+字段(spec id/来源/字段路径)+Explain 轨迹 |
| 生命周期 | 无常驻资源;Close 仅透传日志 Sync 由宿主管理 |
| 验收 | 见验收表 |
## 决策与剩余项
需负责人确认的选择(均有推荐项):
- **D1 实现来源**:全新编写(推荐)vs 移植 Agent-System `internal/intent` 代码。推荐全新:原实现与 Agent-System 会话模型/33 IntentType 强耦合,且可避免跨仓来源与授权问题;机制结构(漏斗/分支/元数据)已在本方案吸收。
- **D2 配置格式**:JSON(推荐,stdlib-only 与生态一致)vs YAML(需引第三方依赖,破坏零依赖边界)。
- **D3 错误码接入模式**:Pattern B 常量+鸭子+catalogcheck(推荐,同 Agent-Native)vs 直接 import errcode(Squad 式,重一级)。
- **D4 Error-Code-System 上游 `intent` 域 PR**:需授权修改另一仓(最小 3 码)。若暂不动上游,备选是本仓 catalogcheck 内 `LoadJSON` 自建域(不推荐,形成事实双权威)。
- **D5 v1 范围**:不含 LLM 分类器实现、不含动作执行器、不含 HTTP 面(推荐;L2/执行留接口给宿主)。
- **D6 生态 pin 组合**:三个 submodule pin 与 Squad/Native 当前一致(guidelines 26b562d2 / errcode 95a03b75 / logsys 2c5716f;实现时实测复核)。
- **D7 远端建仓时机**:P0 阶段用 Gitea API 建远端仓并首推 main(token 在 `~/.secrets/gitea`,按惯例不回显)。
剩余项:Squad/Native 的具体接入 PR 细节在 P4 前另立方案;Agent-System 未来是否以本模块替换 `internal/intent` 不在本方案范围。