Version: 0.9.9.dev.260408
后端: 1. 粗排后分流与顺序守卫落地,支持“无明确微调偏好时粗排后直接收口”,并新增 allow_reorder / needs_refine_after_rough_build 语义,打通 chat→rough_build→execute/order_guard→deliver 路由。 2. execute 工具执行链路修复:清理乱码坏块与重复分支;新增 min_context_switch 未授权拦截;补齐 suggested 顺序基线初始化与顺序守卫联动。 3. 新增复合写工具 min_context_switch(减少上下文切换)并接入注册、参数解析、写工具白名单、提示词与文档;仅在用户明确允许打乱顺序时可用。 4. 工具口径升级:find_first_free 支持 day/day_start/day_end 范围参数并统一文案;移除 find_free 兼容别名;读写工具输出统一到“第N天(星期X)”格式。 5. prompt 同步升级:chat/execute/execute_context 增加粗排后是否继续微调、顺序授权、min_context_switch 使用边界与返回示例约束。 6. handoff 文档重命名并重写下班交接重点:下一步聚焦“工具收敛能力研究 + 运行态必要参数重置(不丢运行态)”。 7. 同步更新调试日志文件。 前端:无 仓库:无
This commit is contained in:
@@ -229,6 +229,25 @@ func (n *AgentNodes) Execute(ctx context.Context, st *newagentmodel.AgentGraphSt
|
||||
return st, nil
|
||||
}
|
||||
|
||||
// OrderGuard 是顺序守卫阶段的正式节点方法。
|
||||
//
|
||||
// 职责边界:
|
||||
// 1. 只负责调用 RunOrderGuardNode 做 suggested 相对顺序校验;
|
||||
// 2. 不负责交付文案生成,校验结果统一交给 Deliver 节点收口;
|
||||
// 3. 节点执行后保存状态,保证异常中断后仍可复盘守卫结果。
|
||||
func (n *AgentNodes) OrderGuard(ctx context.Context, st *newagentmodel.AgentGraphState) (*newagentmodel.AgentGraphState, error) {
|
||||
if st == nil {
|
||||
return nil, errors.New("order_guard node: state is nil")
|
||||
}
|
||||
|
||||
if err := RunOrderGuardNode(ctx, st); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
saveAgentState(ctx, st)
|
||||
return st, nil
|
||||
}
|
||||
|
||||
// Deliver 是交付阶段的正式节点方法。
|
||||
//
|
||||
// 职责边界:
|
||||
|
||||
@@ -21,6 +21,14 @@ const (
|
||||
chatSpeakBlockID = "chat.speak"
|
||||
)
|
||||
|
||||
type reorderPreference int
|
||||
|
||||
const (
|
||||
reorderUnknown reorderPreference = iota
|
||||
reorderAllow
|
||||
reorderDisallow
|
||||
)
|
||||
|
||||
// ChatNodeInput 描述聊天节点单轮运行所需的最小依赖。
|
||||
//
|
||||
// 职责边界:
|
||||
@@ -98,6 +106,7 @@ func RunChatNode(ctx context.Context, input ChatNodeInput) error {
|
||||
|
||||
log.Printf("[DEBUG] chat routing chat=%s route=%s reason=%s",
|
||||
flowState.ConversationID, decision.Route, decision.Reason)
|
||||
flowState.AllowReorder = resolveAllowReorder(input.UserInput, decision.AllowReorder)
|
||||
|
||||
// 3. 按路由决策推进。
|
||||
switch decision.Route {
|
||||
@@ -161,14 +170,89 @@ func handleRouteExecute(
|
||||
// 清空旧 PlanSteps 并设 PhaseExecuting,避免上一次任务残留的步骤被 HasPlan() 误判。
|
||||
flowState.StartDirectExecute()
|
||||
|
||||
// 安全兜底:只有真正持有 task_class_ids 时才开粗排。
|
||||
// 1. 默认不走粗排与粗排后微调,避免沿用上轮遗留标记。
|
||||
// 2. 只有 route 判定为“需要粗排”且确实有 task_class_ids 时,才打开粗排开关。
|
||||
// 3. 粗排后是否立即进入微调,完全由路由决策显式标记控制。
|
||||
flowState.NeedsRoughBuild = false
|
||||
flowState.NeedsRefineAfterRoughBuild = false
|
||||
if decision.NeedsRoughBuild && len(flowState.TaskClassIDs) > 0 {
|
||||
flowState.NeedsRoughBuild = true
|
||||
flowState.NeedsRefineAfterRoughBuild = decision.NeedsRefineAfterRoughBuild
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// resolveAllowReorder 统一计算“本轮是否允许打乱顺序”。
|
||||
//
|
||||
// 步骤化说明:
|
||||
// 1. 后端先做显式语义判定:用户明确允许/明确禁止时,直接以后端判定为准;
|
||||
// 2. 若后端未识别到显式语义,再回退到路由模型的 allow_reorder 字段;
|
||||
// 3. 默认返回 false,确保“保持顺序”是系统默认行为。
|
||||
func resolveAllowReorder(userInput string, modelAllowReorder bool) bool {
|
||||
switch detectReorderPreference(userInput) {
|
||||
case reorderAllow:
|
||||
return true
|
||||
case reorderDisallow:
|
||||
return false
|
||||
default:
|
||||
return modelAllowReorder
|
||||
}
|
||||
}
|
||||
|
||||
// detectReorderPreference 识别用户是否“明确授权打乱顺序”。
|
||||
//
|
||||
// 职责边界:
|
||||
// 1. 只负责关键词级别的显式意图识别,不做复杂语义推理;
|
||||
// 2. 若同时命中“允许”与“禁止”,优先按“禁止”处理,避免误放开顺序约束;
|
||||
// 3. 未命中显式表达时返回 unknown,交给上层兜底策略。
|
||||
func detectReorderPreference(userInput string) reorderPreference {
|
||||
text := strings.ToLower(strings.TrimSpace(userInput))
|
||||
if text == "" {
|
||||
return reorderUnknown
|
||||
}
|
||||
|
||||
disallowPhrases := []string{
|
||||
"不要打乱顺序",
|
||||
"不允许打乱顺序",
|
||||
"保持顺序",
|
||||
"顺序不变",
|
||||
"按原顺序",
|
||||
"不要乱序",
|
||||
"别打乱",
|
||||
}
|
||||
if containsAnyPhrase(text, disallowPhrases) {
|
||||
return reorderDisallow
|
||||
}
|
||||
|
||||
allowPhrases := []string{
|
||||
"可以打乱顺序",
|
||||
"允许打乱顺序",
|
||||
"顺序不重要",
|
||||
"顺序无所谓",
|
||||
"顺序不限",
|
||||
"允许乱序",
|
||||
"可以乱序",
|
||||
"允许重排顺序",
|
||||
"reorder is fine",
|
||||
"any order",
|
||||
}
|
||||
if containsAnyPhrase(text, allowPhrases) {
|
||||
return reorderAllow
|
||||
}
|
||||
|
||||
return reorderUnknown
|
||||
}
|
||||
|
||||
func containsAnyPhrase(text string, phrases []string) bool {
|
||||
for _, phrase := range phrases {
|
||||
if strings.Contains(text, phrase) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// handleDeepAnswer 处理复杂问答:推送过渡语 → 原地开 thinking 再调一次 LLM → 输出深度回答。
|
||||
func handleDeepAnswer(
|
||||
ctx context.Context,
|
||||
|
||||
@@ -23,6 +23,7 @@ const (
|
||||
executeStatusBlockID = "execute.status"
|
||||
executeSpeakBlockID = "execute.speak"
|
||||
executePinnedKey = "execution_context"
|
||||
toolMinContextSwitch = "min_context_switch"
|
||||
|
||||
// maxConsecutiveCorrections 是 Execute 节点连续修正次数上限。
|
||||
// 超过此阈值后终止执行,防止 LLM 陷入无限修正循环。
|
||||
@@ -102,6 +103,14 @@ func RunExecuteNode(ctx context.Context, input ExecuteNodeInput) error {
|
||||
return executePendingTool(ctx, runtimeState, conversationContext, input.ToolRegistry, input.ScheduleState, input.SchedulePersistor, input.OriginalScheduleState, emitter)
|
||||
}
|
||||
|
||||
// 1.6. 顺序守卫基线初始化:
|
||||
// 1) 仅在未授权打乱顺序时记录 suggested 顺序基线;
|
||||
// 2) 只在基线为空时初始化,避免执行循环中反复覆盖;
|
||||
// 3) 后续由 order_guard 节点基于该基线做相对顺序校验。
|
||||
if !flowState.AllowReorder && len(flowState.SuggestedOrderBaseline) == 0 {
|
||||
flowState.SuggestedOrderBaseline = buildSuggestedOrderSnapshot(input.ScheduleState)
|
||||
}
|
||||
|
||||
// 2. 推送执行阶段状态,让前端知道当前进度。
|
||||
if flowState.HasCurrentPlanStep() {
|
||||
// 有 plan:显示步骤进度。
|
||||
@@ -592,6 +601,54 @@ func executeToolCall(
|
||||
}
|
||||
|
||||
// 2. 执行工具。
|
||||
// 顺序护栏:未授权打乱顺序时,拒绝执行 min_context_switch,并写回工具观察结果。
|
||||
if shouldBlockMinContextSwitch(flowState, toolName) {
|
||||
blockedResult := "已拒绝执行 min_context_switch:当前未授权打乱顺序。如需使用该工具,请先由用户明确说明“允许打乱顺序”。"
|
||||
log.Printf(
|
||||
"[WARN] execute tool blocked chat=%s round=%d tool=%s allow_reorder=%v",
|
||||
flowState.ConversationID,
|
||||
flowState.RoundUsed,
|
||||
toolName,
|
||||
flowState.AllowReorder,
|
||||
)
|
||||
_ = emitter.EmitStatus(
|
||||
executeStatusBlockID,
|
||||
executeStageName,
|
||||
"tool_blocked",
|
||||
blockedResult,
|
||||
false,
|
||||
)
|
||||
|
||||
toolCallID := uuid.NewString()
|
||||
argsJSON := "{}"
|
||||
if toolCall.Arguments != nil {
|
||||
if raw, marshalErr := json.Marshal(toolCall.Arguments); marshalErr == nil {
|
||||
argsJSON = string(raw)
|
||||
}
|
||||
}
|
||||
conversationContext.AppendHistory(&schema.Message{
|
||||
Role: schema.Assistant,
|
||||
Content: "",
|
||||
ToolCalls: []schema.ToolCall{
|
||||
{
|
||||
ID: toolCallID,
|
||||
Type: "function",
|
||||
Function: schema.FunctionCall{
|
||||
Name: toolName,
|
||||
Arguments: argsJSON,
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
conversationContext.AppendHistory(&schema.Message{
|
||||
Role: schema.Tool,
|
||||
Content: blockedResult,
|
||||
ToolCallID: toolCallID,
|
||||
ToolName: toolName,
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
beforeDigest := summarizeScheduleStateForDebug(scheduleState)
|
||||
result := registry.Execute(scheduleState, toolName, toolCall.Arguments)
|
||||
afterDigest := summarizeScheduleStateForDebug(scheduleState)
|
||||
@@ -646,6 +703,19 @@ func executeToolCall(
|
||||
return nil
|
||||
}
|
||||
|
||||
// shouldBlockMinContextSwitch 判断是否要拦截 min_context_switch 工具。
|
||||
//
|
||||
// 说明:
|
||||
// 1. 仅当工具名为 min_context_switch 且未授权打乱顺序时返回 true;
|
||||
// 2. 其余场景统一放行;
|
||||
// 3. nil flowState 视为未命中拦截条件,避免因状态缺失导致误阻断。
|
||||
func shouldBlockMinContextSwitch(flowState *newagentmodel.CommonState, toolName string) bool {
|
||||
if flowState == nil {
|
||||
return false
|
||||
}
|
||||
return !flowState.AllowReorder && strings.EqualFold(strings.TrimSpace(toolName), toolMinContextSwitch)
|
||||
}
|
||||
|
||||
// executePendingTool 执行用户已确认的写工具。
|
||||
//
|
||||
// 职责边界:
|
||||
|
||||
207
backend/newAgent/node/order_guard.go
Normal file
207
backend/newAgent/node/order_guard.go
Normal file
@@ -0,0 +1,207 @@
|
||||
package newagentnode
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
newagentmodel "github.com/LoveLosita/smartflow/backend/newAgent/model"
|
||||
newagenttools "github.com/LoveLosita/smartflow/backend/newAgent/tools"
|
||||
)
|
||||
|
||||
const (
|
||||
orderGuardStageName = "order_guard"
|
||||
orderGuardStatusBlock = "order_guard.status"
|
||||
)
|
||||
|
||||
type suggestedOrderItem struct {
|
||||
StateID int
|
||||
Day int
|
||||
SlotStart int
|
||||
SlotEnd int
|
||||
}
|
||||
|
||||
// RunOrderGuardNode 负责在收口前校验 suggested 任务相对顺序是否被打乱。
|
||||
//
|
||||
// 职责边界:
|
||||
// 1. 只做“相对顺序守卫”这一件事,不负责执行调度工具,也不负责写库;
|
||||
// 2. 仅当 AllowReorder=false 时生效,用户明确授权可打乱顺序时直接放行;
|
||||
// 3. 校验失败只写入统一终止结果(Abort),由 Deliver 节点统一收口文案。
|
||||
func RunOrderGuardNode(ctx context.Context, st *newagentmodel.AgentGraphState) error {
|
||||
if st == nil {
|
||||
return fmt.Errorf("order_guard node: state is nil")
|
||||
}
|
||||
|
||||
flowState := st.EnsureFlowState()
|
||||
if flowState == nil {
|
||||
return fmt.Errorf("order_guard node: flow state is nil")
|
||||
}
|
||||
// 1. 用户明确授权可打乱顺序时,顺序守卫节点直接放行。
|
||||
if flowState.AllowReorder {
|
||||
return nil
|
||||
}
|
||||
|
||||
// 2. 读取当前 ScheduleState,提取 suggested 任务的“时间顺序快照”。
|
||||
scheduleState, err := st.EnsureScheduleState(ctx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("order_guard node: load schedule state failed: %w", err)
|
||||
}
|
||||
if scheduleState == nil {
|
||||
return nil
|
||||
}
|
||||
currentOrder := buildSuggestedOrderSnapshot(scheduleState)
|
||||
|
||||
// 3. 基线为空时,仅初始化基线并放行,避免第一次进入守卫就误判。
|
||||
if len(flowState.SuggestedOrderBaseline) == 0 {
|
||||
flowState.SuggestedOrderBaseline = append([]int(nil), currentOrder...)
|
||||
_ = st.EnsureChunkEmitter().EmitStatus(
|
||||
orderGuardStatusBlock,
|
||||
orderGuardStageName,
|
||||
"order_guard_initialized",
|
||||
"已记录本轮建议任务顺序基线,继续交付当前结果。",
|
||||
false,
|
||||
)
|
||||
return nil
|
||||
}
|
||||
|
||||
// 4. 基线存在时做逆序检测;一旦发现逆序,立即终止本轮自动微调。
|
||||
violated, detail := detectRelativeOrderViolation(flowState.SuggestedOrderBaseline, currentOrder)
|
||||
if !violated {
|
||||
_ = st.EnsureChunkEmitter().EmitStatus(
|
||||
orderGuardStatusBlock,
|
||||
orderGuardStageName,
|
||||
"order_guard_passed",
|
||||
"顺序守卫校验通过,保持原有相对顺序。",
|
||||
false,
|
||||
)
|
||||
return nil
|
||||
}
|
||||
|
||||
userMessage := "检测到当前方案打乱了原有建议任务顺序,本轮先停止自动微调。若你确认可以打乱顺序,请明确说明“允许打乱顺序”。"
|
||||
flowState.Abort(
|
||||
orderGuardStageName,
|
||||
"relative_order_violation",
|
||||
userMessage,
|
||||
fmt.Sprintf("baseline=%v current=%v detail=%s", flowState.SuggestedOrderBaseline, currentOrder, detail),
|
||||
)
|
||||
_ = st.EnsureChunkEmitter().EmitStatus(
|
||||
orderGuardStatusBlock,
|
||||
orderGuardStageName,
|
||||
"order_guard_failed",
|
||||
userMessage,
|
||||
true,
|
||||
)
|
||||
return nil
|
||||
}
|
||||
|
||||
// buildSuggestedOrderSnapshot 生成 suggested 任务的相对顺序快照(按时间坐标排序)。
|
||||
//
|
||||
// 说明:
|
||||
// 1. 这里只关心 suggested 任务,因为顺序守卫目标是约束“本轮建议层”的相对次序;
|
||||
// 2. 多 slot 任务取“最早 slot”作为排序锚点,保证排序键稳定;
|
||||
// 3. 返回值是 state_id 列表,便于写入 CommonState 做跨节点持久化。
|
||||
func buildSuggestedOrderSnapshot(state *newagenttools.ScheduleState) []int {
|
||||
if state == nil || len(state.Tasks) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
items := make([]suggestedOrderItem, 0, len(state.Tasks))
|
||||
for i := range state.Tasks {
|
||||
task := state.Tasks[i]
|
||||
if !newagenttools.IsSuggestedTask(task) || len(task.Slots) == 0 {
|
||||
continue
|
||||
}
|
||||
day, slotStart, slotEnd := earliestTaskSlot(task.Slots)
|
||||
items = append(items, suggestedOrderItem{
|
||||
StateID: task.StateID,
|
||||
Day: day,
|
||||
SlotStart: slotStart,
|
||||
SlotEnd: slotEnd,
|
||||
})
|
||||
}
|
||||
|
||||
sort.SliceStable(items, func(i, j int) bool {
|
||||
if items[i].Day != items[j].Day {
|
||||
return items[i].Day < items[j].Day
|
||||
}
|
||||
if items[i].SlotStart != items[j].SlotStart {
|
||||
return items[i].SlotStart < items[j].SlotStart
|
||||
}
|
||||
if items[i].SlotEnd != items[j].SlotEnd {
|
||||
return items[i].SlotEnd < items[j].SlotEnd
|
||||
}
|
||||
return items[i].StateID < items[j].StateID
|
||||
})
|
||||
|
||||
order := make([]int, 0, len(items))
|
||||
for _, item := range items {
|
||||
order = append(order, item.StateID)
|
||||
}
|
||||
return order
|
||||
}
|
||||
|
||||
func earliestTaskSlot(slots []newagenttools.TaskSlot) (day int, slotStart int, slotEnd int) {
|
||||
if len(slots) == 0 {
|
||||
return 0, 0, 0
|
||||
}
|
||||
best := slots[0]
|
||||
for i := 1; i < len(slots); i++ {
|
||||
current := slots[i]
|
||||
if current.Day < best.Day {
|
||||
best = current
|
||||
continue
|
||||
}
|
||||
if current.Day == best.Day && current.SlotStart < best.SlotStart {
|
||||
best = current
|
||||
continue
|
||||
}
|
||||
if current.Day == best.Day && current.SlotStart == best.SlotStart && current.SlotEnd < best.SlotEnd {
|
||||
best = current
|
||||
}
|
||||
}
|
||||
return best.Day, best.SlotStart, best.SlotEnd
|
||||
}
|
||||
|
||||
// detectRelativeOrderViolation 检查 current 是否破坏 baseline 的相对顺序。
|
||||
//
|
||||
// 规则:
|
||||
// 1. 仅比较 baseline 与 current 的交集任务,避免新增/删除任务引发误报;
|
||||
// 2. 一旦出现 rank 逆序即判定为 violation;
|
||||
// 3. detail 只用于内部排查,不直接给用户。
|
||||
func detectRelativeOrderViolation(baseline []int, current []int) (bool, string) {
|
||||
if len(baseline) == 0 || len(current) == 0 {
|
||||
return false, ""
|
||||
}
|
||||
|
||||
rankByID := make(map[int]int, len(baseline))
|
||||
for idx, id := range baseline {
|
||||
rankByID[id] = idx
|
||||
}
|
||||
|
||||
filtered := make([]int, 0, len(current))
|
||||
for _, id := range current {
|
||||
if _, ok := rankByID[id]; ok {
|
||||
filtered = append(filtered, id)
|
||||
}
|
||||
}
|
||||
if len(filtered) < 2 {
|
||||
return false, ""
|
||||
}
|
||||
|
||||
prevID := filtered[0]
|
||||
prevRank := rankByID[prevID]
|
||||
for i := 1; i < len(filtered); i++ {
|
||||
id := filtered[i]
|
||||
rank := rankByID[id]
|
||||
if rank < prevRank {
|
||||
return true, strings.TrimSpace(fmt.Sprintf(
|
||||
"reverse pair detected: prev_id=%d prev_rank=%d current_id=%d current_rank=%d",
|
||||
prevID, prevRank, id, rank,
|
||||
))
|
||||
}
|
||||
prevID = id
|
||||
prevRank = rank
|
||||
}
|
||||
return false, ""
|
||||
}
|
||||
@@ -34,7 +34,9 @@ type roughBuildApplyStats struct {
|
||||
// 4. 调用 RoughBuildFunc 拿到粗排结果([]RoughBuildPlacement);
|
||||
// 5. 把粗排结果写入 ScheduleState,把已落位任务标记为 suggested;
|
||||
// 6. 若粗排后仍存在真实 pending,则写入正式 abort 结果并结束本轮;
|
||||
// 7. 否则推送"粗排完成"状态,清除 NeedsRoughBuild 标记,进入执行阶段。
|
||||
// 7. 否则按“是否需要粗排后立即微调”分流:
|
||||
// - 无明确微调诉求:直接 Done -> Deliver;
|
||||
// - 有明确微调诉求:进入 Execute。
|
||||
func RunRoughBuildNode(ctx context.Context, st *newagentmodel.AgentGraphState) error {
|
||||
if st == nil {
|
||||
return fmt.Errorf("rough build node: state is nil")
|
||||
@@ -63,6 +65,7 @@ func RunRoughBuildNode(ctx context.Context, st *newagentmodel.AgentGraphState) e
|
||||
// 没有任务类 ID 时静默跳过粗排,直接进入执行阶段。
|
||||
flowState.Phase = newagentmodel.PhaseExecuting
|
||||
flowState.NeedsRoughBuild = false
|
||||
flowState.NeedsRefineAfterRoughBuild = false
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -133,16 +136,29 @@ func RunRoughBuildNode(ctx context.Context, st *newagentmodel.AgentGraphState) e
|
||||
return nil
|
||||
}
|
||||
|
||||
// 8. 推送完成状态。
|
||||
// 8. 计算是否需要“粗排后立即微调”。
|
||||
//
|
||||
// 1. 只在“无计划直执行”链路下应用该止血分流;
|
||||
// 2. 有计划链路依旧进入 execute,避免改变既有 plan->execute 语义;
|
||||
// 3. chat 路由明确标记 needs_refine_after_rough_build=true 时才进微调。
|
||||
shouldRefineAfterRoughBuild := flowState.HasPlan() || flowState.NeedsRefineAfterRoughBuild
|
||||
|
||||
// 9. 推送完成状态(区分“继续微调”与“直接收口”两种路径)。
|
||||
doneStatus := "rough_build_done"
|
||||
doneMessage := fmt.Sprintf("初始排课方案已生成,共 %d 个任务已预排,进入微调阶段。", len(placements))
|
||||
if !shouldRefineAfterRoughBuild {
|
||||
doneStatus = "rough_build_done_no_refine"
|
||||
doneMessage = fmt.Sprintf("初始排课方案已生成,共 %d 个任务已预排。本轮按默认策略先结束;如需优化,请继续告诉我你的偏好。", len(placements))
|
||||
}
|
||||
_ = emitter.EmitStatus(
|
||||
roughBuildStatusBlock,
|
||||
roughBuildStageName,
|
||||
"rough_build_done",
|
||||
fmt.Sprintf("初始排课方案已生成,共 %d 个任务已预排,进入微调阶段。", len(placements)),
|
||||
doneStatus,
|
||||
doneMessage,
|
||||
false,
|
||||
)
|
||||
|
||||
// 9. 把粗排完成信息写入 pinned context,让 Execute 阶段的 LLM 直接进入查看和微调。
|
||||
// 10. 把粗排完成信息写入 pinned context,让后续节点能拿到一致事实。
|
||||
|
||||
// 构造任务类 ID 字符串,供 pinned block 明确标注,避免 Execute LLM 因找不到 task_class_id 来源而 ask_user。
|
||||
idParts := make([]string, len(taskClassIDs))
|
||||
@@ -154,18 +170,31 @@ func RunRoughBuildNode(ctx context.Context, st *newagentmodel.AgentGraphState) e
|
||||
pinnedContent := fmt.Sprintf(
|
||||
"后端已自动运行粗排算法(任务类 ID:[%s]),初始排课方案已写入日程状态(共 %d 个任务已预排)。\n"+
|
||||
"这些预排任务已标记为 suggested,表示“可继续优化的建议落位”,不是待补排任务。\n"+
|
||||
"请先调用 get_overview 查看整体分布,再使用 move / swap / unplace 微调不合理的位置。\n"+
|
||||
"本轮不需要再调用 place,也无需再次触发粗排。",
|
||||
idStr, len(placements),
|
||||
)
|
||||
if shouldRefineAfterRoughBuild {
|
||||
pinnedContent += "\n请先调用 get_overview 查看整体分布,再使用 move / swap / unplace 微调不合理的位置。"
|
||||
} else {
|
||||
pinnedContent += "\n当前未收到明确微调偏好,流程将先收口;如需进一步优化,请基于本次结果提出调整要求。"
|
||||
}
|
||||
st.EnsureConversationContext().UpsertPinnedBlock(newagentmodel.ContextBlock{
|
||||
Key: "rough_build_done",
|
||||
Title: "粗排已完成",
|
||||
Content: pinnedContent,
|
||||
})
|
||||
|
||||
// 10. 清除标记,进入执行阶段。
|
||||
// 11. 清除粗排标记,并按分流结果进入执行或直接收口。
|
||||
//
|
||||
// 1. 无明确微调诉求:直接标记 completed,graph 会路由到 deliver;
|
||||
// 2. 有明确微调诉求:进入 execute 节点继续工具微调;
|
||||
// 3. 无论哪条路径,都要重置粗排相关标记,避免污染后续轮次。
|
||||
flowState.NeedsRoughBuild = false
|
||||
flowState.NeedsRefineAfterRoughBuild = false
|
||||
if !shouldRefineAfterRoughBuild {
|
||||
flowState.Done()
|
||||
return nil
|
||||
}
|
||||
flowState.Phase = newagentmodel.PhaseExecuting
|
||||
return nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user