---
url: /blog/yrn9bhmd/index.md
---
# OpenCode 整体架构设计

> 基于 opencode-dev 源码分析

***

## 一、CLI 启动流程

### 1.1 开发模式 (`bun --cwd packages/opencode dev`)

```
bun dev
    ↓
package.json: "dev": "bun run --conditions=browser ./src/index.ts"
    ↓
src/index.ts ← 【第一个执行的文件】
    ↓
yargs(hideBin(process.argv)) ← 【命令行参数解析】
    ↓
.middleware() → 初始化日志系统 + 数据库迁移
    ↓
.command(RunCommand, GenerateCommand, ...) → 注册命令
    ↓
cli.parse() → 解析并执行命令
```

**详细启动序列：**

```
src/index.ts
    │
    ├─→ 全局异常处理 (unhandledRejection, uncaughtException)
    │
    ├─→ yargs 配置
    │       │
    │       └─→ .middleware() — 核心初始化:
    │               ├─→ Log.init() (日志系统)
    │               ├─→ 设置环境变量 (AGENT=1, OPENCODE=1, OPENCODE_PID)
    │               └─→ JsonMigration.run() (数据库迁移检查)
    │
    ├─→ 注册命令
    │
    └─→ await cli.parse() — 解析并执行
            │
            └─→ RunCommand.handler() (默认命令)
                    │
                    └─→ bootstrap(cwd, callback)
                            │
                            └─→ Instance.provide()
                                    │
                                    ├─→ Project.fromDirectory() (解析项目)
                                    └─→ InstanceBootstrap() (核心服务初始化):
                                            ├─→ Plugin.init()
                                            ├─→ Format.init()
                                            ├─→ LSP.init()
                                            ├─→ File.init()
                                            ├─→ FileWatcher.init()
                                            ├─→ Vcs.init()
                                            └─→ Snapshot.init()
```

### 1.2 生产模式 (`bin/opencode`)

```
bin/opencode (Node.js 包装脚本)
    ↓
检测平台: darwin / linux / windows
    ↓
检测架构: x64 / arm64 / arm
    ↓
检测 AVX2 支持 (x64 平台)
    ↓
查找二进制文件:
  - opencode-darwin-arm64
  - opencode-linux-x64 / opencode-linux-x64-baseline
  - opencode-windows-x64.exe
    ↓
childProcess.spawnSync(binary, process.argv.slice(2))
```

### 1.3 参数解析机制

使用 **yargs** 库：

```typescript
// src/index.ts:50
let cli = yargs(hideBin(process.argv))
  .parserConfiguration({ "populate--": true })
  .scriptName("opencode")
  .option("print-logs", { ... })
  .option("log-level", { ... })
  .command(RunCommand)
  .command(GenerateCommand)
  // ... 更多命令
```

* `hideBin(process.argv)` 移除 `process.argv[0]` 和 `process.argv[1]`
* 支持 `--` 传递额外参数
* 每个命令使用 `cmd()` 辅助函数定义

### 1.4 关键文件

| 文件 | 作用 |
|------|------|
| `bin/opencode` | 生产入口，查找并执行预编译二进制 |
| `src/index.ts` | 开发入口 + yargs CLI 定义 |
| `src/cli/cmd/run.ts` | `run` 命令实现（默认命令） |
| `src/cli/bootstrap.ts` | 实例引导封装 |
| `src/project/bootstrap.ts` | 核心服务初始化 |
| `src/global/index.ts` | 全局路径配置（模块加载时执行） |

***

## 二、核心数据流

用户发送 "帮我读取 README.md" 的完整流程：

### 2.1 完整数据流图

