什么是 OpenCode?

OpenCode 它是一款 100% 开源,完全免费的AI智能体,你可以理解为开源版的Claude Code,它可以在命令行、桌面应用和IDE中使用。

核心特点
✅ 完全开源免费:代码全部托管在GitHub,无需登录即可使用

✅ 多模型支持:自由切换Claude、GPT-5、GLM-4.7、MiniMax等多种AI模型

✅ 终端原生体验:专为命令行设计的TUI界面,流畅高效

✅ 上下文感知:智能理解项目结构,提供精准的代码建议

✅ 跨平台支持:支持Windows、macOS、Linux,并提供桌面客户端

✅ LSP自动集成:自动为AI加载正确的语言服务协议

核心架构模块

1. System Prompt 系统 ⭐

System Prompt 是与大语言模型交互时传递给模型的第一条系统级消息,它定义了模型的身份定位、行为规范、工具权限和工作流程。

核心概念:在 OpenCode 中,System Prompt 不仅仅是一段静态文本,而是一个由多个组件动态组合而成的复杂系统,涵盖了 LLM Provider 适配、环境感知、自定义规则和 Agent 特定配置等多个维度

在 OpenCode 中,一个完整的 System Prompt 由以下 四个核心部分 组成:

部分 说明
Provider 特定提示 根据模型提供商选择对应的提示模板
环境信息 动态注入运行环境的上下文
自定义规则 从多个来源加载用户/项目级别的配置指令
Agent 特定提示 根据 Agent 类型注入额外的指令

System Prompt 构建流程 (packages/opencode/src/session/prompt.ts)

System Prompt 组成:
├── Provider 特定 prompt (provider/model 相关)
│   ├── anthropic.txt - Claude 系列模型
│   ├── beast.txt - GPT-4/o 系列模型
│   ├── gemini.txt - Google 模型
│   ├── codex.txt - Codex/GPT-5 模型
│   └── qwen.txt - Qwen 等其他模型
│
├── 环境信息 (SystemPrompt.environment)
│   ├── 工作目录
│   ├── Git 仓库状态
│   ├── 平台信息
│   └── 当前日期
│
├── 自定义规则 (SystemPrompt.custom)
│   ├── 项目级: AGENTS.md, CLAUDE.md, CONTEXT.md
│   ├── 全局级: ~/.claude/CLAUDE.md
│   ├── 配置指令: config.instructions
│   └── URL 远程规则
│
└── Agent 特定 prompt
    ├── build: 默认开发 agent
    ├── plan: 只读计划模式
    ├── explore: 代码探索 agent
    ├── general: 通用子 agent
    └── 自定义 agent
