mirror of
https://github.com/sipeed/picoclaw.git
synced 2026-06-12 18:08:54 +00:00
8.0 KiB
8.0 KiB
loop.go 冲突详细分析
概述
loop.go 有 11 处冲突,涉及核心架构差异:
- HEAD (feat/subturn-poc): 基于 context 的 SubTurn 层级管理,使用
activeTurnStatesmap 支持并发 - Incoming (refactor/agent): 事件驱动架构,使用
EventBus、HookManager,单个activeTurn不支持并发 turn
关键发现:Incoming 的并发限制
重要: Incoming 分支的 activeTurn 设计不支持并发 turn 执行!
// Incoming 的实现
func (al *AgentLoop) runTurn(ctx context.Context, ts *turnState) (turnResult, error) {
al.registerActiveTurn(ts) // 设置 al.activeTurn = ts
defer al.clearActiveTurn(ts) // 清除 al.activeTurn = nil
// ...
}
func (al *AgentLoop) registerActiveTurn(ts *turnState) {
al.activeTurnMu.Lock()
defer al.activeTurnMu.Unlock()
al.activeTurn = ts // 单例!后面的会覆盖前面的
}
问题:
- 如果两个 session 同时调用
runAgentLoop,第二个会覆盖第一个的activeTurn GetActiveTurn()只能返回最后一个注册的 turn- 中断操作 (
InterruptGraceful,InterruptHard) 只能影响当前的activeTurn
HEAD 的优势:
// HEAD 的实现
activeTurnStates sync.Map // 支持多个并发 turn
// key: sessionKey, value: *turnState
// 每个 session 有独立的 turnState
al.activeTurnStates.Store(opts.SessionKey, rootTS)
架构决策的影响
如果采用 Incoming 的架构(方案 B),我们会失去并发 turn 的能力!
选项分析
选项 1: 完全采用 Incoming(会失去并发)
- ✅ 获得事件驱动架构
- ✅ 获得 Hook 系统
- ❌ 失去并发 turn 支持
- ❌ 失去 SubTurn 并发支持
- ❌ 多个 session 无法同时处理
选项 2: 混合方案(推荐)
- ✅ 保留 HEAD 的
activeTurnStates sync.Map - ✅ 采用 Incoming 的
EventBus和HookManager - ✅ 保持并发能力
- ⚠️ 需要调整
GetActiveTurn()等 API
选项 3: 改造 Incoming 支持并发
- 将
activeTurn *turnState改为activeTurns sync.Map - 修改所有相关方法支持 sessionKey 参数
- 工作量大,但架构更清晰
推荐方案:选项 2(混合方案)
AgentLoop 结构体设计
type AgentLoop struct {
// Incoming 的字段
bus *bus.MessageBus
cfg *config.Config
registry *AgentRegistry
state *state.Manager
eventBus *EventBus // ✅ 保留
hooks *HookManager // ✅ 保留
hookRuntime hookRuntime // ✅ 保留
running atomic.Bool
summarizing sync.Map
fallback *providers.FallbackChain
channelManager *channels.Manager
mediaStore media.MediaStore
transcriber voice.Transcriber
cmdRegistry *commands.Registry
mcp mcpRuntime
steering *steeringQueue
mu sync.RWMutex
// HEAD 的并发支持(保留)
activeTurnStates sync.Map // ✅ 保留:支持并发 turn
subTurnCounter atomic.Int64 // ✅ 保留:SubTurn ID 生成
// Incoming 的字段(调整)
turnSeq atomic.Uint64 // ✅ 保留:全局 turn 序列号
activeRequests sync.WaitGroup // ✅ 保留:请求跟踪
reloadFunc func() error
}
关键方法调整
- GetActiveTurn(): 需要接受 sessionKey 参数
- InterruptGraceful/Hard(): 需要接受 sessionKey 参数
- runAgentLoop(): 使用
activeTurnStates而不是单个activeTurn
冲突详情
冲突 1: AgentLoop 结构体 (38-77 行)
HEAD 新增字段:
activeTurnStates sync.Map // key: sessionKey (string), value: *turnState
subTurnCounter atomic.Int64 // Counter for generating unique SubTurn IDs
Incoming 新增字段:
eventBus *EventBus
hooks *HookManager
hookRuntime hookRuntime
activeTurnMu sync.RWMutex
activeTurn *turnState
turnSeq atomic.Uint64
activeRequests sync.WaitGroup
关键差异:
- HEAD: 使用
sync.Map管理多个并发 turn (activeTurnStates) - Incoming: 使用单个
activeTurn+ 锁 (activeTurnMu) - HEAD: SubTurn 计数器 (
subTurnCounter) - Incoming: Turn 序列号 (
turnSeq) - Incoming: 新增事件系统 (
eventBus,hooks,hookRuntime)
解决方案: 采用 Incoming 的结构,但需要考虑如何在新架构中实现 SubTurn 的并发管理。
冲突 2: processOptions 结构体 (92-112 行)
HEAD:
SkipAddUserMessage bool // If true, skip adding UserMessage to session history
Incoming:
InitialSteeringMessages []providers.Message
// 新增结构体
type continuationTarget struct {
SessionKey string
Channel string
ChatID string
}
关键差异:
- HEAD: 使用
SkipAddUserMessage标志 - Incoming: 使用
InitialSteeringMessages数组 + 新的continuationTarget结构体
解决方案: 采用 Incoming 的实现,InitialSteeringMessages 提供更灵活的 steering 消息处理。
冲突 3: runAgentLoop 函数 (1439-1581 行)
这是最大的冲突,涉及核心执行逻辑。
HEAD 的实现:
- 检查是否在 SubTurn 中 (
turnStateFromContext) - 如果是 SubTurn,复用现有 turnState
- 如果是根 turn,创建新的 rootTS
- 使用
activeTurnStates.Store注册 turn - 调用
runLLMIteration执行 LLM 循环
Incoming 的实现:
- 记录 last channel
- 调用
newTurnState创建 turn state - 调用
al.runTurn(ctx, ts)执行 turn - 处理 follow-up 消息
- 发布响应
关键差异:
- HEAD: 复杂的 SubTurn 层级管理,支持嵌套
- Incoming: 简化的 turn 管理,通过
newTurnState和runTurn - HEAD: 使用
runLLMIteration函数 - Incoming: 使用
runTurn函数 - Incoming: 新增 follow-up 消息处理机制
解决方案: 采用 Incoming 的简化架构,但需要在 runTurn 中添加 SubTurn 支持。
冲突 4: runLLMIteration vs runTurn (1672-1689 行)
HEAD: 有独立的 runLLMIteration 函数
Incoming: 使用 runTurn 函数
需要查看具体实现来决定如何合并。
冲突 5-11: 其他冲突点
剩余冲突主要涉及:
- 工具执行逻辑
- Steering 消息处理
- 中断处理
- 变量命名差异(
agentvsts.agent)
架构决策
根据方案 B(采用重构架构),需要:
-
采用 Incoming 的 AgentLoop 结构
- 使用
eventBus,hooks,hookRuntime - 使用单个
activeTurn+activeTurnMu - 保留
turnSeq
- 使用
-
SubTurn 支持策略
- 选项 A: 在
turnState中添加父子关系字段 - 选项 B: 使用 context 传递 SubTurn 信息
- 选项 C: 在 EventBus 中管理 SubTurn 层级
- 选项 A: 在
-
函数迁移顺序
- 先采用 Incoming 的结构体定义
- 更新
newTurnState函数 - 采用
runTurn函数 - 在
runTurn中集成 SubTurn 逻辑
推荐实施步骤
步骤 1: 结构体定义 (30 分钟)
- 采用 Incoming 的
AgentLoop结构体 - 采用 Incoming 的
processOptions结构体 - 添加
continuationTarget结构体
步骤 2: 辅助函数 (30 分钟)
- 更新
NewAgentLoop初始化函数 - 确保 EventBus、Hook 正确初始化
步骤 3: runAgentLoop 函数 (1-2 小时)
- 采用 Incoming 的简化实现
- 保留 channel 记录逻辑
- 调用
newTurnState和runTurn - 处理 follow-up 消息
步骤 4: runTurn 函数 (2-3 小时)
- 采用 Incoming 的
runTurn实现 - 在其中添加 SubTurn 检测和处理逻辑
- 集成 SubTurn 结果回传机制
步骤 5: 其他冲突点 (1-2 小时)
- 逐个解决剩余 7 个冲突
- 确保变量命名一致
- 更新工具执行和 steering 逻辑
风险和注意事项
- SubTurn 语义变化: 新架构中 SubTurn 的实现方式可能不同
- 并发安全: 从
sync.Map迁移到单个activeTurn+ 锁 - 事件系统集成: 需要确保 SubTurn 事件正确触发
- 测试覆盖: 原有 SubTurn 测试需要更新
下一步
建议先实现步骤 1-2(结构体定义和初始化),然后再处理复杂的执行逻辑。