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:
Losita
2026-03-19 23:16:35 +08:00
parent cd95aeeaaa
commit d3cec2a5b9
24 changed files with 2737 additions and 24 deletions

View File

@@ -0,0 +1,101 @@
package agentsvc
import (
"context"
"errors"
"log"
"strings"
"github.com/LoveLosita/smartflow/backend/agent/scheduleplan"
"github.com/LoveLosita/smartflow/backend/conv"
"github.com/LoveLosita/smartflow/backend/pkg"
"github.com/cloudwego/eino-ext/components/model/ark"
"github.com/cloudwego/eino/schema"
)
// runSchedulePlanFlow 执行"智能排程"分支。
//
// 职责边界:
// 1. 负责把本次请求接入 scheduleplan 执行器;
// 2. 负责注入排程依赖SmartPlanning / BatchApplyPlans / GetTaskClassByID
// 3. 负责对话历史获取,支持连续对话微调;
// 4. 不负责聊天持久化(由 AgentChat 主流程统一收口)。
func (s *AgentService) runSchedulePlanFlow(
ctx context.Context,
selectedModel *ark.ChatModel,
userMessage string,
userID int,
chatID string,
traceID string,
extra map[string]any,
emitStage func(stage, detail string),
outChan chan<- string,
modelName string,
) (string, error) {
// 1. 依赖预检:排程依赖函数必须注入,否则无法完成排程链路。
if s.SmartPlanningRawFunc == nil || s.BatchApplyPlansFunc == nil || s.GetTaskClassByIDFunc == nil {
return "", errors.New("schedule plan service dependencies are not ready")
}
if selectedModel == nil {
return "", errors.New("schedule plan model is nil")
}
// 2. 获取对话历史,用于连续对话微调场景。
// 优先从 Redis 读取,未命中时回源 DB。
var chatHistory []*schema.Message
if s.agentCache != nil {
history, err := s.agentCache.GetHistory(ctx, chatID)
if err != nil {
log.Printf("获取排程对话历史失败 chat_id=%s: %v", chatID, err)
} else if history != nil {
chatHistory = history
}
}
// 2.1 缓存未命中时回源 DB。
if chatHistory == nil && s.repo != nil {
histories, hisErr := s.repo.GetUserChatHistories(ctx, userID, pkg.HistoryFetchLimitByModel("worker"), chatID)
if hisErr != nil {
log.Printf("回源 DB 获取排程对话历史失败 chat_id=%s: %v", chatID, hisErr)
} else {
chatHistory = conv.ToEinoMessages(histories)
}
}
// 3. 初始化排程状态对象。
state := scheduleplan.NewSchedulePlanState(traceID, userID, chatID)
// 4. 构建依赖注入并执行 graph。
finalState, runErr := scheduleplan.RunSchedulePlanGraph(ctx, scheduleplan.SchedulePlanGraphRunInput{
Model: selectedModel,
State: state,
Deps: scheduleplan.SchedulePlanToolDeps{
SmartPlanningRaw: s.SmartPlanningRawFunc,
BatchApplyPlans: s.BatchApplyPlansFunc,
GetTaskClassByID: s.GetTaskClassByIDFunc,
HybridScheduleWithPlan: s.HybridScheduleWithPlanFunc,
},
UserMessage: userMessage,
Extra: extra,
ChatHistory: chatHistory,
EmitStage: emitStage,
OutChan: outChan,
ModelName: modelName,
})
if runErr != nil {
return "", runErr
}
// 5. 提取最终回复。
if finalState == nil {
return "排程流程异常,请稍后重试。", nil
}
reply := strings.TrimSpace(finalState.FinalSummary)
if reply == "" {
reply = "排程流程已完成,但未生成结果摘要。"
}
return reply, nil
}