# 主动优化整套改造计划 ## 1. 文档目的 本文档只回答一件事:这轮主动优化链路改完之后,整体会如何工作。 重点不放在实现细节,而放在以下 4 个问题: 1. 哪些能力保留,哪些能力直接删除。 2. `task_class`、粗排、主动优化三者之间的职责如何重新划分。 3. 首次排程时,agent 的完整执行链路会变成什么样。 4. 时间窗口过紧时,agent 应该如何自动放宽要求,避免陷入无意义重试。 --- ## 2. 改造后的核心原则 ### 2.1 LLM 只负责“语义认知优化” 这轮改造后的总原则是: 1. 确定性算法负责“排得下、排得合法、排得别太离谱”。 2. LLM 负责“学起来舒不舒服、搭配顺不顺、是否符合用户偏好”。 换句话说,LLM 不再负责: 1. 全局负载均衡。 2. 均匀铺开任务。 3. 追逐空窗、碎片率、最大 gap 之类的统计指标。 4. 为了把报表修漂亮而反复搬运任务。 LLM 保留的价值只有两类: 1. 学科语义理解。 2. 基于语义和偏好的认知微调。 ### 2.2 主动优化从“统计修表”改成“认知微调” 改造完成后,主动优化不再问: 1. 哪天更满。 2. 哪天更空。 3. 哪门课间隔是不是又多了 1 天。 4. 空窗碎片率是不是还可以再低一点。 改造完成后,主动优化只问: 1. 这一天切换是不是太碎。 2. 两门课放在一起,认知上是不是太累。 3. 连续学习块是不是太长。 4. 当前安排是不是违背了用户偏好。 5. 在当前时间窗口下,这个问题是不是值得继续修。 --- ## 3. 工具去留 ## 3.1 保留 ### 3.1.1 `analyze_health` 保留,且继续作为主动优化的唯一总入口。 新职责: 1. 汇总当前排程在认知节奏上的主要问题。 2. 汇总当前排程和用户偏好的冲突。 3. 判断当前是否还有足够可调整空间继续优化。 4. 判断当前是否已经可以合理收口。 ### 3.1.2 `analyze_rhythm` 保留,作为 `analyze_health` 的下钻工具。 新职责: 1. 解释某一天为什么学起来别扭。 2. 解释某几个任务为什么不适合连在一起。 3. 解释当前切换多、连续块长、高强度相邻等问题落在哪些具体任务上。 ### 3.1.3 现有点查工具 全部保留,尤其是: 1. `query_range` 2. `query_target_tasks` 3. `query_available_slots` 4. `get_task_info` 原因很简单: 1. `health/rhythm` 只负责指出问题和方向。 2. LLM 真正落到“挪哪个任务、往哪里挪”时,仍然必须依赖这些点查工具。 ### 3.1.4 现有写工具 全部保留。 主动优化改的是“如何观察和决策”,不是“如何写入”。 但写工具在主动优化里的使用优先级要重排: 1. `slack` 高时,允许 `move` 和 `swap` 一起参与小范围微调。 2. `slack` 低时,默认优先考虑 `swap`,不优先考虑 `move`。 3. `slack` 低时若使用 `swap`,只允许交换属于不同 `task_class` 的任务。 4. 这样做的目的不是保守,而是用“跨类互换”天然保证类内顺序不被破坏。 --- ## 3.2 删除 ### 3.2.1 删除 `analyze_load` 原因: 1. 负载均衡是确定性算法的职责。 2. 它会强烈诱导 LLM 变成搬格子苦力。 3. 它无法体现 LLM 真正有优势的学科语义判断。 ### 3.2.2 删除 `analyze_tolerance` 原因: 1. 容错本质上是粗排风格与窗口宽松度问题。 2. 它不适合作为主动优化主链路的独立分析工具。 3. 它容易继续把模型引向“留空窗/修空窗”的伪目标。 ### 3.2.3 删除所有 gap/load/tolerance 驱动指标 以下指标全部退出主动优化链路: 1. `max_gap` 2. `avg_gap` 3. `gap_std_dev` 4. `fragmentation_rate` 5. `avg_gap_size` 6. `max_gap_size` 7. `days_without_buffer` 8. `utilization_rate` 9. `load_std_dev` 10. `load_range` 11. `budget_progress` 12. `days_to_end` 说明: 1. 它们可以在未来作为统计观察数据重建。 2. 但本轮改造后,它们不再参与主动优化决策,不再生成 issue,不再生成 next action。 --- ## 4. `task_class` 改造后会怎样 ## 4.1 新增 3 个语义字段 每个 `task_class` 新增以下字段: 1. `subject_type` 2. `difficulty_level` 3. `cognitive_intensity` 这 3 个字段只服务于一个目标:让后续排程和主动优化不再对着学科名裸猜。 ## 4.2 写入时机 这 3 个字段不在排程时临时生成,而是在创建或更新 `task_class` 时就提前写好。 也就是说: 1. 用户创建任务类。 2. LLM 在任务类阶段补全这 3 个字段。 3. 任务类一旦落库,后续粗排和主动优化都直接读取。 兜底策略: 1. 老数据如果没有这 3 个字段,排程时允许临时现判一次。 2. 现判完成后,应补写回 `task_class`,避免下次重复猜。 ## 4.3 这 3 个字段后续如何被使用 粗排阶段: 1. 可以作为轻量参考,但不是主驱动。 主动优化阶段: 1. `analyze_health` 直接消费这 3 个字段。 2. `analyze_rhythm` 直接消费这 3 个字段。 3. LLM 在诊断“背靠背是否太累、连续块是否太长、某种切换是否合理”时,统一以这 3 个字段为事实基础。 --- ## 5. 粗排算法改造后会怎样 ## 5.1 粗排负责的事 改造后,粗排要提前吃掉原本不该交给 LLM 的工作。 粗排的职责固定为: 1. 保证可行。 2. 保证顺序合法。 3. 保证基础分布别太离谱。 4. 保证不要明显堆到少数几天。 5. 保证不要把整段窗口排成毫无操作空间的死局。 ## 5.2 粗排不负责的事 粗排不追求: 1. 认知体验最优。 2. 学科搭配最优。 3. 用户偏好最优。 这些交给 LLM 后续做 1 到 2 轮小范围微调。 ## 5.3 粗排后的预期结果 粗排完成后,产物应该是: 1. 一个合法可执行的初稿。 2. 一个从统计上看不难看,但未必最舒服的日程。 3. 一个仍然留有少量可调整空间的底盘。 也就是说,粗排之后不需要“完美”,只需要“足够好,值得微调”。 --- ## 6. `analyze_health` 改造后会怎样 ## 6.1 定位 `analyze_health` 变成“认知健康总览”。 它不再是统计体检工具,而是 execute 阶段判断“要不要继续动、该往哪种认知方向动”的入口。 ## 6.2 新职责 改造后它只看三件事: 1. 当前认知节奏是否别扭。 2. 当前安排是否违背用户偏好。 3. 当前窗口是否还允许继续优化。 ## 6.3 新输出口径 它输出的问题应该是这种风格: 1. 某天高强度切换过多。 2. 两门高强度课背靠背。 3. 某天连续高强度学习块过长。 4. 当前安排违背“早上别排硬课”之类的用户偏好。 5. 当前可调整空间过低,剩余问题属于必要妥协。 它不再输出这种风格: 1. 哪天负载更满。 2. 最大空窗还有几天。 3. 空窗碎片率还可以再压多少。 4. 某一科是不是再均匀一点更漂亮。 ## 6.4 新 `can_close` 含义 改造后,`can_close` 的语义要收紧为: 1. 当前没有明显值得继续修的认知问题。 2. 当前没有明显违背用户偏好的安排。 3. 或者虽然还存在小问题,但当前 `slack` 已低,继续优化收益不高。 也就是说,`can_close` 不再由统计指标主导,而由“是否还有高价值认知问题”主导。 --- ## 7. `analyze_rhythm` 改造后会怎样 ## 7.1 定位 `analyze_rhythm` 变成 `analyze_health` 的明细镜。 只有当 `health` 发现某类认知问题值得继续查时,才进一步调用 `rhythm`。 ## 7.2 新职责 它要回答的不是“排得均不均”,而是: 1. 哪一天切换太碎。 2. 哪一段连续块太长。 3. 哪几个任务挨在一起会特别累。 4. 哪些切换虽然换科了,但其实仍属于同一种脑力模式。 ## 7.3 新输出风格 它的输出重点应围绕: 1. 日内切换次数。 2. 连续学习块结构。 3. 高强度相邻关系。 4. 同类/异类学科切换关系。 5. 某一天内部的认知压力分布。 它不再承担: 1. 跨天 gap 追踪。 2. 学科分散度统计优化。 3. 预算推进告警。 --- ## 8. 新增 `slack` 后会怎样 ## 8.1 为什么必须加 `slack` 有些用户给的时间窗口非常紧。 这时“高强度背靠背”不一定是错误,而可能是当前窗口下的必要代价。 如果没有 `slack` 概念,agent 会误以为: 1. 这是可修的问题。 2. 我应该继续搬。 3. 继续搬总能更好。 然后就进入无意义重试。 ## 8.2 `slack` 的职责 `slack` 不负责决定“舒服不舒服”,只负责决定“还有没有优化余地”。 也就是说,它是健康分析里的第二层判断: 1. 有问题,不代表值得继续修。 2. 值得继续修,还要看当前有没有空间修。 ## 8.3 `slack` 接入后的行为 改造后: 1. 若 `slack` 高,按正常标准检查认知问题。 2. 若 `slack` 中,允许小问题存在,但仍可做 1 次小范围微调。 3. 若 `slack` 低,自动放宽要求,允许必要的背靠背、较长连续块、略多切换。 4. 若 `slack` 低但仍存在明显可改善的认知问题,优先尝试一次低成本 `swap`,而不是优先尝试 `move`。 5. 这个 `swap` 必须限定为“只交换不同 `task_class` 的任务”,从而避免打乱任一类内部顺序。 6. 若一次 `swap` 后没有明显改善,则倾向收口,不进入连续搬运。 ## 8.4 `slack` 带来的收口变化 改造后,agent 不会再因为下面这类场景反复挣扎: 1. 时间太紧,不得不连着上两门硬课。 2. 可动任务几乎都被前驱后继夹死。 3. 当前再动只会拆东墙补西墙。 这时 `analyze_health` 应直接给出结论: 1. 当前仍有认知妥协点。 2. 但由于可调整空间有限,已属于合理结果。 3. 可以收口,或只在用户明确要求时继续深挖。 --- ## 9. 改造后的首次排程完整链路 这是你最关心的部分:改完以后,首次排程到底怎么跑。 ## 9.1 第 0 步:任务类先带语义字段 在真正排程前,相关 `task_class` 已经具备: 1. `subject_type` 2. `difficulty_level` 3. `cognitive_intensity` 如果缺失,先补齐再进入完整主动优化链路。 ## 9.2 第 1 步:确定性粗排先出底盘 系统先用确定性算法完成一版粗排。 这一步的结果要求是: 1. 可排下。 2. 顺序合法。 3. 分布不难看。 4. 还留有一点可调整空间。 ## 9.3 第 2 步:进入 `analyze_health` 粗排完成后,不再先看 load,不再先看 gap,而是直接进入 `analyze_health`。 这一步会判断: 1. 当前有哪些高价值认知问题。 2. 当前是否存在用户偏好冲突。 3. 当前 `slack` 高不高。 4. 当前是否值得继续动。 ## 9.4 第 3 步:必要时下钻 `analyze_rhythm` 只有当 `health` 发现值得修的问题时,LLM 才进一步调用 `analyze_rhythm`。 这一步的作用是: 1. 把问题定位到某一天、某几个任务、某种相邻关系。 2. 给 LLM 读写工具调用提供更明确的认知方向。 ## 9.5 第 4 步:LLM 用旧点查工具锁定目标 接下来 LLM 不会根据 `health/rhythm` 直接拍脑袋写入。 它仍然要走: 1. `query_range` 2. `query_target_tasks` 3. `query_available_slots` 4. `get_task_info` 也就是说,新的分析工具负责“告诉它为什么动、朝哪动”,旧点查工具负责“告诉它具体怎么动”。 当 `slack` 低时,这一步的目标还会进一步收窄为: 1. 先找有没有值得做的一次性交换机会。 2. 优先找跨 `task_class` 的互换对象。 3. 只有在没有合适 `swap`,且单步 `move` 的收益明显高于风险时,才考虑 `move`。 ## 9.6 第 5 步:LLM 做 1 到 2 次小范围微调 改造后,主动优化默认只做小范围微调,不做全盘翻修。 默认目标是: 1. 消除最明显的认知别扭点。 2. 避免新问题比旧问题更重。 3. 不为了报表漂亮而继续搬运。 这里再补一条强规则: 1. `slack` 高时,可以正常比较 `move` 与 `swap`。 2. `slack` 低时,优先考虑一次跨 `task_class` 的 `swap` 来调整不同科目间的相对顺序。 3. `slack` 低时,不鼓励进入多步 `move` 链路。 4. `swap` 的价值在于:它更像“整理现有坑位里的学科顺序”,而不是“重新开一轮搬家”。 ## 9.7 第 6 步:再做一次 `analyze_health` 写操作后再次进入 `analyze_health`。 这一步不是看统计报表有没有更均匀,而是看: 1. 主要认知问题是否缓解。 2. 用户偏好冲突是否减少。 3. 当前 `slack` 是否已不支持继续动。 4. 是否可以收口。 ## 9.8 第 7 步:合理收口 最终存在三种收口方式: 1. 问题已明显改善,可以正常收口。 2. 还存在小问题,但 `slack` 过低,按“合理妥协”收口。 3. 用户明确还不满意,再继续下一轮。 其中第 2 种收口还要补一层判断: 1. 不是一看到 `slack` 低就立刻停手。 2. 而是先看是否存在一次低成本、跨 `task_class` 的 `swap` 机会。 3. 若存在且收益明确,可先做这一次整理式调整。 4. 若不存在,或做完后仍无明显改善,再按“合理妥协”收口。 --- ## 10. 改造后的局部调整链路 改造后,不是所有用户请求都要走完整主动优化链路。 ## 10.1 默认仍走旧链路 用户如果只是说: 1. 把这个任务挪一下。 2. 这节课换一天。 3. 给我把这个排到周末。 这类请求默认继续走旧点查 + 旧写工具链路。 原因: 1. 这是局部执行问题。 2. 不值得每次都拉起 `health/rhythm` 做一轮体检。 ## 10.2 只有两类情况再启动主动优化分析 1. 首次排程。 2. 用户明确表达认知感受或结构性问题。 例如: 1. “切换太多了,心累。” 2. “这些硬课连着看着就难受。” 3. “帮我整体调顺一点。” --- ## 11. 改造后的最终表现 改完以后,这条链路的预期表现应该是: 1. 用户创建任务类时,学科语义先被沉淀下来。 2. 粗排算法先给出一个合法且分布不难看的初稿。 3. 主动优化不再围着负载、空窗、gap 打转。 4. `analyze_health` 只关心认知体验、偏好冲突、可调整空间。 5. `analyze_rhythm` 只负责解释具体哪段学起来别扭。 6. LLM 只做 1 到 2 次高价值认知微调,不再做长链路苦力搬运。 7. 时间窗口很紧时,agent 会承认“这是必要妥协”,而不是继续死磕。 一句话总结: 改造后,这套链路不再追求“把报表修漂亮”,而是追求“把这份日程修得更像人能学下去”。 --- ## 12. 实施顺序 按以下顺序落地: 1. 先改 `task_class`,补 3 个语义字段及写入链路。 2. 再删 `analyze_load`、`analyze_tolerance` 及相关主链路接入。 3. 再重写 `analyze_health` 的职责、指标和收口口径。 4. 再重写 `analyze_rhythm` 的职责和输出结构。 5. 再补 `slack` 及其自动放宽规则。 6. 最后改 execute prompt 和链路收口逻辑。 这样做的原因是: 1. 先有语义数据,分析工具才不至于空转。 2. 先把旧统计驱动砍掉,execute 才不会继续被错误方向牵着跑。 3. 最后再调 prompt,才不会变成给旧结构打补丁。 --- ## 13. 本轮验收口径 如果改造成功,至少应满足以下表现: 1. 首次排程时,agent 不再为了负载均匀或空窗漂亮反复搬任务。 2. 日志中的主动优化理由,主要变成认知体验和偏好,而不是统计指标。 3. 当时间很紧时,agent 会主动接受必要妥协,不再死循环。 4. 当用户只是提局部挪动需求时,不会动辄拉起全局体检。 5. 主动优化完成后的结果,解释口径更像“为什么这样学更顺”,而不是“哪些数字变好看了”。