picoclaw/flow_diagrams.md

396 lines
21 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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 并发