Version: 0.9.45.dev.260427

后端:
1. execute 主链路重构为“上下文工具域 + 主动优化候选闭环”——移除 order_guard,粗排后默认进入主动微调,先诊断再从后端候选中选择 move/swap,避免 LLM 自由全局乱搜
2. 工具体系升级为动态注入协议——新增 context_tools_add / remove、工具域与二级包映射、主动优化白名单;schedule / taskclass / web 工具按域按包暴露,msg0 规则包与 execute 上下文同步重写
3. analyze_health 升级为主动优化唯一裁判入口——补齐 rhythm / tightness / profile / feasibility 指标、候选扫描与复诊打分、停滞信号、forced imperfection 判定,并把连续优化状态写回运行态
4. 任务类能力并入新 Agent 执行链——新增 upsert_task_class 写工具与启动注入事务写入;任务类模型补充学科画像与整天屏蔽配置,粗排支持 excluded_days_of_week,steady 策略改为基于目标位置/单日负载/分散度/缓冲的候选打分
5. 运行态与路由补齐优化模式语义——新增 active tool domain/packs、pending context hook、active optimize only、taskclass 写入回盘快照;区分 first_full / global_reopt / local_adjust,并完善首次粗排后默认 refine 的判定

前端:
6. 助手时间线渲染细化——推理内容改为独立 reasoning block,支持与工具/状态/正文按时序交错展示,自动收口折叠,修正 confirm reject 恢复动作

仓库:
7. newAgent 文档整体迁入 docs/backend,补充主动优化执行规划与顺序约束拆解文档,删除旧调试日志文件

PS:这次科研了2天,总算是有些进展了——LLM永远只适合做选择题、判断题,不适合做开放创新题。
This commit is contained in:
Losita
2026-04-27 01:09:37 +08:00
parent 04b5836b39
commit 66c06eed0a
60 changed files with 9163 additions and 1819 deletions

View File

