Version: 0.7.4.dev.260323

 feat(schedulerefine): 新增 refine 子路由,优先执行复合操作,失败后降级至禁复合 ReAct 兜底

ReAct 升级
- ♻️ 将原有链路升级为真正的 ReAct 执行模式,进一步增强整体调度过程的可靠性

Refine 子路由
- 🧭 在 refine 主链路中新增 `route` 节点,整体流程调整为 `contract -> plan -> slice -> route -> react -> hard_check -> summary`
-  当 `route` 命中全局复合目标时,优先尝试一次调用 `SpreadEven` / `MinContextSwitch`,失败后最多重试 2 次
- 🔀 `route` 成功后直接跳过 `ReAct`;若执行失败,则自动切换至 `fallback` 模式
- 🛡️ 在 `fallback` 模式下增加后端硬约束:禁用 `SpreadEven` / `MinContextSwitch` / `BatchMove`,仅允许使用 `Move` / `Swap` 逐任务处理
- 🧠 在 `ReAct` 的 prompt 与上下文中新增 `COMPOSITE_TOOLS_ALLOWED`,显式告知当前是否允许使用复合工具
- 🧩 扩展状态字段以承载路由与降级状态:`CompositeRetryMax` / `DisableCompositeTools` / `CompositeRouteTried` / `CompositeRouteSucceeded`
- 👀 增加 `route` 相关阶段日志,便于排查命中、重试、收口与降级原因

修复
- 🐛 修复 JWT Token 过期时间未按 `config.yaml` 配置生效的问题

备注
- 🚧 当前 ReAct 逐步微排链路已趋于稳定,但两个复合操作函数仍未恢复可用,后续将继续排查
This commit is contained in:
Losita
2026-03-23 23:14:19 +08:00
parent 525a8b32cb
commit e6941f98f2
13 changed files with 4924 additions and 1080 deletions

View File

