# Agent-Intent 可定义、可配置、可扩展的通用意图识别基础库:把「输入文本 → 意图 + 槽位 + 分支裁决」做成纯内存、确定性的库能力,供宿主在 LLM 之前完成确定性分流——守卫拦截(安全红线、识别即拒绝)、系统功能命令识别、路由提示与多轮澄清入口。 本仓库是该模块的源码位置。Go 模块路径为 `agent-intent`。不依赖 Agent-Native、Agent-Squad 或任何第三方模块;错误码与日志和 Error-Code-System / Logging-System 轻接入对齐(常量对齐 + 鸭子接口,核心包不依赖错误码库)。 设计目标与边界: - **机制与内容分离**:本仓只提供识别流水线与定义框架,不内置任何业务意图;词表、正则、守卫内容一律由宿主以 Go 注册或 JSON 配置提供。 - **确定性**:同输入 + 同快照 → 同结果;识别路径纯内存、无 I/O、无时间与随机因素,「无匹配」(fallback)是正常结果而非错误。 - **fail-closed 配置**:spec 校验或配置加载失败时整批原子拒绝,保留上一生效快照并给出可定位到字段与来源的错误。 - **扩展点只留接口**:LLM 分类器、会话挂起预期、分句等由宿主实现并注入。 ## 分层 | 包 | 职责 | 依赖边界 | |---|---|---| | intent | spec 定义与校验、注册表与不可变快照、JSON overlay 加载合并、四级漏斗识别器、槽位抽取、诊断 | 标准库 + 可选 logging-system | | conformance | 消费方行为验收套件(Recognizer 公开契约正反例) | 只用公开 API | | catalogcheck | 本仓错误常量与错误码库 `intent` 域的对齐断言 | 仅测试,import error-code-system | | examples | 最小接入示例(embed 配置端到端) | 只用公开 API | | cmd/intentctl | 配置 lint / 单条解释 / 评测集跑批的只读诊断 CLI | 组合根之一 | ## 识别流水线 ``` Stage 0 Guard 守卫域规则(Options.GuardDomains;命中 → GuardHit,可表达"识别即拒绝") Stage 1 Expectation 会话挂起预期(可选注入 ExpectationProvider;命中 → ExpectationHit) Stage 2 Rules 规则计分:includes_any / includes_all / excludes_any / patterns Stage 3 Adjudicate 置信度裁决:唯一过阈 → Matched;前两名分差 < AmbiguityMargin → Ambiguous;全不过 → Fallback ``` 计分与裁决规则: - 单 spec 得分 `score = base + (任一 pattern 命中 ? pattern_bonus : 0)`,上限 1.0,四位小数舍入;`score ≥ 该 spec 的 threshold` 才进入候选。 - 命中条件:`excludes_any` 全不命中 且 `includes_all` 全命中 且(`includes_any` 至少一词命中 或 任一 `pattern` 命中;仅配置 `includes_all` 时以全命中为准)。 - 候选排序:置信度降序 → `priority` 降序 → 合并注册序 → id 字典序,全程确定性。 - Ambiguous 分支可注入 `Classifier`(典型为宿主的 LLM 客户端适配器)重排;分类器失败保持 Ambiguous,不会使识别失败。 - 槽位:Top 候选才抽取槽位;必填槽位未命中不撤销匹配,记录在 `Top.MissingRequiredSlots`,由宿主决定澄清(配合挂起预期)。 ## 快速开始 ```go b := intent.NewRegistryBuilder(intent.Options{ ProtectedDomains: []string{"guard"}, // overlay 不得覆盖/禁用 GuardDomains: []string{"guard"}, // Stage 0 演练的域 }) // 守卫内容宿主内置;业务意图走 JSON overlay(embed 或目录均可)。 if err := b.Register(guardSpecs...); err != nil { ... } reg, err := b.BuildFS(ctx, specFS, "specs") // fs.FS + roots,root 为目录或单文件 if err != nil { ... } // fail-closed:配置坏则构建失败 rec := intent.NewRecognizer(intent.Options{}, nil, nil) // 可选挂起预期 / 分类器 res, err := rec.Recognize(ctx, "查询任务 42 的进度", reg.Snapshot()) // res.Branch == intent.BranchMatched // res.Top.ID == "demo.run.status_query"; res.Top.Slots["run_id"] == "42" // res.Top.Handler / Risk / RequiresConfirm / Metadata 由宿主解释 if err := reg.Reload(ctx); err != nil { ... } // 显式重读配置;失败保留旧快照 ``` 多意图输入用 `intent.NewRuleSegmenter().Segment(input)` 先分句再逐句识别。 ## 意图定义(IntentSpec) Go 与 JSON 共用同一结构;JSON overlay 文档形如: ```json { "intents": [ { "id": "demo.run.status_query", "domain": "demo", "version": 1, "description": "查询任务运行进度", "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.2, "threshold": 0.7}, "priority": 100, "risk": "low", "requires_confirm": false, "handler": "taskrun.query_status", "metadata": {"channel": "dialogue"} } ], "disable": ["demo.run.restart"] } ``` 字段说明: | 字段 | 说明 | |---|---| | `id` | 全局唯一;首段必须等于 `domain`,各段 `^[a-z][a-z0-9_-]{0,63}$`,可多级(`demo.run.status_query`) | | `version` | ≥1;overlay 覆盖同 id 时必须递增 | | `matcher` | 见上文计分规则;至少一个正向条件;`case_sensitive` 只作用于词表,pattern 用 `(?i)` | | `slots` | `group` 默认 1;`required` 未捕获记录到 `MissingRequiredSlots`;`label`/`question` 供 HITL 提问与目录展示;`enum`/`format`/`normalize`(trim\|lower\|upper)/`validator`(`RegisterSlotValidator` 注册)构成确定性校验链,非法值进 `Top.InvalidSlots`(与"缺失"区分) | | `confidence` | 缺省 `base=0.7 / pattern_bonus=0.2 / threshold=0.6` | | `risk` / `requires_confirm` / `handler` / `metadata` | 模块不解释,透传给宿主做授权、确认与路由 | | `disabled` | 定义但停用(灰度暂存) | 合并语义:Go 内置为基线;目录 root 内 `*.json` 按文件名字典序、roots 按给定顺序应用;同 id 覆盖需版本递增;`disable` 按 id 禁用(保护域整批失败);未知字段拒绝并定位到文件。数量上限见 `Options`(spec 总数、单 spec 正则/槽位数、输入长度),防止配置膨胀。 ## 能力目录(Catalog) `Snapshot.Catalog()/CatalogMarkdown()` 把启用的非守卫 spec 渲染为规划者/领导 Agent 可消费的声明式目录:有哪些可直接调用的能力、必填槽位与提问建议、风险与确认要求。用于"目录驱动的编排"——规划者据此把子任务短路给系统功能、按槽位 question 生成定向 HITL(Agent-Squad 已按此接入,见其 `squadintent` 与 `docs/records`)。 ## 诊断(intentctl) ```bash go run ./cmd/intentctl lint -root configs/intents # 校验 + 冲突提示 go run ./cmd/intentctl explain -root configs/intents "查询任务进度" # 分支/候选/槽位/裁决轨迹 go run ./cmd/intentctl eval -root configs/intents evals/corpus.json go run ./cmd/intentctl catalog -root configs/intents # 能力目录(注入规划者提示词) ``` 评测集格式见 `intent/testdata/corpus.json`(`name / input / expect_branch / expect_top / expect_slots / expect_missing_slots`)。评测集放在配置目录之外。注意:宿主以 Go 内置注册守卫时,守卫用例只能在宿主自己的测试(或 conformance.Run)中验证;intentctl 只加载 JSON 配置。 ## 错误与日志 错误常量与 Error-Code-System 对齐(`intent.invalid_spec` / `intent.spec_protected` / `intent.load_failed`,调用方错误复用 `sys.invalid_argument`),`catalogcheck` 测试防双权威;映射表见 [Error-Code-System 的 agent-intent 映射](deps/error-code-system/docs/agent-intent-mapping.md)。错误类型实现 `ErrorCode()` / `ErrorFields()`,logsys 自动展开码与字段。日志为可选注入(`Options.Log`,nil→Nop),事件:`intent recognized`(branch/top/confidence/revision/duration)、`intent snapshot reloaded`、`intent config rejected`;原始输入不写入日志。 ## 作为 submodule 使用 消费方把本库固定为 submodule,推荐路径 `deps/agent-intent`;版本以消费方 gitlink 为准,不执行 `submodule update --remote`: ```bash git submodule add ssh://git@git-ssh.u1680832.nyat.app:31411/weichen/Agent-Intent.git deps/agent-intent ``` ```text require agent-intent v0.0.0 replace agent-intent => ./deps/agent-intent ``` 依赖模块内部的 replace 在上层构建中不生效:消费方须自带 `error-code-system` 与 `logging-system` 的 require + replace,并保持与本仓 gitlink 相同的组合。接入后建议在消费方测试里用 `conformance.Run` 跑自己的用例集。 ## 验证 ```bash GOWORK=off go vet ./... && GOWORK=off go test -count=1 ./... GOWORK=off go test -count=1 -race ./... GOWORK=off go list -m all # 仅标准库 + 两个生态模块 ``` 需要 Go 1.27。规则入口见 [AGENTS.md](AGENTS.md),项目事实见 [PROJECT.md](PROJECT.md),设计与验证记录见 `docs/records/`。