OpenCode Agent 执行循环详解

张开发
2026/4/12 15:47:40 15 分钟阅读

分享文章

OpenCode Agent 执行循环详解
一、整体架构概述OpenCode 的 Agent 执行循环采用双层循环架构由外层的loop()函数和内层的process()函数组成。外层循环负责管理多轮对话的流程控制内层循环负责处理单次 LLM 调用的流式响应。两层之间通过消息历史、共享状态对象和返回值进行联动。二、外层循环2.1 位置与入口外层循环定义在session/prompt.ts文件的loop()函数中是 Agent 执行的主入口。2.2 核心职责外层循环负责以下核心职责第一消息历史管理。每一轮循环开始时从数据库中获取当前会话的所有历史消息包括用户消息、助手消息、工具调用记录等。这些消息经过过滤排除已压缩的部分后作为下一轮处理的输入。第二状态检测与判断。通过遍历历史消息识别最后一条用户消息和最后一条助手消息。根据助手消息的finish字段判断是否应该结束循环。如果finish值为 “stop”、“length” 等非 “tool-calls” 的值说明模型已完成响应可以退出循环。第三特殊任务处理。外层循环负责处理两种特殊任务子任务和上下文压缩。当检测到历史消息中存在待处理的子任务时会直接调用 TaskTool 执行子任务。当检测到上下文溢出时会触发压缩流程。第四创建处理器并调用内层循环。在正常处理流程中外层循环会创建一个 SessionProcessor 实例预创建一条助手消息然后调用内层的process()方法。第五根据返回值控制流程。内层循环返回三种结果“continue” 表示继续下一轮循环“stop” 表示停止循环“compact” 表示需要压缩上下文。2.3 循环终止条件外层循环在以下情况下终止模型的finish字段表示已完成非 “tool-calls” 或 “unknown”内层循环返回 “stop”用户取消操作abort 信号触发发生不可恢复的错误2.4 最大步数限制每个 Agent 可以配置最大步数限制。当循环次数达到配置的最大步数时会在发送给模型的消息中追加一条提示要求模型必须在当前轮次结束。三、内层循环3.1 位置与结构内层循环定义在session/processor.ts文件的process()方法中由 SessionProcessor.create() 创建的处理器实例提供。3.2 核心职责内层循环负责以下核心职责第一调用 LLM 流式接口。通过LLM.stream()函数调用 AI 模型的流式 API获取一个异步流对象。第二处理流式响应事件。遍历流对象处理各种类型的事件start 事件标记会话状态为忙碌text-start/text-delta/text-end 事件处理文本输出实时更新到数据库reasoning-start/reasoning-delta/reasoning-end 事件处理推理过程输出tool-input-start/tool-call 事件处理工具调用开始创建工具调用记录tool-result 事件处理工具执行成功的结果tool-error 事件处理工具执行失败的情况start-step/finish-step 事件标记一个处理步骤的开始和结束记录 token 使用量第三工具执行。当模型发起工具调用时内层循环会执行相应的工具并将结果保存到消息历史中。第四Doom Loop 检测。检测是否出现连续三次相同的工具调用如果检测到则触发权限询问防止无限循环。第五错误处理与重试。捕获执行过程中的错误判断是否可重试如果可重试则等待后继续循环。第六返回控制信号。根据处理结果返回相应的控制信号给外层循环。3.3 返回值含义内层循环返回三种值“continue”正常完成一轮处理可以继续下一轮“stop”遇到阻塞如权限被拒绝或错误应该停止循环“compact”上下文溢出需要压缩后继续3.4 状态管理内层循环维护以下状态toolcalls一个字典记录当前正在执行的工具调用键为工具调用 ID值为工具部分的完整信息snapshot当前步骤的快照用于追踪文件变更blocked是否被阻塞权限被拒绝等attempt重试次数needsCompaction是否需要压缩上下文四、内外层循环的联动机制4.1 数据传递外层循环通过MessageV2.toModelMessages()函数将内部消息格式转换为 AI SDK 要求的 ModelMessage 格式。这个转换过程会将用户消息的各个部分文本、文件等和助手消息的各个部分文本、工具调用、工具结果等转换为模型能够理解的格式。4.2 状态共享外层循环在调用内层循环前会预创建一条助手消息。这个消息对象通过引用传递给内层循环内层循环在处理过程中会直接修改这个对象的属性如添加 parts、设置 finish 值、记录 token 使用量等。由于是引用传递外层循环可以实时看到这些修改。4.3 结果持久化内层循环在处理每个事件时都会调用Session.updatePart()将结果持久化到数据库。这意味着文本输出实时保存工具调用状态实时更新工具执行结果实时记录这种设计保证了即使程序意外中断也能从断点恢复。4.4 流程控制内层循环通过返回值控制外层循环的行为。外层循环根据返回值决定是继续循环、停止循环还是触发压缩流程。4.5 消息累积效应每一轮循环结束后新产生的消息内容工具调用、工具结果、文本输出等都被保存到数据库。下一轮循环开始时外层循环会重新获取完整的历史消息包括上一轮新增的内容。这种累积效应使得模型能够在多轮工具调用中保持上下文连贯。五、完整执行流程示例假设用户输入“读取 config.ts 文件并解释它的作用”第一轮外层循环外层循环首先获取历史消息此时只有一条用户消息。由于没有助手消息或助手消息未完成循环继续。外层循环创建处理器预创建一条空的助手消息然后调用内层循环。第一轮内层循环内层循环调用 LLM.stream()模型分析用户请求后决定调用 read 工具。流式响应依次产生tool-input-start 事件创建工具调用记录tool-call 事件触发工具执行read 工具读取文件内容后返回tool-result 事件保存执行结果。最后产生 finish-step 事件finish 值为 “tool-calls”。内层循环返回 “continue”。第二轮外层循环外层循环重新获取历史消息现在包含用户消息和助手消息带有 read 工具调用和结果。检测到 finish 值为 “tool-calls”说明模型还需要继续循环继续。外层循环创建新的处理器调用内层循环。第二轮内层循环内层循环调用 LLM.stream()模型看到之前的工具结果文件内容开始生成解释文本。流式响应产生 text-delta 事件实时输出解释内容。最后产生 finish-step 事件finish 值为 “stop”。内层循环返回 “continue”。循环结束外层循环检测到 finish 值为 “stop”退出循环返回最终的助手消息给用户。六、关键设计特点总结第一职责分离。外层循环关注宏观流程控制内层循环关注微观事件处理各司其职。第二实时持久化。所有状态变更都实时保存到数据库保证可恢复性。第三流式处理。采用异步流处理模型响应支持实时输出和长时间运行。第四安全防护。通过 Doom Loop 检测、权限检查、最大步数限制等多重机制防止异常情况。第五灵活扩展。消息采用 Part 数组结构易于添加新的消息类型和处理逻辑。

更多文章