后端:
1.Execute 上下文修复(无限循环 / 重复确认根治)
- 更新node/execute.go:speak 写入历史(修复旧 TODO);confirm 动作 speak 不再丢失;
continue 无工具调用时写 reason 保证上下文推进;区分 tool_call 数组/JSON损坏两种
correction hint;goal_check hint 区分 plan/ReAct 模式
- 更新node/execute.go:新增 AlwaysExecute 字段,extra.always_execute=true 时写工具
跳过确认闸门直接执行并持久化
- 更新model/graph_run_state.go:AgentGraphRequest 新增 AlwaysExecute;新增
WriteSchedulePreviewFunc 类型和 WriteSchedulePreview Dep
- 更新service/agentsvc/agent.go:新增 readAgentExtraBool 辅助
2.粗排全链路修复
- 更新service/agentsvc/agent_newagent.go:makeRoughBuildFunc 改用 HybridScheduleEntry
而非 TaskClassItem.EmbeddedTime,普通时段放置不再被丢弃
- 更新conv/schedule_provider.go:LoadScheduleState 从 task class 日期范围推算多周
规划窗口,不再硬编码当前周 7 天;DayMapping 覆盖全部相关周,粗排跨周结果不再
被 WeekDayToDay 静默丢弃
- 更新node/rough_build.go:pinned block 区分有/无未覆盖 pending 任务两种情况,
有 pending 时明确操作顺序(find_free→place)和完成判定,防止 LLM 重复调
list_tasks;新增 countPendingTasks 辅助(只统计 Slots 为空的真正未覆盖任务)
- 更新model/common_state.go:新增 StartDirectExecute(),Chat 直接路由 execute 时
清空旧 PlanSteps,修复跨会话 HasPlan() 误判导致 ReAct 走 plan 模式的 bug
- 更新node/chat.go:handleRouteExecute 改用 StartDirectExecute()
3.排程预览缓存迁移至 Deliver 节点
- 更新node/agent_nodes.go:Deliver 节点完成后调用 WriteSchedulePreview,只有任务
真正完成才写预览缓存,中断路径不写中间态
- 更新service/agentsvc/agent_newagent.go:注入 makeWriteSchedulePreviewFunc;移除
graph 结束后的内联写入;makeRoughBuildFunc 注释修正
- 更新conv/schedule_preview.go:ScheduleStateToPreview 补设 GeneratedAt
- 更新model/agent.go:GetSchedulePlanPreviewResponse 新增 HybridEntries 字段
- 更新service/agentsvc/agent_schedule_preview.go:GET handler Redis/MySQL 两条路径
均透传 HybridEntries
4.Execute thinking 模式修复
- 更新newAgent/llm/ark_adapter.go:thinking 开启时强制 temperature=1,MaxTokens 自
动托底至 16000,调用方与适配层行为对齐
- 更新node/execute.go:调用参数同步改为 temperature=1.0 / MaxTokens=16000
undo:
1.流式推送换行未修复(undo)
2.上下文依然待审视
前端:无
仓库:无
256 lines
8.9 KiB
Go
256 lines
8.9 KiB
Go
package model
|
||
|
||
import (
|
||
"context"
|
||
"strings"
|
||
|
||
newagentllm "github.com/LoveLosita/smartflow/backend/newAgent/llm"
|
||
newagentstream "github.com/LoveLosita/smartflow/backend/newAgent/stream"
|
||
newagenttools "github.com/LoveLosita/smartflow/backend/newAgent/tools"
|
||
)
|
||
|
||
// AgentGraphRequest 描述一次 agent graph 运行的请求级输入。
|
||
//
|
||
// 职责边界:
|
||
// 1. 这里只放“当前这次请求”天然携带的轻量数据,例如用户本轮输入;
|
||
// 2. 不负责承载可持久化流程状态,流程状态仍归 AgentRuntimeState;
|
||
// 3. 不负责承载 LLM / emitter / store 等依赖,这些统一放进 AgentGraphDeps。
|
||
type AgentGraphRequest struct {
|
||
UserInput string
|
||
ConfirmAction string // "accept" / "reject" / "",仅 confirm 恢复场景由前端传入
|
||
AlwaysExecute bool // true 时写工具跳过确认闸门直接执行,适合前端已展示预览、用户无需逐步确认的场景
|
||
}
|
||
|
||
// Normalize 统一清洗请求级输入中的字符串字段。
|
||
func (r *AgentGraphRequest) Normalize() {
|
||
if r == nil {
|
||
return
|
||
}
|
||
r.UserInput = strings.TrimSpace(r.UserInput)
|
||
r.ConfirmAction = strings.TrimSpace(r.ConfirmAction)
|
||
}
|
||
|
||
// RoughBuildPlacement 是粗排算法返回的单条放置结果。
|
||
// 字段使用 DB 坐标系(week/dayOfWeek/section),由 RoughBuild 节点转换为 ScheduleState 的 day_index。
|
||
type RoughBuildPlacement struct {
|
||
TaskItemID int
|
||
Week int
|
||
DayOfWeek int
|
||
SectionFrom int
|
||
SectionTo int
|
||
}
|
||
|
||
// RoughBuildFunc 是粗排算法的依赖注入签名。
|
||
// 由 service 层封装 HybridScheduleWithPlanMulti 后注入,newAgent 层不直接依赖外层 model。
|
||
type RoughBuildFunc func(ctx context.Context, userID int, taskClassIDs []int) ([]RoughBuildPlacement, error)
|
||
|
||
// WriteSchedulePreviewFunc 是排程预览写入的依赖注入签名。
|
||
// 由 service 层封装 cacheDAO 后注入,deliver 节点在任务完成时调用,保证只有真正完成的结果才写入缓存。
|
||
type WriteSchedulePreviewFunc func(ctx context.Context, state *newagenttools.ScheduleState, userID int, conversationID string, taskClassIDs []int) error
|
||
|
||
// AgentGraphDeps 描述 graph/node 层运行时真正依赖的可插拔能力。
|
||
//
|
||
// 设计目的:
|
||
// 1. 让 graph 不再只拿到”裸状态”,而是能拿到上下文、模型和输出能力;
|
||
// 2. Chat/Plan/Execute/Deliver 允许分别挂不同 client,但也允许先复用同一个 client;
|
||
// 3. ChunkEmitter 统一承接阶段提示、正文、工具事件、确认请求等 SSE 输出。
|
||
type AgentGraphDeps struct {
|
||
ChatClient *newagentllm.Client
|
||
PlanClient *newagentllm.Client
|
||
ExecuteClient *newagentllm.Client
|
||
DeliverClient *newagentllm.Client
|
||
ChunkEmitter *newagentstream.ChunkEmitter
|
||
StateStore AgentStateStore
|
||
ToolRegistry *newagenttools.ToolRegistry
|
||
ScheduleProvider ScheduleStateProvider // 按 DAO 注入,Execute 节点按需加载 ScheduleState
|
||
SchedulePersistor SchedulePersistor // 按 DAO 注入,用于写工具执行后持久化变更
|
||
RoughBuildFunc RoughBuildFunc // 按 Service 注入,粗排算法入口
|
||
WriteSchedulePreview WriteSchedulePreviewFunc // 按 Service 注入,排程预览写入入口
|
||
}
|
||
|
||
// EnsureChunkEmitter 保证 graph 运行时始终有一个可用的 chunk 发射器。
|
||
//
|
||
// 步骤说明:
|
||
// 1. 依赖为空时回退到 Noop emitter,避免骨架期因为没接前端而到处判空;
|
||
// 2. 这里只兜底“能安全调用”,不负责填充真实 request_id / model_name;
|
||
// 3. 后续 service 层一旦接上真实 emitter,会自然覆盖这里的空实现。
|
||
func (d *AgentGraphDeps) EnsureChunkEmitter() *newagentstream.ChunkEmitter {
|
||
if d == nil {
|
||
return newagentstream.NewChunkEmitter(newagentstream.NoopPayloadEmitter(), "", "", 0)
|
||
}
|
||
if d.ChunkEmitter == nil {
|
||
d.ChunkEmitter = newagentstream.NewChunkEmitter(newagentstream.NoopPayloadEmitter(), "", "", 0)
|
||
}
|
||
return d.ChunkEmitter
|
||
}
|
||
|
||
// ResolveChatClient 返回 chat 阶段可用的模型客户端。
|
||
func (d *AgentGraphDeps) ResolveChatClient() *newagentllm.Client {
|
||
if d == nil {
|
||
return nil
|
||
}
|
||
return d.ChatClient
|
||
}
|
||
|
||
// ResolvePlanClient 返回 planning 阶段可用的模型客户端。
|
||
//
|
||
// 兜底策略:
|
||
// 1. 优先使用显式注入的 PlanClient;
|
||
// 2. 若未单独注入,则回退到 ChatClient;
|
||
// 3. 这样在骨架期可先用一套 client 跑通,再按需拆分 strategist / worker。
|
||
func (d *AgentGraphDeps) ResolvePlanClient() *newagentllm.Client {
|
||
if d == nil {
|
||
return nil
|
||
}
|
||
if d.PlanClient != nil {
|
||
return d.PlanClient
|
||
}
|
||
return d.ChatClient
|
||
}
|
||
|
||
// ResolveExecuteClient 返回 execute 阶段可用的模型客户端。
|
||
func (d *AgentGraphDeps) ResolveExecuteClient() *newagentllm.Client {
|
||
if d == nil {
|
||
return nil
|
||
}
|
||
if d.ExecuteClient != nil {
|
||
return d.ExecuteClient
|
||
}
|
||
if d.PlanClient != nil {
|
||
return d.PlanClient
|
||
}
|
||
return d.ChatClient
|
||
}
|
||
|
||
// ResolveDeliverClient 返回 deliver 阶段可用的模型客户端。
|
||
func (d *AgentGraphDeps) ResolveDeliverClient() *newagentllm.Client {
|
||
if d == nil {
|
||
return nil
|
||
}
|
||
if d.DeliverClient != nil {
|
||
return d.DeliverClient
|
||
}
|
||
if d.ExecuteClient != nil {
|
||
return d.ExecuteClient
|
||
}
|
||
if d.PlanClient != nil {
|
||
return d.PlanClient
|
||
}
|
||
return d.ChatClient
|
||
}
|
||
|
||
// AgentGraphRunInput 是执行 newAgent 通用 graph 所需的完整入口参数。
|
||
//
|
||
// 字段说明:
|
||
// 1. RuntimeState:可持久化流程状态与 pending interaction;
|
||
// 2. ConversationContext:本轮喂给模型的上下文材料;
|
||
// 3. Request:当前这次请求的轻量输入;
|
||
// 4. Deps:graph/node 层真正依赖的可插拔能力。
|
||
type AgentGraphRunInput struct {
|
||
RuntimeState *AgentRuntimeState
|
||
ConversationContext *ConversationContext
|
||
Request AgentGraphRequest
|
||
Deps AgentGraphDeps
|
||
}
|
||
|
||
// AgentGraphState 是 graph 内部真正流转的运行态容器。
|
||
//
|
||
// 职责边界:
|
||
// 1. 负责把"流程状态 + 对话上下文 + 请求输入 + 运行依赖"收口到同一个对象;
|
||
// 2. 负责给 graph 分支和 node 提供最小必要的兜底访问方法;
|
||
// 3. 不负责持久化,不负责真正业务执行。
|
||
type AgentGraphState struct {
|
||
RuntimeState *AgentRuntimeState
|
||
ConversationContext *ConversationContext
|
||
Request AgentGraphRequest
|
||
Deps AgentGraphDeps
|
||
ScheduleState *newagenttools.ScheduleState // 工具操作的内存数据源,Execute 节点按需加载
|
||
OriginalScheduleState *newagenttools.ScheduleState // 首次加载时的原始快照,供 diff 用
|
||
}
|
||
|
||
// NewAgentGraphState 把入口参数整理成 graph 内部状态。
|
||
func NewAgentGraphState(input AgentGraphRunInput) *AgentGraphState {
|
||
st := &AgentGraphState{
|
||
RuntimeState: input.RuntimeState,
|
||
ConversationContext: input.ConversationContext,
|
||
Request: input.Request,
|
||
Deps: input.Deps,
|
||
}
|
||
st.Request.Normalize()
|
||
st.EnsureRuntimeState()
|
||
st.EnsureConversationContext()
|
||
st.Deps.EnsureChunkEmitter()
|
||
return st
|
||
}
|
||
|
||
// EnsureRuntimeState 保证 graph 内部始终持有一份可用的运行态。
|
||
func (s *AgentGraphState) EnsureRuntimeState() *AgentRuntimeState {
|
||
if s == nil {
|
||
return nil
|
||
}
|
||
if s.RuntimeState == nil {
|
||
s.RuntimeState = NewAgentRuntimeState(nil)
|
||
}
|
||
s.RuntimeState.EnsureCommonState()
|
||
return s.RuntimeState
|
||
}
|
||
|
||
// EnsureFlowState 返回可持久化的主流程状态。
|
||
func (s *AgentGraphState) EnsureFlowState() *CommonState {
|
||
runtimeState := s.EnsureRuntimeState()
|
||
if runtimeState == nil {
|
||
return nil
|
||
}
|
||
return runtimeState.EnsureCommonState()
|
||
}
|
||
|
||
// EnsureConversationContext 保证 graph 内部始终持有一份可用的会话上下文。
|
||
func (s *AgentGraphState) EnsureConversationContext() *ConversationContext {
|
||
if s == nil {
|
||
return nil
|
||
}
|
||
if s.ConversationContext == nil {
|
||
s.ConversationContext = NewConversationContext("")
|
||
}
|
||
return s.ConversationContext
|
||
}
|
||
|
||
// EnsureChunkEmitter 返回 graph 可安全调用的 chunk 发射器。
|
||
func (s *AgentGraphState) EnsureChunkEmitter() *newagentstream.ChunkEmitter {
|
||
if s == nil {
|
||
return newagentstream.NewChunkEmitter(newagentstream.NoopPayloadEmitter(), "", "", 0)
|
||
}
|
||
return s.Deps.EnsureChunkEmitter()
|
||
}
|
||
|
||
// ResolveToolRegistry 返回可用的工具注册表。
|
||
func (s *AgentGraphState) ResolveToolRegistry() *newagenttools.ToolRegistry {
|
||
if s == nil {
|
||
return nil
|
||
}
|
||
return s.Deps.ToolRegistry
|
||
}
|
||
|
||
// EnsureScheduleState 确保 ScheduleState 已加载。
|
||
// 首次调用时通过 ScheduleProvider 从 DB 加载,后续复用内存中的 state。
|
||
func (s *AgentGraphState) EnsureScheduleState(ctx context.Context) (*newagenttools.ScheduleState, error) {
|
||
if s == nil {
|
||
return nil, nil
|
||
}
|
||
if s.ScheduleState != nil {
|
||
return s.ScheduleState, nil
|
||
}
|
||
if s.Deps.ScheduleProvider == nil {
|
||
return nil, nil
|
||
}
|
||
userID := s.EnsureFlowState().UserID
|
||
state, err := s.Deps.ScheduleProvider.LoadScheduleState(ctx, userID)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
s.ScheduleState = state
|
||
// 保存原始快照,供后续 diff 使用。
|
||
s.OriginalScheduleState = state.Clone()
|
||
return state, nil
|
||
}
|