🏛️ 整体架构
欢迎进入第四阶段!前三个阶段你学了理论(F)和框架(LangChain/LangGraph)。现在看一个真实开源的 AI 编码产品如何把这些概念落地。本章鸟瞰 OpenCode 的整体架构,特别是 Agent 循环的完整数据流——你会发现它和 F3 章的图一一对应。
本章目标
- 鸟瞰 OpenCode 的整体架构和核心模块
- 理解 Agent 循环的完整数据流(从用户输入到返回)
- 建立"模块职责映射":每个文件干什么
- 对照 F3 章的循环图,验证理论如何落地
- 认识 Effect 框架在 OpenCode 中的角色
OpenCode 是一个开源的 AI 编码 Agent 产品(类似 Claude Code / Cursor),用 TypeScript + Bun 实现。它是"把前面学的所有 Agent 理论,打磨成可靠产品"的范例。本阶段重点呈现概念对照(看理论如何落地)和工程实践(看产品怎么做)。
技术栈:TypeScript + Effect
OpenCode 用 TypeScript(Bun 运行时),并深度使用 Effect 框架。理解 Effect 是看懂 OpenCode 的前提:
| Effect 概念 | 类比 | 在 OpenCode 的作用 |
|---|---|---|
Effect.gen(function* () {...}) | async/await 的强化版 | 所有副作用都用 Effect 表达 |
Context.Service / Layer | 依赖注入容器 | 每个模块是一个 Service(Agent/Session/Tool...) |
Stream | 异步迭代器 | LLM 事件流、流式输出 |
| typed errors | 类型安全的异常 | 工具错误、权限拒绝等 |
如果你觉得 Effect 眼熟——它和 LangChain 的 Runnable 思想相通:都是"可组合、可中断、可重试的工作单元"。OpenCode 用 Effect 而非 LangChain,是因为 TS 生态和它的需求(强类型副作用管理)。看完本章你会发现思想是一致的,只是语言不同。
核心模块全景
| 模块 | 路径 | 职责 | 对应 LangGraph 概念 |
|---|---|---|---|
| Agent | src/agent/agent.ts | 定义 Agent(提示+模型+权限) | 图的配置 |
| Session | src/session/session.ts | 会话数据持久层 | Checkpointer |
| SessionPrompt | src/session/prompt.ts | Agent 循环(runLoop) | 图执行 / super-step |
| LLM | src/session/llm.ts | LLM 调用服务 | ChatModel |
| Processor | src/session/processor.ts | 流事件处理 | stream 消费 |
| Tool | src/tool/ | 工具定义与注册 | ToolNode / @tool |
| Permission | src/permission/ | 权限控制 | interrupt(HITL) |
| Provider | src/provider/ | 模型提供商抽象 | ChatOpenAI 等 |
Agent 循环完整数据流 ★
这是本章核心。OpenCode 的 Agent 循环数据流,和 F3 章的总参照图一一对应:
数据流对应的具体文件
把上面的数据流落到具体源码文件。这是后续章节的"地图":
| 数据流步骤 | 源码文件 | 精读章节 |
|---|---|---|
| 用户输入 → 创建消息 | session/prompt.ts:1052 prompt() | OC2 |
| ★ 主循环 while(true) | session/prompt.ts:1081 runLoop() | OC2 |
| 读消息历史 | MessageV2.filterCompactedEffect | OC2 |
| 组装 system prompt | session/system.ts | OC5 |
| 解析工具 | session/tools.ts:39 resolve() | OC3 |
| 调 LLM | session/llm.ts LLM.Service.stream() | OC2 |
| 处理流事件 | session/processor.ts:625 process() | OC2 |
| 执行工具 | tool/tool.ts define + wrap | OC3 |
| 权限检查 | permission + ctx.ask() | OC4 |
| 结果持久化 | session/session.ts updatePart | OC5 |
| 上下文压缩 | session/compaction.ts | OC5 |
双运行时架构
OpenCode 的 LLM 调用有个独特设计——双运行时(源自 session/llm/AGENTS.md):
AI SDK(Vercel)是成熟的多厂商抽象,但有时直接调用厂商原生 SDK 更快/更可控。OpenCode 用门控机制(flags.experimentalNativeLlm)逐请求选择:支持的模型走 native,否则 fallback 到 AI SDK。关键设计:两条路径都归一化成同一套 LLMEvent,所以 processor.ts 完全不用关心走了哪条——这是适配器模式的典范。
架构设计原则
从全景图能看出 OpenCode 的几个核心设计原则:
Service 解耦
每个模块是 Effect Service(Agent/Session/Tool...),通过 Context 注入。松耦合,可替换。
事件驱动
状态变更通过事件总线(EventV2Bridge)发布。事件溯源风格,可追溯。
权限贯穿
权限检查在工具执行全链路,每次危险操作都可拦截(OC4)。
全链路流式
从 LLM token 到工具状态,整个执行过程都流式,UI 实时反馈。
与框架概念的全面对照
| 理论/框架概念 | OpenCode 实现 | |
|---|---|---|
| Agent 循环(F3) | → | runLoop while(true) |
| AgentAction/Finish 二态(LC7) | → | hasToolCalls 判断 + continue/stop |
| StateGraph 图(LG1) | → | 隐式(命令式循环,无显式图) |
| Checkpointer 记忆(LG6) | → | Session DB 持久化 |
| interrupt HITL(LG6) | → | Permission.ask |
| ToolNode / @tool(LG8/LC6) | → | Tool.define + registry |
| stream messages(LG7) | → | LLMEvent 流(text-delta...) |
| compaction 裁剪 | → | session/compaction.ts |
| create_react_agent(LG8) | → | build agent(默认配置) |
这张表是 OC6 概念对照表的预览。学完本阶段你会对每一行有深刻理解。
后续章节阅读指南
- OC2 runLoop(核心)—— 精读循环本体,对应 F3 章的循环图
- OC3 工具系统 —— Tool.define 模式,对应 LC6/LG8
- OC4 权限系统 —— HITL 的生产形态,对应 LG6
- OC5 上下文工程 —— compaction/子 agent,对应记忆管理
- OC6 概念对照表(核心)—— 总结性大对照表
小结
- OpenCode 用 TypeScript + Effect 框架,每个模块是一个 Service。
- 核心数据流:prompt() → runLoop(while true) → 读消息 → 调LLM → 处理流 → 判断 → 执行工具 → 回流,和 F3 章循环图一一对应。
- 双运行时架构(AI SDK + Native)通过统一 LLMEvent 解耦。
- 四大设计原则:Service 解耦、事件驱动、权限贯穿、全链路流式。
- 所有概念都能在前三阶段找到对应——这就是"理论落地"的完整图景。
下一章 OC2 · runLoop 主循环——本阶段核心!逐段拆解 prompt.ts:1081 的 while(true) 循环,把 F3 章的 Agent 循环理论在生产代码里"对号入座"。你会看到一个真实产品如何实现那个图。