picoclaw/flow_diagrams.md

21 KiB
Raw Blame History

Agent Loop 流程图对比

1. Incoming (refactor/agent) 流程

整体架构

User Message
    ↓
Message Bus (串行队列)
    ↓
processMessage()
    ↓
runAgentLoop()
    ↓
newTurnState() → 创建 turnState
    ↓
runTurn()
    ↓
registerActiveTurn(ts)  ← 设置 al.activeTurn = ts (单例)
    ↓
[Turn 执行循环]
    ↓
clearActiveTurn(ts)     ← 清除 al.activeTurn = nil

runTurn() 详细流程

┌─────────────────────────────────────────┐
│ runTurn(ctx, turnState)                 │
└─────────────────────────────────────────┘
              ↓
┌─────────────────────────────────────────┐
│ 1. 注册 activeTurn (单例)               │
│    al.registerActiveTurn(ts)            │
│    defer al.clearActiveTurn(ts)         │
└─────────────────────────────────────────┘
              ↓
┌─────────────────────────────────────────┐
│ 2. 发送 TurnStart 事件                  │
│    al.emitEvent(EventKindTurnStart)     │
└─────────────────────────────────────────┘
              ↓
┌─────────────────────────────────────────┐
│ 3. 加载 Session History & Summary       │
│    history = Sessions.GetHistory()      │
│    summary = Sessions.GetSummary()      │
└─────────────────────────────────────────┘
              ↓
┌─────────────────────────────────────────┐
│ 4. 构建消息                             │
│    messages = BuildMessages(...)        │
└─────────────────────────────────────────┘
              ↓
┌─────────────────────────────────────────┐
│ 5. 检查 Context Budget                  │
│    if isOverContextBudget() {           │
│        forceCompression()               │
│        emitEvent(ContextCompress)       │
│    }                                    │
└─────────────────────────────────────────┘
              ↓
┌─────────────────────────────────────────┐
│ 6. 保存用户消息到 Session               │
│    Sessions.AddMessage("user", ...)     │
└─────────────────────────────────────────┘
              ↓
┌─────────────────────────────────────────┐
│ 7. Turn Loop (迭代执行)                 │
│    for iteration < MaxIterations {      │
│        ┌─────────────────────────────┐  │
│        │ 7.1 调用 LLM                │  │
│        │     callLLM()               │  │
│        │     emitEvent(LLMStart)     │  │
│        └─────────────────────────────┘  │
│                  ↓                       │
│        ┌─────────────────────────────┐  │
│        │ 7.2 处理 Tool Calls         │  │
│        │     for each toolCall {     │  │
│        │         emitEvent(ToolStart)│  │
│        │         executeTool()       │  │
│        │         emitEvent(ToolEnd)  │  │
│        │     }                       │  │
│        └─────────────────────────────┘  │
│                  ↓                       │
│        ┌─────────────────────────────┐  │
│        │ 7.3 检查中断                │  │
│        │     if gracefulInterrupt {  │  │
│        │         break               │  │
│        │     }                       │  │
│        └─────────────────────────────┘  │
│                  ↓                       │
│        ┌─────────────────────────────┐  │
│        │ 7.4 处理 Steering Messages  │  │
│        │     pollSteering()          │  │
│        └─────────────────────────────┘  │
│    }                                    │
└─────────────────────────────────────────┘
              ↓
┌─────────────────────────────────────────┐
│ 8. 保存最终响应到 Session               │
│    Sessions.AddMessage("assistant", ...) │
└─────────────────────────────────────────┘
              ↓
┌─────────────────────────────────────────┐
│ 9. 发送 TurnEnd 事件                    │
│    al.emitEvent(EventKindTurnEnd)       │
└─────────────────────────────────────────┘
              ↓
┌─────────────────────────────────────────┐
│ 10. 返回 turnResult                     │
│     {finalContent, status, followUps}   │
└─────────────────────────────────────────┘

关键特点

  • 事件驱动: 每个阶段都发送事件到 EventBus
  • Hook 集成: 在 before_llm, after_llm, before_tool, after_tool 触发 Hook
  • 单 Turn: 使用 activeTurn 单例,同一时间只有一个 turn
  • 无并发: 不支持多个 session 同时执行 turn

2. HEAD (feat/subturn-poc) 流程

整体架构

User Message
    ↓
Message Bus
    ↓
processMessage()
    ↓
runAgentLoop()
    ↓