@@ -0,0 +1,523 @@
# 主动优化顺序约束拆分执行计划
## 1. 本轮目标
本轮要解决的不是单点 bug而是一个架构错位
1. 主动优化希望 LLM 在窗口内自主微调,围绕负载、节奏、容错做多轮观察与挪动。
2. 现有顺序保护却是“全局 suggested 基线 + 收口时自动复原”,本质是事后抢救。
3. 两者叠加后LLM 前面刚优化完,后面又可能被 `order_guard` 否掉,甚至否不回去,只能带着异常结果交付。
因此,本轮的核心目标是:
1. 把“顺序约束”从 graph 收口节点,下沉为写工具层的前置约束。
2. 把“全局顺序冻结”改成“允许跨科目交错,但锁住同任务类内部顺序”。
3. 顺手修掉当前主动优化链路里由旧守卫带来的提示污染、卡片误导、兼容性 bug。
4. 借这次改造,把 `node/execute.go` 继续拆职责,避免后续主动优化逻辑继续堆在单文件里。
---
## 2. 当前问题诊断
### 2.1 产品语义错位
当前系统默认语义仍是:
1. `AllowReorder=false` 时,尽量保持所有 suggested 的全局相对顺序。
2. 若被打乱,则在 `order_guard` 节点尝试按 baseline 复原。
这和我们已经对齐的新产品语义冲突:
1. 用户默认不是“完全不许动顺序”。
2. 用户要的是“每门课内部别乱序,但不同课之间可以交错来换负载”。
3. 主动优化阶段的目标是优化坑位分布,不是死守粗排全局序列。
### 2.2 约束位置放错了
当前顺序保护发生在:
1. `execute` 完成后。
2. `graph/order_guard` 收口前。
这会导致三个问题:
1. 非法移动已经发生,后面只能补救。
2. 补救失败也不会阻断交付,只会吐一句“顺序异常但未复原”。
3. LLM 在执行时完全不知道哪些移动其实不该做,容易白跑。
### 2.3 约束粒度过粗
当前基线是“所有 suggested 任务的时间顺序快照”,这会把下面两类本来合理的操作也一起误伤:
1. 不同任务类之间为了均衡负载而做的交错。
2. 在不破坏科目内部先后关系的前提下做的跨天平衡。
### 2.4 当前 bug 已经暴露
从日志看,至少已有这些具体问题:
1. `order_guard` 尝试复原时出现 `slot_incompatible`
- 本质说明旧复原逻辑对“任务时长单位”和“坑位跨度单位”的理解并不稳。
- 这条链本来就不该继续扩展,而该整体退场。
2. 前端会收到“已记录本轮建议任务顺序基线”“顺序异常但未执行自动复原”这类对用户价值很低的系统话术。
3. `execute` prompt 仍在强调“默认保持 suggested 相对顺序”,这会继续把模型往旧目标上拽。
4. `spread_even` / `move` / `swap` / `batch_move` 当前都不知道“同任务类兄弟节点边界”,所以无法在写入前拦住越界调整。
### 2.5 代码结构已经不适合继续堆功能
当前 `node/execute.go` 已经承载了:
1. execute 主循环。
2. 工具执行。
3. 工具结果摘要。
4. feasibility 守门。
5. task class 写入状态回盘。
6. preview 实时写。
7. 顺序相关拦截。
8. scope 解析。
这类文件继续加主动优化逻辑,后续回归会越来越难定位。
---
## 3. 目标行为
改造后的目标行为如下:
1. LLM 仍然可以主动观察、主动微调、再观察,不退化成一次性确定性求解。
2. 默认允许跨任务类交错调整。
3. 默认不允许打乱同一任务类内部的学习顺序。
4. 每次写工具调用前,后端都能判断这次移动是否越过“同任务类上一个/下一个任务”的合法边界。
5. 如果越界,工具直接返回失败原因,让 LLM 换别的任务或别的坑位,而不是先写进去、最后再抢救。
6. 交付阶段不再出现旧 `order_guard` 的提示文案,也不再依赖它去修复顺序。
一句话概括:
> 允许跨科目穿插优化,但每门课内部始终保持原有学习推进顺序。
---
## 4. 必须补齐的数据
这是本轮最关键的数据面。没有这些字段,后端没法在写工具层判断“这个任务能挪到哪”。
### 4.1 任务类内部顺序 rank
当前 `ScheduleTask` 里有:
1. `TaskClassID`
2. `SourceID``task_item.id`
但没有:
1.`task_item` 在所属任务类里的 `order`
这意味着后端知道“它属于哪门课”,但不知道“它是这门课里的第几个任务”。
本轮需要补:
1.`schedule.ScheduleTask` 增加类似 `TaskOrder` 的运行态字段。
2.`conv/schedule_state.go``model.TaskClassItem.Order` 映射进来。
### 4.2 顺序边界计算所需的同类兄弟信息
有了 `TaskClassID + TaskOrder` 后,不一定非要把前后兄弟 ID 也落进 state两种方案都可行
1. 轻量方案:运行时动态扫描同任务类任务,按 `TaskOrder` 算前驱/后继。
2. 预计算方案:在 state 初始化时直接建立 sibling index。
本轮建议先走轻量方案,原因:
1. 改动面更小。
2. 不引入新的状态同步负担。
3. 足够支撑写工具前置校验。
### 4.3 合法时间边界的统一定义
需要明确一个统一规则:
1. 一个任务的目标位置,必须晚于同任务类前驱任务的结束时间。
2. 必须早于同任务类后继任务的开始时间。
3. 若前驱/后继不存在,则该侧边界开放。
4. 若前驱/后继当前是 pending、未落位则该侧边界暂不收紧。
这样 LLM 仍有自由度,但自由度被严格限制在“本任务合法活动区间”里。
---
## 5. 方案总览
### 5.1 总体策略
本轮不再沿用“先放任移动,最后 graph 收口时修”的模式,而改成:
1. 写工具调用前先验边界。
2. 合法才允许写。
3. 非法直接返回失败。
4. 收口阶段只做轻量断言,不再自动复原。
### 5.2 顺序保护新哲学
旧哲学:
1. 保护粗排全局时间序列。
新哲学:
1. 保护每个任务类内部的推进顺序。
2. 不保护不同任务类之间的相对先后。
### 5.3 对主动优化的意义
这套改法的直接意义是:
1. LLM 终于可以真的做“负载优化”而不是被全局顺序锁死。
2. LLM 即使选错目标,也会在写工具层收到具体失败原因。
3. 失败原因足够明确时,模型下一步就知道该换任务、换天、还是换工具。
---
## 6. 具体拆分与改动计划
## 6.1 第一步:给 ScheduleState 补顺序语义
涉及文件:
1. `backend/newAgent/tools/schedule/state.go`
2. `backend/newAgent/conv/schedule_state.go`
计划动作:
1.`ScheduleTask` 增加任务类内部顺序字段。
- 建议名:`TaskOrder int`
2.`source=task_item` 时填充该字段。
3.`model.TaskClassItem.Order` 注入运行态。
4. 对缺失 order 的历史数据做兜底。
- 优先使用数据库 order。
- 若为空,则按 `TaskClass.Items` 当前顺序补稳定序号。
验收结果:
1. 每个 `task_item` 在工具层都能知道自己是所属任务类里的第几项。
2. 查询工具输出里不一定要暴露这个字段给 LLM但后端必须可用。
## 6.2 第二步:新增“局部顺序约束”公共层
涉及文件:
1. 新增 `backend/newAgent/tools/schedule/order_constraints.go`
2. 复用 `backend/newAgent/tools/schedule/write_helpers.go`
计划动作:
1. 抽一个独立公共层,不把顺序判断散落在每个写工具里重复写。
2. 公共层职责只做一件事:判断某个任务能否落到某个目标时段。
3. 需要提供的核心能力:
- 找到同任务类前驱任务
- 找到同任务类后继任务
- 计算合法最早起点 / 最晚终点
- 判断目标位置是否越界
- 输出中文失败原因
建议返回信息:
1. `ok=true/false`
2. 失败原因中文摘要
3. 命中的前驱/后继任务是谁
4. 合法范围描述
这样后面各写工具都能直接复用,不再复制逻辑。
## 6.3 第三步:把约束前置到基础写工具
涉及文件:
1. `backend/newAgent/tools/schedule/write_tools.go`
计划动作:
1. `move` 接入局部顺序约束。
2. `swap` 在交换前对双方交换后的目标位置分别校验。
3. `batch_move` 在克隆态上统一校验整批目标是否都满足局部顺序约束。
4. `place` 也要接入。
- 因为被 `unplace` 后再次放回,仍然可能破坏同类顺序。
5. `unplace` 暂时不做顺序阻断。
- 它只是把任务拿出来,不直接打乱同类内部先后。
- 真正的顺序问题应在后续 `place/move` 时拦截。
验收目标:
1. 任一基础写工具都不能把任务挪出自己的合法兄弟区间。
2. 非法时工具直接失败,且提示能被 LLM 看懂。
## 6.4 第四步:让复合写工具也遵守边界
涉及文件:
1. `backend/newAgent/tools/schedule/compound_tools.go`
计划动作:
1. `spread_even` 生成候选位置后,回填前逐任务校验局部顺序边界。
2. 若规划器给出的结果越界,整次复合写失败并给出明确原因。
3. `min_context_switch` 继续维持 P1 不暴露。
4. 即使未来重开,也必须走同一套局部顺序约束,不允许绕过。
原因:
1. 复合工具最容易“整体看起来更均匀,但把单科内部顺序打乱”。
2. 如果只拦基础写工具,不拦复合工具,系统规则会不一致。
## 6.5 第五步:退役旧 order_guard
涉及文件:
1. `backend/newAgent/node/order_guard.go`
2. `backend/newAgent/graph/common_graph.go`
3. `backend/newAgent/model/common_state.go`
4. `backend/newAgent/node/execute.go`
计划动作:
1. 移除“全局 baseline + 收口复原”的主逻辑。
2. 删除或停用 `SuggestedOrderBaseline` 运行态。
3. 删除 `order_guard` 节点在主动优化链路中的强依赖。
4. 交付前若仍需要安全兜底,只保留一个轻量 final assert
- 仅检查每个任务类内部顺序是否仍合法
- 不自动复原
- 若非法,视为执行层 bug直接中止交付并打日志
推荐做法:
1. P1 先彻底切掉 graph 层 `order_guard` 分支。
2. 若担心过渡期风险,再补一个极轻的校验函数在 deliver 前调用。
## 6.6 第六步:同步调整 prompt 与模型目标
涉及文件:
1. `backend/newAgent/prompt/execute_context.go`
2. `backend/newAgent/prompt/execute.go`
3. 视需要补充 `prompt/execute_rule_packs.go`
计划动作:
1. 删除“默认保持 suggested 相对顺序”的旧表述。
2. 改成新的明确描述:
- 默认保持同任务类内部顺序
- 允许跨任务类交错调整
- 不得擅自突破同任务类内部先后
3. 把“非法时工具会直接失败”作为模型可感知规则写进 prompt。
这样 LLM 会更接近真实规则,不会一直沿着旧目标空转。
## 6.7 第七步:拆 execute.go 职责
涉及文件:
1. `backend/newAgent/node/execute.go`
2. 新增若干并行文件
建议拆分方向:
1. `node/execute.go`
- 只保留主循环、决策分发、节点入口
2. `node/execute_scope_guard.go`
- 当前步骤作用域解析与日期范围守门
3. `node/execute_tool_runtime.go`
- `executeToolCall` / `executePendingTool` / preview 写入
4. `node/execute_tool_summary.go`
- 工具摘要、参数摘要、结果摘要
5. `node/execute_taskclass_runtime.go`
- task class upsert 状态回盘相关
6. `node/execute_health_runtime.go`
- feasibility / health 快照更新
这一步的目的不是“为了好看”而是避免后面继续把主动优化规则、task class 流程规则、工具结果摘要全塞回一个文件。
---
## 7. 本轮顺手修复的 bug 清单
## 7.1 bug A顺序异常提示污染用户体验
现象:
1. 前端会看到“已记录本轮建议任务顺序基线”
2. 以及“检测到顺序异常,但本次未执行自动复原”
修法:
1.`order_guard` 退役一起移除这两类状态文案。
2. 这类内部守卫信息不再面向用户显式展示。
## 7.2 bug B`slot_incompatible` 兼容性问题
现象:
1. 日志里出现 `expected_duration=1 slot_duration=2`
判断:
1. 这是旧 `order_guard` 复原链上的单位不一致问题。
2. 该问题不值得单独继续修补。
修法:
1. 旧复原链退役后,这条 bug 自然消失。
2. 本轮只保留一个动作:确认写工具本身的时长计算口径仍正确。
## 7.3 bug Cprompt 仍把模型往“全局不乱序”上引
现象:
1. `execute_context` 里仍写着默认保持 suggested 相对顺序。
修法:
1. 改成“默认保持同任务类内部顺序”。
## 7.4 bug D复合工具可能绕过新规则
现象:
1. `spread_even` 当前只校验冲突,不校验同类前后边界。
修法:
1. 接入统一局部顺序约束层。
## 7.5 bug Eactive optimize 链路和 execute 文件职责缠得太紧
现象:
1. 任何主动优化 bug 都容易改进 `execute.go`,继续涨文件体积。
修法:
1. 本轮同步拆文件,至少把工具执行与摘要逻辑拆出去。
---
## 8. 实施顺序
建议按下面顺序推进,避免中途状态既不兼容旧逻辑,也没完全切到新逻辑。
### 阶段 1补数据
1.`ScheduleTask` 增加 `TaskOrder`
2. 在 state loader 中完成映射
3. 保证查询 / 粗排 / 预览链路不受影响
### 阶段 2落局部顺序约束公共层
1. 实现前驱/后继查找
2. 实现目标落位合法性判断
3. 输出中文失败原因
### 阶段 3接入基础写工具
1. `move`
2. `swap`
3. `batch_move`
4. `place`
### 阶段 4接入复合写工具
1. `spread_even`
2. 保持 `min_context_switch` 继续禁用
### 阶段 5切掉旧 order_guard
1. 删除 graph 分支
2. 删除 baseline 运行态
3. 去掉用户可见状态文案
### 阶段 6更新 prompt
1. 改目标描述
2. 改顺序策略说明
3. 明确非法写工具会被后端拒绝
### 阶段 7拆 execute.go
1. 先无行为变化拆文件
2. 再补必要注释与最小验证
---
## 9. 验证口径
## 9.1 正向场景
要验证这些场景能通过:
1. 同任务类内部顺序不变,但不同任务类交错后负载更均衡。
2. LLM 将某任务从第 3 天挪到第 20 天,只要仍在其前后兄弟之间,就允许。
3. `spread_even` 可以把多门课拉开,但不会把某一门课内部顺序反过来。
## 9.2 反向场景
要验证这些场景被拦住:
1. 把某门课的第 4 个任务挪到第 1 个任务前面。
2. 把某门课的中间任务挪到其后继任务之后。
3. `swap` 后导致同任务类内部出现逆序。
4. `batch_move` 中有一条越界时整批失败。
## 9.3 交付场景
要确认这些旧副作用消失:
1. 不再出现 `order_guard_initialized`
2. 不再出现 `order_guard_restore_skipped`
3. 不再依赖 `SuggestedOrderBaseline`
## 9.4 代码结构场景
要确认:
1. `execute.go` 文件职责明显变轻
2. 局部顺序约束逻辑只存在一份公共实现
---
## 10. 本轮建议的最小落地范围
如果要控制风险,本轮建议先做到这里:
1. `TaskOrder` 注入
2. 局部顺序约束公共层
3. `move/swap/batch_move/place` 接入
4. `spread_even` 接入
5. prompt 改口径
6. 切掉旧 `order_guard`
7. 拆出 `execute_tool_runtime.go``execute_tool_summary.go`
这个范围已经足够让主动优化链路从“旧哲学打架”切到“新哲学能跑”。
---
## 11. 预期收益
做完之后,预期表现会变成:
1. LLM 会更敢做真实优化,因为它不再被全局顺序锁死。
2. 后端会在写工具层直接给出“能不能这么挪”的明确反馈。
3. 同一门课的学习推进顺序能被稳定锁住。
4. 不同门课之间仍有足够空间做均衡、分散、减压。
5. 前端不会再收到旧 `order_guard` 带来的迷惑状态。
6. 后续如果继续加主动优化策略,也有更干净的承载位置,不必继续往 `execute.go` 里堆。
---
## 12. 本文档对应的实施结论
本轮建议按以下原则执行:
1. 删除“全局 suggested 顺序守卫”思路。
2. 改为“同任务类内部顺序约束前置到写工具层”。
3. 允许跨任务类交错优化。
4. 顺手清理旧 guard 带来的用户可见噪音与兼容性问题。
5. 同步拆分 execute 相关职责文件,避免继续堆史山。