后端:
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.上下文依然待审视
前端:无
仓库:无
165 lines
5.6 KiB
Go
165 lines
5.6 KiB
Go
package newagentnode
|
||
|
||
import (
|
||
"context"
|
||
"fmt"
|
||
|
||
newagentmodel "github.com/LoveLosita/smartflow/backend/newAgent/model"
|
||
newagenttools "github.com/LoveLosita/smartflow/backend/newAgent/tools"
|
||
)
|
||
|
||
const (
|
||
roughBuildStageName = "rough_build"
|
||
roughBuildStatusBlock = "rough_build.status"
|
||
)
|
||
|
||
// RunRoughBuildNode 执行粗排节点逻辑。
|
||
//
|
||
// 步骤说明:
|
||
// 1. 推送"正在粗排"状态给前端;
|
||
// 2. 从 CommonState 读取 TaskClassIDs,确认有需要排课的任务类;
|
||
// 3. 加载 ScheduleState(含 DayMapping);
|
||
// 4. 调用 RoughBuildFunc 拿到粗排结果([]RoughBuildPlacement);
|
||
// 5. 把粗排结果写入 ScheduleState 的对应 task.Slots(pending 任务预填位置);
|
||
// 6. 推送"粗排完成"状态,清除 NeedsRoughBuild 标记,进入执行阶段。
|
||
func RunRoughBuildNode(ctx context.Context, st *newagentmodel.AgentGraphState) error {
|
||
if st == nil {
|
||
return fmt.Errorf("rough build node: state is nil")
|
||
}
|
||
|
||
flowState := st.EnsureFlowState()
|
||
emitter := st.EnsureChunkEmitter()
|
||
|
||
// 1. 推送状态:告知前端进入粗排环节。
|
||
_ = emitter.EmitStatus(
|
||
roughBuildStatusBlock,
|
||
roughBuildStageName,
|
||
"rough_building",
|
||
"正在为你生成初始排课方案,请稍候。",
|
||
true,
|
||
)
|
||
|
||
// 2. 校验依赖。
|
||
if st.Deps.RoughBuildFunc == nil {
|
||
return fmt.Errorf("rough build node: RoughBuildFunc 未注入")
|
||
}
|
||
|
||
// 3. 读取任务类 IDs。
|
||
taskClassIDs := flowState.TaskClassIDs
|
||
if len(taskClassIDs) == 0 {
|
||
// 没有任务类 ID 时静默跳过粗排,直接进入执行阶段。
|
||
flowState.Phase = newagentmodel.PhaseExecuting
|
||
flowState.NeedsRoughBuild = false
|
||
return nil
|
||
}
|
||
|
||
// 4. 加载 ScheduleState(含 DayMapping,用于坐标转换)。
|
||
scheduleState, err := st.EnsureScheduleState(ctx)
|
||
if err != nil {
|
||
return fmt.Errorf("rough build node: 加载日程状态失败: %w", err)
|
||
}
|
||
if scheduleState == nil {
|
||
return fmt.Errorf("rough build node: ScheduleState 为空,无法执行粗排")
|
||
}
|
||
|
||
// 5. 调用粗排算法。
|
||
placements, err := st.Deps.RoughBuildFunc(ctx, flowState.UserID, taskClassIDs)
|
||
if err != nil {
|
||
return fmt.Errorf("rough build node: 粗排算法失败: %w", err)
|
||
}
|
||
|
||
// 6. 把粗排结果写入 ScheduleState。
|
||
applyRoughBuildPlacements(scheduleState, placements)
|
||
|
||
// 7. 推送完成状态。
|
||
_ = emitter.EmitStatus(
|
||
roughBuildStatusBlock,
|
||
roughBuildStageName,
|
||
"rough_build_done",
|
||
fmt.Sprintf("初始排课方案已生成,共 %d 个任务已预排,进入微调阶段。", len(placements)),
|
||
false,
|
||
)
|
||
|
||
// 8. 把粗排完成信息写入 pinned context,让 Execute 阶段的 LLM 直接进入验证和微调。
|
||
stillPending := countPendingTasks(scheduleState)
|
||
var pinnedContent string
|
||
if stillPending > 0 {
|
||
pinnedContent = fmt.Sprintf(
|
||
"后端已自动运行粗排算法,初始排课方案已写入日程状态(共 %d 个任务已预排)。\n"+
|
||
"注意:仍有 %d 个任务未被粗排覆盖,处于待安排(pending)状态,必须在微调阶段手动安排完毕。\n\n"+
|
||
"处理 pending 任务的正确操作顺序:\n"+
|
||
"1. 调用 get_overview 或 find_free 确认可用空位(不要反复调用 list_tasks,list_tasks 只能看任务列表,看不出空位)\n"+
|
||
"2. 调用 place 将 pending 任务放入空位\n"+
|
||
"3. 重复上述步骤,直到 get_overview 显示待安排任务剩余为 0\n\n"+
|
||
"微调完成的判定标准:所有 pending 任务均已 place(待安排任务剩余=0),且现有排课无明显失衡。\n"+
|
||
"无需再次触发粗排。",
|
||
len(placements), stillPending,
|
||
)
|
||
} else {
|
||
pinnedContent = fmt.Sprintf(
|
||
"后端已自动运行粗排算法,初始排课方案已写入日程状态(共 %d 个任务已预排,无待安排任务)。\n"+
|
||
"请直接调用 get_overview 查看预排结果,然后用 move/swap 微调不合理的位置。\n"+
|
||
"无需再次触发粗排。",
|
||
len(placements),
|
||
)
|
||
}
|
||
st.EnsureConversationContext().UpsertPinnedBlock(newagentmodel.ContextBlock{
|
||
Key: "rough_build_done",
|
||
Title: "粗排已完成",
|
||
Content: pinnedContent,
|
||
})
|
||
|
||
// 9. 清除标记,进入执行阶段。
|
||
flowState.NeedsRoughBuild = false
|
||
flowState.Phase = newagentmodel.PhaseExecuting
|
||
return nil
|
||
}
|
||
|
||
// countPendingTasks 统计粗排后仍无位置的待安排任务数。
|
||
//
|
||
// 粗排只设 Slots,不改 Status(仍为 "pending"),
|
||
// 所以"真正未覆盖"= pending 且 Slots 为空,需要手动 place。
|
||
func countPendingTasks(state *newagenttools.ScheduleState) int {
|
||
if state == nil {
|
||
return 0
|
||
}
|
||
count := 0
|
||
for i := range state.Tasks {
|
||
t := &state.Tasks[i]
|
||
if t.Status == "pending" && len(t.Slots) == 0 {
|
||
count++
|
||
}
|
||
}
|
||
return count
|
||
}
|
||
|
||
// applyRoughBuildPlacements 把粗排结果写入 ScheduleState 对应任务的 Slots。
|
||
//
|
||
// 设计说明:
|
||
// 1. 通过 task_item_id(SourceID)定位任务;
|
||
// 2. 用 DayMapping 把 (week, dayOfWeek) 转为 day_index;
|
||
// 3. task.Status 保持 "pending",让 LLM 在 Execute 阶段看到"有建议位置的待安排任务",
|
||
// 可用 move/swap 微调,也可用 unplace 推翻粗排结果;
|
||
// 4. 转换失败的条目静默跳过,不中断整体流程。
|
||
func applyRoughBuildPlacements(state *newagenttools.ScheduleState, placements []newagentmodel.RoughBuildPlacement) {
|
||
if state == nil {
|
||
return
|
||
}
|
||
for _, p := range placements {
|
||
day, ok := state.WeekDayToDay(p.Week, p.DayOfWeek)
|
||
if !ok {
|
||
continue // DayMapping 里没有对应 day,跳过
|
||
}
|
||
for i := range state.Tasks {
|
||
t := &state.Tasks[i]
|
||
if t.Source != "task_item" || t.SourceID != p.TaskItemID {
|
||
continue
|
||
}
|
||
t.Slots = []newagenttools.TaskSlot{
|
||
{Day: day, SlotStart: p.SectionFrom, SlotEnd: p.SectionTo},
|
||
}
|
||
break
|
||
}
|
||
}
|
||
}
|