```
┌─────────────────────────────────────────────────────────────────────────┐
│ 阶段 1: 用户输入                                                         │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                         │
│  用户输入 "帮我读取 README.md"                                           │
│      ↓                                                                  │
│  [TUI Prompt 组件] submit()                                             │
│  文件: src/cli/cmd/tui/component/prompt/index.tsx:530-673              │
│      ↓                                                                  │
│  sdk.client.session.prompt({                                            │
│    parts: [{ type: "text", text: "帮我读取 README.md" }]               │
│  })                                                                     │
│                                                                         │
└─────────────────────────────────────────────────────────────────────────┘
                                    ↓
┌─────────────────────────────────────────────────────────────────────────┐
│ 阶段 2: 网络传输                                                         │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                         │
│  HTTP POST /session/:sessionID/message                                  │
│  文件: src/server/routes/session.ts:781-821                             │
│      ↓                                                                  │
│  SessionPrompt.prompt(input)                                            │
│                                                                         │
└─────────────────────────────────────────────────────────────────────────┘
                                    ↓
┌─────────────────────────────────────────────────────────────────────────┐
│ 阶段 3: 消息构建                                                         │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                         │
│  SessionPrompt.prompt() → createUserMessage()                           │
│  文件: src/session/prompt.ts:162-189, 966-1356                          │
│      ↓                                                                  │
│  构建 MessageV2.User 对象                                                │
│      ↓                                                                  │
│  Session.updateMessage() → 存储到数据库                                  │
│      ↓                                                                  │
│  SessionPrompt.loop() → 启动 AI 处理循环                                 │
│                                                                         │
└─────────────────────────────────────────────────────────────────────────┘
                                    ↓
┌─────────────────────────────────────────────────────────────────────────┐
│ 阶段 4: AI 调用                                                          │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                         │
│  SessionPrompt.loop()                                                   │
│  文件: src/session/prompt.ts:278-736                                    │
│      ↓                                                                  │
│  resolveTools() → 获取可用工具列表                                       │
│      ↓                                                                  │
│  LLM.stream({ model, messages, tools })                                 │
│  文件: src/session/llm.ts:48-286                                        │
│      ↓                                                                  │
│  streamText({ messages, tools, model })                                 │
│      ↓                                                                  │
│  Provider.getLanguage(model) → 获取 LLM SDK 实例                        │
│  文件: src/provider/provider.ts:1343-1368                               │
│                                                                         │
└─────────────────────────────────────────────────────────────────────────┘
                                    ↓
┌─────────────────────────────────────────────────────────────────────────┐
│ 阶段 5: 流式响应处理                                                     │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                         │
│  SessionProcessor.process()                                             │
│  文件: src/session/processor.ts:46-426                                  │
│      ↓                                                                  │
│  for await (const value of stream.fullStream) {                        │
│    switch (value.type) {                                                │
│      case "text-start":    → 创建 TextPart                              │
│      case "text-delta":    → 追加文本 + 发布事件                        │
│      case "tool-call":     → 执行工具                                   │
│      case "tool-result":   → 返回结果                                   │
│      case "finish-step":   → 计算 token 使用量                          │
│    }                                                                    │
│  }                                                                      │
│                                                                         │
└─────────────────────────────────────────────────────────────────────────┘
                                    ↓
┌─────────────────────────────────────────────────────────────────────────┐
│ 阶段 6: 事件发布                                                         │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                         │
│  Bus.publish(MessageV2.Event.PartUpdated, { part })                    │
│  Bus.publish(MessageV2.Event.PartDelta, { delta })                     │
│  文件: src/session/index.ts:755-789                                     │
│      ↓                                                                  │
│  TUI 通过 SSE/WebSocket 订阅事件                                        │
│                                                                         │
└─────────────────────────────────────────────────────────────────────────┘
                                    ↓
┌─────────────────────────────────────────────────────────────────────────┐
│ 阶段 7: UI 渲染                                                          │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                         │
│  TUI Sync Context → Session 组件                                        │
│  文件: src/cli/cmd/tui/context/sync.tsx                                 │
│  文件: src/cli/cmd/tui/routes/session/index.tsx                         │
│      ↓                                                                  │
│  渲染:                                                                  │
│    > build · claude-sonnet-4                                            │
│                                                                         │
│    → Read README.md                                                     │
│                                                                         │
│    [AI 回复内容...]                                                      │
│                                                                         │
└─────────────────────────────────────────────────────────────────────────┘
```

### 2.2 关键函数/类

| 模块 | 函数/类 | 作用 |
|------|---------|------|
| Prompt | `submit()` | 接收用户输入，发送到后端 |
| SessionPrompt | `prompt()` | 创建用户消息，启动处理循环 |
| SessionPrompt | `loop()` | AI 响应主循环 |
| LLM | `stream()` | 调用 AI SDK streamText |
| SessionProcessor | `process()` | 处理流式响应事件 |
| Provider | `getLanguage()` | 获取 LLM SDK 实例 |
| Session | `updatePart()` | 存储 Part 到数据库 |
| Bus | `publish()` | 发布事件通知 UI |

