# 主动优化整套改造计划

## 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. 主动优化完成后的结果，解释口径更像“为什么这样学更顺”，而不是“哪些数字变好看了”。
