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永远只适合做选择题、判断题,不适合做开放创新题。
This commit is contained in:
674
docs/backend/ARCHITECTURE.md
Normal file
674
docs/backend/ARCHITECTURE.md
Normal file
@@ -0,0 +1,674 @@
|
||||
# NewAgent 架构全景
|
||||
|
||||
> 本文档帮助读者建立对 newAgent 的完整心智模型,从宏观到微观逐层展开。
|
||||
|
||||
---
|
||||
|
||||
## 一、一句话概括
|
||||
|
||||
newAgent 是一个 **状态机驱动的有向图**:用户消息进入 Chat 节点,经过意图分类、计划生成、用户确认、工具执行、最终交付,每一步由 Phase 和 PendingInteraction 驱动路由。
|
||||
|
||||
---
|
||||
|
||||
## 二、宏观架构
|
||||
|
||||
### 2.1 目录结构
|
||||
|
||||
```
|
||||
newAgent/
|
||||
├── graph/ 图骨架:节点注册、边连线、分支路由
|
||||
├── model/ 数据模型:状态、合约、接口定义
|
||||
├── node/ 节点实现:每个节点的业务逻辑
|
||||
├── prompt/ 提示词:每个阶段的 system prompt 和用户 prompt 构造
|
||||
├── llm/ LLM 客户端:文本生成、JSON 解析、流式适配
|
||||
├── stream/ SSE 输出:伪流式推送、OpenAI 兼容格式
|
||||
├── tools/ 工具层:10 个排程工具 + 注册表
|
||||
├── shared/ 公共工具:重试、时区
|
||||
├── router/ 路由(当前为空,路由逻辑在 graph/ 中)
|
||||
├── ROADMAP.md 改造计划
|
||||
└── ARCHITECTURE.md 本文档
|
||||
```
|
||||
|
||||
### 2.2 图结构
|
||||
|
||||
```
|
||||
START
|
||||
│
|
||||
v
|
||||
Chat ──────┬── 意图=chat ────→ END(直接回复)
|
||||
│ │
|
||||
│ └── 意图=task ──→ Plan ──┬── continue ──→ Plan(继续规划)
|
||||
│ │ │
|
||||
│ │ ├── ask_user ──→ Interrupt ──→ END
|
||||
│ │ │
|
||||
│ │ └── plan_done ──→ Confirm
|
||||
│ │ │
|
||||
│ │ ├── 有计划 → 确认卡片
|
||||
│ │ │ │
|
||||
│ │ │ v
|
||||
│ │ │ Interrupt ──→ END
|
||||
│ │ │
|
||||
│ │ └── 有 PendingConfirmTool → 确认卡片
|
||||
│ │
|
||||
│ v
|
||||
│ Interrupt ──→ END
|
||||
│
|
||||
│ [用户确认后重新进入图]
|
||||
│
|
||||
v
|
||||
Chat(resume) ──┬── accept → 恢复 PendingConfirmTool → Execute
|
||||
│
|
||||
└── reject → 回到 planning 或 executing
|
||||
|
||||
Execute ──┬── continue(读工具) ──→ Execute(继续 ReAct)
|
||||
├── continue(无工具) ──→ Execute
|
||||
├── confirm(写工具) ──→ Confirm ──→ Interrupt ──→ END
|
||||
├── ask_user ──→ Interrupt ──→ END
|
||||
├── next_plan ──┬── 有剩余步骤 → Execute
|
||||
│ └── 无剩余步骤 → Deliver
|
||||
├── done ──→ Deliver
|
||||
└── 轮次耗尽 ──→ Deliver(强制)
|
||||
|
||||
Deliver ──→ END(最终总结,清理持久化快照)
|
||||
```
|
||||
|
||||
### 2.3 一次完整排课的请求序列
|
||||
|
||||
```
|
||||
请求1: 用户发 "帮我安排下周的复习"
|
||||
→ Chat(intent=task) → Plan(plan_done, 2步计划) → Confirm → Interrupt → END
|
||||
前端展示确认卡片
|
||||
|
||||
请求2: 用户点 "确认"
|
||||
→ Chat(resume, accept) → 确认通过 → Execute(读工具 get_overview)
|
||||
→ Execute(读工具 find_free)
|
||||
→ Execute(写工具 place → confirm) → Confirm → Interrupt → END
|
||||
前端展示写操作确认卡片
|
||||
|
||||
请求3: 用户点 "确认"
|
||||
→ Chat(resume, accept) → Execute(执行 pending tool place) → 持久化
|
||||
→ Execute(next_plan → 下一步)
|
||||
→ Execute(done) → Deliver → END
|
||||
前端展示最终总结
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 三、状态模型
|
||||
|
||||
理解 newAgent 的关键在于理解 **什么东西在什么时机变化**。
|
||||
|
||||
### 3.1 三个核心状态对象
|
||||
|
||||
```
|
||||
┌─────────────────────┐ 持久化到 Redis
|
||||
│ AgentRuntimeState │ ← StateStore.Save/Load
|
||||
│ │
|
||||
│ ┌───────────────┐ │
|
||||
│ │ CommonState │ │ ← 每个节点都可能修改
|
||||
│ │ - Phase │ │
|
||||
│ │ - PlanSteps │ │
|
||||
│ │ - CurrentStep │ │
|
||||
│ │ - RoundUsed │ │
|
||||
│ └───────────────┘ │
|
||||
│ │
|
||||
│ PendingInteraction │ ← 确认/追问 的交互快照
|
||||
│ PendingConfirmTool │ ← Execute→Confirm 的临时邮箱
|
||||
└─────────────────────┘
|
||||
|
||||
┌─────────────────────────┐ 不持久化,每次请求重建
|
||||
│ ConversationContext │
|
||||
│ - SystemPrompt │ ← 各节点 prompt 函数构造
|
||||
│ - History []*Message │ ← 对话历史(assistant+tool 配对)
|
||||
│ - PinnedBlocks │ ← 置顶上下文(计划、工具摘要)
|
||||
│ - ToolSchemas │ ← 工具 schema 注入
|
||||
└─────────────────────────┘
|
||||
|
||||
┌─────────────────────────┐ 懒加载,首次 Execute 时读取
|
||||
│ ScheduleState │
|
||||
│ - Window (天数+映射) │
|
||||
│ - Tasks []ScheduleTask │ ← 工具操作的数据源
|
||||
└─────────────────────────┘
|
||||
```
|
||||
|
||||
### 3.2 Phase 状态转换
|
||||
|
||||
```
|
||||
PhasePlanning Plan 节点 plan_done + 用户确认后
|
||||
│
|
||||
v
|
||||
PhaseExecuting Execute 节点执行中
|
||||
│
|
||||
├──→ PhaseWaitingConfirm Execute 输出 action=confirm
|
||||
│ │
|
||||
│ v 用户确认
|
||||
│ PhaseExecuting 恢复继续执行
|
||||
│
|
||||
├──→ PhaseDone Execute 输出 done 或所有步骤完成
|
||||
│
|
||||
└──→ PhaseInterrupted 被中断(追问/确认等待用户输入)
|
||||
```
|
||||
|
||||
### 3.3 PendingInteraction 生命周期
|
||||
|
||||
```
|
||||
场景 A: 计划确认
|
||||
Plan → plan_done → Confirm 节点 → OpenConfirmInteraction(type="confirm")
|
||||
→ Interrupt 展示 → END
|
||||
→ 用户确认 → Chat(resume) → ResumeFromPending() → Phase=executing
|
||||
|
||||
场景 B: 写操作确认
|
||||
Execute → action=confirm → 设置 PendingConfirmTool → Confirm 节点
|
||||
→ OpenConfirmInteraction(type="confirm", PendingTool=快照)
|
||||
→ Interrupt 展示 → END
|
||||
→ 用户确认 → Chat(resume) → PendingConfirmTool 从快照恢复 → Execute 执行工具
|
||||
|
||||
场景 C: 追问
|
||||
Execute → action=ask_user → OpenAskUserInteraction(question)
|
||||
→ Interrupt 展示 → END
|
||||
→ 用户回复 → Chat(resume) → Phase 回到 executing
|
||||
```
|
||||
|
||||
### 3.4 PendingConfirmTool 临时邮箱
|
||||
|
||||
这个字段 **不持久化**,只在单次图运行中存在:
|
||||
|
||||
```
|
||||
Execute(action=confirm)
|
||||
→ PendingConfirmTool = {ToolName, ArgsJSON, Summary}
|
||||
→ Phase = waiting_confirm
|
||||
→ Confirm 节点读取 → 转入 PendingInteraction.PendingTool
|
||||
→ PendingConfirmTool 被清空
|
||||
|
||||
用户确认后重新进入图:
|
||||
→ Chat(resume) → 从 PendingInteraction.PendingTool 恢复到 PendingConfirmTool
|
||||
→ Execute 发现 PendingConfirmTool 非空 → 直接执行工具 → 清空
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 四、各节点详解
|
||||
|
||||
### 4.1 Chat 节点 (`node/chat.go`)
|
||||
|
||||
**职责**:入口分流 + 中断恢复
|
||||
|
||||
**两条路径**:
|
||||
1. **首次进入**:调 LLM 做意图分类("chat" / "task"),chat 直接回复,task 转到 Plan
|
||||
2. **中断恢复**:读取 PendingInteraction,根据类型(ask_user / confirm)走不同恢复路径
|
||||
|
||||
**关键逻辑**:
|
||||
```
|
||||
if HasPendingInteraction():
|
||||
handleChatResume() // 不调 LLM
|
||||
else:
|
||||
chatIntentDecision() // 调 LLM 做意图分类
|
||||
```
|
||||
|
||||
**confirm resume 的 accept/reject 处理**:
|
||||
- accept:从 PendingInteraction.PendingTool 恢复 PendingConfirmTool,Phase=executing
|
||||
- reject(有 PendingTool):不恢复 PendingConfirmTool,Phase=executing(LLM 换方案)
|
||||
- reject(无 PendingTool):调用 RejectPlan(),Phase=planning(回到规划)
|
||||
|
||||
### 4.2 Plan 节点 (`node/plan.go`)
|
||||
|
||||
**职责**:LLM 生成结构化计划
|
||||
|
||||
**两阶段 LLM 调用**:
|
||||
1. **Phase 1 快速评估**:temperature=0.2, max_tokens=1600, thinking=关闭
|
||||
- 输出 PlanDecision,判断 Complexity(simple/moderate/complex)
|
||||
2. **Phase 2 深度规划**(仅 complex 任务触发):thinking=开启, max_tokens=3200
|
||||
- 生成更详细的 PlanStep 列表(含 DoneWhen 完成判定条件)
|
||||
|
||||
**三种 action**:
|
||||
- `continue`:继续规划(多轮对话中补充信息)
|
||||
- `ask_user`:追问用户
|
||||
- `plan_done`:规划完成,输出 PlanSteps
|
||||
|
||||
**计划写入 PinnedBlocks**:用 `UpsertPinnedBlock` 把计划文本注入 ConversationContext,后续 Execute 阶段自动带入。
|
||||
|
||||
### 4.3 Confirm 节点 (`node/confirm.go`)
|
||||
|
||||
**职责**:创建确认卡片,不调 LLM
|
||||
|
||||
**两种确认**:
|
||||
1. **计划确认**(Phase=waiting_confirm, PendingConfirmTool 为空):格式化计划摘要,创建 PendingInteraction
|
||||
2. **工具确认**(PendingConfirmTool 非空):格式化工具操作摘要,把 PendingTool 快照转入 PendingInteraction
|
||||
|
||||
**关键**:Confirm 节点执行后,PendingConfirmTool 被清空(数据已转移到 PendingInteraction.PendingTool)。
|
||||
|
||||
### 4.4 Execute 节点 (`node/execute.go`)
|
||||
|
||||
**职责**:LLM 主导的 ReAct 循环,这是最复杂的节点。
|
||||
|
||||
**入口判断优先级**:
|
||||
```
|
||||
1. PendingConfirmTool 非空 → executePendingTool() → 结束
|
||||
2. 无有效 PlanStep → 报错
|
||||
3. 正常 ReAct → 调 LLM → 处理决策
|
||||
```
|
||||
|
||||
**LLM 调用参数**:temperature=0.3, max_tokens=1200, thinking=开启
|
||||
|
||||
**JSON 解析失败处理**(correction 机制):
|
||||
```
|
||||
LLM 输出非 JSON:
|
||||
→ ConsecutiveCorrections++
|
||||
→ 追加修正消息到历史
|
||||
→ return nil(图循环回来,LLM 看到修正消息后重试)
|
||||
→ 连续 3 次失败 → 返回硬错误,终止
|
||||
```
|
||||
|
||||
**五种 action 处理**:
|
||||
| action | 行为 | 工具执行? |
|
||||
|--------|------|-----------|
|
||||
| continue + tool_call | 读工具直接执行 | 是,executeToolCall() |
|
||||
| continue 无 tool | 仅说话,继续循环 | 否 |
|
||||
| confirm | 暂存 PendingConfirmTool | 否,等用户确认 |
|
||||
| ask_user | 打开追问 | 否 |
|
||||
| next_plan | 推进步骤 | 否 |
|
||||
| done | 结束所有步骤 | 否 |
|
||||
|
||||
**工具执行后历史消息格式**:
|
||||
```
|
||||
assistant message: {Role: "assistant", ToolCalls: [{ID, Function: {Name, Arguments}}]}
|
||||
tool message: {Role: "tool", ToolCallID: <匹配ID>, Content: "工具结果"}
|
||||
```
|
||||
这对消息必须配对,否则 OpenAI 兼容 API 会拒绝请求。
|
||||
|
||||
**轮次预算**:MaxRounds 默认 30,耗尽强制进入 Deliver。
|
||||
|
||||
### 4.5 Interrupt 节点 (`node/interrupt.go`)
|
||||
|
||||
**职责**:向用户展示消息后暂停图执行
|
||||
|
||||
**三种类型**:
|
||||
- ask_user:伪流式展示 DisplayText
|
||||
- confirm:展示确认状态
|
||||
- 默认:展示通用中断信息
|
||||
|
||||
### 4.6 Deliver 节点 (`node/deliver.go`)
|
||||
|
||||
**职责**:生成最终总结
|
||||
|
||||
- 调 LLM(temperature=0.5, max_tokens=800)生成总结
|
||||
- 失败时降级到机械格式化(逐条列出步骤 + 完成标记)
|
||||
- 完成后调用 deleteAgentState() 清理 Redis 快照
|
||||
|
||||
---
|
||||
|
||||
## 五、LLM 交互模式
|
||||
|
||||
### 5.1 统一 JSON 协议
|
||||
|
||||
所有 LLM 输出都是严格 JSON,不是纯文本。每个阶段有自己的合约:
|
||||
|
||||
**Plan 合约** (`model/plan_contract.go`):
|
||||
```json
|
||||
{
|
||||
"speak": "...",
|
||||
"action": "continue|ask_user|plan_done",
|
||||
"reason": "...",
|
||||
"complexity": "simple|moderate|complex",
|
||||
"need_thinking": false,
|
||||
"plan_steps": [{"content": "...", "done_when": "..."}]
|
||||
}
|
||||
```
|
||||
|
||||
**Execute 合约** (`model/execute_contract.go`):
|
||||
```json
|
||||
{
|
||||
"speak": "...",
|
||||
"action": "continue|ask_user|confirm|next_plan|done",
|
||||
"reason": "...",
|
||||
"goal_check": "(next_plan/done 时必填)",
|
||||
"tool_call": {"name": "工具名", "arguments": {...}}
|
||||
}
|
||||
```
|
||||
|
||||
### 5.2 JSON 解析容错
|
||||
|
||||
`llm/json.go` 的 `ParseJSONObject` 能处理:
|
||||
- LLM 在 JSON 前后附带文字 → 提取中间的 JSON 对象
|
||||
- Markdown 代码块包裹(```json ... ```)→ 剥离
|
||||
- 嵌套对象(大括号配对计数)
|
||||
|
||||
### 5.3 Correction 循环
|
||||
|
||||
当 LLM 输出非法 JSON 时:
|
||||
```
|
||||
1. 原始输出作为 assistant 消息追加到历史
|
||||
2. 修正提示作为 user 消息追加到历史
|
||||
3. return nil → 图循环回来
|
||||
4. LLM 看到修正消息,下一轮输出合法 JSON
|
||||
5. ConsecutiveCorrections 重置为 0
|
||||
6. 连续 3 次失败 → 硬错误终止
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 六、工具系统
|
||||
|
||||
### 6.1 数据模型
|
||||
|
||||
`ScheduleState` 是工具操作的唯一数据源:
|
||||
|
||||
```
|
||||
ScheduleState
|
||||
├── Window 时间窗口
|
||||
│ ├── TotalDays 总天数(如 5 或 7)
|
||||
│ └── DayMapping[] day_index → (week, day_of_week) 映射
|
||||
└── Tasks[] 扁平任务列表
|
||||
├── source="event" 来自日程表的已有课程/任务
|
||||
│ ├── Slots[] 压缩的时段范围
|
||||
│ ├── CanEmbed 是否允许嵌入
|
||||
│ └── Locked 是否锁定(不可移动)
|
||||
└── source="task_item" 来自任务类的待安排任务
|
||||
├── Duration 需要的连续时段数
|
||||
├── CategoryID 所属 TaskClass.ID
|
||||
└── Status="pending" 待安排
|
||||
```
|
||||
|
||||
### 6.2 10 个工具
|
||||
|
||||
**读工具(直接执行,不需要确认)**:
|
||||
|
||||
| 工具 | 用途 | 典型调用时机 |
|
||||
|------|------|------------|
|
||||
| `get_overview` | 全局概览:天数、占用统计、可嵌入、待安排 | LLM 需要了解全局 |
|
||||
| `query_range` | 查询指定天/时段的详情 | LLM 需要具体位置信息 |
|
||||
| `find_free` | 查找连续空闲时段 | LLM 需要找空位放任务 |
|
||||
| `list_tasks` | 按条件列出任务 | LLM 需要筛选任务 |
|
||||
| `get_task_info` | 单个任务详情(含嵌入关系) | LLM 需要具体任务信息 |
|
||||
|
||||
**写工具(需用户确认)**:
|
||||
|
||||
| 工具 | 用途 | 关键逻辑 |
|
||||
|------|------|---------|
|
||||
| `place` | 放置 pending 任务到时段 | 自动检测嵌入(CanEmbed=true 的宿主) |
|
||||
| `move` | 移动已有任务到新位置 | 冲突检测(排除自身) |
|
||||
| `swap` | 交换两个等时长任务的时段 | 冲突时自动回滚 |
|
||||
| `batch_move` | 批量移动多个任务 | 原子性:任一冲突全部回滚 |
|
||||
| `unplace` | 取消放置,恢复 pending | 清理双向嵌入关系 |
|
||||
|
||||
### 6.3 工具执行流程
|
||||
|
||||
**读工具**(action=continue + tool_call):
|
||||
```
|
||||
Execute → executeToolCall() → registry.Execute() → 追加 assistant+tool 消息对 → return nil → 图循环
|
||||
```
|
||||
|
||||
**写工具**(action=confirm):
|
||||
```
|
||||
Execute → handleExecuteActionConfirm() → 暂存 PendingConfirmTool
|
||||
→ Confirm 节点 → Interrupt → 用户确认
|
||||
→ Chat(resume) → 恢复 PendingConfirmTool
|
||||
→ Execute → executePendingTool() → registry.Execute() + persistor + 追加消息对
|
||||
```
|
||||
|
||||
### 6.4 持久化路径
|
||||
|
||||
```
|
||||
工具执行成功
|
||||
→ DiffScheduleState(original, modified) → []ScheduleChange
|
||||
→ PersistScheduleChanges(事务)
|
||||
→ applyPlaceChange / applyMoveChange / applyUnplaceChange
|
||||
```
|
||||
|
||||
**当前限制**:`applyPlaceChange` 只处理 `source="event"`,`source="task_item"` 会报错。详见 ROADMAP.md P0 缺口。
|
||||
|
||||
---
|
||||
|
||||
## 七、SSE 输出系统
|
||||
|
||||
### 7.1 ChunkEmitter
|
||||
|
||||
所有节点通过 `ChunkEmitter` 向前端推送事件:
|
||||
|
||||
```
|
||||
EmitPseudoAssistantText() → 伪流式文本(分段推送,模拟打字效果)
|
||||
EmitStatus() → 状态推送("正在执行第2步")
|
||||
EmitConfirmRequest() → 确认卡片
|
||||
EmitFinish() / EmitDone() → 结束标记
|
||||
```
|
||||
|
||||
### 7.2 伪流式
|
||||
|
||||
LLM 的一次性文本输出通过 `SplitPseudoStreamText` 拆分成多个 chunk:
|
||||
- 按中英文标点断句
|
||||
- 每个 chunk 8~24 个字符
|
||||
- 间隔 40ms 推送
|
||||
|
||||
### 7.3 OpenAI 兼容格式
|
||||
|
||||
`stream/openai.go` 定义了 OpenAI 兼容的 SSE 格式,通过 `ext` 字段扩展:
|
||||
- `reasoning_text`:思考过程
|
||||
- `assistant_text`:正文
|
||||
- `status`:状态更新
|
||||
- `tool_call` / `tool_result`:工具调用
|
||||
- `confirm_request`:确认卡片
|
||||
- `interrupt`:中断消息
|
||||
|
||||
---
|
||||
|
||||
## 八、持久化模型
|
||||
|
||||
### 8.1 三个持久化层次
|
||||
|
||||
| 层级 | 机制 | 何时触发 | 存什么 |
|
||||
|------|------|---------|--------|
|
||||
| 快照 | AgentStateStore (Redis) | Plan/Confirm/Execute 节点后 | AgentRuntimeState + ConversationContext |
|
||||
| 变更 | SchedulePersistor (MySQL) | 写工具执行后 | ScheduleState 的 diff |
|
||||
| 历史 | Redis + MySQL | 图运行完成后 | 完整对话历史 |
|
||||
|
||||
### 8.2 快照恢复流程
|
||||
|
||||
```
|
||||
用户发送新消息(图需要从中断恢复)
|
||||
→ loadOrCreateRuntimeState()
|
||||
→ StateStore.Load(conversationID)
|
||||
→ 如果存在:恢复 RuntimeState + ConversationContext
|
||||
→ 如果不存在:创建全新状态
|
||||
```
|
||||
|
||||
快照在 Deliver 后被 `deleteAgentState()` 清理。
|
||||
|
||||
---
|
||||
|
||||
## 九、Prompt 体系
|
||||
|
||||
### 9.1 prompt 构造模式
|
||||
|
||||
所有阶段现在统一共享 `buildUnifiedStageMessages()` 函数:
|
||||
|
||||
```
|
||||
msg0(system) = 全局 system prompt + 阶段 system prompt + 工具简表
|
||||
msg1(assistant) = 对话历史 + 归档摘要
|
||||
msg2(assistant) = 阶段工作区
|
||||
msg3(system) = 阶段状态 + 记忆 + 本轮指令
|
||||
```
|
||||
|
||||
统一构造由 `StageMessagesConfig` 驱动,具体阶段只负责填充各自的 `Msg2Content`、`Msg3StageState` 和 `UserInstruction`。
|
||||
|
||||
### 9.2 各阶段 prompt 要点
|
||||
|
||||
| 阶段 | 核心指令 | 关键约束 |
|
||||
|------|---------|---------|
|
||||
| Chat | 分类意图:chat vs task | 保守默认为 task |
|
||||
| Plan | 两阶段:快速评估 + 深度规划 | 简单任务不开启 thinking |
|
||||
| Execute | ReAct:思考→执行→观察 | goal_check 为 next_plan/done 必填 |
|
||||
| Deliver | 总结计划执行结果 | 失败降级到机械格式化 |
|
||||
|
||||
### 9.3 置顶上下文块
|
||||
|
||||
```
|
||||
PinnedBlocks 是跨节点共享的上下文,通过 Key 去重:
|
||||
|
||||
execution_context ← Execute 节点注入(当前步骤、完成判定等)
|
||||
plan ← Plan 节点注入(完整计划文本)
|
||||
tool_summary ← Execute 节点注入(可用工具摘要)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 十、图路由逻辑 (`graph/common_graph.go`)
|
||||
|
||||
路由函数是图的核心控制逻辑,决定了每步之后走向哪个节点:
|
||||
|
||||
### branchAfterChat
|
||||
```
|
||||
if PendingInteraction → Interrupt
|
||||
else switch Phase:
|
||||
planning → Plan
|
||||
executing → Execute
|
||||
done → Deliver
|
||||
chatting → END
|
||||
```
|
||||
|
||||
### branchAfterPlan
|
||||
```
|
||||
if PendingInteraction → Interrupt
|
||||
else switch Phase:
|
||||
waiting_confirm → Confirm
|
||||
planning → Plan(continue,继续规划)
|
||||
executing → Execute(不应该发生,但防御性路由)
|
||||
```
|
||||
|
||||
### branchAfterConfirm
|
||||
```
|
||||
if PendingInteraction → Interrupt
|
||||
else → Execute(确认通过)
|
||||
```
|
||||
|
||||
### branchAfterExecute
|
||||
```
|
||||
if PendingInteraction → Interrupt
|
||||
else switch Phase:
|
||||
executing → Execute(继续循环)
|
||||
done → Deliver
|
||||
waiting_confirm → Confirm(不应该发生,防御性路由)
|
||||
```
|
||||
|
||||
### 关键保护机制
|
||||
|
||||
所有分支函数都以 `branchIfInterrupted()` 开头:
|
||||
```go
|
||||
func branchIfInterrupted(st *AgentGraphState) string {
|
||||
if st.RuntimeState.HasPendingInteraction() {
|
||||
return "interrupt"
|
||||
}
|
||||
return ""
|
||||
}
|
||||
```
|
||||
这确保任何节点设置了 PendingInteraction 后,图都会走向 Interrupt 节点展示给用户。
|
||||
|
||||
---
|
||||
|
||||
## 十一、Service 集成层 (`service/agentsvc/agent_newagent.go`)
|
||||
|
||||
### 入口函数:runNewAgentGraph
|
||||
|
||||
```
|
||||
1. 规范化 conversationID, modelName
|
||||
2. 确保会话存在(Redis 缓存 → DB)
|
||||
3. 构建重试元数据
|
||||
4. 加载或创建 RuntimeState(从 Redis 快照恢复)
|
||||
5. 构建 AgentGraphRequest(ConfirmAction 从 extra 取)
|
||||
6. 包装 Ark 客户端
|
||||
7. 创建 SSE 适配器 + ChunkEmitter
|
||||
8. 组装 AgentGraphDeps(注入所有依赖)
|
||||
9. 调用 RunAgentGraph()
|
||||
10. 持久化对话历史到 Redis + MySQL
|
||||
11. 发送 [DONE] 标记,触发异步标题生成
|
||||
```
|
||||
|
||||
### 依赖注入
|
||||
|
||||
```
|
||||
cmd/start.go:
|
||||
→ NewScheduleProvider(scheduleDAO, taskClassDAO) → SetScheduleProvider()
|
||||
→ NewSchedulePersistorAdapter(repoManager) → SetSchedulePersistor()
|
||||
→ NewDefaultRegistry() → SetToolRegistry()
|
||||
→ NewRedisStateStore(cacheDAO) → SetAgentStateStore()
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 十二、如何调试
|
||||
|
||||
### 12.1 日志关键字
|
||||
|
||||
| 搜索关键字 | 含义 |
|
||||
|-----------|------|
|
||||
| `[DEBUG] execute LLM` | Execute 节点的 LLM 原始输出和解析结果 |
|
||||
| `[DEBUG] plan LLM` | Plan 节点的 LLM 输出 |
|
||||
| `[WARN] execute 决策不合法` | LLM 输出合法 JSON 但 action 不合法 |
|
||||
| `[DEBUG] execute LLM 输出解析失败` | JSON 解析失败,触发 correction |
|
||||
| `PersistScheduleChanges` | 持久化调用 |
|
||||
| `loadOrCreateRuntimeState` | 状态恢复/创建 |
|
||||
|
||||
### 12.2 常见问题排查
|
||||
|
||||
**SSE 断开**:
|
||||
1. 检查 `[DEBUG] execute LLM` 日志,看 LLM 输出是否为合法 JSON
|
||||
2. 如果输出 `[NEXT_PLAN]` 等纯文本 → prompt 问题(已修复,参考 execute.go 的 correction 机制)
|
||||
3. 如果输出合法 JSON 但 action 不对 → 检查 prompt 的合约文本
|
||||
|
||||
**工具不执行**:
|
||||
1. 检查 PendingConfirmTool 是否被正确设置和恢复
|
||||
2. 检查 ScheduleState 是否为 nil(可能 ScheduleProvider 未注入)
|
||||
3. 检查 history 中 assistant+tool 消息是否配对(ToolCallID 是否匹配)
|
||||
|
||||
**图循环不退出**:
|
||||
1. 检查 ConsecutiveCorrections 计数(可能 LLM 反复输出非法 JSON)
|
||||
2. 检查 RoundUsed 是否耗尽(MaxRounds 默认 30)
|
||||
3. 检查 Phase 是否卡在某个状态
|
||||
|
||||
### 12.3 单元测试
|
||||
|
||||
```
|
||||
node/execute_confirm_flow_test.go → 7 个测试,覆盖完整 confirm 回路
|
||||
node/llm_tool_orchestration_test.go → 5 个测试,覆盖真实排课场景
|
||||
```
|
||||
|
||||
测试使用 mock LLM(预定义 JSON 响应序列)和 mock 工具注册表,不依赖外部服务。
|
||||
|
||||
---
|
||||
|
||||
## 十三、关键设计决策及理由
|
||||
|
||||
| 决策 | 理由 |
|
||||
|------|------|
|
||||
| Phase 驱动路由而非硬编码序列 | 同一个图支持多种流程(直接聊天、排课、追问恢复),Phase 是最小状态信号 |
|
||||
| PendingInteraction 作为中断快照 | 图是无状态的(每次请求重新运行),需要一种机制跨请求传递"等用户回复"的上下文 |
|
||||
| PendingConfirmTool 作为临时邮箱 | Execute 和 Confirm 之间不能直接传参(中间隔了 Interrupt+END+Chat),用运行态字段传递 |
|
||||
| JSON 协议而非文本标记 | LLM 输出结构化数据,后端用泛型解析,避免正则匹配的不确定性 |
|
||||
| Correction 机制 | LLM 不是 100% 可靠,需要给修正机会,但限制最大连续次数避免死循环 |
|
||||
| 伪流式而非真流式 | LLM API 的一次性返回更适合分段推送,真流式实现复杂且收益低 |
|
||||
| 工具操作扁平 ScheduleState | 避免嵌套数据结构,工具只需关心"在哪里放什么" |
|
||||
| Diff 持久化 | 只持久化变更部分,减少 DB 操作,支持原子性 |
|
||||
| PinnedBlocks 注入上下文 | 计划、工具摘要等信息不需要每轮都重复,用置顶块注入一次即可 |
|
||||
|
||||
---
|
||||
|
||||
## 十四、关键文件速查
|
||||
|
||||
| 想了解... | 看这个文件 |
|
||||
|----------|----------|
|
||||
| 图怎么连的 | `graph/common_graph.go` |
|
||||
| 每个节点怎么被调用的 | `node/agent_nodes.go` |
|
||||
| Chat 怎么分类意图的 | `prompt/chat.go` + `node/chat.go` |
|
||||
| Plan 怎么生成计划的 | `prompt/plan.go` + `node/plan.go` |
|
||||
| Execute 的 ReAct 循环 | `node/execute.go` |
|
||||
| confirm 回路怎么转的 | `node/confirm.go` + `node/chat.go`(handleConfirmResume) |
|
||||
| LLM 输出什么格式 | `model/plan_contract.go` + `model/execute_contract.go` |
|
||||
| JSON 解析怎么容错的 | `llm/json.go` |
|
||||
| correction 怎么追回的 | `node/correction.go` |
|
||||
| 工具怎么注册和执行的 | `tools/registry.go` |
|
||||
| 工具操作什么数据 | `tools/state.go` |
|
||||
| SSE 输出什么格式 | `stream/openai.go` |
|
||||
| 状态怎么持久化的 | `model/state_store.go` + `conv/schedule_persist.go` |
|
||||
| 日程数据怎么加载的 | `conv/schedule_provider.go` + `conv/schedule_state.go` |
|
||||
| Service 怎么组装的 | `service/agentsvc/agent_newagent.go` |
|
||||
| API 怎么调用的 | `api/agent.go` |
|
||||
| 距离全链路还差什么 | `ROADMAP.md` |
|
||||
142
docs/backend/HANDOFF_WebSearch两阶段实施计划.md
Normal file
142
docs/backend/HANDOFF_WebSearch两阶段实施计划.md
Normal file
@@ -0,0 +1,142 @@
|
||||
# WebSearch 两阶段实施计划(newAgent)
|
||||
|
||||
## 1. 目标与范围
|
||||
|
||||
本文用于把 `newAgent` 的 WebSearch 能力按两阶段落地:
|
||||
|
||||
1. 第一阶段:先接入可用的检索与抓取能力(低风险、快交付)。
|
||||
2. 第二阶段:在第一阶段基础上升级为 WebRAG 语义召回链路(提升复杂问题命中率与可解释性)。
|
||||
|
||||
约束:
|
||||
|
||||
1. 不走 `infra/smartflow-mcp-server`,直接走 `newAgent/tools` 工具注册链路。
|
||||
2. 保持现有执行模式不变:读操作 `action=continue + tool_call`。
|
||||
3. 第一阶段只接单供应商;第二阶段再考虑 provider fallback。
|
||||
|
||||
---
|
||||
|
||||
## 2. 第一阶段(V1):WebSearch + 简单抓取
|
||||
|
||||
### 2.1 交付目标
|
||||
|
||||
让模型可以:
|
||||
|
||||
1. 通过 `web_search` 获得结构化检索结果(标题、摘要、URL、来源域名、时间)。
|
||||
2. 通过 `web_fetch` 拉取指定 URL 正文并做最小清洗。
|
||||
3. 在不改主流程的前提下,把结果作为标准 `tool observation` 写回历史。
|
||||
|
||||
### 2.2 计划新增工具
|
||||
|
||||
1. `web_search`
|
||||
- 输入:`query`、`top_k`、`domain_allow`、`recency_days` 等。
|
||||
- 输出:JSON 字符串(`tool`、`query`、`count`、`items[]`)。
|
||||
2. `web_fetch`
|
||||
- 输入:`url`、`max_chars`。
|
||||
- 输出:JSON 字符串(`tool`、`url`、`title`、`content`、`truncated`)。
|
||||
|
||||
### 2.3 代码落点
|
||||
|
||||
新增文件:
|
||||
|
||||
1. `backend/newAgent/tools/web_tools.go`:工具参数解析、输出组装、错误兜底。
|
||||
2. `backend/newAgent/tools/web_provider.go`:搜索供应商抽象接口与通用数据结构。
|
||||
3. `backend/newAgent/tools/web_provider_tavily.go`(或 `web_provider_brave.go`):首个 provider 实现。
|
||||
4. `backend/newAgent/tools/web_fetcher.go`:URL 抓取与 HTML 最小清洗。
|
||||
|
||||
修改文件:
|
||||
|
||||
1. `backend/newAgent/tools/registry.go`:注册 `web_search`、`web_fetch` 两个读工具。
|
||||
2. `backend/cmd/start.go`:初始化 provider 配置并注入 registry(或通过包级配置读取)。
|
||||
3. `backend/newAgent/prompt/execute_context.go`:补充新工具的 schema 说明与示例。
|
||||
|
||||
### 2.4 V1 验收标准
|
||||
|
||||
1. 模型能稳定调用 `web_search` 并拿到可解析 JSON 结果。
|
||||
2. `web_fetch` 在正文可达时返回正文,在失败时返回明确错误码与原因。
|
||||
3. 工具超时、429、5xx 均不会打断主流程,只返回可恢复 observation。
|
||||
4. 日志可定位:query、tool、耗时、结果数、失败原因。
|
||||
|
||||
---
|
||||
|
||||
## 3. 第二阶段(V2):WebRAG 语义召回
|
||||
|
||||
### 3.1 交付目标
|
||||
|
||||
新增 `web_rag_search`,把“检索 + 抓取 + 分块 + 召回 + 重排 + 证据返回”收敛为一个读工具,提升复杂问答质量。
|
||||
|
||||
### 3.2 链路设计
|
||||
|
||||
1. 查询改写:把用户问题改写为 1~3 个检索子查询。
|
||||
2. WebSearch 召回:拿到候选 URL 集合。
|
||||
3. 抓取清洗:抽正文,去噪。
|
||||
4. 分块:按段落与 token 预算切块。
|
||||
5. 召回:向量召回 + 关键词召回(混合召回)。
|
||||
6. 重排:按 query 相关性重排 chunk。
|
||||
7. 输出:返回答案所需证据片段、来源 URL、片段得分。
|
||||
|
||||
### 3.3 代码落点
|
||||
|
||||
新增文件:
|
||||
|
||||
1. `backend/newAgent/tools/web_rag_tools.go`:`web_rag_search` 工具入口。
|
||||
2. `backend/newAgent/tools/web_rag_chunker.go`:清洗后分块。
|
||||
3. `backend/newAgent/tools/web_rag_retriever.go`:混合召回。
|
||||
4. `backend/newAgent/tools/web_rag_rerank.go`:重排层。
|
||||
5. `backend/newAgent/tools/web_rag_store.go`:会话级索引缓存(先内存/Redis TTL)。
|
||||
|
||||
修改文件:
|
||||
|
||||
1. `backend/newAgent/tools/registry.go`:注册 `web_rag_search`。
|
||||
2. `backend/newAgent/prompt/execute_context.go`:增加 `web_rag_search` 使用规范。
|
||||
|
||||
### 3.4 V2 验收标准
|
||||
|
||||
1. 同类复杂问题下,回答引用质量和相关性明显高于 V1。
|
||||
2. 返回至少包含:`answer_evidence[]`(片段+URL+score)。
|
||||
3. 召回或重排失败时可降级到 V1(`web_search + web_fetch`)路径。
|
||||
4. 提供基础评估指标:命中率、延迟、成本、失败率。
|
||||
|
||||
---
|
||||
|
||||
## 4. 与记忆系统的关系
|
||||
|
||||
`WebRAG` 与记忆系统 RAG 高度重合,建议“共用内核、分语料适配”:
|
||||
|
||||
1. 共用:chunk / embed / retrieve / rerank 的通用接口与实现。
|
||||
2. 分开:`MemoryCorpus`(私有数据)与 `WebCorpus`(公网数据)的数据源适配层。
|
||||
3. 在工具层保持两个入口:`memory_search` 与 `web_rag_search`,返回结构尽量统一。
|
||||
|
||||
---
|
||||
|
||||
## 5. 上线顺序与回滚策略
|
||||
|
||||
### 5.1 上线顺序
|
||||
|
||||
1. 先灰度 V1:仅开放 `web_search`、`web_fetch`。
|
||||
2. 观察稳定性与成本后再灰度 V2:`web_rag_search`。
|
||||
3. V2 稳定后再考虑 provider fallback 与更长周期缓存。
|
||||
|
||||
### 5.2 回滚策略
|
||||
|
||||
1. `web_rag_search` 异常时,快速降级为 V1 工具集。
|
||||
2. V1 供应商异常时,返回“检索暂不可用”的结构化 observation,不阻断主流程。
|
||||
3. 保留 feature flag:按工具级别开关,支持秒级关闭。
|
||||
|
||||
---
|
||||
|
||||
## 6. 风险清单
|
||||
|
||||
1. 供应商配额/限流导致查询失败。
|
||||
2. 页面反爬与正文抽取质量不稳定。
|
||||
3. RAG 链路成本上升(抓取+embedding+重排)。
|
||||
4. 引用片段与最终答案不一致(需要强制证据对齐策略)。
|
||||
|
||||
---
|
||||
|
||||
## 7. 里程碑建议
|
||||
|
||||
1. M1(1~2 天):V1 工具跑通,联调 Execute 节点可调用。
|
||||
2. M2(2~4 天):V1 稳定性优化(超时/限流/日志/错误码)。
|
||||
3. M3(4~7 天):V2 WebRAG MVP(混合召回+基础重排+证据输出)。
|
||||
4. M4(后续):统一 RAG Core,打通记忆系统复用。
|
||||
|
||||
54
docs/backend/HANDOFF_优化待办.md
Normal file
54
docs/backend/HANDOFF_优化待办.md
Normal file
@@ -0,0 +1,54 @@
|
||||
# newAgent 优化待办 Handoff
|
||||
|
||||
> 日期:2026-04-21
|
||||
> 来源:迁移 agent/ → newAgent/ 完成后的架构审视
|
||||
|
||||
---
|
||||
|
||||
## 1. TaskQuery 紧急度提升统一
|
||||
|
||||
### 问题
|
||||
|
||||
LLM 工具查询任务(`AgentService.QueryTasksForTool`)使用 `applyReadTimeUrgencyPromotion` 只做内存态优先级提升,不触发 outbox 写 MySQL。
|
||||
前端查询任务(`TaskService.GetUserTasks`)使用 `deriveTaskUrgencyForRead` + `tryEnqueueTaskUrgencyPromote`,会异步持久化。
|
||||
|
||||
两条路径行为不一致:LLM 看到的优先级可能比 DB 里的高。
|
||||
|
||||
### 方案
|
||||
|
||||
1. `service/task.go` — 从 `GetUserTasks` 中提取公共方法(如 `GetTasksWithUrgencyPromotion`),返回已提升的 `[]model.Task` 并触发 outbox
|
||||
2. `service/agentsvc/agent.go` — 新增 `taskSvc *service.TaskService` 字段
|
||||
3. `service/agentsvc/agent_task_query.go` — 重写 `QueryTasksForTool`,调用 TaskService 公共方法;删除 `applyReadTimeUrgencyPromotion` 死代码
|
||||
4. `cmd/start.go` — 注入 TaskService 到 AgentService
|
||||
|
||||
### 涉及文件
|
||||
|
||||
| 文件 | 改动 |
|
||||
|------|------|
|
||||
| `service/task.go` | 提取公共方法 |
|
||||
| `service/agentsvc/agent.go` | 加 taskSvc 字段 |
|
||||
| `service/agentsvc/agent_task_query.go` | 重写,删 `applyReadTimeUrgencyPromotion` |
|
||||
| `cmd/start.go` | 注入 TaskService |
|
||||
|
||||
---
|
||||
|
||||
## 2. service/agentsvc 层瘦身(低优先级)
|
||||
|
||||
### 现状
|
||||
|
||||
`service/agentsvc/` 目前 11 个文件,大部分是 HTTP→DB 转接层,职责合理。但有两个纯逻辑文件理论上可下沉:
|
||||
|
||||
| 文件 | 内容 | 可移至 |
|
||||
|------|------|--------|
|
||||
| `agent_memory_render.go` | 纯文本转换,零 DB 交互 | `memory/` 包 |
|
||||
| `agent_task_query.go` 的 `taskMatchesQueryFilter` / `sortTasksForQuery` | 纯过滤/排序 | `newAgent/tools/` |
|
||||
|
||||
### 判断
|
||||
|
||||
当前体量小(加起来约 200 行纯函数),搬出去收益不大,反而多一层 import 间接。如果未来这些函数膨胀再搬不迟。
|
||||
|
||||
---
|
||||
|
||||
## 3. go mod tidy
|
||||
|
||||
迁移完成后 `go.mod` 中有未使用的依赖(如 `github.com/bytedance/mockey`)。建议跑一次 `go mod tidy` 清理。
|
||||
634
docs/backend/P1-P1.5执行改动计划.md
Normal file
634
docs/backend/P1-P1.5执行改动计划.md
Normal file
@@ -0,0 +1,634 @@
|
||||
# SmartFlow NewAgent P1-P1.5 执行改动计划(代码实施版)
|
||||
|
||||
## 0. 文档定位
|
||||
- 文档类型:代码实施计划(非 PRD)。
|
||||
- 对齐范围:仅覆盖已冻结 PRD 的 `P1` 与 `P1.5`。
|
||||
- 执行目标:先跑通“首次编排主动优化闭环(P1)+ 对话内任务类共创可用版(P1.5)”。
|
||||
|
||||
---
|
||||
|
||||
## 1. 目标与边界
|
||||
|
||||
### 1.1 本轮目标(必须完成)
|
||||
- P1:在 `execute` 主链路中引入分析型读工具,形成“观测 -> 调整 -> 复盘 -> 收口”的可执行闭环。
|
||||
- P1:保持旧写工具链路主执行地位(`move/swap/unplace/...`),分析工具只做观测,不直接执行改动。
|
||||
- P1.5:在对话内提供“完整任务类草案”能力,并通过 `upsert_task_class` 完成确认后的统一落库。
|
||||
|
||||
### 1.2 本轮非目标(明确不做)
|
||||
- 不做多版本日程管理(已定 P2)。
|
||||
- 不做配置化持久禁改清单(仅对话内轻量语义)。
|
||||
- 不做聊天外按钮触发任务类共创。
|
||||
- 不做 `analyze_deadlines`(当前 `ScheduleState` 无单任务 deadline/priority 数据源,不满足稳定落地条件)。
|
||||
|
||||
---
|
||||
|
||||
## 2. 现状代码锚点(实施入口)
|
||||
|
||||
### 2.1 执行主链路
|
||||
- [execute.go](/D:/SmartFlow-Agent/backend/newAgent/node/execute.go)
|
||||
- [agent_nodes.go](/D:/SmartFlow-Agent/backend/newAgent/node/agent_nodes.go)
|
||||
- [common_graph.go](/D:/SmartFlow-Agent/backend/newAgent/graph/common_graph.go)
|
||||
|
||||
### 2.2 工具注册与调度态
|
||||
- [registry.go](/D:/SmartFlow-Agent/backend/newAgent/tools/registry.go)
|
||||
- [state.go](/D:/SmartFlow-Agent/backend/newAgent/tools/schedule/state.go)
|
||||
- [read_tools.go](/D:/SmartFlow-Agent/backend/newAgent/tools/schedule/read_tools.go)
|
||||
- [read_filter_tools.go](/D:/SmartFlow-Agent/backend/newAgent/tools/schedule/read_filter_tools.go)
|
||||
- [task-class.go](/D:/SmartFlow-Agent/backend/dao/task-class.go)
|
||||
|
||||
### 2.3 Prompt 与工具可见性
|
||||
- [execute.go](/D:/SmartFlow-Agent/backend/newAgent/prompt/execute.go)
|
||||
- [execute_context.go](/D:/SmartFlow-Agent/backend/newAgent/prompt/execute_context.go)
|
||||
- [chat.go](/D:/SmartFlow-Agent/backend/newAgent/node/chat.go)
|
||||
|
||||
### 2.4 状态持久化与恢复
|
||||
- [common_state.go](/D:/SmartFlow-Agent/backend/newAgent/model/common_state.go)
|
||||
- [graph_run_state.go](/D:/SmartFlow-Agent/backend/newAgent/model/graph_run_state.go)
|
||||
- [state_store.go](/D:/SmartFlow-Agent/backend/newAgent/model/state_store.go)
|
||||
|
||||
---
|
||||
|
||||
## 3. 总体改造方案(分层)
|
||||
|
||||
### 3.1 工具层(新增能力)
|
||||
- 新增 5 个分析读工具(P1):
|
||||
- `analyze_health`
|
||||
- `analyze_load`
|
||||
- `analyze_subjects`
|
||||
- `analyze_context`
|
||||
- `analyze_tolerance`
|
||||
- P1.5 不新增“任务类草案工具”;任务类草案由主 LLM 按 prompt 在对话内生成。
|
||||
- P1.5 新增 1 个任务类写库工具:`upsert_task_class`(创建/更新统一入口,走 confirm)。
|
||||
|
||||
### 3.2 策略层(执行行为约束)
|
||||
- 通过 `chat -> execute` 路由策略 + `execute prompt` 约束,控制“何时走全局分析,何时走局部旧链路”。
|
||||
- 保持“单轮单工具调用”与现有 `confirm` 闸门不变。
|
||||
|
||||
### 3.3 状态层(最小增量)
|
||||
- 建议新增轻量执行模式标记到 `CommonState`(避免全靠 prompt 猜):
|
||||
- `OptimizationMode string`,建议取值:
|
||||
- `first_full`(首次编排全流程)
|
||||
- `local_adjust`(后续局部请求)
|
||||
- `global_reopt`(用户明确触发全局重优化)
|
||||
|
||||
说明:如你希望“最小侵入”,该字段也可先不加,改用 `PinnedBlock` 过渡;但建议保留,后续可测试性更好。
|
||||
|
||||
---
|
||||
|
||||
## 4. 统一数据契约(新增工具)
|
||||
|
||||
### 4.1 分析工具统一返回包络(强约束)
|
||||
所有分析工具返回 `string(JSON)`,顶层统一:
|
||||
|
||||
```json
|
||||
{
|
||||
"tool": "analyze_xxx",
|
||||
"success": true,
|
||||
"metric_schema": {},
|
||||
"metrics": {},
|
||||
"issues": [
|
||||
{
|
||||
"issue_id": "issue_xxx",
|
||||
"dimension": "load|subjects|context|tolerance|feasibility|health",
|
||||
"severity": "critical|warning|info",
|
||||
"trigger": {
|
||||
"metric": "metric_key",
|
||||
"operator": ">=|<=|>|<|==",
|
||||
"threshold": 0,
|
||||
"actual": 0
|
||||
}
|
||||
}
|
||||
],
|
||||
"next_actions": [
|
||||
{
|
||||
"action_id": "na_xxx",
|
||||
"priority": 1,
|
||||
"intent_code": "rebalance_load|reduce_switch|increase_tolerance|...",
|
||||
"target_filter": {},
|
||||
"slot_filter": {},
|
||||
"candidate_scope": {
|
||||
"day_range": [],
|
||||
"categories": [],
|
||||
"task_pool": "placed|pending|mixed"
|
||||
},
|
||||
"required_reads": [],
|
||||
"success_criteria": {},
|
||||
"candidate_write_tools": ["move|swap|spread_even|..."]
|
||||
}
|
||||
],
|
||||
"error": "",
|
||||
"error_code": ""
|
||||
}
|
||||
```
|
||||
|
||||
### 4.2 统一错误与成功语义(强约束)
|
||||
- 所有新增工具统一返回 `string(JSON)`。
|
||||
- 顶层字段固定:
|
||||
- `tool`: 工具名。
|
||||
- `success`: `true|false`。
|
||||
- `metric_schema`: 指标字典(每个指标的含义、单位、方向)。
|
||||
- `metrics`: 指标数据本体。
|
||||
- `issues`: 问题数据本体(机器可判定触发条件)。
|
||||
- `next_actions`: 下一步动作意图(不给最终写参数)。
|
||||
- `error`: 失败时的人类可读错误文案。
|
||||
- `error_code`: 失败时稳定机器码(如 `invalid_args` / `insufficient_data`)。
|
||||
- `feasibility`(可选):可行性快照(`is_feasible/capacity_gap/reason_code`)。
|
||||
- 成功时必须返回 `metrics/issues/next_actions`。
|
||||
|
||||
### 4.2.1 精简协议原则(新增)
|
||||
- 不在协议中放大段中文解释,不依赖 `summary/reason` 驱动执行。
|
||||
- 协议只提供三类最小必要信息:
|
||||
- 数据含义:`metric_schema`
|
||||
- 当前状态:`metrics/issues`
|
||||
- 下一步方向:`next_actions`
|
||||
- LLM 通过 prompt 规则 + 上述结构化数据完成后续读写决策。
|
||||
|
||||
### 4.2.2 目标对象选择原则(新增)
|
||||
- 后端只提供“方向+作用范围+成功判据”,不下发最终写参数,不指定唯一 `task_id`。
|
||||
- `next_actions` 的 `candidate_scope/target_filter/slot_filter` 只定义可行动边界与禁区。
|
||||
- LLM 负责在边界内自主选择目标对象与写工具参数(如选哪个任务、挪到哪个槽位)。
|
||||
- 执行层负责合法性校验与证据化回传(本次操作命中哪个 issue、是否满足 success_criteria),不替代 LLM 做确定性选点。
|
||||
|
||||
### 4.3 工具详规:`analyze_load`
|
||||
适用场景:
|
||||
- 首次编排后的全局负载体检。
|
||||
- 用户诉求命中“太满/太空/不均匀/某几天压力大”。
|
||||
|
||||
入参定义(建议):
|
||||
- `scope`: `full|week|day_range`,默认 `full`。
|
||||
- `week_from/week_to`: `scope=week` 时可选;缺失则覆盖窗口内所有周。
|
||||
- `day_from/day_to`: `scope=day_range` 时必填。
|
||||
- `granularity`: `day|week|time_of_day`,默认 `day`。
|
||||
- `detail`: `summary|full`,默认 `summary`。
|
||||
|
||||
计算口径:
|
||||
- `total_used = course_used + task_used`(总占用)。
|
||||
- `utilization = total_used / total_slots`。
|
||||
- `load_std_dev`: 按天 `total_used` 计算样本标准差。
|
||||
- `load_range = max_day_total_used - min_day_total_used`。
|
||||
- 时段拆分固定:上午 `1-4`,下午 `5-8`,晚上 `9-12`。
|
||||
- `delta_from_prev = today_total_used - yesterday_total_used`。
|
||||
|
||||
输出字段重点:
|
||||
- `metrics.summary`: `total_used/course_used/task_used/pending_count`、`utilization_rate`、`peak/valley`、`load_std_dev`、`load_range`。
|
||||
- `metrics.days`: 每日 `total_used/course_used/task_used`、时段分解、负载等级。
|
||||
- `metrics.weeks`: 周级聚合(仅 `granularity=week` 或 `detail=full` 时返回)。
|
||||
|
||||
issues 判定(normal 档):
|
||||
- `critical`: 任意天利用率 `>= 0.90`,或 `load_std_dev >= 3.0` 且 `peak_day_load - valley_day_load >= 7`。
|
||||
- `warning`: 任意天利用率 `0.80~0.90`,或 `load_std_dev 2.0~3.0`。
|
||||
- `info`: 利用率整体正常但有轻微波动。
|
||||
|
||||
阈值档位偏移:
|
||||
- `strict`: 比 normal 更严格(提前约 10% 触发)。
|
||||
- `relaxed`: 比 normal 更宽松(延后约 10% 触发)。
|
||||
|
||||
next_actions 生成规则:
|
||||
- 阈值判断基于 `total_used`,但建议动作仅作用任务层(`task_used/pending`),不建议“优化课程占位”。
|
||||
- 负载过高建议:`move`、`queue_apply_head_move`、`spread_even`。
|
||||
- 波动过大建议:跨天分流,优先“峰值日 -> 低负载日”。
|
||||
- 仅给建议,不输出可直接执行写操作。
|
||||
|
||||
失败返回:
|
||||
- 参数非法、范围越界、窗口为空时返回 `success=false`。
|
||||
|
||||
### 4.4 工具详规:`analyze_subjects`
|
||||
适用场景:
|
||||
- 用户问“某科排得怎么样”“某任务类是不是太集中”。
|
||||
- 首次编排后检查任务类节奏与预算进度。
|
||||
|
||||
入参定义(建议):
|
||||
- `category`: 可选;为空表示全科目。
|
||||
- `include_pending`: `true|false`,默认 `true`。
|
||||
- `detail`: `summary|full`,默认 `summary`。
|
||||
|
||||
计算口径:
|
||||
- `present_days`: 该科目出现过落位的 `day_index` 集合。
|
||||
- `gaps`: 相邻出现日的间隔天数(`next_day - prev_day - 1`)。
|
||||
- `avg/max/min/std_gap`: 基于 `gaps` 统计。
|
||||
- `concentration`: 建议用按天时段占比的归一化 HHI(`0` 分散,`1` 集中)。
|
||||
- `budget_progress = used_slots / total_slots`(`total_slots` 来自 `TaskClassMeta`)。
|
||||
|
||||
输出字段重点:
|
||||
- `metrics.subjects[]`: `task_count/placed/pending`、`present_days/gaps`、`concentration`、`budget_progress`。
|
||||
- 可选返回 `days_to_end`(当任务类存在 `end_date` 且可解析)。
|
||||
|
||||
issues 判定(normal 档):
|
||||
- `critical`: `max_gap >= 6`,或 `concentration >= 0.85`,或 `budget_progress < 0.4` 且截止临近。
|
||||
- `warning`: `max_gap 4~5`,或 `concentration 0.70~0.85`。
|
||||
- `info`: 节奏基本稳定但有可优化空间。
|
||||
|
||||
next_actions 生成规则:
|
||||
- 过于集中:建议 `spread_even` 或多次 `move` 分散。
|
||||
- 空窗过长:建议插入中间复习点。
|
||||
- 预算滞后:建议提高该科目近期优先级。
|
||||
|
||||
失败返回:
|
||||
- `category` 不存在时可返回 `success=true` + 空结果,不建议硬失败。
|
||||
|
||||
### 4.5 工具详规:`analyze_context`
|
||||
适用场景:
|
||||
- 用户反馈“切换太多、心累、一天很碎”。
|
||||
- 首次编排后认知负荷体检。
|
||||
|
||||
入参定义(建议):
|
||||
- `day_from/day_to`: 可选;缺省覆盖窗口全量。
|
||||
- `detail`: `summary|day_detail`,默认 `summary`。
|
||||
- `hard_categories`: 可选数组;用于“硬课相邻”判定。
|
||||
|
||||
计算口径:
|
||||
- `sequence`: 按时段顺序提取当日科目序列(仅已落位任务)。
|
||||
- `switch_count`: 相邻非空且科目变化次数。
|
||||
- `blocks`: 连续同科目块。
|
||||
- `fragmentation = switch_count / max(occupied_slots-1, 1)`。
|
||||
- `heavy_adjacent`: 相邻 pair 同时命中 `hard_categories`。
|
||||
|
||||
输出字段重点:
|
||||
- `metrics.overall`: 总切换次数、日均切换、最长同科目连续块、平均块长度。
|
||||
- `metrics.days[]`: `switch_count`、`fragmentation`、`adjacent_pairs`、`blocks`。
|
||||
|
||||
issues 判定(normal 档):
|
||||
- `critical`: `switch_count >= 5` 且 `fragmentation >= 0.75`。
|
||||
- `warning`: `switch_count 3~4` 或 `fragmentation 0.55~0.75`。
|
||||
- `info`: 结构基本可接受但可继续聚合。
|
||||
|
||||
next_actions 生成规则:
|
||||
- 优先建议同科目聚合(`move/swap`)。
|
||||
- P1 明确不把 `min_context_switch` 作为候选写工具,避免“窗口内强行并排”造成学习间隔恶化。
|
||||
|
||||
失败返回:
|
||||
- 无落位任务时返回 `success=true` + 空指标,不硬失败。
|
||||
|
||||
### 4.6 工具详规:`analyze_tolerance`
|
||||
适用场景:
|
||||
- 用户反馈“排太满”“想留余量”“希望更抗突发”。
|
||||
- 与 PRD 中“容错”概念保持一致(替代旧“空窗”话术)。
|
||||
|
||||
入参定义(建议):
|
||||
- `scope`: `full|week|day_range`,默认 `full`。
|
||||
- `week_from/week_to/day_from/day_to`: 按 scope 生效。
|
||||
- `min_usable_size`: 默认 `2`(>=2 连续空位视为可用块)。
|
||||
- `min_daily_buffer`: 默认 `2`(每日最少可用余量阈值)。
|
||||
- `detail`: `summary|full`,默认 `summary`。
|
||||
|
||||
计算口径:
|
||||
- `total_free_slots`: 所有空闲时段总和。
|
||||
- `usable_slots`: 处于“可用空窗块(长度>=min_usable_size)”内的空闲时段。
|
||||
- `fragmented_slots`: 碎片空窗时段数。
|
||||
- `fragmentation_rate = fragmented_slots / total_free_slots`。
|
||||
- `buffer_sufficient`: 每天 `usable_slots >= min_daily_buffer`。
|
||||
|
||||
输出字段重点:
|
||||
- `metrics.overall`: `total_free/usable/fragmented`、`fragmentation_rate`、`days_without_buffer`。
|
||||
- `metrics.days[]`: 每日空窗块细节、相邻任务类型、是否满足缓冲。
|
||||
|
||||
issues 判定(normal 档):
|
||||
- `critical`: `days_without_buffer >= 2` 或 `fragmentation_rate >= 0.65`。
|
||||
- `warning`: `days_without_buffer = 1` 或 `fragmentation_rate 0.45~0.65`。
|
||||
- `info`: 容错足够但可进一步优化分布。
|
||||
|
||||
next_actions 生成规则:
|
||||
- 容错过低:建议把边缘任务外移、打散拥堵日。
|
||||
- 碎片过高:建议合并连续学习块,减少“1节孤岛”。
|
||||
|
||||
失败返回:
|
||||
- `min_usable_size<=0` 或参数范围非法时返回 `success=false`。
|
||||
|
||||
### 4.7 工具详规:`analyze_health`
|
||||
适用场景:
|
||||
- 首次编排全流程的默认首入口。
|
||||
- 用户明确要求“整体体检/全局重优化”。
|
||||
|
||||
入参定义(建议):
|
||||
- `dimensions`: 可选,默认 `["load","subjects","context","tolerance"]`。
|
||||
- `threshold`: `strict|normal|relaxed`,默认 `normal`。
|
||||
- `detail`: `summary|full`,默认 `summary`。
|
||||
|
||||
聚合策略:
|
||||
- 内部复用各分析器的统计函数,不在工具内二次调用 registry 工具(避免链式循环与重复成本)。
|
||||
- `issues` 合并后按 `severity -> impact_score -> recency` 排序。
|
||||
- 对同源问题去重(同维度、同天、同任务的重复报警合并)。
|
||||
- 聚合前先做可行性判定;若不可行,必须追加 `dimension=feasibility` 的 `critical` 问题。
|
||||
|
||||
输出字段重点:
|
||||
- `metrics`: 各维度精简核心指标。
|
||||
- `issues`: 标准化问题清单(用于 execute 单轮主问题域选择)。
|
||||
- `next_actions`: 最多 3 条高价值建议动作(仅建议)。
|
||||
- `feasibility`: `{ "is_feasible": bool, "capacity_gap": int, "reason_code": string }`。
|
||||
|
||||
issues 生成口径:
|
||||
- 直接沿用各维度阈值档位。
|
||||
- 若 `critical=0 && warning<=1`,在 `metrics` 明确写出“可接受收口”信号。
|
||||
- 若 `is_feasible=false`,无论其它维度如何,都必须输出 `feasibility` 的 `critical` 问题。
|
||||
|
||||
失败返回:
|
||||
- `dimensions` 全非法时返回 `success=false`。
|
||||
|
||||
#### 4.7.1 可行性判定(强约束)
|
||||
目的:
|
||||
- 解决“窗口太小/约束过严,导致持续 critical 且无法优化”的循环问题。
|
||||
|
||||
判定输入:
|
||||
- `required_task_slots`:当前仍需排入或重排的任务时段需求总量。
|
||||
- `feasible_slots`:在当前窗口与约束下,可承载任务的可用时段总量。
|
||||
- `capacity_gap = required_task_slots - feasible_slots`。
|
||||
|
||||
判定规则:
|
||||
- `capacity_gap <= 0`:`is_feasible=true`,继续常规优化。
|
||||
- `capacity_gap > 0`:`is_feasible=false`,进入“不可行协商分支”。
|
||||
|
||||
不可行协商分支(由 `analyze_health.next_actions` 输出建议):
|
||||
- `ask_expand_window`:建议扩展时间窗。
|
||||
- `ask_relax_constraints`:建议放松禁排时段/容错目标/顺序限制。
|
||||
- `ask_reduce_scope_or_budget`:建议缩范围或降低预算。
|
||||
- `accept_risk_and_close`:若用户坚持当前约束,按“有风险收口”结束本轮。
|
||||
|
||||
### 4.8 P1.5 草案生成原则(无新工具)
|
||||
适用场景:
|
||||
- 用户在聊天内要求“帮我设计任务类/补全参数/给个可排的草案”。
|
||||
- 输出应是“完整草案”,不是散点建议。
|
||||
|
||||
生成机制:
|
||||
- 草案由主 LLM 在 prompt 引导下直接生成,不新增后端草案工具。
|
||||
- 来源优先级固定:`user_explicit > memory > web_common_knowledge`。
|
||||
- 冲突必须显式标记为 `conflicts`,不得静默覆盖用户明确偏好。
|
||||
|
||||
字段分级(按 PRD 冻结):
|
||||
- 关键字段(必须 ask_user 确认):`time_window`、`strategy`、`total_slots`、`tolerance_preference`、`excluded_slots`、`task_items_integrity`、`task_item_priority_or_dependency`(用户给出时)。
|
||||
- 普通字段(可静默落):`time_of_day_preference_weight`、`same_category_aggregation_preference`、`milestone_split_suggestion`、`knowledge_tags_and_path_notes`(命中统一标准时结构化)。
|
||||
|
||||
后置校验原则:
|
||||
- 各类字段合法性与完整性校验放在写流程之后执行。
|
||||
- 若写后校验失败,返回可修复反馈并进入下一轮对话修订。
|
||||
|
||||
### 4.9 工具详规:`upsert_task_class`(P1.5 写库)
|
||||
适用场景:
|
||||
- 草案与关键字段确认完成后,将任务类落库(新建或更新)。
|
||||
- 用户明确要求“创建任务类/更新任务类参数”。
|
||||
|
||||
工具定位:
|
||||
- 这是 P1.5 唯一新增写工具,不负责草案生成。
|
||||
- 通过 `id` 语义统一创建与更新:`id=0` 创建,`id>0` 更新。
|
||||
- 必须走 confirm 闸门,避免模型在未确认关键字段时直接写库。
|
||||
|
||||
入参定义(建议):
|
||||
- `id`: `int`,可选,默认为 `0`(创建);`>0` 表示更新已有任务类。
|
||||
- `task_class`: 任务类主体字段(名称、时间窗、策略、总预算、容错偏好、禁排时段等)。
|
||||
- `items`: 任务项数组(任务项名称、时长/预算、优先级或依赖等)。
|
||||
- `source`: 可选,记录来源(`chat|memory|web`),用于审计与回显。
|
||||
|
||||
执行语义:
|
||||
- 工具内部以事务写库:先 upsert 任务类主体,再 upsert 任务项。
|
||||
- 复用 DAO 现有能力:`AddOrUpdateTaskClass` + `AddOrUpdateTaskClassItems`。
|
||||
- 写后执行字段合法性与完整性校验;失败时返回可修复错误,不做静默成功。
|
||||
|
||||
输出字段重点:
|
||||
- `success`: 是否写库成功。
|
||||
- `task_class_id`: 最终任务类 ID(创建时为新 ID,更新时为原 ID)。
|
||||
- `created`: `true|false`(是否新建)。
|
||||
- `validation`: 写后校验结果(`ok/issues[]`)。
|
||||
- `error/error_code`: 写库或校验失败时的稳定错误信息。
|
||||
|
||||
失败返回:
|
||||
- 关键字段缺失、字段非法、用户越权、事务失败时返回 `success=false`。
|
||||
- 校验失败时返回 `success=false` + 可修复 `issues`,由 LLM 继续 ask_user/修订。
|
||||
|
||||
---
|
||||
|
||||
## 5. P1 实施清单(逐项)
|
||||
|
||||
## 5.1 P1-A:分析工具落地(工具层)
|
||||
定义:
|
||||
- 在 `tools/schedule` 新增分析工具实现,全部只读,不改 `ScheduleState`。
|
||||
|
||||
改动动作:
|
||||
- 新增文件建议:
|
||||
- `analyze_common.go`(通用统计、分级、JSON封装)
|
||||
- `analyze_load.go`
|
||||
- `analyze_subjects.go`
|
||||
- `analyze_context.go`
|
||||
- `analyze_tolerance.go`
|
||||
- `analyze_health.go`
|
||||
|
||||
- 每个工具遵循“参数校验失败返回 `success=false` JSON 错误”口径,与 `query_available_slots` 风格一致。
|
||||
|
||||
验收标准:
|
||||
- 每个工具在 `ScheduleState` 空/小/大样本下可稳定返回合法 JSON。
|
||||
- 不产生任何状态写入副作用。
|
||||
|
||||
---
|
||||
|
||||
## 5.2 P1-B:注册表接线(工具可发现)
|
||||
定义:
|
||||
- 将新工具纳入 `ToolRegistry`,并确保被 Execute 看见。
|
||||
|
||||
改动动作:
|
||||
- 修改 [registry.go](/D:/SmartFlow-Agent/backend/newAgent/tools/registry.go)
|
||||
- `NewDefaultRegistryWithDeps` 注册 5 个分析工具。
|
||||
- 保持其为读工具(不加入 `writeTools`)。
|
||||
- 增加 P1 运行态工具可见性约束:`min_context_switch` 对 execute 模型侧默认隐藏(仅保留既有写工具链路中的 `move/swap/...`)。
|
||||
|
||||
验收标准:
|
||||
- `ToolRegistry.ToolNames()` 可见新增工具。
|
||||
- `IsWriteTool` 对新增工具全部返回 `false`。
|
||||
- P1 模式下 execute 可见写工具集合不包含 `min_context_switch`。
|
||||
|
||||
---
|
||||
|
||||
## 5.3 P1-C:Prompt 策略升级(行为约束)
|
||||
定义:
|
||||
- 让 LLM 在正确场景优先使用分析工具,且不过度主动。
|
||||
|
||||
改动动作:
|
||||
- 修改 [execute.go](/D:/SmartFlow-Agent/backend/newAgent/prompt/execute.go)
|
||||
- 增加规则:
|
||||
- `first_full/global_reopt` 模式优先 `analyze_health`。
|
||||
- `local_adjust` 模式默认旧链路(`query_target_tasks/query_available_slots/...`)。
|
||||
- 未命中全局触发条件,不要滥用全局分析。
|
||||
- 增加“先定范围再写入”规则:先用分析/读取工具锁定 `candidate_scope`,再选择写工具执行。
|
||||
- 增加“自主选目标”规则:后端不指定具体任务,LLM 在边界内自行选择目标与参数,并在后续复盘中验证是否命中 success_criteria。
|
||||
- 在 P1 提示词中禁用 `min_context_switch`(不作为候选动作)。
|
||||
|
||||
- 修改 [execute_context.go](/D:/SmartFlow-Agent/backend/newAgent/prompt/execute_context.go)
|
||||
- 为新增工具补“返回类型+最小示例”。
|
||||
|
||||
验收标准:
|
||||
- 同样输入下,首次编排与局部调整的工具选择有明显分流。
|
||||
- 不出现“局部请求强行全局体检”的高频行为。
|
||||
- 日志可还原“本轮 scope 是什么、为何选择该任务、成功判据是否达成”。
|
||||
|
||||
---
|
||||
|
||||
## 5.4 P1-D:执行模式标记(状态层,建议)
|
||||
定义:
|
||||
- 给执行链路显式模式,避免仅靠 prompt 推断。
|
||||
|
||||
改动动作(建议):
|
||||
- 修改 [common_state.go](/D:/SmartFlow-Agent/backend/newAgent/model/common_state.go)
|
||||
- 新增 `OptimizationMode string`。
|
||||
- 在 [chat.go](/D:/SmartFlow-Agent/backend/newAgent/node/chat.go) 路由处设置模式:
|
||||
- 首次编排粗排后微调 -> `first_full`
|
||||
- 局部调整请求 -> `local_adjust`
|
||||
- 明确全局重优化请求 -> `global_reopt`
|
||||
|
||||
验收标准:
|
||||
- `execute` 运行日志中可观测到模式值。
|
||||
- 恢复场景不丢模式(随 RuntimeState 快照持久化)。
|
||||
|
||||
---
|
||||
|
||||
## 5.5 P1-E:收口与质量防抖(执行层)
|
||||
定义:
|
||||
- 不改变现有阈值,只补齐可观测数据与兜底日志。
|
||||
|
||||
改动动作:
|
||||
- 使用现有收口规则:`critical=0 && warning<=1`、连续无效 3 轮收口、60 轮上限。
|
||||
- 在 `analyze_health` 返回里统一输出 `issues`,供 LLM 与日志一致引用。
|
||||
- 当 `feasibility.is_feasible=false` 时,禁止继续常规微调回路(`move/swap` 反复试探)。
|
||||
|
||||
验收标准:
|
||||
- 收口判断与 PRD 一致。
|
||||
- 日志可还原“每轮依据哪个 issue 在优化”。
|
||||
- 不可行场景下不会跑满无意义轮次。
|
||||
|
||||
---
|
||||
|
||||
## 5.6 P1-F:不可行协商分支(新增)
|
||||
定义:
|
||||
- 把“排不下”与“排不好”拆开处理;不可行时转入用户协商,而非继续磨轮次。
|
||||
|
||||
改动动作:
|
||||
- 在 [execute.go](/D:/SmartFlow-Agent/backend/newAgent/node/execute.go) 增加分支规则:
|
||||
- 若最近一次 `analyze_health` 明确 `is_feasible=false`:
|
||||
- 本轮优先 `ask_user`,给出四类选项(扩窗口/放松约束/降范围预算/接受风险收口)。
|
||||
- 若用户未调整约束且明确继续当前方案,允许“有风险收口”。
|
||||
- 若用户调整了约束,重开一轮入场判定并继续。
|
||||
|
||||
- 在 [prompt/execute.go](/D:/SmartFlow-Agent/backend/newAgent/prompt/execute.go) 增加硬约束提示:
|
||||
- 不可行时禁止继续无目标微调。
|
||||
- 不可行时必须先沟通约束变更。
|
||||
|
||||
验收标准:
|
||||
- 人工构造“明显排不下”样例时,模型会在少量轮次内进入协商,不会持续 `critical` 循环。
|
||||
- 协商后可恢复正常优化或风险收口,路径清晰可追溯。
|
||||
|
||||
---
|
||||
|
||||
## 6. P1.5 实施清单(逐项)
|
||||
|
||||
## 6.1 P1.5-A:Prompt 草案生成
|
||||
定义:
|
||||
- 提供“完整任务类草案”生成能力(聊天触发),不新增后端草案工具。
|
||||
|
||||
改动动作:
|
||||
- 修改 [chat.go](/D:/SmartFlow-Agent/backend/newAgent/prompt/chat.go) 与 [execute.go](/D:/SmartFlow-Agent/backend/newAgent/prompt/execute.go):
|
||||
- 明确“任务类草案由 LLM 直接生成”的提示词约束。
|
||||
- 明确“关键字段必须 ask_user,普通字段可静默落”的输出约束。
|
||||
- 明确“来源优先级与冲突显式化”规则。
|
||||
|
||||
验收标准:
|
||||
- 任意输入可生成完整草案结构(无新增工具调用)。
|
||||
- 关键字段缺失时会触发 ask_user,不会直接进入 `upsert_task_class`。
|
||||
|
||||
---
|
||||
|
||||
## 6.2 P1.5-B:写库工具落地(`upsert_task_class`)
|
||||
定义:
|
||||
- 新增统一任务类写库入口,承接“草案确认后落库”。
|
||||
|
||||
改动动作:
|
||||
- 新增文件建议:
|
||||
- `tools/task_class_write.go`
|
||||
- `tools/task_class_write_types.go`
|
||||
- 修改 [registry.go](/D:/SmartFlow-Agent/backend/newAgent/tools/registry.go):
|
||||
- 注册 `upsert_task_class`。
|
||||
- 加入 `writeTools`(必须 confirm)。
|
||||
- 加入 `scheduleFreeTools`(不依赖 `ScheduleState`,可在纯聊天草案场景调用)。
|
||||
- 工具内部复用 DAO 事务能力:`AddOrUpdateTaskClass` + `AddOrUpdateTaskClassItems`。
|
||||
- 写后补齐字段合法性与完整性校验,统一返回可修复 `issues`。
|
||||
|
||||
验收标准:
|
||||
- `id=0` 可创建成功,`id>0` 可更新成功,且返回稳定 `task_class_id`。
|
||||
- confirm 拒绝时不发生写入。
|
||||
- 写后校验失败时可稳定回到对话修订,不出现“写入后静默失败”。
|
||||
|
||||
---
|
||||
|
||||
## 6.3 P1.5-C:触发策略(聊天入口)
|
||||
定义:
|
||||
- 仅聊天触发,不加按钮分支。
|
||||
|
||||
改动动作:
|
||||
- 调整 [chat.go](/D:/SmartFlow-Agent/backend/newAgent/prompt/chat.go) 路由提示:
|
||||
- 识别“设计任务类/补全任务类参数/生成任务类草案”等意图。
|
||||
- 路由建议继续走 `execute`(复用现有链路),不新增节点、不新增草案工具。
|
||||
- 调整 [execute.go](/D:/SmartFlow-Agent/backend/newAgent/prompt/execute.go):
|
||||
- 明确草案阶段只读/对话,落库阶段统一调用 `upsert_task_class`。
|
||||
- 明确“关键字段未确认禁止写库”的硬约束。
|
||||
|
||||
验收标准:
|
||||
- 用户自然语言可稳定触发草案生成流程。
|
||||
- 关键字段确认后可稳定触发 `upsert_task_class` 落库。
|
||||
- 不出现“创建第二聊天区”的交互分叉。
|
||||
|
||||
---
|
||||
|
||||
## 7. 分阶段提交建议(按 PR 切)
|
||||
|
||||
### PR-1(P1 工具层)
|
||||
- 新增 5 分析工具实现 + 单元级自测(本地运行后清理临时测试文件)。
|
||||
- 不动 prompt,不动 chat 路由。
|
||||
|
||||
### PR-2(P1 策略层)
|
||||
- registry 注册 + execute prompt/示例补齐 + 可选 `OptimizationMode`。
|
||||
- 联调首次编排与局部请求两条路径。
|
||||
- 联调“不可行协商分支”(避免持续 critical 循环)。
|
||||
- 联调“后端给边界,LLM 自主选目标”的读写闭环,并验证 P1 隐藏 `min_context_switch`。
|
||||
|
||||
### PR-3(P1.5 草案能力)
|
||||
- prompt 草案生成约束 + 关键字段确认流 + `upsert_task_class` 写库工具接线 + 写后校验回传约束。
|
||||
|
||||
### PR-4(联调与收口)
|
||||
- 统一日志字段、错误返回格式、文档回填(含 PRD 对应项映射)。
|
||||
|
||||
---
|
||||
|
||||
## 8. 验收与回滚
|
||||
|
||||
### 8.1 验收检查
|
||||
- 功能验收:
|
||||
- 首次编排触发全流程分析策略。
|
||||
- 局部调整默认旧链路,不误触发全局分析。
|
||||
- 任务类草案可聊天触发,按“草案 -> 关键字段确认 -> 写入 -> 写后校验”链路闭环。
|
||||
- 任务类最终落库统一通过 `upsert_task_class`,且受 confirm 闸门保护。
|
||||
|
||||
- 质量验收:
|
||||
- 无新增死循环风险(轮次与无效轮次机制保持)。
|
||||
- 写工具确认闸门不退化(A/B/C 硬规则仍生效)。
|
||||
- 不可行场景可被识别并进入协商分支,不再无效磨轮。
|
||||
- 写入前具备 scope 证据,且目标对象由 LLM 自主选择(非后端硬编码选点)。
|
||||
|
||||
### 8.2 回滚策略
|
||||
- 工具级开关:先通过注册表控制可见性(临时下线单工具不影响主链)。
|
||||
- Prompt级回滚:保留旧提示模板版本,出现偏航可快速切回。
|
||||
- 状态字段回滚:新增字段仅追加,删除前先做兼容读取。
|
||||
|
||||
---
|
||||
|
||||
## 9. 本轮对齐清单(逐项勾选)
|
||||
- [ ] 1. 是否采用 `OptimizationMode` 显式模式字段(建议:采用)?
|
||||
- [ ] 2. P1 是否严格限定 5 个分析工具(不含 deadlines)?
|
||||
- [ ] 3. 分析工具返回包络是否冻结为 `metrics/issues/next_actions`?
|
||||
- [ ] 4. P1.5 是否确认复用 execute 链路,不新增独立 graph 节点?
|
||||
- [ ] 5. PR 拆分是否采用 `PR-1~PR-4` 顺序?
|
||||
- [ ] 6. 是否冻结“可行性判定 + 不可行协商分支”为 P1 必做项?
|
||||
- [ ] 7. 是否冻结“后端只给边界,LLM 自主选目标与参数”为执行原则?
|
||||
- [ ] 8. 是否冻结 P1 默认禁用 `min_context_switch`(不暴露给 execute 候选写工具)?
|
||||
- [ ] 9. 是否冻结“P1.5 不新增 `build_task_class_draft`,草案改为纯 prompt 生成 + 写后校验”?
|
||||
- [ ] 10. 是否冻结“P1.5 新增 `upsert_task_class` 作为唯一任务类写库入口(必须 confirm)”?
|
||||
|
||||
---
|
||||
|
||||
## 10. 备注(关键现实约束)
|
||||
- 当前 `ScheduleState` 不含单任务 `deadline_at/priority_group/urgency_threshold_at`,故不建议在 P1 实现 `analyze_deadlines`。
|
||||
- 若后续要做 `analyze_deadlines`,需先在 `conv/schedule_state.go` 映射 task 维度截止信息到工具态,再进入 P2。
|
||||
300
docs/backend/ROADMAP.md
Normal file
300
docs/backend/ROADMAP.md
Normal file
@@ -0,0 +1,300 @@
|
||||
# NewAgent 全链路改造计划
|
||||
|
||||
> 本文档面向后续 coding agent,描述当前 newAgent 的架构现状、改造计划,以及距离"会主动问用户问题、生成多个任务类、自己 ReAct 排日程的智能体"还差什么。
|
||||
|
||||
---
|
||||
|
||||
## 一、目标智能体行为
|
||||
|
||||
用户说:"帮我安排下周的复习计划"。
|
||||
|
||||
期望的完整链路:
|
||||
|
||||
```
|
||||
1. Chat 节点:LLM 主动追问
|
||||
- "这周有几门考试?"
|
||||
- "复习强度偏好?均匀分布还是集中在前几天?"
|
||||
- "有没有要排除的时段?"
|
||||
|
||||
2. Plan 节点:LLM 生成结构化计划
|
||||
- 识别意图为"批量安排任务类到日程"
|
||||
- 输出 needs_rough_build=true + task_class_ids
|
||||
- 或者:LLM 调用 create_task_class 工具创建新的任务类
|
||||
|
||||
3. Confirm 节点:展示计划,用户确认
|
||||
|
||||
4. RoughBuild 节点(新增):确定性粗排
|
||||
- 调用 SmartPlanningRawItemsMulti() 算法
|
||||
- 结果写入 ScheduleState(pending tasks 预填 suggested slots)
|
||||
|
||||
5. Execute 节点:LLM 用读写工具微调
|
||||
- 查看 get_overview,发现粗排结果
|
||||
- 用 move/swap 调整不合理的安排
|
||||
- 用 place 处理粗排未能安排的任务
|
||||
- 每次写操作经 confirm 流程
|
||||
|
||||
6. Deliver 节点:生成最终总结
|
||||
- 变更持久化到 DB
|
||||
- 向用户展示排课结果
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 二、当前架构
|
||||
|
||||
### 图结构
|
||||
|
||||
```
|
||||
Chat → Plan → Confirm → Execute(ReAct) → Deliver
|
||||
```
|
||||
|
||||
### 已实现的能力
|
||||
|
||||
| 模块 | 文件 | 状态 |
|
||||
|------|------|------|
|
||||
| 图骨架 | `node/agent_nodes.go` | 已实现,6 个节点 |
|
||||
| Chat 节点 | `node/chat.go` | 已实现,支持 confirm resume |
|
||||
| Plan 节点 | `node/plan.go` + `prompt/plan.go` | 已实现,LLM 生成结构化 PlanStep |
|
||||
| Confirm 节点 | `node/confirm.go` | 已实现,创建 PendingInteraction |
|
||||
| Execute 节点 | `node/execute.go` + `prompt/execute.go` | 已实现,ReAct + correction + confirm 流 |
|
||||
| Deliver 节点 | `node/deliver.go` | 已实现,LLM 生成总结 |
|
||||
| 10 个读写工具 | `tools/read_tools.go` + `tools/write_tools.go` | 已实现,5 读 5 写 |
|
||||
| 工具注册表 | `tools/registry.go` | 已实现 |
|
||||
| ScheduleState 加载 | `conv/schedule_provider.go` + `conv/schedule_state.go` | 已实现,从 DB 加载日程+任务类 |
|
||||
| Confirm 回路测试 | `node/execute_confirm_flow_test.go` | 7 个测试全通过 |
|
||||
| 端到端排课测试 | `node/llm_tool_orchestration_test.go` | 5 个测试全通过 |
|
||||
| JSON 协议修正 | `prompt/execute.go` | 已修复,LLM 输出严格 JSON |
|
||||
|
||||
### 已有数据流
|
||||
|
||||
```
|
||||
ScheduleProvider.LoadScheduleState(userID)
|
||||
→ ScheduleDAO.GetUserWeeklySchedule() // 现有日程
|
||||
→ TaskClassDAO.GetCompleteTaskClassesByIDs() // 任务类(含 Items)
|
||||
→ LoadScheduleState() // 合并为 ScheduleState
|
||||
- existing tasks (source="event")
|
||||
- pending tasks (source="task_item", status="pending")
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 三、缺口分析
|
||||
|
||||
### 按优先级排列
|
||||
|
||||
#### P0:粗排接入(核心能力)
|
||||
|
||||
**问题**:新 agent 没有 `SmartPlanningRawItemsMulti` 的调用路径。让 LLM 一个个 place 效率极低且全局最优性差。
|
||||
|
||||
**改造内容**:
|
||||
|
||||
1. **Plan 节点输出扩展**
|
||||
- 文件:`model/plan_contract.go`(或 PlanDecision 所在文件)
|
||||
- PlanDecision 增加 `NeedsRoughBuild bool` 和 `TaskClassIDs []int`
|
||||
- `prompt/plan.go` 引导 LLM 判断意图:
|
||||
- 用户意图为"批量安排/智能排课/把任务类排进日程" → `needs_rough_build: true`
|
||||
- 从前端 `extra` 或对话中提取 `task_class_ids`
|
||||
- 其他意图 → `needs_rough_build: false`
|
||||
|
||||
2. **新增 RoughBuild 图节点**
|
||||
- 新文件:`node/rough_build.go`
|
||||
- 不调 LLM,纯确定性逻辑:
|
||||
```
|
||||
输入:task_class_ids, userID
|
||||
步骤:
|
||||
1. 调 ScheduleService.HybridScheduleWithPlanMulti(ctx, userID, taskClassIDs)
|
||||
(内部调用 SmartPlanningRawItemsMulti 粗排)
|
||||
2. 将粗排结果写入 ScheduleState:
|
||||
- pending tasks 的 Slots 字段填入 suggested 位置
|
||||
- pending tasks 的 Status 保持 "pending"(LLM 可调整,也可以改为 "suggested" 区分)
|
||||
3. 推送状态给前端
|
||||
输出:ScheduleState 已填充粗排结果
|
||||
```
|
||||
- 路由:`needs_rough_build=true` 时 Confirm 之后走 RoughBuild,否则跳过
|
||||
|
||||
3. **图路由修改**
|
||||
- 文件:`node/agent_nodes.go`
|
||||
- Confirm 之后、Execute 之前插入条件分支:
|
||||
```go
|
||||
func (n *AgentNodes) branchAfterConfirm(st *AgentGraphState) string {
|
||||
plan := st.RuntimeState.PlanDecision
|
||||
if plan != nil && plan.NeedsRoughBuild {
|
||||
return "rough_build"
|
||||
}
|
||||
return "execute"
|
||||
}
|
||||
```
|
||||
|
||||
4. **依赖注入**
|
||||
- 文件:`model/graph_run_state.go` AgentGraphDeps
|
||||
- 新增 `RoughBuildFunc` 闭包(和旧 agent 的 `HybridScheduleWithPlanMultiFunc` 同理)
|
||||
- 文件:`service/agentsvc/agent_newagent.go`
|
||||
- 注入 `ScheduleService.HybridScheduleWithPlanMulti` 到 AgentGraphDeps
|
||||
|
||||
**参考实现**:旧 agent 的 `agent/node/schedule_plan.go` 的 `runRoughBuildNode()` 函数。
|
||||
|
||||
---
|
||||
|
||||
#### P0:持久化 task_item 放置(必须)
|
||||
|
||||
**问题**:`conv/schedule_persist.go` 的 `applyPlaceChange` 只处理 `source="event"`,当 LLM place 一个 `source="task_item"` 的 pending 任务时会报错。
|
||||
|
||||
**改造内容**:
|
||||
|
||||
- 文件:`conv/schedule_persist.go`
|
||||
- `applyPlaceChange` 增加 `source="task_item"` 分支:
|
||||
```
|
||||
if source == "task_item":
|
||||
1. 创建 ScheduleEvent(type="task", rel_id=SourceID, name=change.Name)
|
||||
2. 为每个 NewCoord 创建 Schedule 记录
|
||||
3. 如果有嵌入关系(EmbedHost 非空):
|
||||
- 找到宿主 schedule 记录
|
||||
- 设置 schedules.embedded_task_id = SourceID
|
||||
4. 更新 task_items.embedded_time(调用 TaskClassDAO.UpdateTaskClassItemEmbeddedTime)
|
||||
5. 更新 task_items.status = 2 (applied)
|
||||
```
|
||||
- `applyUnplaceChange` 也需要增加 `source="task_item"` 分支(反向清理)
|
||||
|
||||
**参考实现**:旧 agent 的 `service/task-class.go` 的 `BatchApplyPlans()` 函数(第 327-536 行)。
|
||||
|
||||
---
|
||||
|
||||
#### P1:TaskClass 约束元数据暴露给 LLM
|
||||
|
||||
**问题**:LLM 看到的 pending task 只有 name/category/duration,缺少 TaskClass 级别的调度约束。
|
||||
|
||||
**改造内容**:
|
||||
|
||||
1. **扩展 ScheduleState 结构**
|
||||
- 文件:`newAgent/tools/state.go`
|
||||
- 新增:
|
||||
```go
|
||||
type TaskClassMeta struct {
|
||||
ID int `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Strategy string `json:"strategy"` // "steady" | "rapid"
|
||||
ExcludedSlots []int `json:"excluded_slots"` // 排除的半天时段索引
|
||||
AllowFillerCourse bool `json:"allow_filler_course"` // 是否允许嵌入水课
|
||||
TotalSlots int `json:"total_slots"` // 总时间预算
|
||||
}
|
||||
```
|
||||
- `ScheduleState` 增加 `TaskClasses []TaskClassMeta`
|
||||
|
||||
2. **LoadScheduleState 填充元数据**
|
||||
- 文件:`conv/schedule_state.go`
|
||||
- 在 Step 4(处理 pending task items)同时填充 `state.TaskClasses`
|
||||
|
||||
3. **读工具输出约束信息**
|
||||
- 文件:`tools/read_tools.go`
|
||||
- `get_overview` 输出中增加任务类约束描述
|
||||
- 示例:
|
||||
```
|
||||
任务类约束:
|
||||
[学习] 策略=均匀分布, 总预算=12节, 允许嵌入水课=是, 排除时段=[3,4]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### P2:任务类 ID 从前端传入
|
||||
|
||||
**问题**:前端请求需要在 `extra` 中传递 `task_class_ids`,后端需要接收并传递到图内部。
|
||||
|
||||
**改造内容**:
|
||||
|
||||
1. **API 层**:`api/agent.go` 的请求结构增加 `Extra map[string]any`
|
||||
2. **Service 层**:`service/agentsvc/agent_newagent.go` 从 `extra` 中提取 `task_class_ids`,存入 RuntimeState 或 AgentGraphRequest
|
||||
3. **Plan 节点**:从 RuntimeState/Request 中读取 `task_class_ids`,合并 LLM 从对话中提取的 IDs
|
||||
|
||||
**参考实现**:旧 agent 的 `agent/node/schedule_plan.go` 的 `normalizeTaskClassIDs()` 函数。
|
||||
|
||||
---
|
||||
|
||||
#### P3:LLM 主动追问能力增强
|
||||
|
||||
**问题**:当前 Chat 节点主要做"接收用户消息 + confirm resume",缺少"LLM 主动收集排课需求"的能力。
|
||||
|
||||
**改造内容**:
|
||||
|
||||
- Chat 节点的 prompt 增强:
|
||||
- 引导 LLM 在信息不足时主动追问
|
||||
- 追问内容:考试科目、复习偏好、时段排除、强度偏好
|
||||
- 追问方式:通过 `ask_user` action 或直接在 speak 中提问
|
||||
- 可能需要新增 ConversationContext 的"收集到的需求"字段
|
||||
- 收集到的需求在 Plan 节点中被使用
|
||||
|
||||
---
|
||||
|
||||
#### P4:LLM 创建任务类工具(锦上添花)
|
||||
|
||||
**问题**:用户说"帮我安排复习",但系统里没有对应的 TaskClass,LLM 无法创建。
|
||||
|
||||
**改造内容**:
|
||||
|
||||
1. **新增工具**:`create_task_class`
|
||||
- 参数:`name`, `strategy`, `total_slots`, `allow_filler_course`, `items: [{content, duration}]`
|
||||
- 行为:调用 TaskClassDAO 在 DB 中创建 TaskClass + Items
|
||||
- 返回:创建结果 + 新的 task_class_id
|
||||
|
||||
2. **新增工具**:`update_task_class`(可选)
|
||||
- 修改已有任务类的参数
|
||||
|
||||
3. **ScheduleState 动态刷新**
|
||||
- 创建后需要重新加载 ScheduleState 以反映新的 pending tasks
|
||||
|
||||
---
|
||||
|
||||
## 四、改造顺序建议
|
||||
|
||||
```
|
||||
Phase 1(打通核心链路)
|
||||
├── P0: 粗排接入(RoughBuild 节点 + 图路由 + 依赖注入)
|
||||
├── P0: 持久化 task_item 放置
|
||||
└── P2: task_class_ids 从前端传入
|
||||
|
||||
Phase 2(提升排课质量)
|
||||
├── P1: TaskClass 约束元数据暴露
|
||||
└── P1: 读工具输出优化(携带约束信息)
|
||||
|
||||
Phase 3(智能化)
|
||||
├── P3: Chat 节点追问能力增强
|
||||
└── P4: create_task_class 工具
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 五、关键设计决策记录
|
||||
|
||||
1. **粗排由图节点驱动,不由 LLM 驱动**:粗排是确定性算法,浪费 LLM 调用不划算。
|
||||
2. **粗排通过 Plan 节点的 `needs_rough_build` 标签触发**:不是所有请求都需要粗排,LLM 判断意图后打标签。
|
||||
3. **粗排结果写入 ScheduleState 的 Slots 字段**:LLM 在 Execute 阶段看到的是"已粗排、可调整"的状态,用 move/swap 微调。
|
||||
4. **task_class_ids 来源**:前端 `extra` 传入为主,LLM 从对话提取为辅。
|
||||
5. **持久化用 Diff 模式**:对比 original 和 modified ScheduleState,只持久化变更部分。
|
||||
|
||||
---
|
||||
|
||||
## 六、关键文件索引
|
||||
|
||||
| 用途 | 文件 |
|
||||
|------|------|
|
||||
| 图骨架与路由 | `newAgent/node/agent_nodes.go` |
|
||||
| Chat 节点 | `newAgent/node/chat.go` |
|
||||
| Plan 节点 | `newAgent/node/plan.go` |
|
||||
| Confirm 节点 | `newAgent/node/confirm.go` |
|
||||
| Execute 节点 | `newAgent/node/execute.go` |
|
||||
| Deliver 节点 | `newAgent/node/deliver.go` |
|
||||
| Execute prompt | `newAgent/prompt/execute.go` |
|
||||
| Plan prompt | `newAgent/prompt/plan.go` |
|
||||
| 工具状态模型 | `newAgent/tools/state.go` |
|
||||
| 读工具 | `newAgent/tools/read_tools.go` |
|
||||
| 写工具 | `newAgent/tools/write_tools.go` |
|
||||
| 工具注册表 | `newAgent/tools/registry.go` |
|
||||
| 图运行态 | `newAgent/model/graph_run_state.go` |
|
||||
| 公共状态 | `newAgent/model/common_state.go` |
|
||||
| 日程状态加载 | `conv/schedule_provider.go` |
|
||||
| DB→State 转换 | `conv/schedule_state.go` |
|
||||
| Diff 算法 | `conv/schedule_state.go` (DiffScheduleState) |
|
||||
| 持久化 | `conv/schedule_persist.go` |
|
||||
| Service 集成 | `service/agentsvc/agent_newagent.go` |
|
||||
| 粗排算法(旧,复用) | `logic/smart_planning.go` |
|
||||
| 旧 agent 粗排节点(参考) | `agent/node/schedule_plan.go` |
|
||||
| 旧 agent 批量应用(参考) | `service/task-class.go` BatchApplyPlans |
|
||||
70
docs/backend/newagent-roadmap.md
Normal file
70
docs/backend/newagent-roadmap.md
Normal file
@@ -0,0 +1,70 @@
|
||||
# NewAgent 改造路线
|
||||
|
||||
## 核心架构
|
||||
|
||||
砍掉路由和多张业务 graph,换成一张通用循环图,用 eino compose 搭建。能力通过 tool 横向扩展,图本身不再变。
|
||||
|
||||
### 循环图结构
|
||||
|
||||
```
|
||||
START → Plan Loop(循环,直到 PLAN_DONE) → Confirm Plan → Execute Loop(ReAct + Reflection) → 交付 → END
|
||||
```
|
||||
|
||||
- Plan Loop:每轮后端注入上下文(当前阶段、已确定步骤、已收集信息),LLM 每次只想一步,可调感知类 tool 收集信息,输出 [PLAN_DONE] 进入下一阶段
|
||||
- Confirm:plan 完成后推给用户确认,不 ok 回 Plan 重来
|
||||
- Execute Loop:按 plan 逐步调 tool,每步完 reflection,发现 plan 有问题可自行修正
|
||||
- 交付:执行结果推给用户检查
|
||||
|
||||
### 关键设计决策
|
||||
|
||||
1. **不需要路由**:整个 agent 就是一个 tool-use 循环,LLM 自己判断聊天还是干活,上下文理解能力本身就是最好的路由器
|
||||
2. **写操作前必须 confirm**:所有会改动日程的 tool 执行前自动触发用户确认,读操作不需要
|
||||
3. **Thinking 策略**:首轮理解意图时开,后续 tool 循环轮次关掉
|
||||
4. **Graph 的角色**:从业务流程编排降级为基础设施,编排 agent loop 本身(LLM → tool → LLM 循环),业务逻辑下沉到 tools
|
||||
|
||||
## 工具设计原则
|
||||
|
||||
工具做计算,LLM 做决策。LLM 不碰原始时间数据,只看自然语言级别的信息。
|
||||
|
||||
### 感知类(让 LLM "看"时间)
|
||||
- `get_free_slots(date_range, min_duration)` — 返回空闲时段
|
||||
- `get_conflicts(proposed_event)` — 返回冲突信息
|
||||
- `get_day_summary(date)` — 某天负载概况
|
||||
- `get_task_context(task_id)` — 任务完整上下文
|
||||
|
||||
### 操作类(让 LLM "动手")
|
||||
- `create_event` / `update_event` / `delete_event` — 基础 CRUD
|
||||
- `batch_create_events(events[])` — 批量创建
|
||||
- `swap_events(event_a, event_b)` — 交换时间段
|
||||
- `reschedule_event(event_id, constraints)` — 自动找合适时段重排
|
||||
|
||||
### 分析类(让 LLM "想")
|
||||
- `estimate_workload(date_range)` — 工作量分布
|
||||
- `find_best_slot(duration, deadline, preferences)` — 给定约束算最优时段
|
||||
- `check_feasibility(task_list, deadline)` — 可行性检测
|
||||
|
||||
## 状态设计
|
||||
|
||||
State 和 Context 分离:
|
||||
|
||||
- `AgentState`:阶段标记、plan 步骤、confirm 状态、tool 调用记录、轮次计数(流程控制)
|
||||
- `ConversationContext`:消息历史、system prompt、tool schemas、注入的阶段上下文(对话管理)
|
||||
|
||||
两者通过 traceID / session 关联,数据结构分开。
|
||||
|
||||
## 落地方式
|
||||
|
||||
- `newagent/` 复制型搬迁,分层结构延续
|
||||
- 已搬迁:`llm/`(client、ark、json)、`stream/`(emitter、openai)、`shared/`(time、retry)
|
||||
- 包名统一为 `newagent` 前缀(newagentllm、newagentstream、newagentshared)
|
||||
- 新增 `tool/` 目录存放可插拔工具
|
||||
- 老 agent 保留对照,跑通再删
|
||||
|
||||
## 优先级
|
||||
|
||||
P0(先做):循环图 + 感知类工具 + 基础 CRUD + ask_user 多轮交互 + confirm 机制
|
||||
P1(后续):记忆机制、websearch、skills、分析类工具
|
||||
|
||||
## 协作模式
|
||||
|
||||
我(人类)搭函数框架 + 写清楚注释,Codex 负责填充实现。架构决策权在人类手里。
|
||||
732
docs/backend/prd文档.md
Normal file
732
docs/backend/prd文档.md
Normal file
@@ -0,0 +1,732 @@
|
||||
# SmartFlow 主动优化功能 PRD(讨论版)
|
||||
|
||||
## 0. 文档信息
|
||||
- 文档状态:讨论中(骨架版)
|
||||
- 适用范围:主动优化(对话内 execute + 对话内任务类共创)
|
||||
- 文档目的:先对齐产品方向,再指导后续实现
|
||||
- 约束说明:本 PRD 只谈产品,不谈技术实现
|
||||
|
||||
---
|
||||
|
||||
## 1. 业务背景与问题定义(已讨论 v0.1)
|
||||
### 1.1 当前用户问题
|
||||
- 用户并不总会明确表达需求,存在两类典型入口:
|
||||
- 默认入口:用户未明确偏好,只希望“尽快排好任务类”。
|
||||
- 偏好入口:用户给出较多约束与倾向(强度、时段、节奏、容错等)。
|
||||
- 现状容易把优化做成“单点最佳实践”或“一次性建议”,缺少可持续迭代与偏好对齐。
|
||||
- 因此,工具体系必须同时支持:
|
||||
- 在信息不足时,按科学界公认最佳实践给出稳健中位方案。
|
||||
- 在用户偏好明确时,优先按用户需求调参,不盲从默认最佳实践。
|
||||
|
||||
### 1.2 核心问题陈述
|
||||
- 我们要解决的问题是:
|
||||
`如何让 AI 在“科学最佳实践”和“用户个性化需求”之间做可解释、可调节、可收敛的主动优化。`
|
||||
- 该问题直接决定工具设计方向:
|
||||
- 读工具覆盖面必须足够广,能够支撑不同偏好下的判断。
|
||||
- 每个核心指标必须是“区间型”而不是“单点型”:
|
||||
- 默认站在中位(平衡值)。
|
||||
- 能向左/向右偏移,对应不同用户诉求。
|
||||
|
||||
### 1.3 本章已确定结论
|
||||
- 首发主用户策略:
|
||||
- 若用户需求不提或较弱,系统默认采用中位最佳实践快速生成。
|
||||
- 若用户需求明确且较多,系统优先满足用户需求,科学原则作为安全边界。
|
||||
- “满意方案”判定口径(本章层面):
|
||||
- 本质不是固定模板,而是“在用户诉求方向上的可接受平衡点”。
|
||||
- 默认用户采用中位平衡;偏好用户采用定向偏移平衡。
|
||||
- 自动优化容忍边界(当前已定项):
|
||||
- 轮次上限暂定 60 轮。
|
||||
- 时长与是否开启深度思考的权衡暂不在本章冻结,后续章节决策。
|
||||
|
||||
### 1.4 对后续章节的约束
|
||||
- 第 6 章(科学原则)必须给出“中位默认 + 双向偏移”的可解释规则。
|
||||
- 第 8 章(工具蓝图)必须体现“覆盖广度 + 区间刻度”的产品能力。
|
||||
- 第 11 章(指标验收)必须衡量“默认模式质量”与“偏好对齐质量”两条线。
|
||||
|
||||
---
|
||||
|
||||
## 2. 产品目标与非目标(已讨论 v0.1)
|
||||
### 2.1 产品目标定义与优先级(已定)
|
||||
- 目标 A(最高优先级):自主迭代收敛
|
||||
- 定义:AI 以“观测-调整-复盘”循环持续优化,直到达到可接受方案再收口。
|
||||
- 用户价值:减少用户逐步指挥成本,体现“主动出击”。
|
||||
- 目标 B(第二优先级):可解释且有改进证据
|
||||
- 定义:每轮调整都要给出“为何调整、调整内容、前后差异”。
|
||||
- 用户价值:可控、可信,避免“黑箱瞎调”。
|
||||
- 目标 C(第三优先级):对话内任务类共创草案
|
||||
- 定义:用户在聊天中触发后,AI 通过反问与检索产出完整任务类草案。
|
||||
- 用户价值:降低冷启动门槛,减少配置负担,避免新增第二交互区。
|
||||
- 优先级结论:`A > B > C`。
|
||||
|
||||
### 2.2 阶段目标策略(已定)
|
||||
- 首发必须保证:A 与 B 构成闭环能力。
|
||||
- 首发可落可迭代:C 以“可用版”上线,后续逐步提高草案准确率与覆盖深度。
|
||||
- 取舍原则:若资源冲突,优先保障 A;若 A 满足基本可用,再保障 B;C 按剩余资源推进。
|
||||
|
||||
### 2.3 非目标(已定)
|
||||
- 不追求一次优化即全局最优,目标是“可收敛的高质量可接受方案”。
|
||||
- 不追求首发覆盖全部学习风格与全部人群偏好。
|
||||
- 不追求在高风险场景下完全替代用户决策。
|
||||
- 不以“工具数量”作为目标,避免能力堆叠但无法形成闭环价值。
|
||||
|
||||
### 2.4 本章已确定结论
|
||||
- 我们的核心差异化能力是 A(主动迭代优化),不是一次性建议或单轮算法执行。
|
||||
- B 是 A 的信任保障,必须同步建设,不能后补。
|
||||
- C 是重要入口能力,但在首发阶段不应挤占 A/B 的闭环建设资源。
|
||||
|
||||
### 2.5 对后续章节的约束
|
||||
- 第 5 章(主动优化流程)必须完整体现 A 的循环收敛机制。
|
||||
- 第 9 章(交互要求)必须体现 B 的解释与改进证据结构。
|
||||
- 第 12 章(分期路线图)必须以 `A > B > C` 排序规划交付。
|
||||
|
||||
---
|
||||
|
||||
## 3. 用户与场景(已讨论 v1.0)
|
||||
### 3.1 目标用户分层(已形成草案)
|
||||
| 用户分层 | 典型特征 | 当前痛点 | 价值诉求 | 首发优先级 |
|
||||
|---|---|---|---|---|
|
||||
| 极速排程型 | 不想多聊,希望尽快出方案 | 参数配置成本高、上手慢 | 一键可用、少改动 | P1 |
|
||||
| 偏好驱动型 | 明确表达强度/时段/节奏偏好 | 通用最佳实践不一定贴合个人需求 | 结果沿偏好方向明显偏移、可控可解释 | P0(首发主优先) |
|
||||
| 反复调优型 | 接受多轮优化,关注持续变好 | 容易遇到来回调整、无效微调 | 稳定收敛、每轮有改进证据 | P1 |
|
||||
|
||||
### 3.2 首发核心场景清单(已形成草案)
|
||||
| 场景 | 触发方式 | 用户期望 | 成功标准 |
|
||||
|---|---|---|---|
|
||||
| 场景 S1:对话内任务类共创草案 | 用户在聊天中提出“帮我设计任务类” | 快速得到完整且可确认的任务类草案 | 用户可直接采纳或仅小幅修改后采纳 |
|
||||
| 场景 S2:对话内“帮我优化一下” | 用户在对话中发起优化请求 | AI 主动多轮调整并收口 | 至少完成 1-2 轮有效改进且最终可交付 |
|
||||
| 场景 S3:对话内“按我的偏好重排” | 用户明确给出偏好/约束 | AI 优先满足偏好,不盲从默认最佳实践 | 结果明显朝偏好方向偏移且不破坏硬约束 |
|
||||
|
||||
### 3.3 场景优先策略(已形成草案)
|
||||
- 首发优先主线:偏好驱动型(P0)。
|
||||
- 原因:该人群最能体现本功能差异化价值,即“可调节的主动优化”,而非一次性默认排程。
|
||||
- 策略要求:所有首发核心场景都必须支持“默认中位 + 偏好偏移”双模式。
|
||||
|
||||
### 3.4 暂不支持场景清单(草案)
|
||||
| 暂不支持场景 | 暂缓原因 | 后续进入条件 |
|
||||
|---|---|---|
|
||||
| 跨超长周期(如整学期/跨学期)全局最优规划 | 目标跨度过大,首发优先保证局部收敛质量 | 收敛稳定性和性能目标达标后再纳入 |
|
||||
| 多主体联合排程(多人协同/冲突协商) | 交互复杂度高,超出首发边界 | 单人场景成熟后评估 |
|
||||
| 高风险不可逆决策自动执行 | 需要更强确认链路与责任边界 | 风险治理机制完善后评估 |
|
||||
|
||||
### 3.5 本章已确定的判定阈值口径
|
||||
- S1(任务类共创草案“小幅修改”阈值):
|
||||
- 定义:关键字段修改率 <= 30% 视为“小幅修改”。
|
||||
- 用途:衡量草案可用性与采纳质量(用于产品验收,不作为用户前台提示)。
|
||||
- S2(主动优化“有效改进”最小标准):
|
||||
- 定义:至少一个核心问题域的严重度下降,视为“有效改进”。
|
||||
- 严重度层级:`critical > warning > info`。
|
||||
- 用途:判断单轮优化是否有实质收益,避免无效循环。
|
||||
- S3(偏好冲突裁决规则):
|
||||
- 定义:用户偏好优先,科学原则兜底。
|
||||
- 用途:在“通用最佳实践 vs 用户个性化需求”冲突时,给出统一裁决路径。
|
||||
|
||||
### 3.6 新增场景候选:对话内任务类共创(WebSearch 增强)
|
||||
#### 3.6.1 场景定义(已讨论结论)
|
||||
- 场景目标:由 AI 在对话中产出“完整任务类草案”,而非仅补全单个参数。
|
||||
- 触发方式:仅支持聊天触发,不新增聊天外按钮入口。
|
||||
- 原因:该能力需要多轮反问与澄清,若放在聊天外容易形成“第二对话区”,增加认知负担。
|
||||
|
||||
#### 3.6.2 信息来源优先级(已讨论结论)
|
||||
- WebSearch 负责:补充通用知识(如课程信息、学习路径共识、考试结构常识)。
|
||||
- 用户输入负责:表达个人偏好与约束(强度、时段、节奏、目标侧重)。
|
||||
- 冲突处理:用户偏好优先,通用知识仅作参考与兜底。
|
||||
|
||||
#### 3.6.3 字段确认策略(已讨论结论)
|
||||
- 关键字段:必须用户确认后落库。
|
||||
- 普通字段:允许静默落库,并在结果摘要中可追溯展示。
|
||||
|
||||
#### 3.6.4 成功标准(草案)
|
||||
- 草案采纳率(用户直接采纳完整草案的比例)。
|
||||
- 草案修改率(用户修改后采纳的比例)。
|
||||
- 后续优化收敛效率(基于该草案进入主动优化后的平均有效轮次变化)。
|
||||
|
||||
---
|
||||
|
||||
## 4. 核心体验原则(已讨论 v1.0)
|
||||
### 4.1 体验总纲(草案)
|
||||
- 原则 1:先看全局,再做局部。
|
||||
- 先识别主要矛盾,再执行局部调整,避免“盲调”。
|
||||
- 原则 2:单轮单主问题域。
|
||||
- 每轮只聚焦一个主问题域,降低震荡与来回改动。
|
||||
- 原则 3:每轮必须复盘并判定有效性。
|
||||
- 任何调整都要有“是否变好”的结论,不允许无结论进入下一轮。
|
||||
- 原则 4:达标即收口。
|
||||
- 达到可接受阈值后立即停止,避免过度优化。
|
||||
- 原则 5:偏好优先、科学兜底。
|
||||
- 用户偏好是目标方向,科学原则提供安全边界。
|
||||
- 原则 6:硬约束优先于体验优化。
|
||||
- 先保证不违约束,再追求负载/节奏/切换等体验改进。
|
||||
|
||||
### 4.2 单轮优化行为规范(草案)
|
||||
- 规范 A:本轮开始前必须声明“主问题域 + 目标变化”。
|
||||
- 规范 B:单轮仅允许一个主问题域,允许附带次问题观察但不展开动作。
|
||||
- 规范 C:同一主问题域若尚未出现有效改进,不应频繁切换到其他问题域。
|
||||
- 规范 D:若用户明确指定优化方向,优先采用用户方向作为本轮主问题域。
|
||||
|
||||
### 4.3 单轮复盘输出规范(草案)
|
||||
- 每轮都应给出三段式结果:
|
||||
- 本轮目标:本轮要改善什么。
|
||||
- 本轮改动:改了哪些关键位置。
|
||||
- 本轮结果:哪些指标或问题严重度发生了变化。
|
||||
- 单轮判定结果仅允许两类:
|
||||
- `有效改进`:至少一个核心问题域严重度下降。
|
||||
- `无效改进`:无严重度下降,需换策略或收口。
|
||||
|
||||
### 4.4 收口与停机原则(已定)
|
||||
- 正常收口条件:
|
||||
- 达到可接受方案阈值;
|
||||
- 或主要问题已降至可接受等级。
|
||||
- 防循环停机条件:
|
||||
- 连续多轮无有效改进;
|
||||
- 或达到轮次上限(当前上限 60)。
|
||||
- 强制人工确认规则(已定):
|
||||
- 只要涉及“移动类改动”,默认都需用户确认后执行。
|
||||
- 仅当用户显式开启“始终同意”时,允许自动通过确认。
|
||||
- 即使自动通过,也需在结果中保留可追溯记录。
|
||||
|
||||
### 4.5 本章已确定结论
|
||||
- Q4-1 结论:支持用户强制覆盖单轮主问题域。
|
||||
- 说明:前端已支持用户自由拖动,该能力与产品原则一致。
|
||||
- Q4-2 结论:采用“移动必确认,始终同意可自动通过”的统一规则。
|
||||
- 说明:确认链路以用户控制权优先,兼顾效率模式。
|
||||
|
||||
---
|
||||
|
||||
## 5. 主动优化产品流程(已讨论 v1.0)
|
||||
### 5.0 模式切换策略(补充,已定)
|
||||
- 首次主动排课(粗排 + 主动微调)默认启用全流程模式。
|
||||
- 后续局部调整请求默认启用局部执行模式(优先旧工具链)。
|
||||
- 仅在以下情况升级为全流程模式:
|
||||
- 用户明确授权“重新全局优化”;
|
||||
- 用户诉求明确命中指标域(如切换过多、太满、容错不足等)。
|
||||
|
||||
### 5.1 流程总览(已定)
|
||||
1. 入场判定:确定本次优化模式(默认中位 / 偏好驱动)、目标窗口、可改动范围。
|
||||
2. 首轮体检:强制先体检,再进入改动(避免盲调)。
|
||||
3. 迭代优化:按“单轮主问题域”执行改动与复盘。
|
||||
4. 收口判定:达标即收口;未达标则继续循环。
|
||||
5. 异常处理:冲突、失败、用户改目标时按规则回退或重开。
|
||||
6. 结果交付:输出改动摘要、改进证据、剩余风险与下一步建议。
|
||||
|
||||
### 5.2 轮次定义(已定)
|
||||
- “1 轮优化”定义为一次完整闭环:
|
||||
1. 选定主问题域;
|
||||
2. 生成本轮改动方案;
|
||||
3. 通过确认门禁;
|
||||
4. 执行改动;
|
||||
5. 复盘并判定有效/无效。
|
||||
- 说明:
|
||||
- 仅观察不改动,不计入优化轮。
|
||||
- “连续无效轮次”仅统计“已执行改动但未出现有效改进”的轮。
|
||||
|
||||
### 5.3 详细流程规则(已定)
|
||||
#### 5.3.1 入场判定
|
||||
- 输入:用户目标、偏好、限制、当前日程状态。
|
||||
- 输出:本次优化上下文(模式、范围、约束、初始问题池)。
|
||||
- 规则:若用户目标不明确,默认按中位最佳实践入场。
|
||||
- 规则补充:
|
||||
- 局部执行模式可跳过全流程体检,直接做最小必要校验后执行。
|
||||
- 全流程模式必须先体检再改动。
|
||||
|
||||
#### 5.3.2 首轮体检(强制)
|
||||
- 必须先完成体检再改动。
|
||||
- 体检结果至少包含:问题清单、严重度排序、建议主问题域。
|
||||
- 禁止跳过体检直接执行改动。
|
||||
|
||||
#### 5.3.3 单轮优化执行
|
||||
- 每轮必须先声明:本轮主问题域与目标变化。
|
||||
- 本轮仅允许一个主问题域,避免并发多目标拉扯。
|
||||
- 涉及移动类改动:
|
||||
- 默认需用户确认;
|
||||
- 用户开启“始终同意”后可自动通过;
|
||||
- 自动通过仍需可追溯记录。
|
||||
|
||||
#### 5.3.4 单轮复盘判定
|
||||
- 有效改进标准:至少一个核心问题域严重度下降。
|
||||
- 无效改进标准:执行改动后无严重度下降。
|
||||
- 无效轮次处置:允许换策略继续,但需计入连续无效轮次计数。
|
||||
|
||||
### 5.4 收口规则(已定)
|
||||
- 正常收口阈值:
|
||||
- `critical = 0`;
|
||||
- `warning <= 1`。
|
||||
- 防循环强制收口:
|
||||
- 连续无效轮次 >= 3;
|
||||
- 或达到总轮次上限(当前 60 轮)。
|
||||
- 收口后必须输出:已解决问题、未解决问题、建议后续动作。
|
||||
|
||||
### 5.5 用户中途改目标处理(已定)
|
||||
- 当用户在优化过程中明确变更目标/偏好时:
|
||||
- 立即重开“入场判定”;
|
||||
- 清空当前主问题域上下文;
|
||||
- 基于新目标重新体检并进入下一轮。
|
||||
- 目的:避免沿旧目标继续优化导致结果跑偏。
|
||||
|
||||
### 5.6 本章已确定结论
|
||||
- 首轮体检强制执行。
|
||||
- 可接受阈值采用 `critical=0 且 warning<=1`。
|
||||
- 连续无效 3 轮即强制收口。
|
||||
- 用户中途改目标时,必须重开入场判定。
|
||||
- 首次主动排课默认全流程;后续局部调整默认旧工具链。
|
||||
|
||||
---
|
||||
|
||||
## 6. 科学安排原则(已讨论 v1.0)
|
||||
### 6.1 原则优先级(已定)
|
||||
按“上位约束可否决下位偏好”的顺序执行:
|
||||
1. 硬约束合法性(不可冲突、不可越界、不可违规改动)
|
||||
2. 截止与时间压力(先保证不发生明显延期风险)
|
||||
3. 用户偏好方向(在上位约束允许范围内优先满足)
|
||||
4. 负载均衡(避免极端堆积与突增)
|
||||
5. 认知切换(控制高频切换与过长连续块)
|
||||
6. 容错能力(可用空窗规模,平衡稳定性与利用率)
|
||||
|
||||
### 6.2 冲突裁决规则(已定)
|
||||
| 冲突场景 | 裁决规则 | 用户可覆盖性 |
|
||||
|---|---|---|
|
||||
| 用户偏好 vs 硬约束合法性 | 硬约束优先,拒绝违规方案并给替代建议 | 不可覆盖 |
|
||||
| 用户偏好 vs 截止/时间压力红线 | 截止压力优先,默认前移高风险任务 | 可显式确认后覆盖部分策略 |
|
||||
| 用户偏好 vs 下位优化项(负载/切换/容错) | 用户偏好优先,科学原则兜底 | 可覆盖 |
|
||||
| 无明确用户偏好 | 采用中位最佳实践 | 不适用 |
|
||||
|
||||
### 6.3 原则刻度化口径(中位默认 + 双向偏移)
|
||||
| 原则维度 | 中位默认 | 左偏 | 右偏 |
|
||||
|---|---|---|---|
|
||||
| 负载强度 | 平衡推进 | 低强度(更松) | 冲刺强度(更满) |
|
||||
| 截止推进 | 均衡前移 | 早缓冲(更早完成) | 临近冲刺(更晚推进) |
|
||||
| 认知切换 | 适度切换 | 低切换(同类聚合) | 高切换(灵活穿插) |
|
||||
| 容错能力 | 平衡容错 | 高容错(多留大空窗) | 低容错(任务排得更满) |
|
||||
|
||||
### 6.4 软硬约束分层(已定)
|
||||
- 硬约束:
|
||||
- 合法性约束(冲突、越界、禁止改动范围)
|
||||
- 截止/时间压力红线
|
||||
- 软约束:
|
||||
- 负载均衡
|
||||
- 认知切换
|
||||
- 容错能力
|
||||
- 执行原则:
|
||||
- 先满足硬约束,再在软约束内做偏好优化。
|
||||
|
||||
### 6.5 本章已确定结论
|
||||
- 科学原则优先级已固定为“硬约束与截止优先,偏好次之,其余体验项随后优化”。
|
||||
- 冲突裁决已固定为“分层裁决”:不可覆盖项直接否决,可覆盖项通过显式确认处理。
|
||||
- “容错”作为用户可理解维度,已替代“空窗/缓冲”作为统一外显术语。
|
||||
|
||||
---
|
||||
|
||||
## 7. 用户需求与偏好模型(已讨论 v1.0)
|
||||
### 7.1 边界定义(已定)
|
||||
- 本章只定义“偏好消费与确认规则”,不定义“偏好采集机制”。
|
||||
- 偏好采集由 memory 系统负责:
|
||||
- 持续采集;
|
||||
- 去重注入;
|
||||
- 产品层直接消费。
|
||||
|
||||
### 7.2 偏好消费优先级(已定)
|
||||
1. 用户显式输入(最高优先级)
|
||||
2. memory 注入偏好(次优先)
|
||||
3. WebSearch 通用知识(仅补全,不可覆盖用户偏好)
|
||||
4. 无信息时采用中位默认值
|
||||
|
||||
### 7.3 必要点判定与 ask_user 规则(已定)
|
||||
- 必要点定义:缺失会导致方案不可执行或高风险误判的关键信息。
|
||||
- 必要点缺失时:必须 ask_user,不允许静默推断。
|
||||
- 当前必要点清单:
|
||||
- 时间窗(至少明确 end,start 可按策略补齐);
|
||||
- 强度方向(均匀/冲刺);
|
||||
- 容错偏好(高容错/平衡/低容错);
|
||||
- 禁排时段(若用户表达了禁忌但未结构化)。
|
||||
|
||||
### 7.4 字段分级(已定)
|
||||
#### 7.4.1 关键字段(必须确认)
|
||||
- 时间窗(start/end,截止时间统一归入 end,不单列重复字段)
|
||||
- 强度策略(均匀/冲刺)
|
||||
- 总预算(total_slots)
|
||||
- 容错偏好(高容错/平衡/低容错)
|
||||
- 禁排时段(excluded_slots)
|
||||
- 任务项清单完整性(是否齐全)
|
||||
- 任务项优先级/依赖关系(如用户提供)
|
||||
|
||||
#### 7.4.2 普通字段(可静默落)
|
||||
- 推荐时段偏好权重(上午/下午/晚间)
|
||||
- 同类任务聚合偏好(聚合/平衡/穿插)
|
||||
- 阶段里程碑拆分建议
|
||||
- 标准化知识标签与学习路径备注(命中统一标准时结构化落地;未命中仅文本备注)
|
||||
|
||||
### 7.5 口径修正(已定)
|
||||
- 不在偏好层管理“单次学习块长度”:
|
||||
- 该项属于任务类/任务项结构属性,不作为本章普通偏好字段。
|
||||
- 统一命名“时间窗”:
|
||||
- “截止时间”视为时间窗 end 的口语表达,不单列独立字段。
|
||||
|
||||
### 7.6 本章已确定结论
|
||||
- 偏好由 memory 采集,产品层只做消费与确认。
|
||||
- 必要点缺失必须 ask_user,避免静默误判。
|
||||
- 字段分级与统一命名口径已固定,可直接指导后续工具设计与交互文案。
|
||||
|
||||
---
|
||||
|
||||
## 8. 工具能力产品蓝图(已讨论 v1.0)
|
||||
### 8.1 工具分层(产品视角)
|
||||
- 事实读取层:告诉 AI“现在是什么”
|
||||
- 分析体检层:告诉 AI“问题在哪”
|
||||
- 评估复盘层:告诉 AI“这轮是否变好”
|
||||
- 执行动作层:让 AI 进行可控调整(以旧工具链为主)
|
||||
|
||||
### 8.2 混合工具策略(新增)
|
||||
- 策略 1:旧工具保留为主执行层,不做全线替换。
|
||||
- 策略 2:新分析工具作为导航层,主要用于首次主动排课与指标域重优化。
|
||||
- 策略 3:局部请求默认旧工具直达执行,避免过度主动出击。
|
||||
- 策略 4:仅在用户授权或命中指标域诉求时,升级为分析链路。
|
||||
|
||||
### 8.3 对话内能力(草案)
|
||||
| 能力 | 适用模式 | 用户价值 | AI 产出 | 风险控制 |
|
||||
|---|---|---|---|---|
|
||||
| analyze_health(总览体检) | 首次编排/明确触发全流程时默认首入口(可跳过) | 快速定位主要问题 | metrics/issues/next_actions | 防盲钻、防误判 |
|
||||
| analyze_load | 全流程模式/指标域触发 | 识别过载与波动 | 负载证据 + 动作建议 | 防局部最优 |
|
||||
| analyze_subjects | 全流程模式/指标域触发 | 识别科目节奏与预算压力 | 分布证据 + 动作建议 | 防断档 |
|
||||
| analyze_context | 全流程模式/指标域触发 | 识别切换过高与碎片化 | 切换证据 + 动作建议 | 防认知疲劳 |
|
||||
| analyze_tolerance | 全流程模式/指标域触发 | 识别容错不足风险 | 容错证据 + 动作建议 | 防计划脆弱 |
|
||||
| build_task_class_draft(WebSearch增强) | 共创模式 | 从 0 到 1 生成可用任务类草案 | 完整任务类草案 + 关键字段确认请求 | 防知识幻觉、防越权落库 |
|
||||
|
||||
### 8.4 分析工具输出结构规范(草案)
|
||||
- 分析工具统一返回三段:
|
||||
- `metrics`:测量值;
|
||||
- `issues`:问题及严重度(critical/warning/info);
|
||||
- `next_actions`:下一步建议(只建议,不自动执行)。
|
||||
- 细节级别:
|
||||
- 默认 `summary`;
|
||||
- 用户追问或需要取证时使用 `full`。
|
||||
|
||||
### 8.5 WebSearch 共创能力边界(新增)
|
||||
- 本能力定位:对话内共创,不替代主动优化主线。
|
||||
- 输出形态:完整任务类草案,不是单字段建议。
|
||||
- 决策边界:用户偏好优先于通用知识。
|
||||
- 安全边界:关键字段需确认,普通字段可静默落并可追溯。
|
||||
|
||||
### 8.6 本章已确定结论
|
||||
- `analyze_health` 仅在“首次编排”或“用户明确触发全流程”时作为默认首入口(可跳过)。
|
||||
- 分析工具默认明细级别统一为 `summary`,用户追问或需取证时切换 `full`。
|
||||
|
||||
---
|
||||
|
||||
## 9. 关键体验与交互要求(已讨论 v1.0)
|
||||
### 9.1 本章定位(已对齐)
|
||||
- 本章只定义“用户看到什么、怎么被解释、何时需要确认”。
|
||||
- 不定义算法细节、不定义工具内部实现。
|
||||
- 目标是让主动优化“有方向、可理解、不过度”。
|
||||
|
||||
### 9.2 双模式对话体验(已对齐)
|
||||
- 首次编排/明确触发全流程时:进入“体检 + 迭代优化”模式,先给全局判断,再给单轮改进。
|
||||
- 后续局部请求时:默认走旧工具的局部执行链,不擅自升级为全流程。
|
||||
- 仅在两类条件下可升级全流程:用户明确授权;用户诉求明确命中指标域(如“切换太多”“太满了”)。
|
||||
|
||||
### 9.3 单轮解释三段式(已定)
|
||||
- 观察段:本轮先说“我看到了什么问题”,并给最小证据(指标或现象)。
|
||||
- 动作段:再说“我准备怎么改、为什么这么改”,同时点明遵循了哪条科学原则与用户偏好。
|
||||
- 结果段:最后说“改完发生了什么变化”,并给下一步建议(继续微调或收口)。
|
||||
- 三段式的意义:让用户始终知道“问题-动作-结果”的闭环,避免 AI 黑箱式挪动。
|
||||
|
||||
### 9.4 解释字段最小集合(已定)
|
||||
- 字段1(必显):本轮主问题域(负载/切换/截止/容错/科目分布等)。
|
||||
- 字段2(必显):本轮改动摘要(改了哪些任务、从哪到哪、影响了哪几天)。
|
||||
- 字段3(必显):改动理由(科学原则 + 用户偏好 + 冲突裁决依据)。
|
||||
- 字段4(建议显):前后对比(至少 1 个核心指标变化)。
|
||||
- 字段5(建议显):副作用提示(例如“容错下降”“切换略增”)。
|
||||
- 字段6(建议显):下一步建议(继续某方向微调,或建议收口)。
|
||||
- 默认规则:最少展示前 3 字段;全流程场景建议展示 1-6 字段。
|
||||
|
||||
### 9.5 用户控制与确认边界(已对齐)
|
||||
- 涉及“移动类改动”默认都要确认;若用户已开启“始终同意”,可自动通过但需可追溯。
|
||||
- 用户可自由手动拖动,系统应尊重手动结果,不反向强改。
|
||||
- 用户可随时改目标;改目标后按既定规则重开入场判定。
|
||||
- AI 可主动给建议,但不能越权执行超出用户授权范围的改动。
|
||||
|
||||
### 9.6 对话内任务类共创体验(已对齐)
|
||||
- 仅聊天触发,不做聊天外按钮触发。
|
||||
- 输出形态为“完整任务类草案”,而非零散参数建议。
|
||||
- 关键字段必须确认;普通字段可静默落并保留可追溯记录。
|
||||
- 用户偏好与 Web 通用知识冲突时,用户偏好优先。
|
||||
|
||||
### 9.7 本章已确定结论
|
||||
- 默认解释风格采用“专业结论 + 通俗补充”双层表达。
|
||||
- 最小必显字段固定为 3 项:主问题域、改动摘要、改动理由。
|
||||
- 局部模式下不强制固定边界提示,是否提示由上下文按需决定。
|
||||
|
||||
---
|
||||
|
||||
## 10. 风险、边界与治理(已讨论 v1.0)
|
||||
### 10.1 风险分层(产品视角)
|
||||
- R1 收敛风险:LLM 长时间小步试探但无实质改进,造成轮次浪费。
|
||||
- R2 体验风险:指标看起来改善,但用户主观体感变差(例如更累、更碎)。
|
||||
- R3 越权风险:AI 在未充分授权下做了超出预期范围的改动。
|
||||
- R4 可信风险:解释与真实改动不一致,导致用户不信任系统。
|
||||
- R5 数据风险:关键信息缺失/冲突,导致判断前提不成立却仍继续优化。
|
||||
|
||||
### 10.2 产品边界(已对齐)
|
||||
- 边界1:全流程优化默认仅用于首次编排或用户明确触发,后续局部请求默认局部执行。
|
||||
- 边界2:涉及移动类改动默认确认;用户开启“始终同意”后可自动通过,但需保留追溯。
|
||||
- 边界3:用户手动拖动结果优先,AI 不得反向强改。
|
||||
- 边界4:用户可随时改目标;改目标后立即重开入场判定。
|
||||
- 边界5:用户偏好与通用知识冲突时,用户偏好优先。
|
||||
|
||||
### 10.3 治理机制(过程治理)
|
||||
- 入场治理:先判定是“全流程模式”还是“局部模式”;必要信息缺失必须 ask_user,不允许静默猜测。
|
||||
- 轮中治理:坚持单轮单主问题域;每轮都输出“观察-动作-结果”,并判断是否有效改进。
|
||||
- 收口治理:命中 `critical=0 且 warning<=1` 立即收口;连续无效 3 轮或达到轮次上限强制收口。
|
||||
- 出口治理:收口时必须显式说明“当前残留问题 + 可选后续动作”,避免用户误以为已全局最优。
|
||||
|
||||
### 10.4 强制确认清单(已定)
|
||||
- A类(必须确认):任何会导致任务/课程位置变化的移动类改动(已拍板规则)。
|
||||
- B类(必须确认):会改变用户明确声明偏好的改动(如偏好时段、偏好节奏)。
|
||||
- C类(必须确认):一次影响多个日期的大范围联动调整(避免“无感大改”)。
|
||||
- 说明:A/B/C 三类均为硬规则;若用户开启“始终同意”,可自动通过但须完整追溯。
|
||||
|
||||
### 10.5 “禁止 AI 改动清单”能力(已定)
|
||||
- 能力定义:用户可声明一组“不可被 AI 主动改动”的对象或范围(例如某类固定课程/某些日期)。
|
||||
- 产品意义:降低越权风险,提升高控制型用户的信任感。
|
||||
- 首发口径:支持“对话内声明即生效”的轻量禁改语义;通过现有上下文注入链路生效,本期不新增 agent 侧治理改动。
|
||||
- 后续演进:配置化、持久化禁改清单能力纳入后续阶段评估。
|
||||
|
||||
### 10.6 可追溯与回退要求(已定)
|
||||
- 每轮必须可追溯:至少记录主问题域、改动摘要、改动理由、影响范围、确认来源。
|
||||
- 对“已执行改动”应支持最小粒度回退能力,避免用户对试错型优化产生风险焦虑。
|
||||
- 回退后应触发一次简版复盘,避免回退导致隐性冲突未被感知。
|
||||
- 首发最低要求:至少支持“回退最近一轮已执行改动”;多版本日程管理(多轮历史回退)纳入 P2。
|
||||
|
||||
### 10.7 本章已确定结论
|
||||
- 强制确认范围升级为 A/B/C 三类全部硬规则。
|
||||
- 首发纳入“禁止 AI 改动清单(对话内轻量版)”。
|
||||
- 回退能力首发最低要求为“回退最近一轮”,多版本管理纳入 P2。
|
||||
|
||||
---
|
||||
|
||||
## 11. 目标指标与验收标准(已讨论 v1.0)
|
||||
### 11.1 指标设计原则(已对齐)
|
||||
- 原则1:指标必须服务于“首次编排全流程”主场景,不用局部请求噪声稀释判断。
|
||||
- 原则2:指标必须同时覆盖“结果好不好、过程稳不稳、体验可不可信”三层。
|
||||
- 原则3:指标必须可落地采集,避免依赖大量主观人工打分。
|
||||
|
||||
### 11.2 首发核心指标(已定)
|
||||
| 指标层级 | 指标名 | 指标定义(产品口径) | 首发目标 |
|
||||
|---|---|---|---|
|
||||
| 结果指标 | 首次编排可接受收口率 | 首次编排全流程中,满足 `critical=0 且 warning<=1` 并进入收口的会话占比 | >= 70% |
|
||||
| 过程指标 | 有效优化轮次占比 | 全流程会话内,“有效轮次”占总轮次比例 | >= 50% |
|
||||
| 质量指标 | 无效回摆率 | 近两轮内被反向撤回的改动占全部改动比例(衡量“折返跑”) | <= 15% |
|
||||
|
||||
### 11.3 关键口径定义(已定)
|
||||
- 有效优化轮次:至少满足“一个核心问题域严重度下降”,且不引入新的 `critical` 问题。
|
||||
- 可接受收口:达到既定收口阈值(`critical=0 且 warning<=1`)并完成收口说明。
|
||||
- 无效回摆:同一任务/课程在短窗口内出现“改过去又改回来”的反向变更。
|
||||
|
||||
### 11.4 辅助观测指标(不作为首发硬门槛)
|
||||
- 平均收口轮次:成功收口会话平均用了多少轮(用于评估效率,不单独卡上线)。
|
||||
- 强制确认后撤销率:已确认改动后被用户撤销的比例(用于识别解释质量问题)。
|
||||
- 对话内追问率:用户对“为什么这么改”继续追问的比例(用于评估解释清晰度)。
|
||||
|
||||
### 11.5 验收规则(已定)
|
||||
- 验收窗口:按自然周滚动观测,至少连续 2 个观察窗口达标再判定“阶段通过”。
|
||||
- 达标判定:第 11.2 的 3 个核心指标同时达标。
|
||||
- 未达标处理:按指标归因回到对应章节优化(流程、工具、解释、确认边界),不允许只调阈值“做数字”。
|
||||
|
||||
### 11.6 本章已确定结论
|
||||
- 首发核心指标冻结为:可接受收口率 + 有效优化轮次占比 + 无效回摆率。
|
||||
- “有效优化轮次”口径冻结为:至少一个问题域下降,且不新增 `critical`。
|
||||
- 首发目标值冻结为:`>=70% / >=50% / <=15%`。
|
||||
|
||||
---
|
||||
|
||||
## 12. 分期路线图(已讨论 v1.0)
|
||||
### 12.1 分期原则(执行导向)
|
||||
- 原则1:先闭环再扩面。先把“首次编排可收敛”做扎实,再扩展高级能力。
|
||||
- 原则2:每期都有“明确不做”,避免执行期目标漂移。
|
||||
- 原则3:每期必须有可量化出场标准,未达标不进入下一期主目标。
|
||||
|
||||
### 12.2 分期总览(已定)
|
||||
| 阶段 | 核心目标 | 必做交付范围(产品) | 明确不做(冻结范围) | 出场标准(产品) |
|
||||
|---|---|---|---|---|
|
||||
| Phase 1 | 建立首次编排的主动优化闭环 | 首次编排默认全流程;后续局部默认旧工具;6个分析工具口径落地;A/B/C三类确认规则;最近一轮回退;第11章三核心指标可观测 | 不做多版本日程管理;不做配置化禁改清单;不扩展到聊天外触发 | 连续2个观察窗口达到第11章目标值(70%/50%/15%) |
|
||||
| Phase 1.5 | 建立对话内任务类共创可用版 | 聊天触发的完整任务类草案;关键字段确认+普通字段静默落;用户偏好优先于Web通识 | 不做按钮触发;不做全自动无确认落库;不做课程库平台化治理 | 任务类草案一次可用率达到预设阈值(阈值在阶段启动前冻结) |
|
||||
| Phase 2 | 强化个性化和治理能力 | 配置化禁改清单;多版本日程管理(含多轮回退);解释与确认策略按用户类型分层 | 不做跨终端复杂编排协同;不做完全自治无人值守优化 | 在保持Phase 1核心指标不退化前提下,撤销率与追问率下降 |
|
||||
| Phase 3 | 平台化与长期稳定性 | 能力模块化复用;跨场景复用统一口径;长期策略调优与治理看板 | 不新增未经验证的大跨度能力域 | 核心指标长期稳定且新增能力不破坏既有闭环 |
|
||||
|
||||
### 12.3 Phase 1 最小可用闭环(MVP)定义(已定)
|
||||
- 入口:仅“首次编排”自动进入全流程,或用户明确触发全流程。
|
||||
- 执行:按既定单轮机制运行(观察-动作-结果),并遵守A/B/C确认规则。
|
||||
- 收口:按既定阈值收口(`critical=0 且 warning<=1`;或触发强制收口)。
|
||||
- 保障:支持最近一轮回退、保留可追溯记录、支持对话内轻量禁改。
|
||||
- 验收:以第11章三核心指标作为唯一阶段通过标准。
|
||||
|
||||
### 12.4 跨期依赖关系(已定)
|
||||
- Phase 1 是所有后续阶段前置,未通过则不进入 Phase 2 的主交付。
|
||||
- Phase 1.5 可与 Phase 1 后段并行推进,但不得影响 Phase 1 指标达标。
|
||||
- Phase 2 的多版本管理与配置化禁改,依赖 Phase 1 的追溯数据结构稳定。
|
||||
|
||||
### 12.5 本章已确定结论
|
||||
- Phase 1 出场标准固定为:第11章三核心指标连续 2 个窗口达标。
|
||||
- Phase 1.5 与 Phase 1 时序固定为:允许后半程并行推进,前提是不影响 Phase 1 指标达标。
|
||||
- Phase 2 主目标冻结为:配置化禁改清单 + 多版本日程管理。
|
||||
|
||||
### 12.6 当前执行优先级(新增)
|
||||
- 当前版本优先目标为“先跑通 Phase 1 ~ Phase 1.5”。
|
||||
- Phase 2 / Phase 3 暂缓,待前两阶段稳定后再回到路线图继续推进。
|
||||
|
||||
---
|
||||
|
||||
## 13. 待决策清单(滚动更新)
|
||||
| 编号 | 议题 | 决策选项 | 当前状态 | 负责人 |
|
||||
|---|---|---|---|---|
|
||||
| D-001 | 对话内主动优化目标优先级 | A>B>C / A=C>B / C>A>B | 已确定(A>B>C) | 产品 |
|
||||
| D-002 | WebSearch 任务类设计触发形态 | 聊天触发 / 聊天外按钮触发 | 已确定(聊天触发) | 产品 |
|
||||
| D-003 | WebSearch 与用户偏好冲突策略 | 通用知识优先 / 用户偏好优先 | 已确定(用户偏好优先) | 产品 |
|
||||
| D-004 | 任务类草案落库确认策略 | 全字段确认 / 关键字段确认+普通字段静默落 | 已确定(后者) | 产品 |
|
||||
| D-005 | 任务类草案“小幅修改”阈值 | 20% / 30% / 40% | 已确定(30%) | 产品 |
|
||||
| D-006 | 主动优化“有效改进”最小标准 | 严重度下降 / 分数提升 / 二者同时满足 | 已确定(至少一个问题域严重度下降) | 产品 |
|
||||
| D-007 | 用户是否可强制覆盖单轮主问题域 | 支持 / 不支持 / 有条件支持 | 已确定(支持) | 产品 |
|
||||
| D-008 | 强制人工确认触发条件 | 精简2类 / 标准3类 / 扩展4类+ | 已确定(涉及移动默认确认;始终同意可自动通过) | 产品 |
|
||||
| D-009 | 连续无效轮次强制收口阈值 | 2 / 3 / 4 | 已确定(3) | 产品 |
|
||||
| D-010 | 可接受方案阈值 | critical=0且warning<=0/1/2 | 已确定(critical=0 且 warning<=1) | 产品 |
|
||||
| D-011 | 用户中途改目标处理策略 | 延续当前轮 / 下轮生效 / 立即重开入场判定 | 已确定(立即重开入场判定) | 产品 |
|
||||
| D-012 | 科学原则优先级 | 多种排序方案 | 已确定(硬约束 > 截止压力 > 用户偏好 > 负载 > 切换 > 容错) | 产品 |
|
||||
| D-013 | 原则冲突裁决口径 | 用户优先 / 科学优先 / 分层裁决 | 已确定(分层裁决) | 产品 |
|
||||
| D-014 | 偏好模型边界 | 产品层负责采集+消费 / 仅消费不采集 | 已确定(仅消费不采集) | 产品 |
|
||||
| D-015 | 必要点缺失处理 | 静默推断 / ask_user / 混合策略 | 已确定(必要点缺失必须 ask_user) | 产品 |
|
||||
| D-016 | 后续局部请求默认模式 | 全流程优先 / 局部执行优先 | 已确定(局部执行优先) | 产品 |
|
||||
| D-017 | 旧工具与新工具关系 | 全替换 / 并行混合 | 已确定(并行混合,旧工具主执行) | 产品 |
|
||||
| D-018 | `analyze_health` 默认入口触发条件 | 全程默认 / 首次与明确触发默认 | 已确定(首次与明确触发默认) | 产品 |
|
||||
| D-019 | 分析工具默认明细级别 | summary / full | 已确定(summary) | 产品 |
|
||||
| D-020 | 第九章默认解释风格 | 纯专业 / 纯通俗 / 专业结论+通俗补充 | 已确定(专业结论+通俗补充) | 产品 |
|
||||
| D-021 | 第九章最小必显字段 | 2项 / 3项 / 4项+ | 已确定(3项) | 产品 |
|
||||
| D-022 | 局部模式是否固定边界提示 | 固定提示 / 按需提示 | 已确定(按需提示) | 产品 |
|
||||
| D-023 | 第十章强制确认范围 | 仅A类(移动类)硬规则 / A+B类硬规则 / A+B+C类硬规则 | 已确定(A+B+C类硬规则) | 产品 |
|
||||
| D-024 | 首发是否支持禁改清单 | 不支持 / 支持对话内轻量版 / 直接支持配置化 | 已确定(支持对话内轻量版) | 产品 |
|
||||
| D-025 | 回退能力最低要求 | 不要求 / 回退最近一轮 / 多轮可选回退 | 已确定(回退最近一轮;多版本管理纳入P2) | 产品 |
|
||||
| D-026 | 第十一章首发核心指标组合 | 多种组合方案 | 已确定(收口率+有效轮次占比+无效回摆率) | 产品 |
|
||||
| D-027 | “有效优化轮次”口径 | 仅严重度下降 / 严重度下降且不新增critical / 复合打分 | 已确定(严重度下降且不新增critical) | 产品 |
|
||||
| D-028 | 第十一章首发目标值 | 激进/中性/保守三档 | 已确定(70% / 50% / 15%) | 产品 |
|
||||
| D-029 | Phase 1 出场标准 | 三核心指标连续1/2/3窗口达标 | 已确定(连续2窗口) | 产品 |
|
||||
| D-030 | Phase 1.5 与 Phase 1 时序 | 串行 / 后半程并行 / 完全并行 | 已确定(后半程并行) | 产品 |
|
||||
| D-031 | Phase 2 主目标冻结范围 | 多方案 | 已确定(配置化禁改+多版本管理) | 产品 |
|
||||
| D-032 | 当前版本执行优先级 | 全路线并推 / 先P1~P1.5后续暂缓 | 已确定(先P1~P1.5后续暂缓) | 产品 |
|
||||
|
||||
---
|
||||
|
||||
## 14. 章节讨论记录(按“讨论一章、定一章”推进)
|
||||
### 记录模板
|
||||
- 讨论章节:
|
||||
- 结论:
|
||||
- 未决问题:
|
||||
- 下一步动作:
|
||||
- 更新时间:
|
||||
|
||||
### 已讨论记录
|
||||
- 讨论章节:第 1 章 业务背景与问题定义
|
||||
- 结论:采用“双模式策略”(默认中位最佳实践 + 偏好优先偏移);读工具按“广覆盖+区间指标”设计;自动优化轮次上限暂定 60。
|
||||
- 未决问题:时长目标与是否默认开启深度思考的策略未冻结。
|
||||
- 下一步动作:进入第 2 章,冻结“满意方案”与目标优先级定义。
|
||||
- 更新时间:2026-04-24
|
||||
- 讨论章节:第 2 章 产品目标与非目标
|
||||
- 结论:目标优先级确定为 A(自主迭代收敛)> B(可解释与改进证据)> C(对话内任务类共创草案);首发先保 A+B 闭环,C 走可用版。
|
||||
- 未决问题:C 可用版的覆盖范围与补全字段边界待在第 8 章细化。
|
||||
- 下一步动作:进入第 3 章,明确首发用户分层与高频场景清单。
|
||||
- 更新时间:2026-04-24
|
||||
- 讨论章节:第 3 章补充议题 WebSearch 任务类共创
|
||||
- 结论:定位为“对话内触发、产出完整任务类草案”的增强能力;知识来源为 WebSearch 通用信息 + 用户偏好,冲突时用户优先;字段按关键/普通分级确认。
|
||||
- 未决问题:关键字段名单与普通字段名单待在后续章节细化。
|
||||
- 下一步动作:在第 8 章与第 12 章细化能力边界与分期。
|
||||
- 更新时间:2026-04-24
|
||||
- 讨论章节:第 3 章阈值口径补充(S1/S2)
|
||||
- 结论:S1 采用“关键字段修改率<=30%”作为小幅修改阈值;S2 采用“至少一个核心问题域严重度下降”作为有效改进最小标准。
|
||||
- 未决问题:关键字段清单与核心问题域枚举待后续章节细化。
|
||||
- 下一步动作:推进第 4 章核心体验原则,固化“单轮单问题域 + 复盘判定”。
|
||||
- 更新时间:2026-04-24
|
||||
- 讨论章节:第 3 章 用户与场景(v1.0)
|
||||
- 结论:用户分层、首发场景、场景优先级、暂不支持边界、S1/S2/S3 判定口径均已形成可冻结版本。
|
||||
- 未决问题:无(本章内容进入后续引用阶段)。
|
||||
- 下一步动作:推进第 4 章,明确“单轮策略、复盘规范、停机确认”的执行口径。
|
||||
- 更新时间:2026-04-24
|
||||
- 讨论章节:第 4 章 核心体验原则(v0.1 草案)
|
||||
- 结论:已形成“总纲-单轮规范-复盘规范-停机原则”的完整草案结构。
|
||||
- 未决问题:D-007(用户强制覆盖策略)与 D-008(强制确认触发条件)待拍板。
|
||||
- 下一步动作:根据 D-007/D-008 决策冻结第 4 章。
|
||||
- 更新时间:2026-04-24
|
||||
- 讨论章节:第 4 章 核心体验原则(v1.0)
|
||||
- 结论:支持用户强制覆盖单轮主问题域;涉及移动类改动默认确认,用户开启“始终同意”后可自动通过并保留追溯记录。
|
||||
- 未决问题:无(本章已冻结)。
|
||||
- 下一步动作:进入第 5 章,细化主动优化流程与收口判定口径。
|
||||
- 更新时间:2026-04-24
|
||||
- 讨论章节:第 5 章 主动优化产品流程(v1.0)
|
||||
- 结论:明确了“轮次定义、首轮强制体检、单轮执行闭环、连续无效3轮收口、critical=0且warning<=1收口、用户改目标即重开入场判定”。
|
||||
- 未决问题:无(本章已冻结)。
|
||||
- 下一步动作:进入第 6 章,细化科学安排原则与冲突优先级口径。
|
||||
- 更新时间:2026-04-24
|
||||
- 讨论章节:第 6 章 科学安排原则(v1.0)
|
||||
- 结论:优先级确定为“硬约束 > 截止压力 > 用户偏好 > 负载 > 切换 > 容错”;冲突裁决采用分层规则;“容错”作为统一用户解释术语。
|
||||
- 未决问题:无(本章已冻结)。
|
||||
- 下一步动作:进入第 7 章,细化偏好模型与关键字段清单。
|
||||
- 更新时间:2026-04-24
|
||||
- 讨论章节:第 7 章 用户需求与偏好模型(v1.0)
|
||||
- 结论:偏好采集由 memory 负责,产品层仅消费;必要点缺失必须 ask_user;关键/普通字段分级与“时间窗”统一口径已确定。
|
||||
- 未决问题:无(本章已冻结)。
|
||||
- 下一步动作:进入第 8 章,细化工具能力蓝图与工具边界。
|
||||
- 更新时间:2026-04-24
|
||||
- 讨论章节:第 8 章补充议题(首次全流程 vs 后续局部执行)
|
||||
- 结论:首次主动排课默认全流程;后续局部请求默认旧工具链;仅在授权或命中指标域诉求时升级分析链路。
|
||||
- 未决问题:`analyze_health` 是否固定为默认首入口(可跳过)仍待拍板。
|
||||
- 下一步动作:继续冻结第 8 章细项后推进第 9 章。
|
||||
- 更新时间:2026-04-24
|
||||
- 讨论章节:第 8 章 工具能力产品蓝图(v1.0)
|
||||
- 结论:`analyze_health` 仅在首次编排或明确触发全流程时默认首入口;分析工具默认 `summary`,按需切换 `full`。
|
||||
- 未决问题:无(本章已冻结)。
|
||||
- 下一步动作:进入第 9 章,细化对话内体验文案与解释字段规范。
|
||||
- 更新时间:2026-04-24
|
||||
- 讨论章节:第 9 章 关键体验与交互要求(v0.1 草案)
|
||||
- 结论:已形成“双模式体验 + 单轮三段式解释 + 最小解释字段 + 用户控制边界 + 共创体验”的完整草案。
|
||||
- 未决问题:D-020(默认解释风格)、D-021(最小必显字段数量)、D-022(局部模式固定边界提示)待拍板。
|
||||
- 下一步动作:完成 D-020~D-022 拍板后冻结第 9 章,进入第 10 章风险与治理。
|
||||
- 更新时间:2026-04-24
|
||||
- 讨论章节:第 9 章 关键体验与交互要求(v1.0)
|
||||
- 结论:解释风格定为“专业结论+通俗补充”;最小必显字段固定 3 项;局部模式边界提示改为按需提示;第 9 章冻结。
|
||||
- 未决问题:无(本章已冻结)。
|
||||
- 下一步动作:进入第 10 章,讨论风险、边界与治理策略。
|
||||
- 更新时间:2026-04-24
|
||||
- 讨论章节:第 10 章 风险、边界与治理(v0.1 草案)
|
||||
- 结论:已形成“风险分层 + 过程治理 + 强制确认分级 + 禁改清单 + 回退追溯”的完整草案结构。
|
||||
- 未决问题:D-023(强制确认范围)、D-024(禁改清单首发形态)、D-025(回退能力最低要求)待拍板。
|
||||
- 下一步动作:完成 D-023~D-025 拍板后冻结第 10 章,进入第 11 章指标与验收。
|
||||
- 更新时间:2026-04-24
|
||||
- 讨论章节:第 10 章 风险、边界与治理(v1.0)
|
||||
- 结论:强制确认范围定为 A/B/C 全硬规则;首发支持对话内轻量禁改清单;回退最低要求定为“最近一轮”,多版本管理纳入 P2;第 10 章冻结。
|
||||
- 未决问题:无(本章已冻结)。
|
||||
- 下一步动作:进入第 11 章,讨论目标指标与验收标准。
|
||||
- 更新时间:2026-04-24
|
||||
- 讨论章节:第 11 章 目标指标与验收标准(v0.1 草案)
|
||||
- 结论:已形成“首发三核心指标 + 关键口径定义 + 验收窗口规则”的完整草案结构。
|
||||
- 未决问题:D-026(核心指标组合)、D-027(有效轮次口径)、D-028(首发目标值)待拍板。
|
||||
- 下一步动作:完成 D-026~D-028 拍板后冻结第 11 章,进入第 12 章分期路线图。
|
||||
- 更新时间:2026-04-24
|
||||
- 讨论章节:第 11 章 目标指标与验收标准(v1.0)
|
||||
- 结论:首发核心指标冻结为“收口率+有效轮次占比+无效回摆率”;有效轮次口径冻结为“问题域下降且不新增critical”;目标值冻结为“70% / 50% / 15%”;第 11 章冻结。
|
||||
- 未决问题:无(本章已冻结)。
|
||||
- 下一步动作:进入第 12 章,讨论分期路线图与每期冻结范围。
|
||||
- 更新时间:2026-04-24
|
||||
- 讨论章节:第 12 章 分期路线图(v0.1 草案)
|
||||
- 结论:已形成“分期总览 + 每期明确不做 + 出场标准 + 跨期依赖”的执行导向草案。
|
||||
- 未决问题:D-029(Phase 1出场标准窗口数)、D-030(Phase 1.5与Phase 1时序)、D-031(Phase 2主目标冻结范围)待拍板。
|
||||
- 下一步动作:完成 D-029~D-031 拍板后冻结第 12 章。
|
||||
- 更新时间:2026-04-24
|
||||
- 讨论章节:第 12 章 分期路线图(v1.0)
|
||||
- 结论:Phase 1 出场标准定为连续2窗口达标;Phase 1.5 采用后半程并行;Phase 2 主目标冻结为“配置化禁改+多版本管理”;当前执行优先级定为先跑通 P1~P1.5、后续阶段暂缓;第 12 章冻结。
|
||||
- 未决问题:无(本章已冻结)。
|
||||
- 下一步动作:进入收尾阶段,统一检查决策表与章节状态一致性。
|
||||
- 更新时间:2026-04-24
|
||||
|
||||
---
|
||||
|
||||
## 15. 术语表(持续补充)
|
||||
| 术语 | 业务定义 |
|
||||
|---|---|
|
||||
| 主动优化 | AI 连续观测-调整-复盘-收口的优化过程 |
|
||||
| 收口 | 达到阈值后停止迭代并输出最终方案 |
|
||||
| 主问题域 | 单轮优化聚焦的首要问题类型 |
|
||||
547
docs/backend/主动优化整套改造计划.md
Normal file
547
docs/backend/主动优化整套改造计划.md
Normal file
@@ -0,0 +1,547 @@
|
||||
# 主动优化整套改造计划
|
||||
|
||||
## 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. 主动优化完成后的结果,解释口径更像“为什么这样学更顺”,而不是“哪些数字变好看了”。
|
||||
523
docs/backend/主动优化顺序约束拆分执行计划.md
Normal file
523
docs/backend/主动优化顺序约束拆分执行计划.md
Normal file
@@ -0,0 +1,523 @@
|
||||
# 主动优化顺序约束拆分执行计划
|
||||
|
||||
## 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. `SourceID`(`task_item.id`)
|
||||
|
||||
但没有:
|
||||
|
||||
1. 该 `task_item` 在所属任务类里的 `order`
|
||||
|
||||
这意味着后端知道“它属于哪门课”,但不知道“它是这门课里的第几个任务”。
|
||||
|
||||
本轮需要补:
|
||||
|
||||
1. 在 `schedule.ScheduleTask` 增加类似 `TaskOrder` 的运行态字段。
|
||||
2. 在 `conv/schedule_state.go` 从 `model.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 B:`slot_incompatible` 兼容性问题
|
||||
|
||||
现象:
|
||||
|
||||
1. 日志里出现 `expected_duration=1 slot_duration=2`
|
||||
|
||||
判断:
|
||||
|
||||
1. 这是旧 `order_guard` 复原链上的单位不一致问题。
|
||||
2. 该问题不值得单独继续修补。
|
||||
|
||||
修法:
|
||||
|
||||
1. 旧复原链退役后,这条 bug 自然消失。
|
||||
2. 本轮只保留一个动作:确认写工具本身的时长计算口径仍正确。
|
||||
|
||||
## 7.3 bug C:prompt 仍把模型往“全局不乱序”上引
|
||||
|
||||
现象:
|
||||
|
||||
1. `execute_context` 里仍写着默认保持 suggested 相对顺序。
|
||||
|
||||
修法:
|
||||
|
||||
1. 改成“默认保持同任务类内部顺序”。
|
||||
|
||||
## 7.4 bug D:复合工具可能绕过新规则
|
||||
|
||||
现象:
|
||||
|
||||
1. `spread_even` 当前只校验冲突,不校验同类前后边界。
|
||||
|
||||
修法:
|
||||
|
||||
1. 接入统一局部顺序约束层。
|
||||
|
||||
## 7.5 bug E:active 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.go` 与 `execute_tool_summary.go`
|
||||
|
||||
这个范围已经足够让主动优化链路从“旧哲学打架”切到“新哲学能跑”。
|
||||
|
||||
---
|
||||
|
||||
## 11. 预期收益
|
||||
|
||||
做完之后,预期表现会变成:
|
||||
|
||||
1. LLM 会更敢做真实优化,因为它不再被全局顺序锁死。
|
||||
2. 后端会在写工具层直接给出“能不能这么挪”的明确反馈。
|
||||
3. 同一门课的学习推进顺序能被稳定锁住。
|
||||
4. 不同门课之间仍有足够空间做均衡、分散、减压。
|
||||
5. 前端不会再收到旧 `order_guard` 带来的迷惑状态。
|
||||
6. 后续如果继续加主动优化策略,也有更干净的承载位置,不必继续往 `execute.go` 里堆。
|
||||
|
||||
---
|
||||
|
||||
## 12. 本文档对应的实施结论
|
||||
|
||||
本轮建议按以下原则执行:
|
||||
|
||||
1. 删除“全局 suggested 顺序守卫”思路。
|
||||
2. 改为“同任务类内部顺序约束前置到写工具层”。
|
||||
3. 允许跨任务类交错优化。
|
||||
4. 顺手清理旧 guard 带来的用户可见噪音与兼容性问题。
|
||||
5. 同步拆分 execute 相关职责文件,避免继续堆史山。
|
||||
|
||||
199
docs/backend/智能排程四步走实施方案.md
Normal file
199
docs/backend/智能排程四步走实施方案.md
Normal file
@@ -0,0 +1,199 @@
|
||||
# 智能排程四步走实施方案(实施稿)
|
||||
|
||||
## 1. 文档目标
|
||||
- 目标 1:给出 SmartFlow 智能排程 Agent 的分阶段落地路径,确保功能可持续上线,而不是一次性大改。
|
||||
- 目标 2:在保证项目推进速度的前提下,提升系统“可解释性、可回滚性、可复用性”,同时增强项目叙事深度。
|
||||
- 目标 3:明确每一阶段的输入输出、复用点、验收标准、风险与回滚策略。
|
||||
|
||||
## 2. 当前项目可复用资产盘点
|
||||
- 已有粗排能力:`ScheduleService.SmartPlanning` + `logic.SmartPlanningMainLogic`,可直接作为“候选方案生成器”。
|
||||
- 已有强校验+事务落库能力:`TaskClassService.BatchApplyPlans`,可直接作为“最终落地执行器”。
|
||||
- 已有单条插入与冲突检查能力:`TaskClassService.AddTaskClassItemIntoSchedule`,可作为局部修补路径。
|
||||
- 已有 Agent 分流和 Graph 基建:`route` / `quicknote` / `taskquery`,可以按同样范式扩展 `scheduleplan`。
|
||||
- 已有流式输出链路:可继续使用阶段推送 + OpenAI 兼容流,不破坏现有客户端协议。
|
||||
|
||||
## 3. 总体架构(目标态)
|
||||
- 分层思路:`路由层(识别意图) -> 规划层(生成/调整排程计划) -> 执行层(校验+落库) -> 反馈层(解释结果与下一步建议)`。
|
||||
- 核心原则:
|
||||
- 原则 1:模型负责“提议”,后端负责“裁决”(硬校验)。
|
||||
- 原则 2:所有写库动作都走既有服务层,不在 Agent 内直接拼 SQL。
|
||||
- 原则 3:每个阶段都可独立开关,支持灰度和快速回滚。
|
||||
|
||||
---
|
||||
|
||||
## 4. 四步走实施计划
|
||||
|
||||
## 阶段 1:基于现成任务类的粗排 + ReAct 调优 + 连续对话微调
|
||||
|
||||
### 4.1 阶段目标
|
||||
- 用户已存在任务类时,支持一轮“粗排 -> 校验 -> 自动修补重试 -> 最终落库”。
|
||||
- 支持连续对话微调(例如“我早八不想学习”)并在已有方案基础上局部调整。
|
||||
|
||||
### 4.2 关键实现
|
||||
- 新增路由动作:`schedule_plan_create`、`schedule_plan_adjust`。
|
||||
- 新增 `backend/agent/scheduleplan` 包,建议结构:
|
||||
- `graph.go`:只做节点连线与分支。
|
||||
- `state.go`:保存计划状态、重试计数、错误上下文、版本号。
|
||||
- `tool.go`:封装服务调用工具(粗排、批量应用、局部修补)。
|
||||
- `nodes.go`:节点逻辑(plan/materialize/apply/reflect/finalize)。
|
||||
- `prompt.go`:统一管理系统提示词和结构化输出约束。
|
||||
- 执行链路建议:
|
||||
- `plan`:产出结构化“排程意图与约束”。
|
||||
- `preview`:调用粗排服务生成候选。
|
||||
- `materialize`:把候选转换为 `BatchApplyPlans` 可落库结构。
|
||||
- `apply`:调用 `BatchApplyPlans`。
|
||||
- `reflect`:若失败,把后端错误喂给模型生成修补方案,最多重试 2 次。
|
||||
- `finalize`:输出结果摘要并持久化对话。
|
||||
|
||||
### 4.3 连续对话微调机制
|
||||
- 设计 `PlanSession + PlanVersion`:
|
||||
- `PlanSession`:同一轮长期规划会话 ID。
|
||||
- `PlanVersion`:每次调整生成新版本,支持回滚。
|
||||
- 微调原则:
|
||||
- 用户约束变更(如不学早八)只触发受影响槽位重排,不重建全量任务类。
|
||||
- 保留“上版方案 + 本次 diff”,方便解释“为什么这样改”。
|
||||
|
||||
### 4.4 验收标准
|
||||
- 指标 1:排程请求成功率 >= 95%(含自动重试后)。
|
||||
- 指标 2:平均重试次数 <= 1.2。
|
||||
- 指标 3:连续对话微调后,最终落库成功率 >= 95%。
|
||||
|
||||
### 4.5 风险与回滚
|
||||
- 风险:模型给出的方案结构不稳定。
|
||||
- 应对:严格 JSON Schema 校验,失败直接走默认修补/人工规则。
|
||||
- 回滚:关闭 `ENABLE_SCHEDULE_PLAN_AGENT`,回退到原接口链路。
|
||||
|
||||
### 4.6 总流程规划
|
||||
|
||||
```
|
||||
任务目标:实现一个基于 ReAct 范式的智能排程微调引擎,将“粗排结果”与“既有日程”混合,并通过 AI 进行语义化优化。
|
||||
1.需要新建的前置函数:
|
||||
(1)HybridScheduleWithPlan:从数据库中提取和排程时间范围相同的日程,放在sv/schedules.go里面,通过回调来作为一个节点然后调用(如果你有更好的结构建议,欢迎告诉我)
|
||||
2.需要新建的tool(直接改State):
|
||||
(1)Swap:LLM传入带交换两个任务的相对时间(从json中获取,第x周第x-x节),这个工具会自动寻找并交换时间(通过修改Schedule结构体内部数据实现的),找不到就报错。
|
||||
(2)Move:同上,传入一个任务的相对时间(第x周第x-x节),直接寻找并修改State中的Schedule中的时间。
|
||||
注意,上述(1)和(2)都必须带合法性检验。
|
||||
(3)timeAvailable:检测目标时间在当前日程中是否可用,用于服务(2)。
|
||||
(4)GetAvailableSlots:反馈给AI(json格式)可用时间的列表,用于让AI选择挪动过去的时间。
|
||||
3.基本流程如下:
|
||||
(1)获取用户智能排程意图,提取task_class_id,调用SmartPlanning进行粗排,然后再通过上面的前置函数(1)将日程和已经安排好的任务混合,并传入State。
|
||||
(2)LLM启动深度思考(必须开深度思考),告诉它上述工具及其作用,让它自由选择调用。prompt你自己写,差不多就是:
|
||||
考虑不同科目的"上下文切换成本",某科目更加适合学习的时间段以及人一天的学习效率曲线等因素,修改上述json中status为suggested且type为task的任务,最终形成无论从复习效果,还是学习体感上来看都十分科学合理的学习方案。
|
||||
(3)此时,模型开启深度思考,推送reasoning stream到前端,和既定的状态chunk穿插。
|
||||
(4)在思考中,模型一次看好改动逻辑(这就是为啥要开深度思考的原因,逻辑有点绕),然后思考结束,出结果后一次性调用这些tool。
|
||||
注意,这里有备选方案,如果模型逻辑不够,那就一次只调一次tool,多调用几次llm,这样用时间换正确率。
|
||||
(4.1)若调用成功,则直接返回排程结果到前端(禁止落库,用户得看效果再决定是否正式落库,而正式落库用不着agent)
|
||||
(4.2)若失败,则把失败原因返回LLM,LLM再看情况自己思考并重试。
|
||||
|
||||
|
||||
```
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
## 阶段 2:从“我想复习概率论”自动生成任务类,并接入阶段 1
|
||||
|
||||
### 5.1 阶段目标
|
||||
- 用户只给学习目标(如“我想复习概率论”)时,系统自动生成任务类(章节/题型/轮次/估算工作量),并直接进入阶段 1 的排程链路。
|
||||
|
||||
### 5.2 关键实现
|
||||
- 新增“任务类规划节点(TaskClass Planner)”:
|
||||
- 输入:目标、时间范围、偏好、当前课程负载。
|
||||
- 输出:`UserAddTaskClassRequest` 兼容结构。
|
||||
- 采用“单次聚合生成 + 后端硬校验”:
|
||||
- 模型一次输出任务类结构(减少往返延迟)。
|
||||
- 后端用现有 `AddOrUpdateTaskClass` 做合法性校验与写库。
|
||||
- 打通链路:
|
||||
- `Goal -> TaskClass -> SmartPlanning -> BatchApplyPlans`。
|
||||
|
||||
### 5.3 验收标准
|
||||
- 指标 1:任务类自动生成可落库率 >= 90%。
|
||||
- 指标 2:生成后进入排程全链路成功率 >= 85%。
|
||||
- 指标 3:任务项粒度可读性(人工抽检)合格率 >= 90%。
|
||||
|
||||
### 5.4 风险与回滚
|
||||
- 风险:生成任务过粗/过细,导致排程质量差。
|
||||
- 应对:加入任务项数量上下限、单项时长约束、自动裁剪规则。
|
||||
- 回滚:关闭 `ENABLE_AUTO_TASKCLASS_FROM_GOAL`,保留手动任务类模式。
|
||||
|
||||
---
|
||||
|
||||
## 阶段 3:引入 Milvus RAG(时间管理知识)并提炼短规则注入排程上下文
|
||||
|
||||
### 6.1 阶段目标
|
||||
- 将“时间管理方法论”转化为可检索规则,提升排程质量和解释性。
|
||||
|
||||
### 6.2 知识库策略(建议)
|
||||
- 不直接喂整本书原文,改为“方法卡片库”:
|
||||
- 字段建议:`rule_id`、`title`、`principle`、`适用场景`、`反例`、`执行建议`、`source`。
|
||||
- 流程建议:
|
||||
- 原始文章入库 -> 向量化 -> 检索 TopK -> LLM 提炼“短规则集” -> 注入排程 Planner 提示词。
|
||||
- 规则注入方式:
|
||||
- 注入结构化规则对象,不只注入自由文本,避免提示词漂移。
|
||||
|
||||
### 6.3 验收标准
|
||||
- 指标 1:RAG 命中率(有有效规则输出)>= 80%。
|
||||
- 指标 2:用户二次修改率相比阶段 2 下降 >= 15%。
|
||||
- 指标 3:排程解释中可引用规则比例 >= 90%。
|
||||
|
||||
### 6.4 风险与回滚
|
||||
- 风险:检索噪声导致排程变差。
|
||||
- 应对:规则打分阈值、低分规则不注入、A/B 对比评估。
|
||||
- 回滚:关闭 `ENABLE_RAG_SCHEDULE_RULES`,继续使用纯模型规划。
|
||||
|
||||
---
|
||||
|
||||
## 阶段 4:引入 WebSearch + 记忆系统(长期偏好)
|
||||
|
||||
### 7.1 阶段目标
|
||||
- 在本地知识不足时用 WebSearch 补充最新学习建议;同时沉淀用户长期偏好,实现更个性化排程。
|
||||
|
||||
### 7.2 WebSearch 触发策略
|
||||
- 仅在以下情况触发:
|
||||
- 本地 RAG 低置信/无结果;
|
||||
- 用户明确要求“最新资料/趋势/考试变化”。
|
||||
- 结果处理:
|
||||
- 抓取 -> 摘要 -> 可信度过滤 -> 形成规则候选,再注入 Planner。
|
||||
|
||||
### 7.3 记忆系统设计
|
||||
- 记忆分层:
|
||||
- 长期稳定偏好:早八不学、每日最大学习时长、偏好学习时段。
|
||||
- 短期上下文偏好:本次会话临时约束。
|
||||
- 写入策略:
|
||||
- 只有“高置信 + 用户确认”才写长期记忆。
|
||||
- 可撤销、可查看、可重置。
|
||||
|
||||
### 7.4 验收标准
|
||||
- 指标 1:偏好命中后用户手动改计划次数下降 >= 20%。
|
||||
- 指标 2:WebSearch 触发请求中有效增益比例 >= 60%。
|
||||
- 指标 3:记忆误写率(用户否认)<= 5%。
|
||||
|
||||
### 7.5 风险与回滚
|
||||
- 风险:外部信息质量不稳定、记忆污染。
|
||||
- 应对:来源白名单、置信度阈值、显式确认机制。
|
||||
- 回滚:分别关闭 `ENABLE_WEBSEARCH`、`ENABLE_USER_MEMORY`。
|
||||
|
||||
---
|
||||
|
||||
## 8. 工程深度增强建议(可选但推荐)
|
||||
- 建议 1:引入“排程模拟器(Dry Run)”,先算冲突与可行性评分,再决定是否落库。
|
||||
- 建议 2:建立“离线评测集”(固定 50~100 条真实场景)做回归评测,避免模型升级导致排程退化。
|
||||
- 建议 3:把反思失败样本沉淀为“错误词典 + 修复策略库”,提升下一轮成功率。
|
||||
- 建议 4:增加成本与耗时预算控制(每请求最多模型调用次数、超时兜底回复)。
|
||||
- 建议 5:在日志中记录“规则来源、模型决策、后端裁决结果”,形成可审计链路。
|
||||
|
||||
## 9. 里程碑建议(可按周)
|
||||
- 里程碑 1(第 1~2 周):完成阶段 1,打通粗排+重试+连续微调。
|
||||
- 里程碑 2(第 3~4 周):完成阶段 2,打通目标到任务类自动生成。
|
||||
- 里程碑 3(第 5~6 周):完成阶段 3,接入 Milvus RAG 规则注入。
|
||||
- 里程碑 4(第 7~8 周):完成阶段 4,接入 WebSearch 与记忆系统。
|
||||
|
||||
## 10. 面试叙事建议(附加价值)
|
||||
- 叙事主线:从“规则系统”升级为“可解释、可进化、可回滚”的 Agent 排程平台。
|
||||
- 技术亮点:
|
||||
- 亮点 1:模型提议 + 后端裁决的双层安全架构。
|
||||
- 亮点 2:连续对话下的增量重排与版本管理。
|
||||
- 亮点 3:RAG 规则注入和记忆系统带来的个性化规划。
|
||||
- 亮点 4:全链路可观测(成功率、重试率、采纳率、成本)。
|
||||
|
||||
Reference in New Issue
Block a user