From c0868b690dddf37600806e0f9ed4750cbcf8cff5 Mon Sep 17 00:00:00 2001 From: LoveLosita <2810873701@qq.com> Date: Wed, 29 Apr 2026 20:38:52 +0800 Subject: [PATCH] Version: 0.9.57.dev.260429 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 后端: 1.更新了文档,准备开始实施第二阶段 --- docs/backend/第二阶段主动调度MVP功能预期.md | 571 ++++++++++++++++++++ 1 file changed, 571 insertions(+) create mode 100644 docs/backend/第二阶段主动调度MVP功能预期.md diff --git a/docs/backend/第二阶段主动调度MVP功能预期.md b/docs/backend/第二阶段主动调度MVP功能预期.md new file mode 100644 index 0000000..b6a89c1 --- /dev/null +++ b/docs/backend/第二阶段主动调度MVP功能预期.md @@ -0,0 +1,571 @@ +# 第二阶段主动调度 MVP 功能预期 + +## 1. 文档目的 + +本文档先讨论第二阶段最终想做成什么功能,不进入具体代码拆分和表结构实现。 + +第二阶段的核心目标不是让 Agent 获得直接改正式日程的能力,而是让系统具备一条可控的主动发现链路: + +```text +课程 / 任务事实变化 + -> 后台观测滚动 24 小时内的任务与日程风险 + -> 生成结构化诊断和候选方案 + -> 让 LLM 在候选方案中做选择与表达 + -> 写入预览 / 触达用户 + -> 用户回系统确认后再进入正式应用 +``` + +这里的关键是:**系统主动观测,后端给候选,LLM 做选择题,用户掌握最终确认权**。 + +--- + +## 2. 产品边界 + +### 2.1 只处理滚动 24 小时内的局部问题 + +第一版主动调度只关注从当前时刻起滚动 24 小时内的问题。 + +不处理: + +1. 未来 72 小时的全局计划。 +2. 本周整体重排。 +3. 多天任务均衡。 +4. 为了优化指标而跨天反复搬动任务。 + +这样做的原因: + +1. 滚动 24 小时内的问题最接近用户当下行动,主动触达更有必要。 +2. 范围越小,误改日程的风险越低。 +3. 第二阶段重点是验证“任务池与日程视图联通”,不是做完整排程引擎。 + +### 2.2 不给 Agent 直接写正式日程的权限 + +日程是高风险数据。第一版不计划让 Agent 绕过用户确认直接写 `schedule`。 + +允许: + +1. 读取任务与日程事实。 +2. 生成风险诊断。 +3. 生成候选方案。 +4. 写入待确认预览。 +5. 发布飞书触达事件。 + +不允许: + +1. 后台 worker 直接修改正式日程。 +2. 飞书通知直接应用日程变更。 +3. LLM 根据自然语言自由构造正式日程写入。 +4. 因为后台诊断认为存在风险,就绕过用户确认改动正式日程。 + +### 2.3 主动调度不是新增一批通用日程工具 + +现有日程工具已经有大量可复用能力,包括读取、分析、局部移动、交换、队列处理、结构化结果展示等。 + +第二阶段新增能力的重点不应是重做 `schedule` 工具,而是补上“任务池与日程视图联通”所缺的观测能力: + +1. 哪些任务已经进入四象限“重要且紧急”池,应该被拉入日程视图。 +2. 哪些任务尚未进入 schedule 视图但应该被提醒安排。 +3. 用户明确反馈未完成后,哪些后继任务会被挤压。 +4. 当前是否存在值得打扰用户的候选方案。 + +--- + +## 3. 设计理念 + +### 3.1 参考 `analyze_health`,做观测与候选生成 + +当前 `analyze_health` 的有效经验是: + +1. 后端先做结构化观测。 +2. 后端输出问题、指标、裁决和候选操作。 +3. LLM 不做开放式全窗搜索,而是在候选项里选择。 +4. 候选必须是后端验证过合法性和收益的。 +5. 如果没有值得继续处理的问题,后端明确返回 close / ask_user,而不是继续诱导 LLM 硬调。 + +第二阶段应延续这个模式。 + +也就是说,新能力更像一个主动调度观测能力,而不是一个自由排程工具。具体工程工具名后续再确认,本阶段只固定职责边界。 + +### 3.2 让 LLM 做选择题 + +LLM 的职责: + +1. 在后端候选方案中选择更符合上下文的一项。 +2. 把结构化诊断转换成用户能理解的解释。 +3. 在候选不足、信息不足、风险过高时选择 ask_user / close。 +4. 根据主动注入的上下文理解用户偏好,但不调用单独的 `user_preference.get` 工具。 + +后端的职责: + +1. 注入用户上下文、偏好和必要任务事实。 +2. 消费四象限“重要且紧急”任务池,不在主动调度内重复管理 DDL 阈值。 +3. 枚举候选方案。 +4. 校验候选方案是否可行。 +5. 明确给出候选的风险、收益和确认要求。 + +--- + +## 4. 上下文注入方式 + +第二阶段不新增 `user_preference.get` 工具。 + +偏好、近期反馈、必要上下文应由主动调度入口在运行前注入,例如: + +```text +ActiveScheduleContext + user_id + now + time_window = now ~ now+24h(滚动 24 小时) + course_context + task_context + schedule_context + quadrant_context + injected_preferences + recent_user_feedback + trigger_source +``` + +这样可以保证: + +1. LLM 不需要额外调用偏好读取工具。 +2. worker 和 API 测试触发走同一套上下文构造逻辑。 +3. 偏好采集仍归 memory / 上下文系统负责,主动调度只消费注入后的上下文。 +4. 后续飞书入口、Web 入口、后台定时入口可以共享上下文协议。 + +--- + +## 5. MVP 触发来源 + +第二阶段触发基本由课程和任务本身驱动,暂不做泛化生活习惯型触发。 + +### 5.1 重要且紧急任务进入日程视图 + +触发条件: + +1. 任务来自四象限“重要且紧急”池。 +2. 任务未完成。 +3. 当前日程视图中没有足够明确的完成安排,或任务尚未进入 schedule 视图。 +4. 主动调度不单独维护 DDL 临近阈值,阈值判断交给四象限自动轮换机制。 + +系统目标: + +1. 判断该任务是否应该被加入 schedule 预览。 +2. 判断滚动 24 小时内是否存在合适的可用窗口。 +3. 给出一个或多个“加入日程视图”的候选方案。 +4. 若没有可用窗口,则给出 ask_user 或风险提醒。 +5. 注意当前四象限轮换机制是懒加载:只有用户访问时才会轮换。第二阶段需要考虑在后台触发前刷新轮换结果,或补一个 worker 侧轮换入口,避免主动调度读到过期任务池。 + +### 5.2 用户反馈已排任务未完成 + +触发条件: + +1. 任务已经出现在 schedule 视图中。 +2. 用户明确反馈该任务没有完成,例如“刚才那个没做完”“这项要延后”。 +3. 该任务仍然影响滚动 24 小时内的 DDL、后继任务或今日计划。 + +系统目标: + +1. 默认情况下,动态任务计划时间过去后按已完成推进,不主动追问。 +2. 用户明确反馈未完成时,再把该任务拉回主动调度链路。 +3. 分析滚动 24 小时内后继任务是否被挤压。 +4. 生成局部补救候选,例如今天补做、延后低优先级后继任务,或者暂不调整。 + +### 5.3 课程表变化触发后置 + +课程变化触发暂不进入第一版。 + +原因: + +1. 当前课程基本不可移动,主动调度很难在课程层直接补救。 +2. 调停课、补课、课程调整等机制还需要先完善。 +3. 等课程调整机制稳定后,再把课程变化纳入主动调度触发源。 + +后续触发条件可以是: + +1. 滚动 24 小时内课程时间发生变化。 +2. 变化挤占了原本计划给任务的时间。 +3. 被挤占任务尚未开始,或用户明确反馈未完成,且仍有 DDL 或后继影响。 + +系统目标: + +1. 判断受影响任务是否需要重新进入候选。 +2. 仅围绕受影响任务和直接后继任务生成局部方案。 +3. 不做全局重排。 + +### 5.4 用户反馈疲劳 + +触发条件: + +1. 用户在对话或反馈中明确表达“很累”“今天撑不住”“不想继续”等状态。 +2. 滚动 24 小时内仍存在待执行任务、DDL 高压任务,或用户明确反馈未完成的任务。 + +系统目标: + +1. 判断是否有低优先级任务可以移出今日预览。 +2. 判断是否应只提醒用户确认而不是主动给移动候选。 +3. 不因疲劳反馈自动取消 DDL 高风险任务。 + +--- + +## 6. 核心场景的功能预期 + +### 6.1 重要且紧急任务还没进入日程视图 + +这是“从任务池进入日程视图”的问题。 + +用户视角: + +```text +系统发现你有一个重要且紧急任务还没有安排到接下来 24 小时的时间表里。 +系统给出 1~3 个可选安排建议。 +你确认后,它才进入正式日程。 +``` + +系统预期: + +1. 从四象限“重要且紧急”池识别应被拉入日程视图的任务。 +2. 排除已完成、已取消、信息不足的任务。 +3. 检查滚动 24 小时内可用时间。 +4. 输出候选项,而不是直接调用正式 schedule 写入。 + +候选项示例: + +```json +{ + "candidate_id": "place_task_123_slot_a", + "action": "加入日程预览", + "target": { + "task_id": 123, + "source": "task_pool" + }, + "preview_change": { + "type": "add_to_schedule_view", + "day": 0, + "slot_start": 7, + "slot_end": 8 + }, + "why": "任务已进入重要且紧急池,当前尚未进入日程视图;该时段未被课程占用。", + "risk": "需要用户确认预计耗时是否足够。", + "requires_user_confirm": true +} +``` + +### 6.2 Schedule 内任务由用户反馈未完成 + +这是“用户明确说没做完之后的后继调整”问题。 + +用户视角: + +```text +系统默认刚才安排的动态任务已经完成,不会频繁追问。 +如果你告诉系统“没做完”,它会基于接下来 24 小时给出补救候选。 +``` + +系统预期: + +1. 动态任务计划时间过去后默认按 `assumed_completed` 处理。 +2. 不因为缺少完成证据就主动进入风险确认。 +3. 用户反馈未完成后,才分析滚动 24 小时内后继任务是否被影响。 +4. 输出“今天补做 / 延后后继 / 暂不调整”类型候选。 + +候选项示例: + +```json +{ + "candidate_id": "unfinished_task_456_repair_today", + "action": "未完成补救预览", + "target": { + "task_id": 456, + "source": "schedule_task" + }, + "preview_change": { + "type": "repair_unfinished_task", + "day": 0, + "slot_start": 9, + "slot_end": 10 + }, + "why": "用户反馈该任务没有完成,且它仍影响 24 小时内的后续安排。", + "risk": "需要用户确认是否接受挤占后续低优先级任务。", + "requires_user_confirm": true +} +``` + +### 6.3 动态任务失败后的后继挤压补救 + +这是“当前动态任务炸了,但后面还有后继任务”的问题。 + +用户视角: + +```text +如果你告诉系统当前任务没做完,而后面还有安排,系统会先尝试在滚动 24 小时内局部补救。 +它会优先找不那么痛的方案;如果时间真的不够,会把选择交给你。 +``` + +系统预期: + +1. 先判断当前动态任务是否仍值得补救,而不是直接跳过。 +2. 如果滚动 24 小时内空间足够,生成“把当前动态任务往后挤进去”的预览。 +3. 这个预览允许在必要时打破部分用户偏好,例如原本偏好的学习时段、任务顺序或轻重搭配,但必须清楚说明代价。 +4. 如果空间不够,优先询问用户是否能延后结束时间。 +5. 如果用户不能延后,或延后仍然不够,再生成“强行融入下一个动态任务”的 rush 预览。 + +补救策略顺序: + +```text +策略 A:局部重排补救 + 直接复用并魔改粗排思路,但范围只限滚动 24 小时。 + 输入只传受影响的部分 task item,而不是整批任务。 + 用户偏好 weekday / slot 优先遵守;如果无法达成,再打破偏好。 + 打破偏好的优先级低于“把任务压进时间表”。 + 目标是把失败的动态任务重新塞回时间表,并尽量少影响后继任务。 + +策略 B:协商延后结束时间 + 如果当前 24 小时容量不够,询问用户能否延后结束时间或放宽边界。 + +策略 C:压缩融合到下一个动态任务 + 如果不能延后,就把失败任务强行融入下一个动态任务。 + 第一版默认两个动态任务各 50% 压缩,让用户 rush 一下。 +``` + +策略 C 的产品判断: + +1. 它不是理想方案,但通常比直接跳过失败任务更合理。 +2. 两节课各自减半的影响,往往小于完全放弃其中一项。 +3. 第一版只把它作为兜底候选,不默认自动执行。 +4. 第一版默认 50% / 50%,暂不按优先级、DDL 或预计耗时动态分配。 +5. 后续如果出现更好的补救策略,可以替换或降低它的优先级。 + +候选项示例: + +```json +{ + "candidate_id": "rush_merge_task_456_into_789", + "action": "压缩融合预览", + "target": { + "unfinished_task_id": 456, + "next_task_id": 789 + }, + "preview_change": { + "type": "rush_merge", + "unfinished_task_ratio": "50%", + "next_task_ratio": "50%" + }, + "why": "用户反馈当前动态任务未完成,且 24 小时内没有足够独立空档;直接跳过会损失更大。", + "risk": "两个任务都会被压缩,需要用户接受 rush 模式。", + "requires_user_confirm": true +} +``` + +--- + +## 7. 产品能力边界 + +本阶段可以讨论“系统需要哪些能力”,但不在这里冻结具体工具名、入参 schema、handler 注册方式或内部实现路径。 + +也就是说,本章只定义产品级能力边界;工程级工具设计在产品预期敲定后另开文档。 + +### 7.1 主动观测能力 + +第二阶段需要一个类似 `analyze_health` 的主动观测能力。 + +它负责: + +1. 汇总滚动 24 小时内任务 / 日程联通风险。 +2. 区分 task DDL 临近和用户反馈未完成后的补救两类问题。 +3. 输出结构化指标、问题、裁决和候选。 +4. 告诉 LLM 是否应该继续、提醒用户、生成预览或收口。 + +它不负责: + +1. 直接写正式 schedule。 +2. 替代现有 `place` / `move` / `swap` 等日程工具。 +3. 自行读取偏好工具。 +4. 做 24 小时外全局重排。 +5. 在飞书内完成复杂确认。 + +### 7.2 候选生成能力 + +候选生成能力的产品职责是“出选择题”,不是让 LLM 自由编写正式日程参数。 + +第一版候选只允许覆盖这些产品动作: + +```text +加入日程预览 + 将任务池中的 DDL 临近任务加入预览,不直接写正式日程。 + +未完成补救预览 + 对用户明确反馈未完成的日程任务生成补救预览。 + +后继挤压重排预览 + 当前动态任务失败且影响后继时,在滚动 24 小时内做局部重排候选。 + +延后结束询问 + 当前容量不足时,询问用户是否允许延后结束时间或放宽边界。 + +压缩融合预览 + 无法延后时,把失败任务融入下一个动态任务,形成 rush 候选。 + +询问用户 + 信息不足、风险过高、用户反馈不够明确时询问用户。 + +仅提醒 + 只提醒用户查看,不建议调整。 + +收口 + 当前无值得打扰用户的问题。 +``` + +第一版明确不支持这些产品动作: + +```text +直接写正式日程 +全局重排 +自动标记完成 +飞书内直接应用日程 +后台自动进入 rush 模式 +``` + +### 7.3 预览写入能力 + +预览写入能力只负责保存待确认方案,让用户能看到“改前 / 改后 / 为什么”。 + +它不代表正式日程已经变化,也不承担复杂排程判断。 + +### 7.4 用户确认能力 + +用户确认能力负责把用户选择的候选项转成正式应用请求。 + +第一版确认粒度建议按候选项确认,而不是整版黑盒确认。 + +正式应用时优先复用现有 service 逻辑,不在主动调度模块里绕过既有写入链路单独改状态或改日程。 + +### 7.5 通知触达能力 + +通知触达能力只负责把“系统发现问题并生成了建议”这件事告诉用户。 + +它不负责判断怎么调度,也不负责在飞书内完成确认。 + +### 7.6 前后对比与回滚能力 + +当前版本还没有完整的前后对比机制。第二阶段如果要让用户放心确认主动调度建议,需要补齐“改前 / 改后 / 可回滚”的预览能力。 + +建议方向: + +1. 先为当前版本补上前后对比和回滚机制。 +2. 主动调度生成的候选复用这套机制,不另起一套预览协议。 +3. 用户确认前只看到预览,不修改正式日程。 +4. 用户确认后进入现有 service 写入链路;失败时可根据预览快照回滚或提示未应用。 + +--- + +## 8. 用户确认与预览预期 + +第二阶段的用户确认粒度建议按候选项确认,而不是整版黑盒确认。 + +预览应至少展示: + +1. 为什么触发。 +2. 当前风险是什么。 +3. 建议改变哪一个任务。 +4. 改到哪里或准备问用户什么。 +5. 不调整会有什么后果。 +6. 这个建议是否会影响后继任务。 +7. 如果打破了用户偏好,需要明确说明打破了什么、为什么值得这样做。 +8. 如果进入 rush 模式,需要明确说明两个任务分别被压缩到什么程度。 + +确认后才进入正式应用链路。 + +如果候选是 `ask_user`,用户回答后可以重新触发一次同一链路,但必须带上新的上下文和幂等键,避免重复打扰。 + +--- + +## 9. 飞书触达预期 + +飞书第一版只负责提醒,不承载复杂调度判断。 + +触达内容应该是: + +```text +我发现你接下来 24 小时内有一个任务可能需要安排 / 你反馈的未完成任务需要补救。 +我已经生成了一个待确认建议,请回到系统查看。 +``` + +飞书不做: + +1. 直接确认日程调整。 +2. 直接标记任务完成。 +3. 多轮 Agent Chat。 +4. 复杂卡片交互。 + +飞书事件仍建议走: + +```text +notification.feishu.requested +``` + +后续如果要做飞书聊天入口,再单独走 `agent.channel.*`,不要污染通知投递模型。 + +--- + +## 10. 完成状态语义 + +第二阶段采用“默认完成,用户反馈纠偏”的口径。 + +建议拆成两个产品概念: + +```text +assumed_completed + 动态任务计划时间过去后,系统默认按已完成推进体验。 + +用户反馈未完成 + 用户明确告诉系统任务没有完成,系统再进入补救链路。 +``` + +规则: + +1. 动态任务计划时间过去后,默认按 `assumed_completed` 处理。 +2. 系统不因为缺少完成证据而主动追问用户。 +3. 用户明确反馈没完成后,作为触发上下文进入补救分析;它不一定需要落成新的数据库状态。 +4. 补救分析仍只生成预览或候选,不能直接改正式日程。 + +--- + +## 11. MVP 验收标准 + +第一版功能算完成,需要满足: + +1. 只扫描滚动 24 小时内的问题。 +2. 能识别四象限“重要且紧急”池中尚未进入日程视图的任务。 +3. 能在用户反馈未完成后,识别受影响的 schedule 任务与后继任务。 +4. 动态任务计划时间过去后默认按已完成推进,不主动追问。 +5. 当前动态任务失败且影响后继时,能按“局部重排 -> 延后结束 -> 压缩融合”顺序生成候选。 +6. 能输出结构化 metrics / issues / decision / candidates。 +7. 候选项必须是选择题,不让 LLM 自由生成正式写库参数。 +8. 不直接写正式 schedule,只写预览或触达用户。 +9. 能发布 `notification.feishu.requested` 提醒用户回系统确认。 +10. 用户确认后才允许进入正式应用链路。 + +--- + +## 12. 已确定结论 + +1. 时间范围采用滚动 24 小时,不按自然日切分。 +2. 主动调度不单独管理 DDL 临近阈值,只消费四象限“重要且紧急”任务池。 +3. 当前四象限自动轮换是懒加载机制,这是第二阶段需要处理的工程风险。 +4. 用户反馈未完成不是必须新增数据库状态;第一版可以先作为触发上下文。 +5. 正式应用优先复用现有 service 逻辑,不在主动调度模块里另写一套状态更新权限。 +6. 预览能力建议先补齐当前版本的前后对比和回滚机制,再由主动调度复用。 +7. 后继挤压重排优先复用并魔改粗排:传入滚动 24 小时时间窗和部分 task item。 +8. 局部重排中,用户偏好 weekday / slot 优先遵守;无法达成时允许打破,但要说明代价,并优先把任务压进去。 +9. 压缩融合第一版默认 50% / 50%。 +10. 候选项第一版限制为 1~3 个。 +11. 课程变化触发后置,等调停课 / 补课 / 课程调整机制完善后再接入。 + +--- + +## 13. 待讨论问题 + +1. 主动观测能力是否最终落成独立工具,以及具体命名是什么。 +2. 四象限懒加载轮换要如何补齐:后台 worker 定时刷新、主动调度前同步刷新,还是抽公共轮换服务。 +3. 前后对比回滚机制复用现有哪条链路,是否需要新增主动调度预览类型。 +4. 魔改粗排时,哪些用户偏好可以被打破,哪些偏好必须保持为硬约束。