***

## 三、工具调用机制

### 3.1 工具如何被 AI 知道

#### 工具定义层 (`src/tool/read.ts`)

```typescript
export const ReadTool = Tool.define("read", {
  description: DESCRIPTION,           // 从 read.txt 加载
  parameters: z.object({              // Zod schema 定义参数
    filePath: z.string().describe("The absolute path..."),
    offset: z.coerce.number().optional(),
    limit: z.coerce.number().optional(),
  }),
  async execute(params, ctx) {        // 执行逻辑
    const content = await Bun.file(params.filePath).text()
    return { title, output, metadata }
  }
})
```

#### 工具注册层 (`src/tool/registry.ts`)

```typescript
async function all(): Promise<Tool.Info[]> {
  return [
    InvalidTool,
    BashTool,
    ReadTool,      // ← read 工具在这里注册
    GlobTool,
    GrepTool,
    EditTool,
    WriteTool,
    TaskTool,
    WebFetchTool,
    // ... 更多内置工具
    ...custom,     // ← 用户自定义工具
  ]
}

export async function tools(model, agent) {
  return all().then(tools => tools.map(t => t.init({ agent })))
}
```

#### Schema 生成 / 暴露给 AI (`src/session/prompt.ts`)

```typescript
async function resolveTools(input) {
  const tools: Record<string, AITool> = {}
  
  for (const item of await ToolRegistry.tools(model, agent)) {
    // 1. Zod → JSON Schema
    const schema = ProviderTransform.schema(
      model, 
      z.toJSONSchema(item.parameters)
    )
    
    // 2. 包装成 Vercel AI SDK 格式
    tools[item.id] = tool({
      description: item.description,  // ← AI 看到的描述
      inputSchema: jsonSchema(schema), // ← AI 看到的参数 schema
      async execute(args, options) {
        const ctx = context(args, options)
        return item.execute(args, ctx)
      }
    })
  }
  
  return tools  // 传递给 LLM.stream()
}
```

#### AI 看到的 tools 参数

```json
{
  "read": {
    "description": "读取文件内容...",
    "parameters": {
      "type": "object",
      "properties": {
        "filePath": { "type": "string", "description": "..." },
        "offset": { "type": "number" },
        "limit": { "type": "number" }
      },
      "required": ["filePath"]
    }
  },
  "bash": { ... },
  "edit": { ... }
}
```

### 3.2 工具执行流程

```
┌─────────────────────────────────────────────────────────────────────────┐
│ AI 返回 tool_call                                                        │
├─────────────────────────────────────────────────────────────────────────┤
│  {                                                                      │
│    "type": "tool_call",                                                 │
│    "toolCallId": "call_123",                                            │
│    "toolName": "read",                                                  │
│    "input": { "filePath": "README.md" }                                 │
│  }                                                                      │
└─────────────────────────────────────────────────────────────────────────┘
                                    ↓
┌─────────────────────────────────────────────────────────────────────────┐
│ Vercel AI SDK 自动处理                                                   │
├─────────────────────────────────────────────────────────────────────────┤
│  // streamText 内部逻辑                                                  │
│  1. 解析 tool_call                                                      │
│  2. 查找 tools["read"].execute                                          │
│  3. 调用 execute({ filePath: "README.md" }, ctx)                        │
│  4. 等待结果                                                             │
└─────────────────────────────────────────────────────────────────────────┘
                                    ↓
┌─────────────────────────────────────────────────────────────────────────┐
│ ReadTool.execute() 执行                                                  │
├─────────────────────────────────────────────────────────────────────────┤
│  async execute(params, ctx) {                                           │
│    // 1. 参数验证 (Zod)                                                  │
│    // 2. 读取文件                                                        │
│    const content = await Bun.file(params.filePath).text()              │
│    // 3. 输出截断 (防止 token 超限)                                       │
│    const truncated = await Truncate.output(content, ...)               │
│    // 4. 返回结果                                                        │
│    return {                                                             │
│      title: "README.md",                                                │
│      output: truncated.content,                                         │
│      metadata: { truncated: truncated.truncated }                       │
│    }                                                                    │
│  }                                                                      │
└─────────────────────────────────────────────────────────────────────────┘
                                    ↓
┌─────────────────────────────────────────────────────────────────────────┐
│ 结果返回给 AI                                                            │
├─────────────────────────────────────────────────────────────────────────┤
│  {                                                                      │
│    "type": "tool_result",                                               │
│    "toolCallId": "call_123",                                            │
│    "output": "# README\n\n文件内容..."                                   │
│  }                                                                      │
│      ↓                                                                  │
│  AI 继续生成回复                                                         │
└─────────────────────────────────────────────────────────────────────────┘
```