├── packages/opencode/src/session/system.ts              # Prompt 组装逻辑
├── packages/opencode/src/session/prompt.ts              # LLM 调用注入点
├── packages/opencode/src/agent/agent.ts                 # Agent 定义与 prompt
└── packages/opencode/src/session/prompt/*.txt           # 专用 agent 提示词(如Claude 系统提示)

2. 权限审核系统⭐

权限流程概览:

┌─────────────────────────────────────────────────────────────────┐
│                    权限请求流程                                  │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  Tool.execute()                                                 │
│       │                                                         │
│       ▼                                                         │
│  ctx.ask({ permission, patterns, always, metadata })            │
│       │                                                         │
│       ▼                                                         │
│  PermissionNext.ask()                                           │
│       │                                                         │
│       ├─→ 检查 approved 规则 ──命中──→ 返回 Promise.resolve()   │                                                    │
│       ├─→ 检查 deny 规则 ──命中──→ 抛出 DeniedError             │
│       │                                                         │
│       └─→ action === "ask"                                      │
│                   │                                             │
│                   ▼                                             │
│           发布 permission.asked 事件                             │
│                   │                                             │
│                   ▼                                             │
│           TUI 接收事件,显示权限对话框                            │
│                   │                                             │
│         ┌────────┼────────┐                                     │
│         ▼        ▼        ▼                                     │
│       Once   Always   Reject                                    │
│         │        │        │                                     │
│         └────────┴────────┘                                     │
│                   │                                             │
│                   ▼                                             │
│           Bus.publish(Event.Replied)                            │
│                   │                                             │
│                   ▼                                             │
│           Promise resolve/reject                                 │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

权限类型:

// 支持的权限类型
  edit - // 文件编辑 (包含创建和修改)
  read - // 文件读取
  glob - // 文件 glob
  grep - // 代码搜索
  list - // 目录列表
  bash - // 命令执行
  task - // 子 agent 调用
  todoread - // 待办事项读取
  todowrite - // 待办事项写入
  question - // 向用户提问
  webfetch - // 网页抓取
  websearch - // 网络搜索
  codesearch - // 代码搜索
  external_directory - // 外部目录
  doom_loop - // 死循环检测

权限动作:

Action = "allow" | "deny" | "ask"
// Reply = "once" | "always" | "reject"

Agent 权限配置示例 (packages/opencode/src/agent/agent.ts):

const defaults = PermissionNext.fromConfig({
  "*": "allow",
  doom_loop: "ask",
  external_directory: "ask",
  read: {
    "*": "allow",
    "*.env": "deny", // 禁止读取 .env
    "*.env.*": "deny",
    "*.env.example": "allow",
  },
})

工具调用权限请求示例 (packages/opencode/src/tool/bash.ts):

// 请求外部目录访问权限
await ctx.ask({
  permission: "external_directory",
  patterns: Array.from(directories),
  always: Array.from(directories).map((x) => path.dirname(x) + "*"),
  metadata: {},
})

// 请求 bash 执行权限
await ctx.ask({
  permission: "bash",
  patterns: Array.from(patterns),
  always: Array.from(always),
  metadata: {},
})

3. Agent 系统 (src/agent)

// Agent 定义示例 (packages/opencode/src/agent/agent.ts)
build: {
  name: "build"
  mode: "primary"      // primary/subagent/all
  native: true
  permission: {...}
}

plan: {
  name: "plan"
  mode: "primary"
  prompt: PROMPT_PLAN  // 读-only 模式
  permission: {
    edit: "*": "deny"  // 禁止编辑
  }
}

4. Tool 系统 (src/tool)

工具定义模式:

export const ReadTool = Tool.define("read", async (ctx) => {
  return {
    description: "Read a file...",
    parameters: z.object({
      filePath: z.string(),
    }),
    async execute(params, ctx) {
      // 工具实现
    },
  }
})
├── packages/opencode/src/tool/tool.ts                   # Tool 接口定义
├── packages/opencode/src/tool/registry.ts               # 工具注册表
└── packages/opencode/src/tool/*.ts                      # 具体工具实现

5. 迭代信息收集模式 (src/session/processor.ts)

核心灵魂 - OpenCode 的智能决策循环,让 Agent 能够根据用户问题探索代码库、迭代收集信息、直到获取足够信息后再采取行动。

迭代决策流程:

┌─────────────────────────────────────────────────────────────────┐
│                    迭代信息收集流程                               │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  用户提问                                                        │
│     │                                                           │
│     ▼                                                           │
│  ┌─────────────────────────────────────────────────────────┐   │
│  │  步骤 1: LLM 分析问题                                      │   │
│  │  - 理解用户意图                                            │   │
│  │  - 决定:需要更多信息?还是直接行动?                       │   │
│  └─────────────────────────────────────────────────────────┘   │
│           │                    │                                 │
│           ▼                    ▼                                 │
│     ┌──────────┐          ┌──────────────────┐                 │
│     │ 需要探索  │          │ 信息足够          │                 │
│     └──────────┘          └──────────────────┘                 │
│           │                    │                                 │
│           ▼                    ▼                                 │
│     ┌──────────┐          ┌──────────────────┐                 │
│     │ 调用工具  │          │ 执行最终行动      │                 │
│     │ 收集信息  │          │ (write/edit等)   │                 │
│     └──────────┘          └──────────────────┘                 │
│           │                                                       │
│           ▼                                                       │
│     ┌─────────────────────────────────────────────────────────┐ │
│  │  步骤 2: 处理工具结果                                        │ │
│  │  - LLM 评估结果是否足够                                      │ │
│  │  - 决定:继续探索?还是结束?                                │ │
│  └─────────────────────────────────────────────────────────┘   │
│           │                    │                                 │
│           └────────────────────┘                                 │
│                  (循环)                                          │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

核心实现:

// processor.ts - 处理器主循环
async process(streamInput: LLM.StreamInput) {
  while (true) {  // 外层循环:迭代决策
    const stream = await LLM.stream(streamInput)

    for await (const value of stream.fullStream) {  // 内层循环:工具调用
      switch (value.type) {
        case "tool-call":
          // 执行工具,收集信息
          break
        case "tool-result":
          // 处理结果,LLM 决定是否继续
          break
      }
    }
    // 根据上下文决定是否继续循环
  }
}

// 死循环检测
const DOOM_LOOP_THRESHOLD = 3
if (lastThree.length === DOOM_LOOP_THRESHOLD &&
    all_same_tool_calls &&
    no_errors) {
  throw new DoomLoopError()  // 防止无限循环
}

Tool Chaining 模式:

// 典型探索链示例
1. glob("**/*.ts")      // 发现文件2. grep("API|export")   // 搜索关键内容3. read("file.ts")      // 读取具体文件4. bash("npm test")     // 验证发现

Explore Agent 专用探索:

// packages/opencode/src/agent/agent.ts - 专为快速探索优化的 Agent
explore: {
  name: "explore",
  mode: "subagent",
  permission: PermissionNext.merge(defaults, {
    "*": "deny",            // 禁止所有操作
    grep: "allow",          // 显式允许搜索
    glob: "allow",          // 允许文件匹配
    read: "allow",          // 允许读取
    list: "allow",          // 允许目录列表
    bash: "allow",          // 允许命令
    codesearch: "allow",    // 允许代码搜索
    external_directory: {   // 允许外部目录访问
      [Truncate.DIR]: "allow",
    },
    webfetch: "allow",
    websearch: "allow",
  }),
  description: "Fast agent specialized for exploring codebases...",
  prompt: PROMPT_EXPLORE,
}
文件 关键函数/行号 说明
packages/opencode/src/session/processor.ts process() 主循环 迭代决策核心
packages/opencode/src/session/processor.ts 死循环检测 DOOM_LOOP_THRESHOLD
packages/opencode/src/tool/grep.ts grep 工具 代码搜索
packages/opencode/src/tool/glob.ts glob 工具 文件发现
packages/opencode/src/tool/read.ts read 工具 内容读取
packages/opencode/src/agent/agent.ts explore agent 专用探索