检查 Context 中是否有 turnState
    ├─ 有 → 复用 (SubTurn 场景)
    └─ 无 → 创建新的 rootTS
         ↓
         存储到 activeTurnStates[sessionKey]
         ↓
         runLLMIteration()
         ↓
         [并发 SubTurn 支持]

runAgentLoop() 详细流程

┌─────────────────────────────────────────┐
│ runAgentLoop(ctx, agent, opts)          │
└─────────────────────────────────────────┘
              ↓
┌─────────────────────────────────────────┐
│ 1. 检查是否在 SubTurn 中                │
│    existingTS = turnStateFromContext()  │
│    if existingTS != nil {               │
│        rootTS = existingTS  (复用)      │
│        isRootTurn = false               │
│    } else {                             │
│        rootTS = new turnState           │
│        isRootTurn = true                │
│    }                                    │
└─────────────────────────────────────────┘
              ↓
┌─────────────────────────────────────────┐
│ 2. 注册 Turn State (支持并发)           │
│    if isRootTurn {                      │
│        al.activeTurnStates.Store(       │
│            sessionKey, rootTS)          │
│        defer activeTurnStates.Delete()  │
│    }                                    │
└─────────────────────────────────────────┘
              ↓
┌─────────────────────────────────────────┐
│ 3. 记录 Last Channel                    │
└─────────────────────────────────────────┘
              ↓
┌─────────────────────────────────────────┐
│ 4. 构建消息                             │
│    messages = BuildMessages(...)        │
│    messages = resolveMediaRefs(...)     │
└─────────────────────────────────────────┘
              ↓
┌─────────────────────────────────────────┐
│ 5. 覆盖 System Prompt (如果需要)        │
│    if opts.SystemPromptOverride != "" { │
│        // 用于 SubTurn 的特殊 prompt   │
│    }                                    │
└─────────────────────────────────────────┘
              ↓
┌─────────────────────────────────────────┐
│ 6. 保存用户消息                         │
│    if !opts.SkipAddUserMessage {        │
│        Sessions.AddMessage(...)         │
│    }                                    │
└─────────────────────────────────────────┘
              ↓
┌─────────────────────────────────────────┐
│ 7. 执行 LLM 迭代                        │
│    finalContent, iteration, err =       │
│        runLLMIteration(ctx, agent, ...) │
└─────────────────────────────────────────┘
              ↓
┌─────────────────────────────────────────┐
│ 8. 轮询 SubTurn 结果 (如果是根 turn)    │
│    if isRootTurn {                      │
│        results =                        │
│            dequeuePendingSubTurnResults()│
│        // 将结果注入到最终响应          │
│    }                                    │
└─────────────────────────────────────────┘
              ↓
┌─────────────────────────────────────────┐
│ 9. 处理空响应                           │
│    if finalContent == "" {              │
│        finalContent = DefaultResponse   │
│    }                                    │
└─────────────────────────────────────────┘
              ↓
┌─────────────────────────────────────────┐
│ 10. 保存助手响应                        │
│     Sessions.AddMessage("assistant"...) │
└─────────────────────────────────────────┘
              ↓
┌─────────────────────────────────────────┐
│ 11. 发送响应 (如果需要)                 │
│     if opts.SendResponse {              │
│         bus.PublishOutbound(...)        │
│     }                                   │
└─────────────────────────────────────────┘

SubTurn 执行流程

┌─────────────────────────────────────────┐
│ Tool: spawn                             │
│   args: {task: "...", label: "..."}    │
└─────────────────────────────────────────┘
              ↓
┌─────────────────────────────────────────┐
│ SpawnTool.Execute()                     │
│   if spawner != nil {                   │
│       // 直接 SubTurn 路径             │
│   } else {                              │
│       // SubagentManager 路径          │
│   }                                     │
└─────────────────────────────────────────┘
              ↓
┌─────────────────────────────────────────┐
│ spawner.SpawnSubTurn()                  │
│   ┌─────────────────────────────────┐   │
│   │ 1. 生成 SubTurn ID              │   │
│   │    subTurnID = atomic.Add()     │   │
│   └─────────────────────────────────┘   │
│              ↓                          │
│   ┌─────────────────────────────────┐   │
│   │ 2. 创建 SubTurn Context         │   │
│   │    subCtx = withTurnState(...)  │   │
│   │    // 继承父 turnState          │   │
│   └─────────────────────────────────┘   │
│              ↓                          │
│   ┌─────────────────────────────────┐   │
│   │ 3. 获取并发信号量               │   │
│   │    <-rootTS.concurrencySem      │   │
│   │    defer release                │   │
│   └─────────────────────────────────┘   │
│              ↓                          │
│   ┌─────────────────────────────────┐   │
│   │ 4. 启动 Goroutine               │   │
│   │    go func() {                  │   │
│   │        result = runAgentLoop(   │   │
│   │            subCtx, ...)         │   │
│   │        // 将结果发送到 channel  │   │
│   │        rootTS.pendingResults <- │   │
│   │    }()                          │   │
│   └─────────────────────────────────┘   │
└─────────────────────────────────────────┘
              ↓
