Files
Agent-Intent/docs/records/2026-09-29-agent-intent-design.md
T
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

22 KiB
Raw Blame History

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(可定义的核心)

{
  "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 注入惯例)。

结果契约

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 不在本方案范围。