Files
smartmate/backend/agent2/通用能力接入文档.md
LoveLosita f4ef6fb256 Version: 0.7.5.dev.260324
🐛 fix(agent/schedulerefine): 修复复合微调分支链路问题,并将 MinContextSwitch 重构为固定坑位重排语义

- 🔧 修复 `schedulerefine` 复合路由中参数透传不完整、缺少 deterministic objective 时错误降级,以及“复合工具执行成功”与“终审通过”语义混淆的问题
-  保证新的独立复合分支能够正确执行、正确出站,并统一交由 `hard_check` 裁决最终结果
- 🔍 排查时发现 `MinContextSwitch` 上游 `context_tag` 存在整体退化为 `General` 的风险,影响MinContextSwitch
- 🛡️ 为 `MinContextSwitch` 增加兜底策略:当标签整体退化时,按任务名关键词推断学科分组,避免分组能力失效
- ♻️ 将 `MinContextSwitch` 从“整周重新寻找新坑位”调整为“坑位不变,任务顺序改变”
- 🎯 将落地方式从顺序 `BatchMove` 改为固定坑位原子重写,避免出现远距离跳位、跨天错迁、异常嵌入课位及循环换位冲突
- 🧹 修复 `hard_check` 在 `MinContextSwitch` 成功后仍执行 `origin_rank` 顺序归位、并导致逆序终审误判的问题
- 🚦 命中该分支后跳过顺序归位与顺序硬校验,避免 `summary` / `hard_check` 将有效重排结果误判为失败

📈 当前连续微调规划涉及的全部功能已可以稳定运行;下一步将继续扩展能力边界,并进一步优化 `schedule_plan` 流程

♻️ refactor: 重整 agent2 架构,并迁移 quicknote/chat 新链路,目前还剩3个模块未迁移,后续迁移完成后会删除原agent并将此目录命名为agent

- 🏗️ 明确 `agent2` 采用“统一分层目录 + 文件分层 + 依赖注入”的重构方案,不再沿用模块目录多层嵌套结构
- 🧩 完善 `agent2` 基础骨架,统一收口 `entrance` / `router` / `llm` / `stream` / `shared` / `model` / `prompt` / `node` / `graph` 等层级职责
- 🚚 将通用路由能力迁移至 `agent2/router`,沉淀统一的 `Action`、`RoutingDecision`、控制码解析,以及 `Dispatcher` / `Resolver` 抽象
- 💬 将普通聊天链路迁移至 `agent2/chat`,复用 `stream` 的 OpenAI 兼容输出协议与 LLM usage 聚合能力
- 📝 将 `quicknote` 链路迁移到 `agent2` 新结构,拆分为 `model` / `prompt` / `llm` / `node` / `graph` 多层实现,替换对旧 `agent/quicknote` 的直接依赖
- 🔌 调整 `agentsvc` 对 `agent2` 的引用,普通聊天、通用分流与 `quicknote` 全部切换到新链路
- ✂️ 去除 graph 内部 `runner` 转接层,改为由 node 层直接持有请求级依赖,并向 graph 暴露节点方法
- 🧹 合并 `graph/quicknote` 与 `graph/quicknote_run`,删除冗余骨架文件,收敛为单一 `quicknote graph` 文件
- 📚 新增 `agent2`《通用能力接入文档》,明确公共能力边界、接入方式以及 graph/node 协作约定
- 📝 更新 `AGENTS.md`,要求后续扩展 `agent2` 通用能力时必须同步维护接入文档

♻️ refactor: 删除了现Agent目录内Chat模块的两条冗余Prompt
2026-03-24 21:35:22 +08:00

