Files
smartmate/docs/backend/主动优化顺序约束拆分执行计划.md
Losita 66c06eed0a 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永远只适合做选择题、判断题,不适合做开放创新题。
2026-04-27 01:09:37 +08:00

16 KiB
Raw Permalink Blame History

主动优化顺序约束拆分执行计划

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. SourceIDtask_item.id

但没有:

  1. task_item 在所属任务类里的 order

这意味着后端知道“它属于哪门课”,但不知道“它是这门课里的第几个任务”。

本轮需要补:

  1. schedule.ScheduleTask 增加类似 TaskOrder 的运行态字段。
  2. conv/schedule_state.gomodel.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 Bslot_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.goexecute_tool_summary.go

这个范围已经足够让主动优化链路从“旧哲学打架”切到“新哲学能跑”。


11. 预期收益

做完之后,预期表现会变成:

  1. LLM 会更敢做真实优化,因为它不再被全局顺序锁死。
  2. 后端会在写工具层直接给出“能不能这么挪”的明确反馈。
  3. 同一门课的学习推进顺序能被稳定锁住。
  4. 不同门课之间仍有足够空间做均衡、分散、减压。
  5. 前端不会再收到旧 order_guard 带来的迷惑状态。
  6. 后续如果继续加主动优化策略,也有更干净的承载位置,不必继续往 execute.go 里堆。

12. 本文档对应的实施结论

本轮建议按以下原则执行:

  1. 删除“全局 suggested 顺序守卫”思路。
  2. 改为“同任务类内部顺序约束前置到写工具层”。
  3. 允许跨任务类交错优化。
  4. 顺手清理旧 guard 带来的用户可见噪音与兼容性问题。
  5. 同步拆分 execute 相关职责文件,避免继续堆史山。