Version: 0.9.10.dev.260409

后端:
1. newAgent 运行态重置双保险落地,并补齐写工具后的实时排程预览刷新
   - 更新 model/common_state.go:新增 ResetForNextRun,统一清理 round/plan/rough_build/allow_reorder/terminal 等执行期临时状态
   - 更新 node/chat.go + service/agentsvc/agent_newagent.go:在“无 pending 且上一轮已 done”时分别于 chat 主入口与 loadOrCreateRuntimeState 冷加载处执行兜底重置,覆盖正常新一轮对话与断线恢复场景
   - 更新 model/graph_run_state.go + node/agent_nodes.go + node/execute.go:写工具执行后立即刷新 Redis 排程预览,Deliver 继续保留最终覆盖写,保证前端能及时看到最新操作结果
2. 顺序守卫从“直接中止”改为“优先自动复原 suggested 相对顺序”
   - 更新 node/order_guard.go:检测到 suggested 顺序被打乱后,不再直接 abort;改为复用当前坑位按 baseline 自动回填,并在复原失败时仅记录诊断日志后继续交付
   - 更新 tools/state.go:ScheduleState 新增 RuntimeQueue 运行态快照字段,支持队列化处理与断线恢复
3. 多任务微调工具链升级:新增筛选/队列工具并替换首空位查询口径
   - 新建 tools/read_filter_tools.go + tools/runtime_queue.go + tools/queue_tools.go:新增 query_available_slots / query_target_tasks / queue_pop_head / queue_apply_head_move / queue_skip_head / queue_status,支持“先筛选目标,再逐项处理”的稳定微调链路
   - 更新 tools/registry.go + tools/write_tools.go + tools/read_helpers.go:移除 find_first_free 注册口径;batch_move 限制为最多 2 条,超过时引导改走队列逐项处理;queue_apply_head_move 纳入写工具集合
4. 复合规划工具扩充,并改为在 newAgent/tools 本地实现以规避循环导入
   - 更新 tools/compound_tools.go + tools/registry.go:spread_even 正式接入,并与 min_context_switch 一起作为复合写工具保留在 newAgent/tools 内部实现,不再依赖外层 logic
5. prompt 与工具文档同步升级,明确当前用户诉求锚点与队列化执行约束
   - 更新 prompt/execute.go + prompt/execute_context.go + prompt/plan.go:执行提示默认引导 query_target_tasks(enqueue=true) → queue_pop_head → query_available_slots → queue_apply_head_move / queue_skip_head;补齐 batch_move 上限、spread_even 使用边界、顺序策略与工具 JSON 返回示例
   - 更新 prompt/execute_context.go:将“初始用户目标”改为“当前用户诉求”,并保留首轮目标来源;旧 observation 折叠文案改为“当前工具调用结果已经被使用过,当前无需使用,为节省上下文空间,已折叠”
   - 更新 tools/SCHEDULE_TOOLS.md:同步补齐 query_* / queue_* / spread_even / min_context_switch 的说明、限制与返回示例
6. 同步更新调试日志文件
前端:无
仓库:无
This commit is contained in:
LoveLosita
2026-04-09 16:17:56 +08:00
parent 21b864390b
commit 821c2cde5d
20 changed files with 5809 additions and 2685 deletions

View File