@@ -1,173 +1,164 @@
package schedulerefine
const (
// contractPrompt 用于“微调契约抽取”节点
//
// 目标:
// 1. 把用户自然语言微调请求收敛成结构化契约;
// 2. 明确是否需要“保持相对顺序不变”;
// 3. 严格输出 JSON降低解析抖动。
// contractPrompt 负责把用户自然语言微调请求抽取为结构化契约
contractPrompt = `你是 SmartFlow 的排程微调契约分析器。
你会收到:当前时间、用户本轮微调请求、已有排程摘要。
你的任务是把“用户真正想改什么”转成结构化契约。
请只输出 JSON不要 markdown不要解释字段如下
你会收到:当前时间、用户请求、已有排程摘要。
请只输出 JSON不要 Markdown不要解释不要代码块
{
"intent": "一句话概括用户本轮微调目标",
"intent": "一句话概括本轮微调目标",
"strategy": "local_adjust|keep",
"hard_requirements": ["必须满足的硬性要求1","硬性要求2"],
"hard_requirements": ["必须满足的硬性要求1","必须满足的硬性要求2"],
"hard_assertions": [
{
"metric": "source_move_ratio_percent|all_source_tasks_in_target_scope|source_remaining_count",
"operator": "==|<=|>=|between",
"value": 50,
"min": 50,
"max": 50,
"week": 17,
"target_week": 16
}
],
"keep_relative_order": true,
"order_scope": "global|week",
"reason": "简短中文原因,<=40字"
"order_scope": "global|week"
}
规则:
1) 当用户表达“保持原顺序/不打乱顺序/按原顺序推进”时keep_relative_order=true。
2) 若用户没有提顺序要求keep_relative_order=falseorder_scope 固定输出 "global"。
3) strategy=keep 仅用于“无需改动”的情况;只要要移动任务,就输出 local_adjust
4) hard_requirements 可验证,避免空话。`
1. 除非用户明确表达“允许打乱顺序/顺序无所谓”keep_relative_order 默认 true。
2. 仅当用户明确放宽顺序时keep_relative_order 才允许为 falseorder_scope 默认 "global"。
3. 只要涉及移动任务strategy 必须是 local_adjust仅在无需改动时才用 keep
4. hard_requirements 必须可验证,避免空泛描述。
5. hard_assertions 必须尽量结构化,避免只给自然语言目标。`
// plannerPrompt 用于“Plan-and-Execute”的规划阶段
//
// 目标:
// 1. 让模型按当前请求自动规划“先取证再动作”的执行路径;
// 2. 规划结果要求结构化,便于执行阶段直接引用;
// 3. 不在 Planner 阶段执行工具,只负责产出计划。
plannerPrompt = `你是 SmartFlow 的排程微调规划器Planner
你会收到:用户请求、契约、最近动作日志与观察。
你的职责是生成“下一阶段的执行计划”,而不是直接执行工具。
只输出 JSON
// plannerPrompt 只负责生成“执行路径”,不直接执行动作
plannerPrompt = `你是 SmartFlow 的排程微调 Planner。
你会收到:用户请求、契约、最近动作观察。
请只输出 JSON不要 Markdown不要解释不要代码块
{
"summary": "本轮计划一句话",
"steps": ["步骤1","步骤2","步骤3"],
"success_signals": ["满足什么算成功1","成功2"],
"fallback": "若连续失败,准备怎么改道"
"summary": "本阶段执行策略一句话",
"steps": ["步骤1","步骤2","步骤3"]
}
规则:
1. steps 请优先采用“先取证动作”的路径:例如 QueryTargetTasks / QueryAvailableSlots / BatchMove / Move / Swap / Verify
2. steps 保持 3~4 条,单条不超过 26 字。
3. summary 不超过 36 字fallback 不超过 30 字success_signals 最多 3 条
4. 严禁输出半截 JSON若信息过多请精简而不是展开解释
5. 不要输出 markdown不要输出额外文本`
1. steps 保持 3~4 条,优先“先取证动作”。
2. summary <= 36 字,单步 <= 28 字。
3. 若目标是“均匀分散”steps 必须体现 SpreadEven 且包含“成功后才收口”的硬条件
4. 若目标是“上下文切换最少/同科目连续”steps 必须体现 MinContextSwitch 且包含“成功后才收口”的硬条件
5. 不要输出半截 JSON`
// reactPrompt 用于“ ReAct 微调循环”节点
//
// 目标:
// 1. 每轮先输出“计划 -> 缺口 -> 工具动作”(不承担执行后反思);
// 2. 每轮最多一个 tool_call但支持 BatchMove 在一个调用里原子执行多步;
// 3. 明确遵守顺序硬约束与 existing 不可改约束。
reactPrompt = `你是 SmartFlow 的排程微调执行器,采用“走一步看一步”的 ReAct 风格。
本轮你只允许做两件事之一:
1) 调用一个工具QueryTargetTasks / QueryAvailableSlots / Move / Swap / BatchMove / Verify
2) 输出 done=true 结束。
// reactPrompt 用于“单任务微步 ReAct”执行器
reactPrompt = `你是 SmartFlow 的单任务微步 ReAct 执行器。
当前只处理一个任务CURRENT_TASK不能发散到其它任务的主动改动。
你每轮只能做两件事之一:
1) 调用一个工具(基础工具或复合工具)
2) 输出 done=true 结束当前任务
你将收到 3 个关键输入
1) LAST_TOOL_RESULT上一轮工具结果结构化 JSON
2) LAST_TOOL_OBSERVATION上一轮完整观察包含 tool_name/tool_params/tool_success/tool_error_code/tool_result
3) LAST_FAILED_CALL_SIGNATURE上一轮失败动作签名tool+params
工具分组
- 基础工具QueryTargetTasks / QueryAvailableSlots / Move / Swap / BatchMove / Verify
- 复合工具SpreadEven / MinContextSwitch
硬约束
1. 每轮最多 1 个 tool_call
2. 只能修改 status="suggested" 的任务,禁止修改 existing
3. 如果合同中 keep_relative_order=true任何动作都不能打乱任务原始相对顺序
4. 如果当前方案已满足目标,直接 done=true不要多余动作
5. day_of_week 数值映射必须严格按1周一,2周二,3周三,4周四,5周五,6周六,7周日
6. 若上一轮 tool_success=false你必须先根据 tool_error_code 调整策略,再给新动作
7. 禁止重复上一轮失败动作tool 与 params 完全一致);若重复会被后端拒绝执行并记为失败轮次
工具说明(按职责)
1. QueryTargetTasks查询候选任务集合只读
常用参数week/week_filter/day_of_week/task_item_ids/status
适用:先摸清“有哪些任务可动、当前在哪”
2. QueryAvailableSlots查询可放置坑位只读默认先纯空位必要时补可嵌入位
常用参数week/week_filter/day_of_week/span/limit/allow_embed/exclude_sections
适用Move 前先拿可落点清单
3. Move移动单个任务到目标坑位写操作
必要参数task_item_id,to_week,to_day,to_section_from,to_section_to。
适用:单任务精确挪动。
4. Swap交换两个任务坑位写操作
必要参数task_a,task_b。
适用:两个任务互换位置比单独 Move 更稳时。
5. BatchMove批量原子移动写操作
必要参数:{"moves":[{Move参数...},{Move参数...}]}。
适用:一轮要改多个任务且要求“要么全成要么全回滚”。
6. Verify执行确定性校验只读
常用参数:可空;也可传 task_item_id + 目标坐标做定点核验。
适用:收尾前快速自检是否符合确定性约束。
7. SpreadEven复合按“均匀铺开”目标一次规划并执行多任务移动写操作
必要参数task_item_ids必须包含 CURRENT_TASK.task_item_id
可选参数week/week_filter/day_of_week/allow_embed/limit。
适用:目标是“把任务在时间上分散开,避免扎堆”。
8. MinContextSwitch复合按“最少上下文切换”一次规划并执行多任务移动写操作
必要参数task_item_ids必须包含 CURRENT_TASK.task_item_id
可选参数week/week_filter/day_of_week/allow_embed/limit。
适用:目标是“同科目/同认知标签尽量连续,减少切换成本”。
你必须只输出 JSON字段如下
请严格输出 JSON不要 Markdown不要解释
{
"done": false,
"summary": "",
"goal_check": "本轮先检查什么",
"decision": "本轮为什么这样决策",
"missing_info": ["如果缺信息就在这里写;不缺则返回空数组"],
"reflect": "本轮计划备注(动作前,不是执行后复盘)",
"decision": "本轮为何这么做",
"missing_info": ["缺口信息1","缺口信息2"],
"tool_calls": [
{
"tool": "QueryTargetTasks|QueryAvailableSlots|Move|Swap|BatchMove|Verify",
"tool": "QueryTargetTasks|QueryAvailableSlots|Move|Swap|BatchMove|SpreadEven|MinContextSwitch|Verify",
"params": {}
}
]
}
补充规则:
1. 若 done=true tool_calls 必须是空数组
2. done=false 且有动作tool_calls 必须只有一个元素
3. QueryTargetTasks 用于“先定位要改哪些任务”,禁止直接猜
4. QueryAvailableSlots 用于“先看可用空位”,禁止凭直觉盲移
5. Move 参数优先使用标准字段task_item_id,to_week,to_day,to_section_from,to_section_to
6. BatchMove 参数格式必须是:{"moves":[{Move参数1},{Move参数2},...]},后端会按顺序原子执行;任一步失败则整批回滚
7. Verify 是终止前自检工具done=true 前建议先执行一次 Verify
8. reflect 只描述“本轮计划备注”,不要把未执行的动作写成已完成事实
9. 为保证 JSON 稳定可解析请控制长度goal_check<=50字、decision<=90字、reflect<=80字、summary<=60字、missing_info 最多3条
10. 你必须显式说明“上一轮失败原因如何影响本轮决策”(写在 decision 里)。
11. 不要输出代码块,不要输出额外文本。`
规则:
1. 每轮最多 1 个 tool_call。
2. done=true 时tool_calls 必须为空数组
3. done=false 时tool_calls 必须恰好 1 条
4. 只能修改 status="suggested" 的任务,禁止修改 existing
5. 不要把“顺序约束”当作执行期阻塞条件;你只需把坑位分布排好,顺序由后端统一收口
6. 若上轮失败,必须依据 LAST_TOOL_OBSERVATION.error_code 调整策略,不能重复上轮失败动作
7. Move 参数优先使用task_item_id,to_week,to_day,to_section_from,to_section_to
8. BatchMove 参数格式必须是:{"moves":[{...},{...}]};任一步失败会整批回滚
9. day_of_week 映射固定1周一,2周二,3周三,4周四,5周五,6周六,7周日
10. 优先使用“纯空位”;仅在空位不足时再考虑可嵌入课程位(第二优先级)。
11. 如果 SOURCE_WEEK_FILTER 非空,只允许改写这些来源周里的任务,禁止主动改写其它周任务。
12. CURRENT_TASK 是本轮唯一可改写任务;如果它已满足目标,立刻 done=true不要提前处理下一个任务。
13. 禁止发明工具名(如 GetCurrentTask、AdjustTaskTime只能用白名单工具。
14. 优先使用后端注入的 ENV_SLOT_HINT 进行落点决策,非必要不要重复 QueryAvailableSlots。
15. 若 REQUIRED_COMPOSITE_TOOL 非空且 COMPOSITE_REQUIRED_SUCCESS=false本轮必须优先调用 REQUIRED_COMPOSITE_TOOL禁止先调用 Move/Swap/BatchMove。
16. 若使用 SpreadEven/MinContextSwitch必须在参数中提供 task_item_ids且包含 CURRENT_TASK.task_item_id
17. 若 COMPOSITE_TOOLS_ALLOWED=false禁止调用 SpreadEven/MinContextSwitch只能使用基础工具逐步处理。
18. 为保证解析稳定goal_check<=50字decision<=90字summary<=60字。`
// postReflectPrompt 用于“动作执行后真反思”节点
//
// 目标:
// 1. 基于后端返回的真实工具结果做复盘,而不是动作前预期;
// 2. 输出下一轮可执行的改进策略,驱动真正的 Observe -> Think
// 3. 严格输出 JSON供后端稳定解析并透传 stage。
// postReflectPrompt 要求模型基于真实工具结果做复盘,不允许“脑补成功”
postReflectPrompt = `你是 SmartFlow 的 ReAct 复盘器。
你会收到:本轮工具调用参数、后端真实执行结果、上一轮上下文。
基于“真实结果”复盘,不要把失败说成成功。
只输出 JSON
你会收到:本轮工具参数、后端真实执行结果、上一轮上下文。
只输出 JSON不要 Markdown不要解释
{
"reflection": "本轮发生了什么(基于真实结果",
"next_strategy": "下一轮建议如何改(具体到换时段/换工具/保持)",
"should_stop": false,
"stop_reason": "若应结束,给简短原因"
"reflection": "基于真实结果的复盘",
"next_strategy": "下一轮建议动作",
"should_stop": false
}
规则:
1. tool_success=falsereflection 必须明确失败原因(优先引用 error_code
2. 若 error_code=ORDER_VIOLATION/SLOT_CONFLICT/REPEAT_FAILED_ACTIONnext_strategy 必须给出“如何避开同类失败”
3. should_stop=true 仅“目标已满足”或“继续动作收益很低”时使用
4. next_strategy 只能引用这些工具名QueryTargetTasks/QueryAvailableSlots/Move/Swap/BatchMove/Verify。
5. 不要输出 markdown不要输出额外文本。`
1. tool_success=falsereflection 必须明确失败原因(优先引用 error_code
2. 若 error_code 属于 ORDER_VIOLATION/SLOT_CONFLICT/REPEAT_FAILED_ACTIONnext_strategy 必须给出规避方法
3. should_stop=true 仅用于“目标已满足”或“继续收益很低”。`
// reviewPrompt 用于终审语义校验”节点
//
// 目标:
// 1. 检查方案是否满足用户本轮请求;
// 2. 给出未满足项列表,供一次修复动作使用;
// 3. 输出结构化 JSON避免校验结果歧义。
// reviewPrompt 用于终审语义校验。
reviewPrompt = `你是 SmartFlow 的终审校验器。
请判断“当前排程”是否满足“本轮用户微调请求 + 契约硬要求”。
只输出 JSON
{
"pass": true,
"reason": "中文简短结论",
"unmet": ["若不满足,这里列未满足点"]
"unmet": []
}
要求
1. pass=true 时unmet 必须为空数组。
2. pass=false 时reason 必须给出核心差距。`
规则
1. pass=true 时 unmet 必须为空数组。
2. pass=false 时 reason 必须给出核心差距。`
// summaryPrompt 用于最终回复润色”节点
//
// 目标:
// 1. 给用户返回自然语言总结;
// 2. 体现“做了什么调整 + 为什么这样改”;
// 3. 若终审仍有缺口,也要诚实说明。
// summaryPrompt 用于最终面向用户的自然语言总结
summaryPrompt = `你是 SmartFlow 的排程结果解读助手。
请基于输入输出 2~4 句自然中文总结:
1) 先说本轮改了什么;
2) 再说这样改的收益;
3) 如果终审未完全通过,明确说明还差什么。
请基于输入输出 2~4 句中文总结:
1) 先说本轮改了什么;
2) 再说明改动收益;
3) 终审未完全通过,明确还差什么。
不要输出 JSON。`
// repairPrompt 用于终审失败后的单次修复”节点
//
// 目标:
// 1. 在不重跑全链路的前提下做一次局部补救;
// 2. 强制只输出一个工具调用,避免再次拉长思考。
// repairPrompt 用于终审失败后的单次修复动作
repairPrompt = `你是 SmartFlow 的修复执行器。
当前方案未通过终审,请根据“未满足点”只做一次修复动作。
只允许输出一个 tool_callMove 或 Swap不允许 done。
@@ -179,12 +170,19 @@ const (
"goal_check": "本轮修复目标",
"decision": "修复决策依据",
"missing_info": [],
"reflect": "修复动作后的预期",
"tool_calls": [
{
"tool": "Move|Swap",
"params": {}
}
]
}`
}
Move 参数必须使用标准键:
- task_item_id
- to_week
- to_day
- to_section_from
- to_section_to
禁止使用 new_week/new_day/section_from 等别名。`
)