Version: 0.9.11.dev.260409
后端: 1. conv 并行迁移与切流接线(旧目录下沉到 newAgent/conv) - 新建 newAgent/conv/schedule_provider.go、schedule_state.go、schedule_preview.go、schedule_persist.go,保持原有排程转换/预览/持久化能力; - 删除旧目录 conv/schedule_provider.go、schedule_state.go、schedule_preview.go、schedule_persist.go; - 更新 cmd/start.go 与 service/agentsvc/agent_newagent.go,ScheduleProvider/SchedulePersistor 与 preview 转换统一切到 newAgent/conv; - 删除旧 conv/schedule_state_test.go(迁移期测试文件清理)。 2. execute 循环上下文收口增强(历史归档 + 当前轮清晰化) - 更新 node/chat.go:仅在 completed 收口时写 execute_loop_closed marker,供后续 prompt 分层归档; - 更新 prompt/execute_context.go:msg1/msg2 升级为 V3,按收口标记拆分“历史归档 loop / 当前活跃 loop”,并增加 msg1 长度预算裁剪; - 更新 node/execute.go:新增 execute 置顶上下文同步(execution_context/current_step),在轮次开始与 next_plan 后即时刷新; - 更新 prompt/execute.go + execute_context.go:补齐“当前计划步骤 + done_when”强约束,禁止未达成判定时提前 next_plan。 3. 图路由与执行策略微调 - 更新 graph/common_graph.go:Plan/Confirm 分支允许直接进入 Deliver 收口; - 更新 node/plan.go:always_execute 链路下补发计划摘要并写入历史,保证自动执行与手动确认文案一致; - 更新 model/common_state.go:DefaultMaxRounds 从 30 提升到 60。 4. 复合工具规划器重构(去重实现,复用 logic 公共能力) - 更新 tools/compound_tools.go:min_context_switch / spread_even 改为调用 backend/logic 规划器(PlanMinContextSwitchMoves / PlanEvenSpreadMoves); - 新增 state_id↔logic_id 映射层,统一入参与回填,避免工具层与规划层 ID 语义耦合; - 删除 compound_tools 内部重复的规划/归一化/分组/打分实现,减少第三份复制逻辑。 5. 同步调试与文档 - 更新 newAgent/Log.txt 调试日志; - 新增 memory/记忆模块实施计划.md(面试优先版到产品可用版的落地路线)。 前端:无 仓库:无
This commit is contained in:
@@ -19,6 +19,11 @@ const (
|
||||
chatStageName = "chat"
|
||||
chatStatusBlockID = "chat.status"
|
||||
chatSpeakBlockID = "chat.speak"
|
||||
// chatHistoryKindKey 用于在 history 中打运行态标记,供 prompt 层做上下文分层。
|
||||
chatHistoryKindKey = "newagent_history_kind"
|
||||
// chatHistoryKindExecuteLoopClosed 表示“上一轮 execute loop 已正常收口”。
|
||||
// prompt 侧会据此把旧 loop 归档到 msg1,而不是继续占用 msg2 窗口。
|
||||
chatHistoryKindExecuteLoopClosed = "execute_loop_closed"
|
||||
)
|
||||
|
||||
type reorderPreference int
|
||||
@@ -70,6 +75,12 @@ func RunChatNode(ctx context.Context, input ChatNodeInput) error {
|
||||
if !runtimeState.HasPendingInteraction() && flowState.Phase == newagentmodel.PhaseDone {
|
||||
terminalBefore := flowState.TerminalStatus()
|
||||
roundBefore := flowState.RoundUsed
|
||||
// 1. 只有“正常完成(completed)”才打 loop 收口标记:
|
||||
// 1.1 这样下一轮进入 execute 时,msg2 会只保留“当前活跃循环”窗口;
|
||||
// 1.2 异常收口(exhausted/aborted)不打标记,允许后续“继续”时沿用上一轮 loop 轨迹。
|
||||
if terminalBefore == newagentmodel.FlowTerminalStatusCompleted {
|
||||
appendExecuteLoopClosedMarker(conversationContext)
|
||||
}
|
||||
flowState.ResetForNextRun()
|
||||
log.Printf(
|
||||
"[DEBUG] chat reset runtime for next run chat=%s round_before=%d terminal_before=%s",
|
||||
@@ -139,6 +150,45 @@ func RunChatNode(ctx context.Context, input ChatNodeInput) error {
|
||||
}
|
||||
}
|
||||
|
||||
// appendExecuteLoopClosedMarker 在 history 中写入“execute loop 已正常收口”标记。
|
||||
//
|
||||
// 职责边界:
|
||||
// 1. 只负责写一个轻量 marker,供 prompt 分层;
|
||||
// 2. 不负责历史裁剪,不负责消息摘要;
|
||||
// 3. 若末尾已经是同类 marker,则幂等跳过,避免重复写入。
|
||||
func appendExecuteLoopClosedMarker(conversationContext *newagentmodel.ConversationContext) {
|
||||
if conversationContext == nil {
|
||||
return
|
||||
}
|
||||
|
||||
history := conversationContext.HistorySnapshot()
|
||||
if len(history) > 0 {
|
||||
last := history[len(history)-1]
|
||||
if isExecuteLoopClosedMarker(last) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
conversationContext.AppendHistory(&schema.Message{
|
||||
Role: schema.Assistant,
|
||||
Content: "",
|
||||
Extra: map[string]any{
|
||||
chatHistoryKindKey: chatHistoryKindExecuteLoopClosed,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func isExecuteLoopClosedMarker(msg *schema.Message) bool {
|
||||
if msg == nil || msg.Extra == nil {
|
||||
return false
|
||||
}
|
||||
kind, ok := msg.Extra[chatHistoryKindKey].(string)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
return strings.TrimSpace(kind) == chatHistoryKindExecuteLoopClosed
|
||||
}
|
||||
|
||||
// handleDirectReply 处理简单任务:直接输出回复。
|
||||
func handleDirectReply(
|
||||
ctx context.Context,
|
||||
|
||||
@@ -130,6 +130,10 @@ func RunExecuteNode(ctx context.Context, input ExecuteNodeInput) error {
|
||||
flowState.SuggestedOrderBaseline = buildSuggestedOrderSnapshot(input.ScheduleState)
|
||||
}
|
||||
|
||||
// 1. 每轮 execute 开始前先刷新一次执行锚点,避免 LLM 继续读取旧的当前步骤。
|
||||
// 2. 这里仅维护上下文一致性,不改变流程状态。
|
||||
syncExecutePinnedContext(conversationContext, flowState)
|
||||
|
||||
// 2. 推送执行阶段状态,让前端知道当前进度。
|
||||
if flowState.HasCurrentPlanStep() {
|
||||
// 有 plan:显示步骤进度。
|
||||
@@ -400,6 +404,9 @@ func RunExecuteNode(ctx context.Context, input ExecuteNodeInput) error {
|
||||
// 所有步骤已完成,进入交付阶段。
|
||||
flowState.Done()
|
||||
}
|
||||
// 1. next_plan 推进后立刻刷新 current_step / execution_context。
|
||||
// 2. 若计划已结束,这里会移除 current_step,避免下轮读取到旧步骤。
|
||||
syncExecutePinnedContext(conversationContext, flowState)
|
||||
return nil
|
||||
|
||||
case newagentmodel.ExecuteActionDone:
|
||||
@@ -462,6 +469,114 @@ func prepareExecuteNodeInput(input ExecuteNodeInput) (*newagentmodel.AgentRuntim
|
||||
// 1. 优先使用 LLM 输出的 speak;
|
||||
// 2. 其次使用 reason;
|
||||
// 3. 最后使用默认文案。
|
||||
// syncExecutePinnedContext 同步 execute 阶段的置顶上下文。
|
||||
//
|
||||
// 步骤说明:
|
||||
// 1. 每轮先刷新 execution_context,确保模型始终看到最新执行锚点。
|
||||
// 2. 若当前仍在计划执行且 current_step 可读,则覆盖 current_step 置顶块。
|
||||
// 3. 若计划已执行完或当前步骤不可读,则移除 current_step,避免模型误读旧步骤。
|
||||
func syncExecutePinnedContext(
|
||||
conversationContext *newagentmodel.ConversationContext,
|
||||
flowState *newagentmodel.CommonState,
|
||||
) {
|
||||
if conversationContext == nil || flowState == nil {
|
||||
return
|
||||
}
|
||||
|
||||
execContent := buildExecuteContextPinnedMarkdown(flowState)
|
||||
if strings.TrimSpace(execContent) != "" {
|
||||
conversationContext.UpsertPinnedBlock(newagentmodel.ContextBlock{
|
||||
Key: executePinnedKey,
|
||||
Title: "执行上下文",
|
||||
Content: execContent,
|
||||
})
|
||||
}
|
||||
|
||||
if !flowState.HasPlan() {
|
||||
conversationContext.RemovePinnedBlock(planCurrentStepKey)
|
||||
return
|
||||
}
|
||||
|
||||
step, ok := flowState.CurrentPlanStep()
|
||||
if !ok {
|
||||
conversationContext.RemovePinnedBlock(planCurrentStepKey)
|
||||
return
|
||||
}
|
||||
|
||||
current, total := flowState.PlanProgress()
|
||||
title := strings.TrimSpace(planCurrentStepTitle)
|
||||
if title == "" {
|
||||
title = "当前步骤"
|
||||
}
|
||||
conversationContext.UpsertPinnedBlock(newagentmodel.ContextBlock{
|
||||
Key: planCurrentStepKey,
|
||||
Title: title,
|
||||
Content: buildCurrentPlanStepPinnedMarkdown(step, current, total),
|
||||
})
|
||||
}
|
||||
|
||||
// buildExecuteContextPinnedMarkdown 构造 execute 节点给模型的执行锚点文本。
|
||||
func buildExecuteContextPinnedMarkdown(flowState *newagentmodel.CommonState) string {
|
||||
if flowState == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
lines := make([]string, 0, 8)
|
||||
if flowState.HasPlan() {
|
||||
lines = append(lines, "执行模式:计划执行(按步骤推进)")
|
||||
current, total := flowState.PlanProgress()
|
||||
lines = append(lines, fmt.Sprintf("计划进度:第 %d/%d 步", current, total))
|
||||
|
||||
if step, ok := flowState.CurrentPlanStep(); ok {
|
||||
lines = append(lines, "当前步骤:"+compactExecutePinnedText(step.Content))
|
||||
doneWhen := compactExecutePinnedText(step.DoneWhen)
|
||||
if doneWhen != "" {
|
||||
lines = append(lines, "完成判定(done_when):"+doneWhen)
|
||||
}
|
||||
lines = append(lines, "动作纪律:未满足 done_when 禁止 next_plan;满足后优先 next_plan。")
|
||||
} else {
|
||||
lines = append(lines, "当前步骤:不可读(可能已执行完成)")
|
||||
}
|
||||
} else {
|
||||
lines = append(lines, "执行模式:自由执行(无预定义步骤)")
|
||||
}
|
||||
|
||||
if flowState.MaxRounds > 0 {
|
||||
lines = append(lines, fmt.Sprintf("轮次预算:%d/%d", flowState.RoundUsed, flowState.MaxRounds))
|
||||
}
|
||||
return strings.TrimSpace(strings.Join(lines, "\n"))
|
||||
}
|
||||
|
||||
// buildCurrentPlanStepPinnedMarkdown 构造 current_step 置顶块内容。
|
||||
func buildCurrentPlanStepPinnedMarkdown(step newagentmodel.PlanStep, current, total int) string {
|
||||
lines := make([]string, 0, 4)
|
||||
lines = append(lines, fmt.Sprintf("步骤进度:第 %d/%d 步", current, total))
|
||||
|
||||
content := compactExecutePinnedText(step.Content)
|
||||
if content == "" {
|
||||
content = "(空)"
|
||||
}
|
||||
lines = append(lines, "步骤内容:"+content)
|
||||
|
||||
doneWhen := compactExecutePinnedText(step.DoneWhen)
|
||||
if doneWhen != "" {
|
||||
lines = append(lines, "完成判定:"+doneWhen)
|
||||
}
|
||||
|
||||
return strings.TrimSpace(strings.Join(lines, "\n"))
|
||||
}
|
||||
|
||||
// compactExecutePinnedText 把多行文本压成单行,避免置顶块出现冗长换行噪音。
|
||||
func compactExecutePinnedText(text string) string {
|
||||
text = strings.TrimSpace(text)
|
||||
if text == "" {
|
||||
return ""
|
||||
}
|
||||
text = strings.ReplaceAll(text, "\r\n", "\n")
|
||||
text = strings.ReplaceAll(text, "\n", ";")
|
||||
return strings.TrimSpace(text)
|
||||
}
|
||||
|
||||
func resolveExecuteAskUserText(decision *newagentmodel.ExecuteDecision) string {
|
||||
if decision == nil {
|
||||
return "执行过程中遇到不确定的情况,需要向你确认。"
|
||||
|
||||
@@ -19,6 +19,7 @@ const (
|
||||
planStageName = "plan"
|
||||
planStatusBlockID = "plan.status"
|
||||
planSpeakBlockID = "plan.speak"
|
||||
planSummaryBlockID = "plan.summary"
|
||||
planPinnedKey = "current_plan"
|
||||
planCurrentStepKey = "current_step"
|
||||
planCurrentStepTitle = "当前步骤"
|
||||
@@ -170,6 +171,23 @@ func RunPlanNode(ctx context.Context, input PlanNodeInput) error {
|
||||
// always_execute 开启时,计划层跳过确认闸门,直接进入执行阶段。
|
||||
// 这样可以与 Execute 节点的“写工具跳过确认”语义保持一致。
|
||||
if input.AlwaysExecute {
|
||||
// 1. 自动执行模式不会经过 Confirm 卡片,因此这里先把完整计划明确展示给用户。
|
||||
// 2. 摘要格式复用 Confirm 节点,保证“手动确认”和“自动执行”两条链路文案一致。
|
||||
// 3. 推流后同步写入历史,确保后续 Execute 阶段的上下文也能看到这份计划。
|
||||
summary := strings.TrimSpace(buildPlanSummary(decision.PlanSteps))
|
||||
if summary != "" {
|
||||
if err := emitter.EmitPseudoAssistantText(
|
||||
ctx,
|
||||
planSummaryBlockID,
|
||||
planStageName,
|
||||
summary,
|
||||
newagentstream.DefaultPseudoStreamOptions(),
|
||||
); err != nil {
|
||||
return fmt.Errorf("自动执行前计划摘要推送失败: %w", err)
|
||||
}
|
||||
conversationContext.AppendHistory(schema.AssistantMessage(summary, nil))
|
||||
}
|
||||
|
||||
flowState.ConfirmPlan()
|
||||
_ = emitter.EmitStatus(
|
||||
planStatusBlockID,
|
||||
|
||||
Reference in New Issue
Block a user