Version: 0.9.75.dev.260505

后端:
1.收口阶段 6 agent 结构迁移,将 newAgent 内核与 agentsvc 编排层迁入 services/agent
- 切换 Agent 启动装配与 HTTP handler 直连 agent sv,移除旧 service agent bridge
- 补齐 Agent 对 memory、task、task-class、schedule 的 RPC 适配与契约字段
- 扩展 schedule、task、task-class RPC/contract 支撑 Agent 查询、写入与 provider 切流
- 更新迁移文档、README 与相关注释,明确 agent 当前切流点和剩余 memory 迁移面
This commit is contained in:
Losita
2026-05-05 16:00:57 +08:00
parent e1819c5653
commit d7184b776b
174 changed files with 2189 additions and 1236 deletions

View File

@@ -0,0 +1,208 @@
package agentnode
import (
"context"
"encoding/json"
"fmt"
"strings"
"time"
agentmodel "github.com/LoveLosita/smartflow/backend/services/agent/model"
agentstream "github.com/LoveLosita/smartflow/backend/services/agent/stream"
)
const (
confirmStageName = "confirm"
confirmStatusBlockID = "confirm.status"
)
// ConfirmNodeInput 描述确认节点单轮运行所需的最小依赖。
//
// 职责边界:
// 1. 不需要 LLM Client — 确认内容由已有状态机械格式化,不调模型;
// 2. RuntimeState 提供计划步骤和待确认工具快照;
// 3. ChunkEmitter 负责推送确认事件到前端。
type ConfirmNodeInput struct {
RuntimeState *agentmodel.AgentRuntimeState
ConversationContext *agentmodel.ConversationContext
ChunkEmitter *agentstream.ChunkEmitter
}
// RunConfirmNode 执行一轮确认节点逻辑。
//
// 核心职责:
// 1. 判断确认来源:有 PendingConfirmTool → 工具确认;有 PlanSteps → 计划确认;
// 2. 机械格式化确认内容(不需要 LLM 调用);
// 3. 推送确认事件 EmitConfirmRequest → 前端渲染确认卡片;
// 4. 调用 OpenConfirmInteraction 固化中断快照Phase 自动变为 interrupted。
//
// 设计原则:
// 1. 不等待用户响应 — 等待是 interruptNode 的职责;
// 2. 不执行任何工具 — 只固化"意图",执行留给恢复后的 Execute
// 3. Confirm 是图里唯一负责"生成确认事件 + 固化快照"的地方,上游节点只设 Phase。
func RunConfirmNode(ctx context.Context, input ConfirmNodeInput) error {
runtimeState, _, emitter, err := prepareConfirmNodeInput(input)
if err != nil {
return err
}
flowState := runtimeState.EnsureCommonState()
// 优先处理工具确认Execute 发起的写操作确认)。
if runtimeState.PendingConfirmTool != nil {
return handleToolConfirm(ctx, runtimeState, flowState, emitter)
}
// 其次处理计划确认Plan 完成后的整体验收)。
if flowState.HasPlan() {
return handlePlanConfirm(ctx, runtimeState, flowState, emitter)
}
// 既没有工具也没有计划 → 异常状态,不应到达此处。
return fmt.Errorf("confirm node: 没有可确认的内容(无计划、无待确认工具)")
}
// handlePlanConfirm 处理计划确认。
//
// 流程:
// 1. 从 flowState.PlanSteps 格式化可读摘要;
// 2. 推送确认事件到前端;
// 3. 调用 OpenConfirmInteraction 固化快照(无 PendingTool
func handlePlanConfirm(
ctx context.Context,
runtimeState *agentmodel.AgentRuntimeState,
flowState *agentmodel.CommonState,
emitter *agentstream.ChunkEmitter,
) error {
summary := buildPlanSummary(flowState.PlanSteps)
interactionID := generateConfirmInteractionID(flowState)
if err := emitter.EmitConfirmRequest(
ctx, confirmStatusBlockID, confirmStageName,
interactionID,
"计划确认",
summary,
agentstream.DefaultPseudoStreamOptions(),
); err != nil {
return fmt.Errorf("计划确认事件推送失败: %w", err)
}
runtimeState.OpenConfirmInteraction(
interactionID,
summary,
"plan",
nil,
)
_ = emitter.EmitStatus(
confirmStatusBlockID, confirmStageName,
"plan_confirm", "计划已生成,等待用户确认。", false,
)
return nil
}
// handleToolConfirm 处理工具确认。
//
// 流程:
// 1. 从 PendingConfirmTool 构建确认摘要;
// 2. 推送确认事件到前端;
// 3. 调用 OpenConfirmInteraction 固化快照(含 PendingTool
// 4. 清空 PendingConfirmTool 临时邮箱。
func handleToolConfirm(
ctx context.Context,
runtimeState *agentmodel.AgentRuntimeState,
flowState *agentmodel.CommonState,
emitter *agentstream.ChunkEmitter,
) error {
pendingTool := runtimeState.PendingConfirmTool
summary := buildToolConfirmSummary(pendingTool)
interactionID := generateConfirmInteractionID(flowState)
if err := emitter.EmitConfirmRequest(
ctx, confirmStatusBlockID, confirmStageName,
interactionID,
"操作确认",
summary,
agentstream.DefaultPseudoStreamOptions(),
); err != nil {
return fmt.Errorf("工具确认事件推送失败: %w", err)
}
runtimeState.OpenConfirmInteraction(
interactionID,
summary,
"execute",
pendingTool,
)
// 确认快照已固化到 PendingInteraction清空临时邮箱。
runtimeState.PendingConfirmTool = nil
_ = emitter.EmitStatus(
confirmStatusBlockID, confirmStageName,
"tool_confirm", "操作等待确认。", false,
)
return nil
}
// buildPlanSummary 把 PlanSteps 格式化成人类可读的确认摘要。
func buildPlanSummary(steps []agentmodel.PlanStep) string {
var sb strings.Builder
sb.WriteString(fmt.Sprintf("共 %d 步:\n", len(steps)))
for i, step := range steps {
sb.WriteString(fmt.Sprintf("%d. %s", i+1, step.Content))
if step.DoneWhen != "" {
sb.WriteString(fmt.Sprintf("(完成条件:%s", step.DoneWhen))
}
sb.WriteString("\n")
}
return strings.TrimSpace(sb.String())
}
// buildToolConfirmSummary 从工具快照构建确认摘要。
func buildToolConfirmSummary(tool *agentmodel.PendingToolCallSnapshot) string {
if tool == nil {
return "待确认操作"
}
if tool.Summary != "" {
return tool.Summary
}
detail := fmt.Sprintf("即将执行工具:%s", tool.ToolName)
if tool.ArgsJSON != "" {
var args map[string]any
if json.Unmarshal([]byte(tool.ArgsJSON), &args) == nil && len(args) > 0 {
detail += fmt.Sprintf(",参数:%s", tool.ArgsJSON)
}
}
return detail
}
// generateConfirmInteractionID 生成确认交互的唯一标识。
func generateConfirmInteractionID(flowState *agentmodel.CommonState) string {
prefix := flowState.TraceID
if prefix == "" {
prefix = "confirm"
}
return fmt.Sprintf("%s-%d", prefix, time.Now().UnixMilli())
}
// prepareConfirmNodeInput 校验并准备确认节点的运行态依赖。
func prepareConfirmNodeInput(input ConfirmNodeInput) (
*agentmodel.AgentRuntimeState,
*agentmodel.ConversationContext,
*agentstream.ChunkEmitter,
error,
) {
if input.RuntimeState == nil {
return nil, nil, nil, fmt.Errorf("confirm node: runtime state 不能为空")
}
input.RuntimeState.EnsureCommonState()
if input.ConversationContext == nil {
input.ConversationContext = agentmodel.NewConversationContext("")
}
if input.ChunkEmitter == nil {
input.ChunkEmitter = agentstream.NewChunkEmitter(
agentstream.NoopPayloadEmitter(), "", "", time.Now().Unix(),
)
}
return input.RuntimeState, input.ConversationContext, input.ChunkEmitter, nil
}