Version: 0.7.0.dev.260319
✨ feat(agent): 新增智能排程 Agent 全链路 + ReAct 精排引擎 🏗️ 智能排程 Graph 编排(阶段 1 基础链路) - 新增 scheduleplan 包:state / tool / prompt / nodes / runner / graph 六件套 - 实现 plan → preview → materialize → apply → reflect → finalize 完整图编排 - 通过函数注入解耦 agent 层与 service 层,避免循环依赖 - 路由层新增 schedule_plan 动作,复用现有 SSE + 持久化链路 🧠 ReAct 精排引擎(阶段 1.5 语义化微调) - 粗排后构建"混合日程"(既有课程 + 建议任务),统一为 HybridScheduleEntry - LLM 开启深度思考,通过 Swap / Move / TimeAvailable / GetAvailableSlots 四个 Tool 在内存中优化任务时间 - reasoning_content 实时流式推送前端,用户可见 AI 思考过程 - 精排结果仅预览不落库,向后兼容(未注入依赖时走原有 materialize 路径) 📝 文档 - 新增 ReAct 精排引擎决策记录 ⚠️ 已知问题:深度思考模式耗时较长,超时策略待优化
This commit is contained in:
139
backend/agent/scheduleplan/state.go
Normal file
139
backend/agent/scheduleplan/state.go
Normal file
@@ -0,0 +1,139 @@
|
||||
package scheduleplan
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/LoveLosita/smartflow/backend/model"
|
||||
)
|
||||
|
||||
const (
|
||||
// schedulePlanTimezoneName 是排程链路默认业务时区。
|
||||
// 与随口记保持一致,固定东八区,避免容器运行在 UTC 导致"明天/今晚"偏移。
|
||||
schedulePlanTimezoneName = "Asia/Shanghai"
|
||||
|
||||
// schedulePlanDatetimeLayout 是排程链路内部统一的分钟级时间格式。
|
||||
schedulePlanDatetimeLayout = "2006-01-02 15:04"
|
||||
)
|
||||
|
||||
// SchedulePlanState 是"智能排程"链路在 graph 节点间传递的统一状态容器。
|
||||
//
|
||||
// 设计目标:
|
||||
// 1) 收拢排程请求全生命周期的上下文,降低节点间参数散<E695B0><E695A3><EFBFBD>;
|
||||
// 2) 支持"粗排 -> 校验 -> 修补重试 -> 落库"的完整链路追踪;
|
||||
// 3) 支持连续对话微调:保留上版方案 + 本次约束变更,便于增量重排。
|
||||
type SchedulePlanState struct {
|
||||
// ── 基础上下文 ──
|
||||
TraceID string
|
||||
UserID int
|
||||
ConversationID string
|
||||
RequestNow time.Time
|
||||
RequestNowText string
|
||||
|
||||
// ── plan 节点输出 ──
|
||||
|
||||
// UserIntent 是模型对用户排程意图的结构化摘要(如"帮我安排高数复习计划")。
|
||||
UserIntent string
|
||||
// Constraints 是用户提出的硬约束列表(如 ["早八不排", "周末休息"])。
|
||||
Constraints []string
|
||||
// TaskClassID 是目标任务类 ID,由 Extra 字段或模型抽取获得。
|
||||
TaskClassID int
|
||||
// Strategy 是排程策略(steady/rapid),默认 steady。
|
||||
Strategy string
|
||||
|
||||
// ── preview 节点输出 ──
|
||||
|
||||
// CandidatePlans 是粗排算法生成的候选方案(展示型结构,供 SSE 推送给前端预览)。
|
||||
CandidatePlans []model.UserWeekSchedule
|
||||
// AllocatedItems 是粗排算法已分配的任务项(EmbeddedTime 已回填),供 materialize 直接转换。
|
||||
AllocatedItems []model.TaskClassItem
|
||||
|
||||
// ── ReAct 精排阶段 ──
|
||||
|
||||
// HybridEntries 是混合日程条目列表,包含既有日程(existing)和粗排建议(suggested)。
|
||||
// ReAct 工具直接在此切片上操作(内存修改,不涉及 DB)。
|
||||
HybridEntries []model.HybridScheduleEntry
|
||||
// ReactRound 当前 ReAct 循环轮次。
|
||||
ReactRound int
|
||||
// ReactMaxRound 最大循环轮次(建议 3)。
|
||||
ReactMaxRound int
|
||||
// ReactSummary LLM 输出的优化摘要。
|
||||
ReactSummary string
|
||||
// ReactDone 标记 ReAct 是否已完成。
|
||||
ReactDone bool
|
||||
|
||||
// ── materialize 节点输出 ──
|
||||
|
||||
// ApplyRequest 是转换后的落库请求体。
|
||||
ApplyRequest *model.UserInsertTaskClassItemToScheduleRequestBatch
|
||||
|
||||
// ── apply 节点输出 ──
|
||||
|
||||
// Applied 标记是否落库成功。
|
||||
Applied bool
|
||||
// ApplyError 记录落库失败的错误信息,供 reflect 节点分析。
|
||||
ApplyError string
|
||||
|
||||
// ── reflect 节点状态 ──
|
||||
|
||||
// RetryCount 记录当前重试次数。
|
||||
RetryCount int
|
||||
// MaxRetry 是最大重试次数(建议 = 2)。
|
||||
MaxRetry int
|
||||
// ReflectAction 记录模型给出的修补动作(retry_with_patch / partial_apply / give_up)。
|
||||
ReflectAction string
|
||||
|
||||
// ── 连续对话微调 ──
|
||||
|
||||
// PreviousPlanJSON 是上一版已落库方案的 JSON 序列化,用于增量微调。
|
||||
// 从对话历史中提取,不做持久化。
|
||||
PreviousPlanJSON string
|
||||
// IsAdjustment 标记本次是否为微调请求(而非全新排程)。
|
||||
IsAdjustment bool
|
||||
|
||||
// ── 最终输出 ──
|
||||
|
||||
// FinalSummary 是 graph 最终给用户的回复文案。
|
||||
FinalSummary string
|
||||
// Completed 标记整个排程链路是否成功完成。
|
||||
Completed bool
|
||||
}
|
||||
|
||||
// NewSchedulePlanState 创建排程状态对象并初始化默认值。
|
||||
func NewSchedulePlanState(traceID string, userID int, conversationID string) *SchedulePlanState {
|
||||
now := schedulePlanNowToMinute()
|
||||
return &SchedulePlanState{
|
||||
TraceID: traceID,
|
||||
UserID: userID,
|
||||
ConversationID: conversationID,
|
||||
RequestNow: now,
|
||||
RequestNowText: now.In(schedulePlanLocation()).Format(schedulePlanDatetimeLayout),
|
||||
MaxRetry: 2,
|
||||
Strategy: "steady",
|
||||
ReactMaxRound: 3,
|
||||
}
|
||||
}
|
||||
|
||||
// CanRetry 判断当前是否还能继续重试落库。
|
||||
func (s *SchedulePlanState) CanRetry() bool {
|
||||
return s.RetryCount < s.MaxRetry
|
||||
}
|
||||
|
||||
// RecordApplyError 记录一次落库失败。
|
||||
func (s *SchedulePlanState) RecordApplyError(errMsg string) {
|
||||
s.RetryCount++
|
||||
s.ApplyError = errMsg
|
||||
}
|
||||
|
||||
// schedulePlanLocation 返回排程链路使用的业务时区。
|
||||
func schedulePlanLocation() *time.Location {
|
||||
loc, err := time.LoadLocation(schedulePlanTimezoneName)
|
||||
if err != nil {
|
||||
return time.Local
|
||||
}
|
||||
return loc
|
||||
}
|
||||
|
||||
// schedulePlanNowToMinute 返回当前时间并截断到分钟级。
|
||||
func schedulePlanNowToMinute() time.Time {
|
||||
return time.Now().In(schedulePlanLocation()).Truncate(time.Minute)
|
||||
}
|
||||
Reference in New Issue
Block a user