### 3.3 事件流处理 (`src/session/processor.ts`)

```typescript
for await (const value of stream.fullStream) {
  switch (value.type) {
    case "tool-call":
      // 工具开始执行
      await Session.updatePart({
        type: "tool",
        tool: value.toolName,
        state: { status: "running", input: value.input }
      })
      break
      
    case "tool-result":
      // 工具执行完成
      await Session.updatePart({
        state: { 
          status: "completed", 
          output: value.output.output,
          metadata: value.output.metadata 
        }
      })
      break
      
    case "tool-error":
      // 工具执行失败
      await Session.updatePart({
        state: { status: "error", error: value.error.message }
      })
      break
  }
}
```

***

## 四、关键文件索引

| 领域 | 文件 | 作用 |
|------|------|------|
| **入口** | `src/index.ts` | CLI 入口，yargs 配置 |
| **入口** | `bin/opencode` | 生产入口，查找二进制 |
| **命令** | `src/cli/cmd/run.ts` | run 命令（默认） |
| **初始化** | `src/project/bootstrap.ts` | 核心服务初始化 |
| **会话** | `src/session/index.ts` | Session 数据层 |
| **会话** | `src/session/prompt.ts` | 消息处理核心 |
| **会话** | `src/session/llm.ts` | AI 调用层 |
| **会话** | `src/session/processor.ts` | 流式响应处理 |
| **工具** | `src/tool/tool.ts` | 工具定义框架 |
| **工具** | `src/tool/registry.ts` | 工具注册表 |
| **工具** | `src/tool/read.ts` | read 工具实现 |
| **Provider** | `src/provider/provider.ts` | LLM SDK 管理 |
| **事件** | `src/bus/bus-event.ts` | 事件总线 |

***

## 五、架构总览图

```
┌─────────────────────────────────────────────────────────────────────┐
│                           CLI 入口                                   │
│  bin/opencode (生产) / src/index.ts (开发)                          │
│              ↓ yargs 解析                                            │
│         RunCommand                                                   │
└──────────────────────────────┬──────────────────────────────────────┘
                               ↓
┌─────────────────────────────────────────────────────────────────────┐
│                        SDK Client Layer                              │
│  @opencode-ai/sdk/v2 → createOpencodeClient()                       │
│  - session.create()    - session.prompt()    - event.subscribe()    │
└──────────────────────────────┬──────────────────────────────────────┘
                               ↓
┌─────────────────────────────────────────────────────────────────────┐
│                        Server Layer                                  │
│  Server.Default() → Hono HTTP Server                                │
│  routes/session.ts → SessionPrompt                                  │
└──────────────────────────────┬──────────────────────────────────────┘
                               ↓
┌─────────────────────────────────────────────────────────────────────┐
│                     Session Processing                               │
│  SessionProcessor.create().process()                                │
│  ├── LLM.stream() → 调用 AI API                                     │
│  ├── 处理流式事件 (text-delta, tool-call, etc.)                     │
│  └── 更新 Message/Part 到数据库                                     │
└──────────────────────────────┬──────────────────────────────────────┘
                               ↓
┌─────────────────────────────────────────────────────────────────────┐
│                          Tool Layer                                  │
│  ToolRegistry.tools() → 获取所有工具定义                             │
│  Tool.define() → 定义工具 (Zod schema + execute)                    │
│  Tool.execute() → 执行工具                                          │
└─────────────────────────────────────────────────────────────────────┘
```
