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
This commit is contained in:
388
agent2逐批搬迁实施细节.md
Normal file
388
agent2逐批搬迁实施细节.md
Normal file
@@ -0,0 +1,388 @@
|
||||
# agent2 逐批搬迁实施细节
|
||||
|
||||
## 1. 文档目的
|
||||
- 本文档用于指导 `backend/agent -> backend/agent2` 的渐进式重构。
|
||||
- 目标是“在逻辑不变的前提下重整结构、消除冗余、逐步切流”,而不是一次性重写。
|
||||
- 该文档优先服务于施工过程,强调“怎么搬、先搬什么、如何验证、何时删除旧代码”。
|
||||
|
||||
## 2. 总体迁移原则
|
||||
|
||||
### 2.1 不直接改老目录,先并行维护
|
||||
- 保持现有 `backend/agent` 不动,作为稳定对照组。
|
||||
- 新建 `backend/agent2`,所有重构都先落在新目录。
|
||||
- `backend/service/agentsvc` 在迁移期充当“总开关层”,决定某条链路接旧目录还是新目录。
|
||||
|
||||
### 2.2 先搬结构,再收冗余
|
||||
- 第一优先级不是“顺手优化业务逻辑”,而是把职责边界搬顺。
|
||||
- 迁移时允许短期存在“代码原样搬过去”的情况,但必须同步记录哪些地方后续要抽公共层。
|
||||
- 严禁一边大改业务逻辑、一边改目录结构,否则回归问题无法定位。
|
||||
|
||||
### 2.3 新旧链路必须可对照
|
||||
- 每搬完一个 skill,都要保证:
|
||||
- 老链路还能跑;
|
||||
- 新链路能独立接入;
|
||||
- service 层可以随时切回旧实现。
|
||||
- 迁移期间所有新入口都必须保留 feature flag 或切换点。
|
||||
|
||||
### 2.4 先做“能收敛冗余”的迁移
|
||||
- 优先搬那些已经出现明显重复代码的能力,不要先搬边缘功能。
|
||||
- 迁移不是“把史山平移到新目录”,而是“借搬迁之机把重复公共件抽出来”。
|
||||
|
||||
---
|
||||
|
||||
## 3. agent2 目标结构
|
||||
|
||||
```text
|
||||
backend/agent2/
|
||||
entrance.go
|
||||
|
||||
router/
|
||||
route.go
|
||||
route_model.go
|
||||
|
||||
graph/
|
||||
quicknote.go
|
||||
taskquery.go
|
||||
schedule.go
|
||||
|
||||
node/
|
||||
quicknote.go
|
||||
taskquery.go
|
||||
schedule_plan.go
|
||||
schedule_refine.go
|
||||
|
||||
llm/
|
||||
client.go
|
||||
json.go
|
||||
route.go
|
||||
quicknote.go
|
||||
taskquery.go
|
||||
schedule.go
|
||||
|
||||
model/
|
||||
common.go
|
||||
route.go
|
||||
quicknote.go
|
||||
taskquery.go
|
||||
schedule.go
|
||||
|
||||
prompt/
|
||||
route.go
|
||||
quicknote.go
|
||||
taskquery.go
|
||||
schedule.go
|
||||
|
||||
stream/
|
||||
emitter.go
|
||||
openai.go
|
||||
|
||||
shared/
|
||||
clone.go
|
||||
retry.go
|
||||
time.go
|
||||
```
|
||||
|
||||
## 4. 各层职责约束
|
||||
|
||||
### 4.1 `entrance.go`
|
||||
- 是整个 `agent2` 模块唯一总入口。
|
||||
- 负责:
|
||||
- 接收请求上下文;
|
||||
- 调路由;
|
||||
- 调 skill graph;
|
||||
- 收口 token、SSE、持久化。
|
||||
- 不负责:
|
||||
- 某个 skill 内部节点的业务实现;
|
||||
- 直接写 prompt;
|
||||
- 直接调用模型。
|
||||
|
||||
### 4.2 `router/`
|
||||
- 只负责一级分流。
|
||||
- 输出统一路由结果,例如:
|
||||
- `chat`
|
||||
- `quick_note_create`
|
||||
- `task_query`
|
||||
- `schedule_plan_create`
|
||||
- `schedule_plan_refine`
|
||||
- 不负责 skill 内部二次判断。
|
||||
|
||||
### 4.3 `graph/`
|
||||
- 只负责画 graph。
|
||||
- 文件里应尽量只出现:
|
||||
- `AddLambdaNode`
|
||||
- `AddEdge`
|
||||
- `AddBranch`
|
||||
- 不允许在 `graph/` 内直接写复杂业务逻辑、模型调用或数据转换。
|
||||
|
||||
### 4.4 `node/`
|
||||
- 只负责每个节点内部的业务逻辑。
|
||||
- 可以调用:
|
||||
- `llm/`
|
||||
- `shared/`
|
||||
- service/dao 注入依赖
|
||||
- 不负责:
|
||||
- Graph 连线;
|
||||
- OpenAI chunk 输出格式;
|
||||
- 大段 prompt 常量定义。
|
||||
|
||||
### 4.5 `llm/`
|
||||
- 专门负责和模型交互。
|
||||
- 统一收口:
|
||||
- `GenerateText`
|
||||
- `GenerateJSON`
|
||||
- `Stream`
|
||||
- JSON 提取与解析
|
||||
- thinking/temperature/maxTokens 等参数
|
||||
- 这一层的定位,等价于“模型数据访问层”。
|
||||
|
||||
### 4.6 `model/`
|
||||
- 存放 agent 域模型,而不是数据库模型。
|
||||
- 包括:
|
||||
- state
|
||||
- dto
|
||||
- route decision
|
||||
- tool result
|
||||
|
||||
### 4.7 `prompt/`
|
||||
- 统一收口 prompt 文本。
|
||||
- 禁止在 `node/` 和 `llm/` 中内联大段 prompt。
|
||||
|
||||
### 4.8 `stream/`
|
||||
- 统一收口 SSE/OpenAI 兼容块输出。
|
||||
- 所有阶段推送、单条 assistant 回复、finish chunk 都从这里走。
|
||||
|
||||
### 4.9 `shared/`
|
||||
- 只放纯工具,不放业务编排。
|
||||
- 当前预计优先放:
|
||||
- 深拷贝
|
||||
- 重试策略
|
||||
- 时间解析
|
||||
|
||||
---
|
||||
|
||||
## 5. 逐批搬迁顺序
|
||||
|
||||
## 第 0 批:先搭骨架,不接流量
|
||||
|
||||
### 目标
|
||||
- 先把 `agent2` 目录骨架建起来。
|
||||
- 不迁任何业务,只搭结构。
|
||||
|
||||
### 产物
|
||||
- `backend/agent2/` 目录
|
||||
- `entrance.go`
|
||||
- `router/graph/node/llm/model/prompt/stream/shared` 空壳
|
||||
|
||||
### 验证
|
||||
- 能编译通过。
|
||||
- 不接任何线上/实际业务入口。
|
||||
|
||||
---
|
||||
|
||||
## 第 1 批:先迁公共能力,不迁 skill
|
||||
|
||||
### 目标
|
||||
- 先抽最明显的公共重复件,给后续 skill 迁移打基础。
|
||||
|
||||
### 优先抽取的公共件
|
||||
- `llm/json.go`
|
||||
- 统一 `GenerateJSON`
|
||||
- 统一 JSON object 提取
|
||||
- 统一空响应/解析失败错误
|
||||
- `llm/client.go`
|
||||
- 统一 `GenerateText`
|
||||
- 统一 thinking/maxTokens/temperature 参数装配
|
||||
- `stream/openai.go`
|
||||
- 迁入现有 `ToOpenAIStream`
|
||||
- 迁入 `ToOpenAIFinishStream`
|
||||
- `stream/emitter.go`
|
||||
- 统一阶段推送
|
||||
- 统一单条 assistant completion 输出
|
||||
- `shared/clone.go`
|
||||
- 抽 `cloneWeekSchedules`
|
||||
- 抽 `cloneHybridEntries`
|
||||
- 抽 `cloneTaskClassItems`
|
||||
|
||||
### 说明
|
||||
- 这一批不碰 quicknote/taskquery/schedule 的业务语义。
|
||||
- 只把公共重复代码先搬进 `agent2`。
|
||||
|
||||
---
|
||||
|
||||
## 第 2 批:迁 `quicknote`,作为样板 skill
|
||||
|
||||
### 目标
|
||||
- 用最小 skill 验证三层结构是否顺手。
|
||||
|
||||
### 对应关系
|
||||
- 旧:
|
||||
- `backend/agent/quicknote/graph.go`
|
||||
- `backend/agent/quicknote/nodes.go`
|
||||
- `backend/agent/quicknote/tool.go`
|
||||
- `backend/agent/quicknote/state.go`
|
||||
- `backend/agent/quicknote/prompt.go`
|
||||
- 新:
|
||||
- `backend/agent2/graph/quicknote.go`
|
||||
- `backend/agent2/node/quicknote.go`
|
||||
- `backend/agent2/llm/quicknote.go`
|
||||
- `backend/agent2/model/quicknote.go`
|
||||
- `backend/agent2/prompt/quicknote.go`
|
||||
|
||||
### 迁移要求
|
||||
- 逻辑不变。
|
||||
- 原有测试优先复用。
|
||||
- 如果出现可直接删除的冗余函数,必须当场删除,不允许“先复制一份以后再说”。
|
||||
|
||||
### 接入方式
|
||||
- 在 `agentsvc` 里增加切换开关:
|
||||
- `run quicknote with agent`
|
||||
- `run quicknote with agent2`
|
||||
|
||||
---
|
||||
|
||||
## 第 3 批:迁 `taskquery`
|
||||
|
||||
### 目标
|
||||
- 用第二个 skill 验证公共层抽取是否真的通用,而不是只服务于 quicknote。
|
||||
|
||||
### 核心检查点
|
||||
- `taskquery` 是否能完全复用第 1 批抽出的 `llm/json.go`。
|
||||
- fallback/retry 是否还能保持一致。
|
||||
- 阶段推送是否已经不需要 skill 自己再包一层。
|
||||
|
||||
### 完成标准
|
||||
- `taskquery` 不再保留自己私有的 JSON 调用封装。
|
||||
|
||||
---
|
||||
|
||||
## 第 4 批:迁 `route`
|
||||
|
||||
### 目标
|
||||
- 把统一分流也纳入 `agent2`。
|
||||
|
||||
### 要求
|
||||
- `entrance.go` 以后只依赖 `agent2/router`。
|
||||
- `router` 只做一级分流,不再夹杂 skill 级 fallback。
|
||||
|
||||
---
|
||||
|
||||
## 第 5 批:迁 `scheduleplan`
|
||||
|
||||
### 目标
|
||||
- 把排程首次创建链路迁入新结构。
|
||||
|
||||
### 迁移策略
|
||||
- 不直接优化复杂业务。
|
||||
- 先把它拆成:
|
||||
- `graph/schedule.go`
|
||||
- `node/schedule_plan.go`
|
||||
- `llm/schedule.go`
|
||||
- `model/schedule.go`
|
||||
- `prompt/schedule.go`
|
||||
|
||||
### 强制要求
|
||||
- 不允许继续保留“节点文件里夹杂模型调用帮助函数”的写法。
|
||||
- 不允许在 `node/` 里再内联 JSON parse helper。
|
||||
|
||||
---
|
||||
|
||||
## 第 6 批:迁 `schedulerefine`
|
||||
|
||||
### 目标
|
||||
- 处理当前最重的史山。
|
||||
|
||||
### 原则
|
||||
- 这一步不是“原样平移”。
|
||||
- 这是第一次允许在迁移中顺手拆大文件。
|
||||
|
||||
### 最低拆分要求
|
||||
- 旧 `nodes.go` 至少拆成:
|
||||
- `schedule_refine_contract`
|
||||
- `schedule_refine_plan`
|
||||
- `schedule_refine_react`
|
||||
- `schedule_refine_review`
|
||||
- `schedule_refine_summary`
|
||||
- 旧 `tool.go` 至少拆成:
|
||||
- `tool_defs`
|
||||
- `tool_dispatch`
|
||||
- `tool_payload`
|
||||
- `tool_policy`
|
||||
|
||||
---
|
||||
|
||||
## 第 7 批:切流与删除旧目录
|
||||
|
||||
### 前提条件
|
||||
- `quicknote`
|
||||
- `taskquery`
|
||||
- `route`
|
||||
- `scheduleplan`
|
||||
- `schedulerefine`
|
||||
|
||||
以上链路都已经切到 `agent2`,并经过回归验证。
|
||||
|
||||
### 操作顺序
|
||||
- 1. service 层入口全部切到 `agent2`
|
||||
- 2. 跑一轮完整回归
|
||||
- 3. 删除旧 `backend/agent`
|
||||
- 4. `backend/agent2` 改名为 `backend/agent`
|
||||
|
||||
### 注意
|
||||
- 第 4 步必须最后做。
|
||||
- 在此之前不要提前重命名,避免 review 和 diff 变得混乱。
|
||||
|
||||
---
|
||||
|
||||
## 6. 迁移过程中的强约束
|
||||
|
||||
### 6.1 逻辑不变优先于结构完美
|
||||
- 只要旧逻辑还能清晰搬进新层次,就先搬。
|
||||
- 不要在同一轮同时追求“换架构 + 换策略 + 换 prompt”。
|
||||
|
||||
### 6.2 每次只迁一个能力域
|
||||
- 一次只动一个 skill 或一类公共件。
|
||||
- 严禁“这一轮顺手把三个 skill 一起改了”。
|
||||
|
||||
### 6.3 旧代码不删前,新代码必须已接入验证
|
||||
- 先搬
|
||||
- 再切
|
||||
- 再验
|
||||
- 最后删
|
||||
|
||||
### 6.4 公共能力一旦发现第二处重复,就必须考虑抽层
|
||||
- 不允许在 `agent2` 里继续复制第三份、第四份。
|
||||
- `agent2` 的目标不是“新目录复刻旧史山”,而是“迁移过程中顺手收口公共能力”。
|
||||
|
||||
---
|
||||
|
||||
## 7. 每一批迁移完成后的检查项
|
||||
- 是否出现了新的重复模型调用 helper?
|
||||
- 是否出现了新的重复 JSON parse helper?
|
||||
- 是否又把 prompt 写回节点文件里了?
|
||||
- 是否又在 service 层偷偷加了一层业务桥接?
|
||||
- 是否还能一眼看出:
|
||||
- 入口在哪
|
||||
- 分流在哪
|
||||
- 编排在哪
|
||||
- 节点逻辑在哪
|
||||
- 模型交互在哪
|
||||
|
||||
---
|
||||
|
||||
## 8. 当前最值得优先搬的公共件
|
||||
- `GenerateJSON/GenerateText`
|
||||
- JSON 提取与解析
|
||||
- OpenAI 兼容 SSE 包装
|
||||
- 阶段推送 emitter
|
||||
- schedule 相关深拷贝工具
|
||||
- schedule preview / snapshot 读写逻辑
|
||||
|
||||
---
|
||||
|
||||
## 9. 一句话版本
|
||||
- 先建 `agent2` 骨架。
|
||||
- 先抽公共层,再迁 skill。
|
||||
- 先迁 `quicknote` 做样板,再迁 `taskquery` 验证复用,再动 `schedule`。
|
||||
- 迁移期间新旧并存、随时可切回。
|
||||
- 删除旧 `agent` 只能发生在所有 skill 全部切流并验证通过之后。
|
||||
|
||||
Reference in New Issue
Block a user