4.4 SessionProcessor:Agentic Loop 的核心

模型: claude-opus-4-6 (anthropic/claude-opus-4-6) 生成日期: 2025-02-17


SessionProcessorsession/processor.ts)是 OpenCode 最关键的模块——它实现了 Agentic Loop 的完整逻辑。理解它就理解了 AI Agent 如何"思考"和"行动"。

4.4.1 流式处理的状态机

SessionProcessor.create() 返回一个处理器对象,核心是 process() 方法。这个方法内部是一个 while(true) 循环——每一轮循环代表 LLM 的一次生成步骤(Step)。

状态机的完整事件处理(源码简化版):

async process(streamInput: LLM.StreamInput) {
  while (true) {
    const stream = await LLM.stream(streamInput)

    for await (const value of stream.fullStream) {
      input.abort.throwIfAborted()  // 检查是否被用户中止

      switch (value.type) {
        // ═══ 思考链处理 ═══
        case "reasoning-start":
          // 创建新的 ReasoningPart
          reasoningMap[value.id] = {
            type: "reasoning", text: "",
            time: { start: Date.now() },
          }
          break

        case "reasoning-delta":
          // 追加思考链文本增量
          reasoningMap[value.id].text += value.text
          await Session.updatePart({ part, delta: value.text })
          break

        case "reasoning-end":
          // 完成思考链,记录结束时间
          part.time.end = Date.now()
          await Session.updatePart(part)
          break

        // ═══ 文本生成处理 ═══
        case "text-start":
          currentText = { type: "text", text: "", time: { start: Date.now() } }
          break

        case "text-delta":
          currentText.text += value.text
          await Session.updatePart({ part: currentText, delta: value.text })
          break

        // ═══ 工具调用处理 ═══
        case "tool-input-start":
          // 创建 ToolPart,状态 = pending
          toolcalls[value.id] = await Session.updatePart({
            type: "tool", tool: value.toolName,
            state: { status: "pending", input: {}, raw: "" },
          })
          break

        case "tool-call":
          // 参数完整,状态 → running
          // ★ 此处执行 Doom Loop 检测 ★
          break

        case "tool-result":
          // 执行成功,状态 → completed
          break

        case "tool-error":
          // 执行失败,状态 → error
          break

        // ═══ 步骤边界 ═══
        case "start-step":
          snapshot = await Snapshot.track()  // 创建文件快照
          break

        case "finish-step":
          // 统计 token、成本
          // 检查是否需要 Compaction
          break
      }
    }

    // 本轮完成后的判断逻辑...
  }
}

4.4.2 工具执行循环

工具调用的完整生命周期在 Processor 中体现为四个事件:

Doom Loop 检测(源码):

衍生概念:什么是 Doom Loop?

在 AI Agent 系统中,Doom Loop(死亡循环)是指 Agent 反复执行相同的操作,却期望得到不同的结果。例如:

  1. AI 读取文件 → 发现错误 → 修改文件 → 修改失败 → 读取文件 → 发现同样的错误 → 修改文件 → 修改又失败 → ...

  2. AI 执行测试 → 测试失败 → 修改代码 → 修改后代码相同 → 执行测试 → 测试又失败 → ...

OpenCode 的检测策略很直接:如果最近 3 次工具调用是同一个工具参数完全相同,就判定为 Doom Loop。此时会触发 doom_loop 权限检查,默认配置是 "ask"——暂停执行,让用户决定是否继续。

4.4.3 Snapshot 触发时机

在每个 Step 的开始和结束时,Processor 都会与 Snapshot 系统交互:

这意味着每一步(Step)的文件变更都被记录为一个 Patch。用户可以查看每一步修改了哪些文件,也可以通过 Revert 功能回滚到任意步骤。

4.4.4 Retry 机制

session/retry.ts 实现了对 LLM API 调用失败的自动重试:

重试触发条件:

  • API 返回可重试错误(如速率限制 429、服务端错误 500)

  • 输出长度超限(OutputLengthError

  • 网络错误

重试策略:

  • 使用指数退避(Exponential Backoff)

  • 重试时会创建 RetryPart 记录,让用户可以看到重试历史

  • 有最大重试次数限制

4.4.5 错误恢复策略

Processor 中的错误处理分为几个层级:

关键设计:工具执行失败不会中断 Agentic Loop。错误信息会作为工具结果返回给 LLM,让 AI 自己决定如何处理——这正是"Agentic"的核心:AI 有能力从错误中恢复。

Last updated