┌─────────────────────────────────────────┐
│ 父 Turn 继续执行                        │
│   - 不等待 SubTurn 完成                 │
│   - SubTurn 异步执行                    │
└─────────────────────────────────────────┘
              ↓
┌─────────────────────────────────────────┐
│ 父 Turn 轮询 SubTurn 结果               │
│   results = dequeuePendingSubTurnResults│
│   for each result {                     │
│       // 注入到响应或下一次迭代         │
│   }                                     │
└─────────────────────────────────────────┘

SubTurn 层级结构

Root Turn (Session A)
  ├─ turnState (depth=0)
  │   ├─ turnID: "session-a"
  │   ├─ pendingResults: chan
  │   └─ concurrencySem: chan (限制并发数)
  │
  ├─ SubTurn 1 (depth=1)
  │   ├─ turnState (继承父 context)
  │   ├─ parentTurnID: "session-a"
  │   └─ 独立的 goroutine
  │
  ├─ SubTurn 2 (depth=1)
  │   ├─ turnState (继承父 context)
  │   ├─ parentTurnID: "session-a"
  │   └─ 独立的 goroutine
  │
  └─ SubTurn 3 (depth=1)
      └─ SubTurn 3.1 (depth=2)  ← 嵌套 SubTurn
          └─ ...

Root Turn (Session B) - 并发执行
  ├─ turnState (depth=0)
  └─ ...

关键特点

  • 并发支持: activeTurnStates map 支持多个 session 并发
  • SubTurn 层级: 通过 context 传递 turnState支持嵌套
  • 并发控制: concurrencySem 限制 SubTurn 并发数
  • 异步执行: SubTurn 在独立 goroutine 中执行
  • 结果回传: 通过 pendingResults channel 传递结果
  • 无事件系统: 没有 EventBus 和 Hook 集成

3. 对比总结

特性 Incoming (refactor/agent) HEAD (feat/subturn-poc)
并发模型 单 Turn (串行) 多 Turn (并发)
Turn 管理 activeTurn (单例) activeTurnStates (map)
事件系统 EventBus
Hook 系统 HookManager
SubTurn 未实现或不同方式 完整实现
并发 Session 不支持 支持
嵌套 SubTurn 不支持 支持
架构复杂度 简单 复杂
可扩展性 高 (Hook)
调试难度 高 (并发)

4. 混合方案流程

结合两者优点的混合方案:

┌─────────────────────────────────────────┐
│ runAgentLoop(ctx, agent, opts)          │
└─────────────────────────────────────────┘
              ↓
┌─────────────────────────────────────────┐
│ 1. 检查 SubTurn Context                 │
│    existingTS = turnStateFromContext()  │
└─────────────────────────────────────────┘
              ↓
┌─────────────────────────────────────────┐
│ 2. 创建/复用 turnState                  │
│    ts = newTurnState(agent, opts, ...)  │
│    if isRootTurn {                      │
│        activeTurnStates.Store(key, ts)  │
│    }                                    │
└─────────────────────────────────────────┘
              ↓
┌─────────────────────────────────────────┐
│ 3. 执行 Turn (带事件和 Hook)            │
│    result = runTurn(ctx, ts)            │
│    ├─ emitEvent(TurnStart)              │
│    ├─ Hook: before_llm                  │
│    ├─ callLLM()                         │
│    ├─ Hook: after_llm                   │
│    ├─ Hook: before_tool                 │
│    ├─ executeTool()                     │
│    │   └─ 如果是 spawn → SpawnSubTurn   │
│    ├─ Hook: after_tool                  │
│    └─ emitEvent(TurnEnd)                │
└─────────────────────────────────────────┘
              ↓
┌─────────────────────────────────────────┐
│ 4. 处理 SubTurn 结果                    │
│    if isRootTurn {                      │
│        pollSubTurnResults()             │
│    }                                    │
└─────────────────────────────────────────┘

混合方案优势

  • 保留并发能力 (activeTurnStates)
  • 获得事件系统 (EventBus)
  • 获得扩展能力 (HookManager)
  • 支持 SubTurn 并发
  • 支持多 Session 并发