@@ -51,6 +51,7 @@ type ExecuteNodeInput struct {
ToolRegistry *newagenttools.ToolRegistry
ScheduleState *newagenttools.ScheduleState
SchedulePersistor newagentmodel.SchedulePersistor
WriteSchedulePreview newagentmodel.WriteSchedulePreviewFunc
OriginalScheduleState *newagenttools.ScheduleState
AlwaysExecute bool // true 时写工具跳过确认闸门直接执行
}
@@ -100,13 +101,31 @@ func RunExecuteNode(ctx context.Context, input ExecuteNodeInput) error {
// 1.5. 确认执行分支:如果用户已确认写操作,直接执行工具。
if runtimeState.PendingConfirmTool != nil {
return executePendingTool(ctx, runtimeState, conversationContext, input.ToolRegistry, input.ScheduleState, input.SchedulePersistor, input.OriginalScheduleState, emitter)
return executePendingTool(
ctx,
runtimeState,
conversationContext,
input.ToolRegistry,
input.ScheduleState,
input.SchedulePersistor,
input.OriginalScheduleState,
input.WriteSchedulePreview,
emitter,
)
}
// 1.6. 顺序守卫基线初始化:
// 1) 仅在未授权打乱顺序时记录 suggested 顺序基线;
// 2) 只在基线为空时初始化,避免执行循环中反复覆盖;
// 3) 后续由 order_guard 节点基于该基线做相对顺序校验。
//
// 同时在“本轮 execute 首轮”重置一次临时队列,避免上一轮残留队列污染新请求。
// 判定依据:
// 1. RoundUsed==0 说明当前还未消耗执行预算;
// 2. 此时清理不会影响断线恢复中的中间进度(恢复场景通常 RoundUsed>0
if input.ScheduleState != nil && flowState.RoundUsed == 0 {
newagenttools.ResetTaskProcessingQueue(input.ScheduleState)
}
if !flowState.AllowReorder && len(flowState.SuggestedOrderBaseline) == 0 {
flowState.SuggestedOrderBaseline = buildSuggestedOrderSnapshot(input.ScheduleState)
}
@@ -329,7 +348,16 @@ func RunExecuteNode(ctx context.Context, input ExecuteNodeInput) error {
// 继续当前步骤的 ReAct 循环。
// 若有工具调用意图,则执行工具并记录证据。
if decision.ToolCall != nil {
return executeToolCall(ctx, flowState, conversationContext, decision.ToolCall, emitter, input.ToolRegistry, input.ScheduleState)
return executeToolCall(
ctx,
flowState,
conversationContext,
decision.ToolCall,
emitter,
input.ToolRegistry,
input.ScheduleState,
input.WriteSchedulePreview,
)
}
// 无工具调用且 speak 为空speak 非空时已在步骤 6 写入历史)。
// 若 history 本轮完全没有更新,下一轮 LLM 会收到完全相同的上下文,容易死循环。
@@ -351,7 +379,16 @@ func RunExecuteNode(ctx context.Context, input ExecuteNodeInput) error {
case newagentmodel.ExecuteActionConfirm:
// AlwaysExecute=true跳过确认闸门直接执行内存写工具不走 confirm 节点。
if input.AlwaysExecute && decision.ToolCall != nil {
return executeToolCall(ctx, flowState, conversationContext, decision.ToolCall, emitter, input.ToolRegistry, input.ScheduleState)
return executeToolCall(
ctx,
flowState,
conversationContext,
decision.ToolCall,
emitter,
input.ToolRegistry,
input.ScheduleState,
input.WriteSchedulePreview,
)
}
// AlwaysExecute=false默认暂存工具意图设 Phase → 下游 confirm 节点接管。
return handleExecuteActionConfirm(decision, runtimeState, flowState)
@@ -552,6 +589,7 @@ func executeToolCall(
emitter *newagentstream.ChunkEmitter,
registry *newagenttools.ToolRegistry,
scheduleState *newagenttools.ScheduleState,
writePreview newagentmodel.WriteSchedulePreviewFunc,
) error {
if toolCall == nil {
return nil
@@ -700,6 +738,14 @@ func executeToolCall(
ToolName: toolName,
})
// 4. 写工具实时预览:每次写工具执行后都尝试刷新 Redis 预览,确保前端可见“最新操作结果”。
//
// 步骤化说明:
// 1. 仅写工具触发实时预览刷新,读工具不触发,避免无意义放大写流量;
// 2. 这里采用“失败不阻断主流程”策略:预览写失败只记日志,不影响当前执行链路;
// 3. Deliver 节点仍保留最终覆盖写,保证 order_guard/收口后的最终态一致。
tryWritePreviewAfterWriteTool(ctx, flowState, scheduleState, registry, toolName, writePreview)
return nil
}
@@ -732,6 +778,7 @@ func executePendingTool(
scheduleState *newagenttools.ScheduleState,
persistor newagentmodel.SchedulePersistor,
originalState *newagenttools.ScheduleState,
writePreview newagentmodel.WriteSchedulePreviewFunc,
emitter *newagentstream.ChunkEmitter,
) error {
pending := runtimeState.PendingConfirmTool
@@ -804,12 +851,53 @@ func executePendingTool(
ToolName: pending.ToolName,
})
// 5. 写工具实时预览confirm accept 后真实执行写工具时,立即刷新一次预览缓存。
tryWritePreviewAfterWriteTool(ctx, flowState, scheduleState, registry, pending.ToolName, writePreview)
// 6. 清空临时邮箱,避免重复执行。
runtimeState.PendingConfirmTool = nil
return nil
}
// tryWritePreviewAfterWriteTool 在写工具执行后尝试刷新一次排程预览缓存。
//
// 职责边界:
// 1. 只负责“写工具后实时可见”的旁路写入,不负责最终收口;
// 2. 只在 write tool 命中时执行,读工具直接跳过;
// 3. 失败只记日志,不影响主流程,避免因为缓存抖动打断执行。
func tryWritePreviewAfterWriteTool(
ctx context.Context,
flowState *newagentmodel.CommonState,
scheduleState *newagenttools.ScheduleState,
registry *newagenttools.ToolRegistry,
toolName string,
writePreview newagentmodel.WriteSchedulePreviewFunc,
) {
if flowState == nil || scheduleState == nil || registry == nil || writePreview == nil {
return
}
if !registry.IsWriteTool(toolName) {
return
}
if err := writePreview(ctx, scheduleState, flowState.UserID, flowState.ConversationID, flowState.TaskClassIDs); err != nil {
log.Printf(
"[WARN] execute realtime preview write failed chat=%s tool=%s err=%v",
flowState.ConversationID,
toolName,
err,
)
return
}
log.Printf(
"[DEBUG] execute realtime preview write success chat=%s tool=%s",
flowState.ConversationID,
toolName,
)
}
// listItemRe 匹配被粘连在一起的列表序号(如 "2. " "水课3. "),用于自动补换行。
// 规则:非换行字符后紧跟 2-9 的序号("2. " "3、" 等),说明 LLM 漏写了换行。
var listItemRe = regexp.MustCompile(`([^\n])([2-9][\.、]\s)`)