6. Skill 系统 (src/skill)

Skill 定义格式 (.opencode/skill/test-skill/SKILL.md):

---
name: test-skill
description: use this when asked to test skill
---
# Skill 文档
这里是技能的详细说明和使用指南
├── packages/opencode/src/skill/skill.ts                 # Skill 发现
├── packages/opencode/src/tool/skill.ts                  # Skill 工具
└── .opencode/skill/*/SKILL.md         # Skill 定义

7. Session 管理 (src/session)

  • 会话创建与状态管理
  • 消息流处理 (MessageV2)
  • 上下文压缩与摘要
  • 权限继承

8. Provider 系统 (src/provider)

  • 多模型提供商适配
  • 工具 schema 转换
  • API 集成

Agent 特定 Prompt

源码位置:packages/opencode/src/agent/agent.ts

在 OpenCode 中,Agent 是实际执行任务的主体。每个 Agent 都可以有自己的特定提示词(Prompt),这些提示词会在 System Prompt 之后追加注入。本节将深入解析 Agent 的定义机制、内置 Agent 的设计思路,以及如何自定义 Agent 配置。

7.1 Agent 的三种模式

理解 Agent 的第一步是理解它的模式(mode),这决定了 Agent 在整个系统中的角色定位。

模式 说明 使用场景
primary 主 Agent 模式,直接响应用户的顶层请求 buildplan Agent
subagent 子 Agent 模式,只能被其他 Agent 调用 exploregeneral Agent
all 兼具两种角色的能力 自定义 Agent 默认模式

primary 模式是主 Agent 模式。在这种模式下,Agent 直接响应用户的顶层请求,是用户交互的主要对象。一个会话中只能有一个 primary Agent 处于活动状态。

subagent 模式是子 Agent 模式。这种 Agent 不能独立响应用户请求,只能被其他 Agent 通过 Task 工具调用。它们通常用于执行特定领域的任务,如代码探索、日志分析等。

all 模式兼具两种角色的能力,既可以作为主 Agent 响应用户,也可以作为子 Agent 被调用。自定义 Agent 默认使用这种模式。

设计思路:将 Agent 分为 primary 和 subagent 两种模式,体现了"职责分离"的设计原则。primary Agent 负责整体规划和协调,subagent 负责具体执行特定任务。这种分层架构使得系统更加灵活,也便于权限控制。

7.2 Agent 定义结构

文件位置packages/opencode/src/agent/agent.ts

每个 Agent 的核心配置包含以下几个关键维度:

配置维度 属性 说明
身份标识 name Agent 的唯一标识符
能力边界 mode 角色类型(primary/subagent/all)
能力边界 steps 最大执行步数限制
行为特征 prompt Agent 专属提示词
行为特征 temperature 生成内容的随机性
行为特征 topP 采样阈值控制
权限控制 permission 可执行的操作范围
模型配置 model 指定的模型(providerID/modelID)

7.3 内置 Agent 分析

OpenCode 内置了 7 个 Agent,分布在 packages/opencode/src/agent/agent.ts 中定义。通过分析它们的配置可以深入理解设计决策。

Agent 模式 用途 可见性
build primary 默认的主要开发 Agent 公开
plan primary 只读计划模式 公开
general subagent 通用多任务 Agent 公开
explore subagent 代码探索专用 Agent 公开
compaction primary 上下文压缩 隐藏
title primary 会话标题生成 隐藏
summary primary 会话摘要生成 隐藏

build Agent 是默认的主开发 Agent。它的设计哲学是"最小化配置"——不设置额外的 prompt 属性,完全依赖 System Prompt(Provider 提示、环境信息、自定义规则)来指导行为。这种设计的好处是简洁,同时保留了最大的灵活性。在权限方面,build Agent 具有完整的操作权限,但有两个例外:doom_loopexternal_directory 需要用户确认才能执行。

plan Agent 采用了"限制性设计"策略。它的核心用途是规划和阅读,因此它的权限配置禁止了所有编辑操作,只允许在 .opencode/plans/*.md 目录下创建文件。这种设计确保了 plan Agent 只能用于制定计划,而不会意外修改代码库。plan Agent 的 Prompt 是通过 insertReminders 函数动态注入的,这种方式可以让提示词参与上下文压缩。

explore Agent 是 subagent 模式的典型代表。它专门用于代码探索,因此它的权限被严格限制为只读操作:允许 grepgloblistreadcodesearch 等查找和阅读类工具,但禁止 editbash 等可能产生副作用的操作。explore Agent 有自己专属的提示词文件(prompt/explore.txt),这个提示词会指导 Agent 如何系统性地探索代码库。

general Agent 是通用子 Agent,用于执行复杂的研究任务和多步骤任务。它的权限配置禁止了 todoreadtodowrite 操作,这是为了避免与主 Agent 的任务管理产生冲突。

设计思路:内置 Agent 的配置差异体现了"按需定制"的原则。每个 Agent 都根据其职责被赋予了适当的权限和提示词,既保证了功能完整性,又实现了最小权限原则。

权限请求流程

每个 Agent 的权限在定义时就被设定:

整体流程图

┌──────────────────────────────────────────────────────────────────────────┐
│                          权限请求完整流程                                 │
├──────────────────────────────────────────────────────────────────────────┤
│                                                                          │
│  Tool.execute()                                                           │
│       │                                                                   │
│       ▼                                                                   │
│  ctx.ask({ permission, patterns, always, metadata })                      │
│       │                                                                   │
│       ▼                                                                   │
│  PermissionNext.ask()                                                     │
│       │                                                                   │
│       ├─────────────────────────────────────────────────────────────┐     │
│       │                                                             │     │
│       ▼                                                             ▼     │
│  evaluate() ──命中 deny ──→ DeniedError                      evaluate()    │
│       │                                                        │          │
│       │                                                     未命中         │
│       │◀──────────────────────────────────────────────────────┘          │
│       │                                                             ▲     │
│       ├─────────────────────────────────────────────────────────────┼     │
│       │                                                             │     │
│       ▼                                                             │     │
│  命中 ask ──→ Promise 阻塞 ──→ Bus.publish(Event.Asked)            │     │
│       │                     │                                     │     │
│       │                     ▼                                     │     │
│       │             TUI 显示权限对话框                              │     │
│       │                     │                                     │     │
│       │         ┌───────────┼───────────┐                         │     │
│       │         ▼           ▼           ▼                         │     │
│       │       Once      Always      Reject                        │     │
│       │         │           │           │                         │     │
│       │         └───────────┴───────────┘                         │     │
│       │                     │                                     │     │
│       │                     ▼                                     │     │
│       │             Bus.publish(Event.Replied)                    │     │
│       │                     │                                     │     │
│       │                     ▼                                     │     │
│       │             Promise resolve/reject                        │     │
│       │                     │                                     │     │
│       └─────────────────────┴─────────────────────────────────────┘     │
│                               │                                         │
│                               ▼                                         │
│                    继续执行或抛出错误                                     │
│                                                                          │
└──────────────────────────────────────────────────────────────────────────┘

配置项目权限规则

目标:通过配置文件控制项目权限

步骤:在 opencode.json 中添加权限配置:

{
  "permission": {
    "bash": {
      "rm -rf *": "deny",
      "chmod 777 *": "deny"
    },
    "read": {
      "*.key": "deny",
      "*.pem": "deny",
      "secrets/**": "deny"
    }
  }
}

测试效果:

  • 执行被禁止的命令应该立即失败
  • 执行敏感文件读取应该被拒绝

创建只读 Agent

目标:创建一个禁止所有编辑操作的 Agent

Agent 配置

{
  "agent": {
    "readonly": {
      "name": "readonly",
      "description": "Read-only analysis agent",
      "mode": "primary",
      "permission": {
        "edit": "*:deny",
        "write": "*:deny",
        "bash": {
          "*": "ask",
          "rm *": "deny",
          "mv *": "deny"
        }
      }
    }
  }
}

权限继承链

┌─────────────────────────────────────────────────────────────────┐
│                    权限继承流程                                   │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  defaults (硬编码)                                               │
│       │                                                         │
│       ▼                                                         │
│  PermissionNext.fromConfig({                                     │
│    "*": "allow",                                                │
│    "*.env": "deny",                                             │
│    ...                                                          │
│  })                                                             │
│       │                                                         │
│       ▼                                                         │
│  ┌─────────────────────────────────────────────────────────┐   │
│  │ Agent Specific Permission                                │   │
│  │                                                         │   │
│  │ plan: merge(defaults, { edit: { "*": "deny" } }, user) │   │
│  │ explore: merge(defaults, { "*": "deny", grep: "allow" }) │   │
│  │ general: merge(defaults, { todoread: "deny" })          │   │
│  └─────────────────────────────────────────────────────────┘   │
│       │                                                         │
│       ▼                                                         │
│  user (opencode.json [permission])                              │
│       │                                                         │
│       ▼                                                         │
│  Final Ruleset                                                  │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

Agent系统

Agent 系统 是 OpenCode 的核心执行引擎,负责协调 LLM 与工具的交互、管理会话状态和执行用户指令。

核心职责:

职责 说明
会话管理 创建和管理 Agent 会话
指令执行 接收用户指令,调用工具执行
状态维护 维护会话上下文和历史消息
权限控制 结合权限系统控制操作范围
上下文压缩 管理长对话的上下文摘要
┌─────────────────────────────────────────────────────────────────┐
│                        OpenCode 架构                             │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  ┌─────────────┐    ┌──────────────────┐    ┌─────────────┐    │
│  │   CLI/TUI   │───▶│   Agent System   │───▶│  Provider   │    │
│  └─────────────┘    └────────┬─────────┘    └─────────────┘    │
│                              │                                    │
│              ┌───────────────┼───────────────┐                  │
│              ▼               ▼               ▼                  │
│      ┌─────────────┐ ┌─────────────┐ ┌─────────────┐           │
│      │   build     │ │   plan      │ │  explore    │           │
│      │   agent     │ │   agent     │ │   agent     │           │
│      └─────────────┘ └─────────────┘ └─────────────┘           │
│                              │                                    │
│              ┌───────────────┼───────────────┐                  │
│              ▼               ▼               ▼                  │
│      ┌─────────────┐ ┌─────────────┐ ┌─────────────┐           │
│      │   Tool      │ │ Permission  │ │   Session   │           │
│      │  Registry   │ │   System    │ │   Manager   │           │
│      └─────────────┘ └─────────────┘ └─────────────┘           │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

Agent Info 结构

类型定义

Agent 的核心数据结构定义在 agent.ts:18-42

// packages/opencode/src/agent/agent.ts:18-42
export const Info = z.object({
  name: z.string(),                    // Agent 标识符
  description: z.string().optional(),  // Agent 功能描述
  mode: z.enum(["subagent", "primary", "all"]),  // 运行模式
  native: z.boolean().optional(),      // 是否内置 Agent
  hidden: z.boolean().optional(),      // 是否隐藏(不显示在列表)
  topP: z.number().optional(),         // Top-P 采样参数
  temperature: z.number().optional(),  // 温度参数
  color: z.string().optional(),        // UI 颜色标识
  permission: PermissionNext.Ruleset,  // 权限规则集
  model: z.object({
    modelID: z.string(),               // 模型 ID
    providerID: z.string(),            // 提供商 ID
  }).optional(),                       // 指定模型
  prompt: z.string().optional(),       // System Prompt
  options: z.record(z.string(), z.any()),  // 其他选项
  steps: z.number().int().positive().optional(),  // 最大步数
})

内置 Agent 详解

OpenCode 内置了 7 个核心 Agent:

┌─────────────────────────────────────────────────────────────────┐
│                      内置 Agent 矩阵                             │
├────────────┬────────┬────────┬──────────────────────────────────┤
│ Name       │ Mode   │ Hidden │ Purpose                          │
├────────────┼────────┼────────┼──────────────────────────────────┤
│ build      │ primary│ ❌     │ 默认开发 Agent    承担主要的代码编写和修改任务。               │
│ plan       │ primary│ ❌     │ 只读计划模式       用于制定开发计划,不允许修改文件。           │
│ general    │ subagent│ ❌    │ 通用研究 Agent     专门用于快速搜索和分析代码库。              │
│ explore    │ subagent│ ❌    │ 代码探索 Agent      用于执行多步骤的研究和复杂任务。           │
│ compaction │ primary│ ✅     │ 上下文压缩(内部使用)            │
│ title      │ primary│ ✅     │ 标题生成(内部使用)              │
│ summary    │ primary│ ✅     │ 摘要生成(内部使用)              │
└────────────┴────────┴────────┴──────────────────────────────────┘

动态 Agent 生成

generate.txt 用于根据用户描述生成新 Agent 配置:

// agent.ts:224-260
export async function generate(input: { description: string }) {
  const defaultModel = await Provider.defaultModel()
  const model = await Provider.getModel(defaultModel.providerID, defaultModel.modelID)

  // 加载系统 prompt
  const system = SystemPrompt.header(defaultModel.providerID)
  system.push(PROMPT_GENERATE)

  // 调用 LLM 生成 Agent 配置
  const result = await generateObject({
    messages: [
      ...system.map((item) => ({ role: "system", content: item })),
      {
        role: "user",
        content: `Create an agent configuration based on this request: "${input.description}"`,
      },
    ],
    model,
    schema: z.object({
      identifier: z.string(),
      whenToUse: z.string(),
      systemPrompt: z.string(),
    }),
  })

  return result.object
}

生成输出示例:

{
  "identifier": "code-reviewer",
  "whenToUse": "Use this agent when you need to review code for bugs, security issues, or style violations. Example: Review the recently added authentication module.",
  "systemPrompt": "You are an expert code reviewer..."
}

Tool 工具系统

工具是 OpenCode 中 Agent 与外部环境交互的桥梁。每个 Tool 都是一个封装了特定功能的可调用单元,Agent 通过调用 Tool 来执行文件操作、命令执行、代码搜索等任务。

每个 Tool 由以下 四个核心部分 组成:

部分 说明 代码位置
ID 工具唯一标识符 Tool.Info.id
Description 工具功能描述(供 LLM 理解) Tool.Info.init().description
Parameters 参数模式(Zod Schema) Tool.Info.init().parameters
Execute 执行逻辑 Tool.Info.init().execute

内置工具列表

工具 功能 权限类型
read 读取文件内容 read
write 写入文件(完整覆盖) edit
edit 编辑文件(局部修改) edit
glob 文件 glob 模式匹配 glob
grep 代码内容搜索 grep
bash 执行 Shell 命令 bash
task 调用子 Agent task
skill 加载 Skill 技能 skill
webfetch 抓取网页内容 webfetch
websearch 网络搜索 websearch
codesearch 代码库搜索 codesearch
todowrite 写入待办事项 todowrite
todoread 读取待办事项 todoread
list 列出目录内容 list

Tool 命名空间详解

Tool 命名空间是整个 Tool 系统的核心,位于 packages/opencode/src/tool/tool.ts三个核心类型:

类型 说明
Tool.Info 工具定义接口
Tool.Context 工具执行上下文
Tool.InitContext 工具初始化上下文
Context 接口:工具执行上下文

Context 是工具执行时接收的上下文对象,提供了与系统交互的能力。

export type Context<M extends Metadata = Metadata> = {
  sessionID: string // 会话 ID
  messageID: string // 消息 ID
  agent: string // 当前 Agent 名称
  abort: AbortSignal // 中止信号
  callID?: string // 调用 ID(可选)
  extra?: { [key: string]: any } // 额外数据
  metadata(input: { title?: string; metadata?: M }): void // 更新元数据
  ask(input: Omit<PermissionNext.Request, "id" | "sessionID" | "tool">): Promise<void> // 请求权限
}
Info 接口:工具定义

Info 是工具的静态定义接口,描述了工具的元数据和初始化逻辑。

export interface Info<Parameters extends z.ZodType = z.ZodType, M extends Metadata = Metadata> {
  id: string
  init: (ctx?: InitContext) => Promise<{
    description: string
    parameters: Parameters
    execute(
      args: z.infer<Parameters>,
      ctx: Context,
    ): Promise<{
      title: string
      metadata: M
      output: string
      attachments?: MessageV2.FilePart[]
    }>
    formatValidationError?(error: z.ZodError): string
  }>
}
define 函数:工具定义核心

define 是定义工具的核心函数,封装了参数验证和输出截断逻辑。

export function define<Parameters extends z.ZodType, Result extends Metadata>(
  id: string,
  init: Info<Parameters, Result>["init"] | Awaited<ReturnType<Info<Parameters, Result>["init"]>>,
): Info<Parameters, Result> {
  return {
    id,
    init: async (initCtx) => {
      const toolInfo = init instanceof Function ? await init(initCtx) : init
      const execute = toolInfo.execute

      // 包装 execute 函数,添加参数验证和输出截断
      toolInfo.execute = async (args, ctx) => {
        try {
          toolInfo.parameters.parse(args)
        } catch (error) {
          if (error instanceof z.ZodError && toolInfo.formatValidationError) {
            throw new Error(toolInfo.formatValidationError(error), { cause: error })
          }
          throw new Error(
            `The ${id} tool was called with invalid arguments: ${error}.\nPlease rewrite the input so it satisfies the expected schema.`,
            { cause: error },
          )
        }
        const result = await execute(args, ctx)

        // 如果工具自己处理了截断,跳过自动截断
        if (result.metadata.truncated !== undefined) {
          return result
        }

        // 自动截断输出
        const truncated = await Truncate.output(result.output, {}, initCtx?.agent)
        return {
          ...result,
          output: truncated.content,
          metadata: {
            ...result.metadata,
            truncated: truncated.truncated,
            ...(truncated.truncated && { outputPath: truncated.outputPath }),
          },
        }
      }
      return toolInfo
    },
  }
}

define 函数的三层封装:

层级 功能
第一层 参数验证:调用 parameters.parse(args) 验证参数
第二层 错误格式化:调用 formatValidationError 自定义错误消息
第三层 输出截断:调用 Truncate.output 处理大输出
InitContext 接口:初始化上下文

InitContext 是工具初始化时接收的上下文,用于获取 Agent 信息。

export interface InitContext {
  agent?: Agent.Info // 当前 Agent 信息(可选)
}

使用场景: 工具可以根据当前 Agent 调整行为,如 TaskTool 根据调用者权限过滤可用的子 Agent。

使用示例 (packages/opencode/src/tool/task.ts:27-30):

const caller = ctx?.agent
const accessibleAgents = caller
  ? agents.filter((a) => PermissionNext.evaluate("task", a.name, caller.permission).action !== "deny")
  : agents

工具注册机制

ToolRegistry 负责管理所有工具的注册、发现和查询。

核心职责:

职责 说明
工具注册 注册自定义工具
工具发现 发现和加载自定义工具(从 tool/ 目录)
工具过滤 根据 Provider 和 Agent 过滤可用工具
插件集成 从 Plugin 加载工具定义

工具注册流程

工具列表来源 (packages/opencode/src/tool/registry.ts:89-112):

async function all(): Promise<Tool.Info[]> {
  const custom = await state().then((x) => x.custom)
  const config = await Config.get()

  return [
    InvalidTool,
    BashTool,
    ReadTool,
    GlobTool,
    GrepTool,
    EditTool,
    WriteTool,
    TaskTool,
    WebFetchTool,
    TodoWriteTool,
    TodoReadTool,
    WebSearchTool,
    CodeSearchTool,
    SkillTool,
    ...(Flag.OPENCODE_EXPERIMENTAL_LSP_TOOL ? [LspTool] : []),
    ...(config.experimental?.batch_tool === true ? [BatchTool] : []),
    ...custom,
  ]
}

工具来源优先级:

优先级 来源 说明
1 内置工具 核心功能工具(read、bash、edit 等)
2 实验性工具 根据 Flag 启用的工具(lsp、batch)
3 自定义工具 tool/ 目录加载的工具
4 插件工具 从 Plugin 加载的工具

工具查询接口

获取所有工具 ID (packages/opencode/src/tool/registry.ts:114-116):

export async function ids() {
  return all().then((x) => x.map((t) => t.id))
}

获取可用工具 (packages/opencode/src/tool/registry.ts:118-138):

export async function tools(providerID: string, agent?: Agent.Info) {
  const tools = await all()
  const result = await Promise.all(
    tools
      .filter((t) => {
        // 根据 Provider 过滤工具
        if (t.id === "codesearch" || t.id === "websearch") {
          return providerID === "opencode" || Flag.OPENCODE_ENABLE_EXA
        }
        return true
      })
      .map(async (t) => {
        using _ = log.time(t.id)
        return {
          id: t.id,
          ...(await t.init({ agent })),  // 初始化工具
        },
      }),
  )
  return result
}

迭代信息收集

迭代信息收集模式 是 OpenCode 的核心决策机制,让 Agent 能够:

  1. 理解用户意图 - 分析用户问题的本质
  2. 评估信息需求 - 判断当前信息是否足够
  3. 探索收集信息 - 调用工具搜索代码库
  4. 迭代决策 - 根据收集结果决定是否继续
  5. 采取行动 - 信息足够后执行最终任务

与传统编程的区别:

传统程序: 输入 → 处理 → 输出 (单次执行)

OpenCode:  输入 → 分析 → 探索? → 收集 → 分析 → 探索? → ... → 行动
                                    ↑___________|
                                    (循环迭代)

核心架构

┌─────────────────────────────────────────────────────────────────┐
│                    迭代信息收集架构                              │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  ┌─────────────────────────────────────────────────────────┐   │
│  │                   Session Processor                      │   │
│  │                                                           │   │
│  │    while (true) {  ← 外层循环: 迭代决策                   │   │
│  │      LLM.stream()  ← 调用 LLM                            │   │
│  │        ↓                                                  │   │
│  │      for await (value) {  ← 内层循环: 处理流              │   │
│  │        switch (value.type) {                              │   │
│  │          case "tool-call":    执行工具                    │   │
│  │          case "tool-result":  处理结果                    │   │
│  │          case "text-delta":   构建响应                    │   │
│  │        }                                                  │   │
│  │      }                                                    │   │
│  │    }                                                      │   │
│  └─────────────────────────────────────────────────────────┘   │
│                              ↓                                  │
│  ┌─────────────────────────────────────────────────────────┐   │
│  │                    工具层                                 │   │
│  │  ┌─────────┐  ┌─────────┐  ┌─────────┐  ┌─────────┐    │   │
│  │  │  glob   │→ │  grep   │→ │  read   │→ │  bash   │    │   │
│  │  └─────────┘  └─────────┘  └─────────┘  └─────────┘    │   │
│  │      ↓            ↓            ↓            ↓           │   │
│  │    发现文件     搜索内容      读取代码     验证结果      │   │
│  └─────────────────────────────────────────────────────────┘   │
│                              ↓                                  │
│  ┌─────────────────────────────────────────────────────────┐   │
│  │                    Agent 层                              │   │
│  │  ┌─────────────────────────────────────────────────┐    │   │
│  │  │              Explore Agent                       │    │   │
│  │  │  - 专用探索优化的 Agent                          │    │   │
│  │  │  - 白名单权限模式                                │    │   │
│  │  │  - 快速文件搜索专家                              │    │   │
│  │  └─────────────────────────────────────────────────┘    │   │
│  └─────────────────────────────────────────────────────────┘   │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

处理器主循环

循环结构解析

处理器是整个迭代模式的核心,包含双层循环结构:

// processor.ts:44-397
export function create(input: {...}) {
  const result = {
    async process(streamInput: LLM.StreamInput) {
      // === 外层循环:迭代决策 ===
      while (true) {
        try {
          let currentText: MessageV2.TextPart | undefined
          let reasoningMap: Record<string, MessageV2.ReasoningPart> = {}

          // 调用 LLM 获取响应流
          const stream = await LLM.stream(streamInput)

          // === 内层循环:处理流事件 ===
          for await (const value of stream.fullStream) {
            input.abort.throwIfAborted()
            switch (value.type) {
              case "start":
                // 开始处理
                break
              case "reasoning-start/delta/end":
                // 处理思考过程
                break
              case "tool-call":
                // 执行工具调用
                break
              case "tool-result":
                // 处理工具结果
                break
              case "text-delta/end":
                // 构建文本响应
                break
              case "finish-step":
                // 步骤完成
                break
            }
            // 检查是否需要压缩上下文
            if (needsCompaction) break
          }
        } catch (e) {
          // 错误处理和重试
        }

        // === 循环退出条件 ===
        if (needsCompaction) return "compact"   // 需要压缩
        if (blocked) return "stop"              // 被阻止
        if (input.assistantMessage.error) return "stop"  // 有错误
        return "continue"                       // 继续迭代
      }
    }
  }
  return result
}
外层循环功能
功能 说明 代码位置
迭代控制 控制是否继续收集信息 :48
错误处理 捕获和处理执行错误 :335-359
重试机制 自动重试可恢复错误 :341-352
上下文检查 检查是否需要压缩 :270-272
退出决策 决定继续或停止 :393-396
内层循环事件处理

事件类型表:

事件类型 说明 处理逻辑
start 会话开始 设置状态为 busy
reasoning-start 思考开始 创建推理 Part
reasoning-delta 思考内容 增量更新推理
reasoning-end 思考结束 完成推理 Part
tool-input-start 工具输入开始 创建工具 Part
tool-call 工具调用 执行工具
tool-result 工具结果 更新结果状态
tool-error 工具错误 处理错误
text-start 文本开始 创建文本 Part
text-delta 文本内容 增量更新文本
text-end 文本结束 完成文本 Part
finish-step 步骤完成 统计成本 Token
finish 完成 结束处理
循环退出条件
// processor.ts:393-396
if (needsCompaction) return "compact"   // 上下文超限,需要压缩
if (blocked) return "stop"              // 权限被拒绝
if (input.assistantMessage.error) return "stop"  // 发生错误
return "continue"                       // 继续下一次迭代

工具执行流程

工具调用状态机:

┌─────────────────────────────────────────────────────────────────┐
│                    工具调用状态机                                │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  pending (等待)                                                  │
│      │                                                          │
│      ▼                                                          │
│  running (执行中)                                                │
│      │                                                          │
│      ├─────────────────────────────────────────────────────┐    │
│      │                                                     │    │
│      ▼                                                     ▼    │
│  completed (完成)                                     error (错误)
│      │                                                     │
│      └─────────────────────────────────────────────────────┘    │
│                            │                                    │
│                            ▼                                    │
│                    结果反馈给 LLM                               │
│                            │                                    │
│                            ▼                                    │
│              LLM 决定:继续调用工具?还是结束?                   │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

死循环检测机制

检测条件详解

// 条件 1: 至少有 3 个最近的工具调用
lastThree.length === DOOM_LOOP_THRESHOLD

// 条件 2: 所有调用都是同一个工具
lastThree.every((p) => p.tool === value.toolName)

// 条件 3: 所有调用都已完成(非 pending)
p.state.status !== "pending"

// 条件 4: 所有调用的输入完全相同
JSON.stringify(p.state.input) === JSON.stringify(value.input)

处理流程

检测到死循环
    │
    ▼
请求 doom_loop 权限
    │
    ├── 允许 (allow) → 继续执行
    │
    ├── 拒绝 (deny) → 抛出 DeniedError
    │
    └── 询问 (ask) → 用户决定

完整执行流程

端到端流程图
┌─────────────────────────────────────────────────────────────────┐
│                    完整执行流程                                  │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  用户输入: "修复认证模块的漏洞"                                   │
│           │                                                     │
│           ▼                                                     │
│  ┌─────────────────────────────────────────────────────────┐   │
│  │  Session Processor (step 1)                             │   │
│  │  - LLM 分析用户问题                                      │   │
│  │  - 决定: 需要更多信息                                    │   │
│  │  - 调用 @explore subagent                               │   │
│  └─────────────────────────────────────────────────────────┘   │
│           │                                                     │
│           ▼                                                     │
│  ┌─────────────────────────────────────────────────────────┐   │
│  │  Explore Agent (step 1.1)                               │   │
│  │  - glob("**/auth*.ts")  → 发现 5 个文件                  │   │
│  │  - grep("auth|login|verify")  → 找到关键函数             │   │
│  │  - read("auth/controller.ts")  → 分析代码               │   │
│  │  - 返回分析结果                                          │   │
│  └─────────────────────────────────────────────────────────┘   │
│           │                                                     │
│           ▼                                                     │
│  ┌─────────────────────────────────────────────────────────┐   │
│  │  Session Processor (step 2)                             │   │
│  │  - 接收 explore 结果                                     │   │
│  │  - LLM 评估: 信息足够?                                  │   │
│  │  - 决定: 可以开始修复                                    │   │
│  └─────────────────────────────────────────────────────────┘   │
│           │                                                     │
│           ▼                                                     │
│  ┌─────────────────────────────────────────────────────────┐   │
│  │  工具执行 (step 3)                                       │   │
│  │  - edit("auth/controller.ts")  → 修复漏洞               │   │
│  │  - read("auth/service.ts")  → 验证依赖                  │   │
│  │  - bash("npm test")  → 运行测试                         │   │
│  └─────────────────────────────────────────────────────────┘   │
│           │                                                     │
│           ▼                                                     │
│  ┌─────────────────────────────────────────────────────────┐   │
│  │  结果返回                                                │   │
│  │  - 输出修复总结                                          │   │
│  │  - 更新文件差异                                          │   │
│  │  - 完成会话                                              │   │
│  └─────────────────────────────────────────────────────────┘   │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘
详细时序图
用户          LLM              SessionProcessor      Explore Agent      工具
 │              │                   │                     │              │
 │ "修复漏洞"   │                   │                     │              │
 │─────────────→│                   │                     │              │
 │              │ 分析问题           │                     │              │
 │              │→──需要探索───────→│                     │              │
 │              │                   │ 调用 @explore       │              │
 │              │                   │────────────────────→│              │
 │              │                   │                     │ glob("auth*") │
 │              │                   │                     │──────────────→│
 │              │                   │                     │ 发现 5 文件   │
 │              │                   │                     │←──────────────│
 │              │                   │                     │ grep("auth")  │
 │              │                   │                     │──────────────→│
 │              │                   │                     │ 找到 10 处    │
 │              │                   │                     │←──────────────│
 │              │                   │                     │ read(文件)    │
 │              │                   │                     │──────────────→│
 │              │                   │                     │ 返回代码      │
 │              │                   │                     │←──────────────│
 │              │                   │ 返回探索结果        │              │
 │              │←──────────────────│←────────────────────│              │
 │              │                   │                     │              │
 │              │ 评估: 信息足够    │                     │              │
 │              │→──可以修复───────→│                     │              │
 │              │                   │ edit("auth.ts")     │              │
 │              │                   │──────────────────────────────────→│
 │              │                   │                     │              │ 修复代码
 │              │                   │                     │              │←─────────│
 │              │                   │                     │              │
 │              │ 总结修复结果      │                     │              │
 │←─────────────│                   │                     │              │
 │              │                   │                     │              │
Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