338 lines
9.6 KiB
Markdown
Raw 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.
# agent2 通用能力接入文档
## 1. 文档目的
本文档用于说明 `agent2` 目录下“通用能力”的边界、放置位置、接入方式与维护要求。
这里的“通用能力”特指:
1. 不只服务于某一个技能链路,而是可能被 `chat``quicknote``taskquery``schedule` 等多个模块共同复用的能力。
2. 与具体业务语义弱耦合,抽出来后不会强行把某个单一技能的 prompt、状态字段、业务规则污染到其它模块。
3. 抽出来后,能够明显减少样板代码、降低重复实现和后续迁移成本。
本文档不负责描述某个具体技能的业务流程技能自身的图编排、状态字段、prompt 细节,应继续放在对应技能目录或对应决策记录中维护。
## 2. 当前目录分层
### 2.1 总入口层
文件:
- `entrance.go`
职责:
1. 作为 `agent2` 模块对上层服务的统一入口。
2. 负责把“路由器 + 各技能 handler”装配到一起。
3. 不负责具体技能逻辑,不负责直接调模型,也不负责工具执行。
适合放什么:
1. 模块级入口对象。
2. 通用注册方法。
3. 与“总分发”有关的最小门面封装。
不适合放什么:
1. 某个具体技能的节点逻辑。
2. 具体业务 DAO 调用。
3. 某个技能独占的 prompt 或状态机。
### 2.2 路由层
目录:
- `router/`
当前通用能力:
1. `Dispatcher`
2. `Resolver`
3. `AgentRequest / AgentResponse`
4. `Action` 与路由控制码解析
职责:
1. 统一处理“请求该走哪条技能链路”的分流问题。
2. 提供对上层稳定的动作枚举与请求壳结构。
3. 兼容迁移期的新旧 action 语义,避免上层服务直接依赖旧目录。
适合放什么:
1. 通用路由协议。
2. 控制码解析。
3. 分发器。
4. 所有技能共用的路由请求/响应结构。
不适合放什么:
1. 某个技能内部的二次判断。
2. 某个技能专属的 prompt。
3. 技能内部重试或状态流转逻辑。
### 2.3 模型交互层
目录:
- `llm/`
当前通用能力:
1. `Client`
2. `GenerateOptions`
3. `TextResult`
4. `BuildSystemUserMessages`
5. `GenerateJSON`
6. `ParseJSONObject`
7. `MergeUsage / CloneUsage`
8. `ark.go` 中的 Ark 适配实现
职责:
1. 统一收口模型调用接口,减少各技能重复拼装 `messages``thinking``temperature``max_tokens`
2. 提供通用 JSON 解析与 usage 合并能力。
3. 把具体厂商 SDK 细节尽量压在适配层,不向上层节点扩散。
适合放什么:
1. 所有技能都可能复用的模型调用壳。
2. 通用 JSON 提取与反序列化。
3. 流式/非流式调用的统一适配接口。
4. usage 收敛、空响应错误格式化。
不适合放什么:
1. 只服务于某一个技能的 prompt 文案。
2. 某一个技能特有的输出结构体。
3. 某一个技能独占的“成功文案润色”规则。
说明:
1. 如果只是“基于通用 `Client` 再包一层技能专用函数”,例如 quicknote 的聚合规划调用,这种代码可以放在 `llm/`,但文件名应明确带技能语义,避免误认为完全通用能力。
2. 真正跨技能复用的内容,优先沉到 `client.go``ark.go``json.go` 这类公共文件。
### 2.4 流输出协议层
目录:
- `stream/`
当前通用能力:
1. OpenAI 兼容 chunk DTO
2. reasoning chunk 构造
3. assistant chunk 构造
4. finish / done 输出
5. 阶段推送 emitter
职责:
1. 统一处理 SSE / OpenAI 兼容输出格式。
2. 让 service、graph、node 只关心“我要推什么内容”,而不是自己拼 JSON。
3. 为后续前端协议升级预留统一修改点。
适合放什么:
1. chunk DTO。
2. reasoning / content / finish 的统一封装。
3. 阶段消息推送器接口。
不适合放什么:
1. 某个技能的阶段命名表。
2. 某个技能专属的正文文案。
3. 具体业务状态对象。
### 2.5 共享工具层
目录:
- `shared/`
当前通用能力:
1. 时间格式化与分钟级归一化
2. 深拷贝
3. 通用重试辅助
职责:
1. 承载纯工具型、无业务语义、无技能耦合的辅助函数。
2. 作为多个技能都能直接复用的最底层工具层。
适合放什么:
1. 时间工具。
2. clone 工具。
3. retry 帮助函数。
4. 纯函数型小工具。
不适合放什么:
1. 夹带具体技能字段名的工具。
2. 依赖数据库、缓存、模型、路由动作的逻辑。
### 2.6 技能内部层
目录:
- `graph/`
- `node/`
- `prompt/`
- `model/`
- `chat/`
职责:
1. 这几层主要承载技能内部能力。
2. 即使其中某个文件现在位于 `agent2` 根体系内,只要它带明显技能语义,就不要误判成“通用能力”。
判断标准:
1. 如果代码里天然绑定某个技能状态结构、某个技能阶段名、某个技能 prompt 输出契约,一般不应硬抽成通用能力。
2. 如果只是多个技能都重复了同一段样板代码,且抽出后不会让抽象变形,就应该评估下沉。
### 2.7 图层与节点层的协作约定
这是当前 `agent2` 已经明确下来的结构约束:
1. `graph/` 只负责“画图”:
- 注册节点
- 添加边
- 添加分支
- 编译与运行图
2. `graph/` 不再负责:
- 额外创建 runner 适配层
- 在图内继续堆请求级依赖转发逻辑
- 直接实现节点业务
3. `node/` 负责:
- 定义节点容器(例如 `QuickNoteNodes`
- 通过对象方法直接向 graph 暴露可挂载节点
- 在节点方法内部消费请求级依赖
推荐形态:
1. `graph` 里直接挂:
- `nodes.Intent`
- `nodes.Priority`
- `nodes.Persist`
- `nodes.Exit`
2. 分支也直接挂:
- `nodes.NextAfterIntent`
- `nodes.NextAfterPersist`
3. 不推荐再额外引入 `runner -> node` 这种转接层。
这样设计的目的:
1. 避免 graph 文件随着模块变多再次长成“大装配文件”。
2. 让“请求级依赖注入”回到 node 层自己的节点容器里。
3. 让阅读路径稳定成:
- 先看 graph 知道流程图
- 再跳 node 看节点方法实现
- 不需要在 graph 和 runner 两层之间来回跳。
## 3. 什么应该抽成通用能力
满足以下任意两条,一般就应该认真评估抽公共层:
1. 在第二个技能里出现了明显重复实现。
2. 这段逻辑本质上不依赖某个技能独占状态。
3. 抽出来后接口可以做到“入参少、职责清、语义稳定”。
4. 上层重复代码主要是在做样板装配,而不是业务决策。
典型例子:
1. 模型消息拼装。
2. JSON 提取与解析。
3. usage 合并。
4. OpenAI chunk 构造。
5. 时间归一化。
6. 深拷贝与重试工具。
7. 总入口路由与技能分发。
## 4. 什么不应该强行抽公共层
出现以下情况时,不要为了“看起来复用”而硬抽:
1. 抽完之后函数签名反而要塞一堆技能专属参数。
2. 公共层开始知道某个技能的状态字段、阶段名、错误文案。
3. 表面相似,实则每个技能的业务约束完全不同。
4. 为了复用而引入大量 `if action == xxx``switch skill` 这类分支。
典型例子:
1. quicknote 的优先级判定输出结构。
2. taskquery 的查询规划字段。
3. schedule 的排程状态快照。
4. 某个技能特有的 prompt 模板。
## 5. 新增通用能力的接入步骤
### 5.1 先判断是否值得抽
1. 先确认这段逻辑是否已经在第二处出现重复。
2. 再确认它是不是可以脱离单一技能语义独立存在。
3. 如果暂时还不能抽,也要在代码注释或决策记录里写明原因,避免后面第三次重复时忘记。
### 5.2 选择落点目录
按职责优先落到以下目录:
1. 路由协议与分发:`router/`
2. 模型调用与 JSON 解析:`llm/`
3. 流输出协议:`stream/`
4. 纯工具:`shared/`
5. 技能专属但可复用的壳:放对应技能语义文件,不要伪装成完全公共层
### 5.3 定义最小接口
1. 先定义最小可复用接口,只暴露上层真正需要的能力。
2. 不要把下层 SDK、DAO、缓存实现细节直接透传到所有调用方。
3. 优先让“公共层知道得更少”,而不是让“上层为了复用被迫知道更多”。
### 5.4 补注释
必须写清楚:
1. 这个通用能力负责什么。
2. 不负责什么。
3. 为什么它适合抽到公共层。
4. 失败时由谁兜底。
### 5.5 补测试
至少覆盖:
1. 正常路径。
2. 关键边界。
3. 明确的失败路径。
如果迁移期暂时没法完整补齐,也要优先保证公共函数本身有最小回归测试。
### 5.6 更新本文档
只要出现以下任一情况,必须同步更新本文档:
1. 新增了一个通用能力。
2. 调整了某个通用能力的落点目录。
3. 修改了某个公共接口的职责边界。
4. 删掉了某个旧的公共实现,并由新实现替代。
## 6. 推荐接入模板
可以按下面这个思路接入:
1. 先在技能代码里识别重复片段。
2. 提炼出“最小公共函数 / 最小公共结构体 / 最小公共接口”。
3. 放进 `router / llm / stream / shared` 之一。
4. 先让新技能接这个公共实现。
5. 再逐步回收旧技能里重复的老代码。
6. 最后补本文档,说明这个能力现在归谁管、上层该怎么用。
## 7. 当前维护要求
1. `agent2` 的公共层要尽量保持“低耦合、强注释、易迁移”。
2. 新技能开发时,优先复用这里已有的公共能力,而不是直接复制旧技能代码。
3. 如果发现某段逻辑已经出现第二份实现,应优先评估抽公共层,而不是继续写第三份。
4. 后续只要扩展通用能力,必须同步更新本文档,否则视为迁移或重构未完成。