OpenCode Build 智能体深度分析:执行机制与工具集成
摘要
本文深入分析了 OpenCode 项目中 Build 智能体的完整实现机制。Build 智能体是 OpenCode 的默认代理,承担着代码实施和执行的核心职责。与 Plan 智能体的只读特性形成鲜明对比,Build 智能体拥有完整的工具访问权限,能够执行文件编辑、命令运行、代码搜索等操作。本文从架构设计、权限系统、工作流程、工具集成和错误处理等多个维度进行了详细剖析,为理解现代 AI 编程助手的执行能力提供了参考。
关键词:OpenCode;Build 智能体;工具执行;权限管理;Effect.ts;AI 编程助手
目录
- 引言
- Build 智能体核心设计
2.1 智能体定义与配置
2.2 权限系统设计
2.3 模式分类与角色 - 工作流程详解
3.1 计划执行模式
3.2 直接执行模式
3.3 混合模式
3.4 主动进入计划模式 - 工具系统集成
4.1 文件操作工具
4.2 命令执行工具
4.3 代码搜索工具
4.4 任务委托工具 - 权限请求与用户交互
5.1 权限评估机制
5.2 交互式授权
5.3 自动接受策略 - 错误处理与恢复
6.1 工具执行失败
6.2 权限拒绝处理
6.3 会话回滚机制 - 性能优化策略
7.1 并行工具调用
7.2 上下文管理
7.3 缓存策略 - 前端集成与可视化
- 设计模式与最佳实践
- 总结与展望
1. 引言
1.1 研究背景
在 AI 编程助手系统中,规划与执行是两个核心阶段。Plan 智能体负责前期的需求分析和方案设计,而 Build 智能体则承担着将计划转化为实际代码的重任。Build 智能体需要具备以下关键能力:
- 完整的工具访问权限:能够读取、编辑文件,执行命令
- 灵活的执行策略:根据任务复杂度选择直接执行或重新规划
- 健壮的错误处理:应对工具执行失败和权限拒绝
- 高效的性能表现:通过并行调用和上下文优化提升效率
1.2 研究意义
Build 智能体的设计体现了以下软件工程理念:
- 最小惊讶原则:默认行为符合开发者预期
- 渐进式授权:根据风险等级动态请求权限
- 可恢复性:支持撤销和回滚操作
- 透明性:清晰展示执行过程和状态
1.3 研究方法
本文通过对 OpenCode 源码的深入分析,重点关注:
packages/opencode/src/agent/agent.ts:Build 智能体定义packages/opencode/src/tool/*.ts:各种工具的实现packages/opencode/src/permission/index.ts:权限评估系统packages/opencode/src/session/prompt.ts:工作流程控制packages/opencode/src/session/revert.ts:回滚机制
2. Build 智能体核心设计
2.1 智能体定义与配置
Build 智能体在 packages/opencode/src/agent/agent.ts 中定义:
build: {
name: "build",
description: "The default agent. Executes tools based on configured permissions.",
options: {},
permission: Permission.merge(
defaults,
Permission.fromConfig({
question: "allow",
plan_enter: "allow",
}),
user,
),
mode: "primary",
native: true,
}
关键特性:
- 默认智能体:当用户未指定时自动使用
- 主智能体模式:可直接与用户交互
- 完整权限:继承默认权限,允许大部分操作
- 原生实现:系统内置,无需额外配置
2.2 权限系统设计
2.2.1 三层权限架构
Build 智能体的权限同样由三个层次组成:
第一层:默认权限(defaults)
const defaults = Permission.fromConfig({
"*": "allow", // 默认允许所有操作
doom_loop: "ask", // 循环检测需要询问
external_directory: { // 外部目录访问
"*": "ask",
...Object.fromEntries(whitelistedDirs.map((dir) => [dir, "allow"])),
},
question: "deny", // 默认禁止提问(但被第二层覆盖)
plan_enter: "deny", // 默认禁止进入计划模式(但被第二层覆盖)
plan_exit: "deny", // 禁止退出计划模式
read: { // 读取权限细化
"*": "allow",
"*.env": "ask", // .env 文件需要询问
"*.env.*": "ask",
"*.env.example": "allow", // 示例文件允许
},
})
第二层:Build 特定权限
Permission.fromConfig({
question: "allow", // 允许向用户提问
plan_enter: "allow", // 允许进入计划模式
})
第三层:用户配置权限
const user = Permission.fromConfig(cfg.permission ?? {})
用户可以全局覆盖某些权限,例如:
{
"permission": {
"bash": "ask", // 所有命令执行都需要确认
"edit": "allow" // 允许所有编辑操作
}
}
2.2.2 权限合并算法
permission: Permission.merge(defaults, buildSpecific, user)
Permission.merge 的实现逻辑:
// packages/opencode/src/permission/index.ts
export function merge(...rulesets: Ruleset[]): Ruleset {
const merged: Rule[] = []
for (const ruleset of rulesets) {
for (const rule of ruleset) {
// 检查是否已存在相同权限和模式的规则
const existingIndex = merged.findIndex(
r => r.permission === rule.permission && r.pattern === rule.pattern
)
if (existingIndex !== -1) {
// 后定义的规则覆盖先前的规则
merged[existingIndex] = rule
} else {
merged.push(rule)
}
}
}
return merged
}
优先级策略:
- 最后定义优先:后续层的规则会覆盖前面的规则
- 明确性优先:具体路径匹配优先于通配符
- 安全性保障:用户可以通过配置收紧权限,但不能突破系统限制
2.2.3 权限评估流程
// packages/opencode/src/permission/evaluate.ts
export function evaluate(
permission: string,
pattern: string,
...rulesets: Ruleset[]
): Rule {
const allRules = rulesets.flat()
// 收集所有匹配的规则
const matchingRules = allRules.filter(rule =>
Wildcard.match(permission, rule.permission) &&
Wildcard.match(pattern, rule.pattern)
)
if (matchingRules.length === 0) {
// 没有匹配规则,返回默认允许
return { permission, pattern, action: "allow" }
}
// 按特异性排序(具体路径优先)
matchingRules.sort((a, b) => {
const aSpecificity = specificity(a.pattern)
const bSpecificity = specificity(b.pattern)
return bSpecificity - aSpecificity
})
// 返回最具体的规则
return matchingRules[0]
}
function specificity(pattern: string): number {
// 计算模式的具体性得分
// 通配符越少,得分越高
return pattern.split("*").length - 1
}
评估示例:
// 场景:Build agent 尝试编辑 src/component.tsx
evaluate("edit", "src/component.tsx", defaults, buildSpecific, user)
// 匹配的规则:
// 1. { permission: "*", pattern: "*", action: "allow" } (来自 defaults)
// 2. { permission: "edit", pattern: "*", action: "allow" } (隐含)
// 结果:allow
2.3 模式分类与角色
2.3.1 Primary Agent 特性
Build 智能体作为 primary agent,具有以下特性:
- 直接用户交互:可以接收用户输入并直接响应
- 工具调用权限:可以使用所有注册的工具
- 会话管理:可以创建和管理会话消息
- 子代理调度:可以调用 subagents 完成子任务
2.3.2 与其他智能体的关系
┌─────────────────────────────────────┐
│ User Input │
└──────────┬──────────────────────────┘
│
┌──────▼───────┐
│ Build Agent │ ◄── Primary Agent
└──────┬───────┘
│
┌─────┴─────┐
│ │
┌────▼────┐ ┌───▼────────┐
│ Explore │ │ General │ ◄── Subagents
│ Agent │ │ Agent │
└─────────┘ └────────────┘
│ │
└─────┬─────┘
│
┌──────▼───────┐
│ Plan Agent │ ◄── Primary Agent (optional)
└──────────────┘
协作模式:
- Build → Explore:快速探索代码库
- Build → General:复杂研究和多步任务
- Build → Plan:发现任务复杂时切换到规划模式
3. 工作流程详解
Build 智能体的工作流程根据任务类型和用户意图动态调整,主要分为三种模式:计划执行模式、直接执行模式和混合模式。
3.1 计划执行模式
当 Build 智能体检测到存在计划文件时,会自动进入计划执行模式。
3.1.1 模式切换检测
// packages/opencode/src/session/prompt.ts
if (input.agent.name !== "plan" && assistantMessage?.info.agent === "plan") {
const plan = Session.plan(input.session)
if (!(yield* fsys.existsSafe(plan))) return input.messages
const part = yield* sessions.updatePart({
id: PartID.ascending(),
messageID: userMessage.info.id,
sessionID: userMessage.info.sessionID,
type: "text",
text: BUILD_SWITCH + "\n\n" + `A plan file exists at ${plan}. You should execute on the plan defined within it`,
synthetic: true,
})
userMessage.parts.push(part)
return input.messages
}
触发条件:
- 当前智能体不是 plan(即 build 或其他)
- 上一条助手消息来自 plan 智能体
- 计划文件存在
BUILD_SWITCH 提示词:
<system-reminder>
Your operational mode has changed from plan to build.
You are no longer in read-only mode.
You are permitted to make file changes, run shell commands, and utilize your arsenal of tools as needed.
</system-reminder>
系统行为:
- 自动注入合成文本部分(synthetic: true)
- 通知 Build 智能体权限已变更
- 指示智能体按照计划文件执行
3.1.2 计划执行流程
用户批准计划
↓
plan_exit 工具执行
↓
创建新的 User 消息(agent: "build")
↓
注入 BUILD_SWITCH 提示词
↓
Build 智能体接收上下文
↓
读取计划文件
↓
按步骤执行实施
↓
验证完成
典型对话流:
Plan Agent: [完成计划撰写,调用 plan_exit]
System: [显示确认对话框]
User: Yes, switch to build agent
System: [创建新消息,agent="build", 注入提示词]
Build Agent: [读取计划文件,开始执行]
├─ Read: .opencode/plans/xxx.md
├─ Edit: src/component.tsx
├─ Bash: npm test
└─ Write: src/utils.ts
3.2 直接执行模式
对于简单任务,Build 智能体直接进入执行模式,无需经过规划阶段。
3.2.1 任务复杂度判断
Build 智能体根据以下因素判断是否需要规划:
适合直接执行的任务:
- 单文件修改(typo 修复、变量重命名)
- 简单的配置更改
- 明确的命令执行
- 小范围的代码重构
需要规划的任务:
- 涉及多个文件的架构变更
- 新功能开发
- 复杂的 bug 修复
- 需要调研的技术选型
3.2.2 执行策略
// 伪代码示例
async function executeTask(task: Task) {
if (isSimple(task)) {
// 直接执行
await directExecute(task)
} else {
// 建议用户切换到 plan 模式
await suggestPlanning(task)
}
}
function isSimple(task: Task): boolean {
return (
task.files.length <= 1 &&
task.complexity < THRESHOLD &&
!task.requiresResearch
)
}
3.3 混合模式
在实际使用中,Build 智能体经常在两种模式之间切换。
3.3.1 迭代优化流程
Build 执行 Step 1
↓
发现问题需要重新设计
↓
调用 plan_enter
↓
Plan 重新规划
↓
plan_exit 返回 Build
↓
继续执行 Step 2
3.3.2 增量实施
读取计划文件
↓
执行 Step 1
↓
提交更改
↓
验证 Step 1
↓
执行 Step 2
↓
...
3.4 主动进入计划模式
Build 智能体可以在执行过程中发现任务复杂时主动切换到 Plan 模式。
3.4.1 plan_enter 工具(概念性)
虽然当前代码中 plan_enter 工具被注释掉,但其设计理念值得关注:
/*
export const PlanEnterTool = Tool.define("plan_enter", {
description: ENTER_DESCRIPTION,
parameters: z.object({}),
async execute(_params, ctx) {
const session = await Session.get(ctx.sessionID)
const plan = path.relative(Instance.worktree, Session.plan(session))
const answers = await Question.ask({
sessionID: ctx.sessionID,
questions: [
{
question: `Would you like to switch to the plan agent and create a plan saved to ${plan}?`,
header: "Plan Mode",
custom: false,
options: [
{ label: "Yes", description: "Switch to plan agent for research and planning" },
{ label: "No", description: "Stay with build agent to continue making changes" },
],
},
],
tool: ctx.callID ? { messageID: ctx.messageID, callID: ctx.callID } : undefined,
})
const answer = answers[0]?.[0]
if (answer === "No") throw new Question.RejectedError()
const model = await getLastModel(ctx.sessionID)
const userMsg: MessageV2.User = {
id: MessageID.ascending(),
sessionID: ctx.sessionID,
role: "user",
time: { created: Date.now() },
agent: "plan",
model,
}
await Session.updateMessage(userMsg)
await Session.updatePart({
id: PartID.ascending(),
messageID: userMsg.id,
sessionID: ctx.sessionID,
type: "text",
text: "User has requested to enter plan mode. Switch to plan mode and begin planning.",
synthetic: true,
})
return {
title: "Switching to plan agent",
output: `User confirmed to switch to plan mode. A new message has been created to switch you to plan mode. The plan file will be at ${plan}. Begin planning.`,
metadata: {},
}
},
})
*/
使用场景:
User: 帮我添加用户认证功能
Build Agent: [评估任务复杂度]
├─ 需要设计认证方案
├─ 需要选择认证提供商
├─ 需要修改多个文件
└─ 需要数据库 schema 变更
Build Agent: [调用 plan_enter 工具]
→ 切换到 Plan 模式进行详细规划
4. 工具系统集成
Build 智能体可以访问多种工具来完成代码实施任务。
4.1 文件操作工具
4.1.1 Read 工具
// packages/opencode/src/tool/read.ts
export const ReadTool = Tool.define("read", {
description: "Read the contents of a file",
parameters: z.object({
filePath: z.string(),
offset: z.number().optional(),
limit: z.number().optional(),
}),
async execute(params, ctx) {
const content = await fs.readFile(params.filePath, "utf-8")
const lines = content.split("\n")
const start = params.offset ?? 0
const end = params.limit ? start + params.limit : lines.length
return {
title: `Read ${params.filePath}`,
output: lines.slice(start, end).join("\n"),
metadata: {
totalLines: lines.length,
shownLines: end - start,
},
}
},
})
使用场景:
- 理解现有代码结构
- 查看配置文件
- 分析依赖关系
4.1.2 Edit 工具
// packages/opencode/src/tool/edit.ts
export const EditTool = Tool.define("edit", {
description: "Edit specific sections of a file",
parameters: z.object({
filePath: z.string(),
edits: z.array(z.object({
oldText: z.string(),
newText: z.string(),
})),
}),
async execute(params, ctx) {
let content = await fs.readFile(params.filePath, "utf-8")
for (const edit of params.edits) {
if (!content.includes(edit.oldText)) {
throw new Error(`Could not find text to replace in ${params.filePath}`)
}
content = content.replace(edit.oldText, edit.newText)
}
await fs.writeFile(params.filePath, content)
return {
title: `Edited ${params.filePath}`,
output: `Applied ${params.edits.length} edit(s)`,
metadata: {
filePath: params.filePath,
editsCount: params.edits.length,
},
}
},
})
安全机制:
- 精确匹配:必须完全匹配旧文本
- 原子操作:所有编辑成功后才写入文件
- 权限检查:在编辑前验证权限
4.1.3 Write 工具
// packages/opencode/src/tool/write.ts
export const WriteTool = Tool.define("write", {
description: "Write content to a new file or overwrite an existing file",
parameters: z.object({
filePath: z.string(),
content: z.string(),
}),
async execute(params, ctx) {
await fs.ensureDir(path.dirname(params.filePath))
await fs.writeFile(params.filePath, params.content)
return {
title: `Wrote ${params.filePath}`,
output: `Created/overwrote file with ${params.content.length} characters`,
metadata: {
filePath: params.filePath,
size: params.content.length,
},
}
},
})
4.2 命令执行工具
4.2.1 Bash 工具
// packages/opencode/src/tool/bash.ts
export const BashTool = Tool.define("bash", {
description: "Execute a bash command",
parameters: z.object({
command: z.string(),
description: z.string().optional(),
workdir: z.string().optional(),
}),
async execute(params, ctx) {
const { stdout, stderr, exitCode } = await exec(params.command, {
cwd: params.workdir ?? Instance.worktree,
timeout: 30000,
})
return {
title: params.description ?? `Executed: ${params.command}`,
output: stdout || stderr,
metadata: {
exitCode,
stdout,
stderr,
},
}
},
})
安全措施:
- 超时限制:默认 30 秒超时
- 工作目录限制:限制在项目目录内执行
- 权限询问:高风险命令需要用户确认
典型用法:
// 安装依赖
{
"tool": "bash",
"input": {
"command": "npm install axios",
"description": "Install axios package"
}
}
// 运行测试
{
"tool": "bash",
"input": {
"command": "npm test",
"description": "Run test suite"
}
}
// Git 操作
{
"tool": "bash",
"input": {
"command": "git status",
"description": "Check git status"
}
}
4.3 代码搜索工具
4.3.1 Grep 工具
// packages/opencode/src/tool/grep.ts
export const GrepTool = Tool.define("grep", {
description: "Search for patterns in files",
parameters: z.object({
pattern: z.string(),
path: z.string().optional(),
caseSensitive: z.boolean().optional(),
}),
async execute(params, ctx) {
const results = await grep(params.pattern, {
path: params.path ?? Instance.worktree,
caseSensitive: params.caseSensitive ?? false,
})
return {
title: `Searched for "${params.pattern}"`,
output: results.map(r => `${r.file}:${r.line}: ${r.match}`).join("\n"),
metadata: {
matchCount: results.length,
files: [...new Set(results.map(r => r.file))],
},
}
},
})
4.3.2 Glob 工具
// packages/opencode/src/tool/glob.ts
export const GlobTool = Tool.define("glob", {
description: "Find files matching a glob pattern",
parameters: z.object({
pattern: z.string(),
path: z.string().optional(),
}),
async execute(params, ctx) {
const files = await glob(params.pattern, {
cwd: params.path ?? Instance.worktree,
})
return {
title: `Found files matching "${params.pattern}"`,
output: files.join("\n"),
metadata: {
fileCount: files.length,
},
}
},
})
4.4 任务委托工具
4.4.1 Task 工具
// packages/opencode/src/tool/task.ts
export const TaskTool = Tool.define("task", {
description: "Delegate a subtask to another agent",
parameters: z.object({
description: z.string(),
agent: z.string(),
prompt: z.string(),
}),
async execute(params, ctx) {
const result = await Agent.generate({
agent: params.agent,
prompt: params.prompt,
context: ctx,
})
return {
title: `Delegated to ${params.agent}: ${params.description}`,
output: result.output,
metadata: {
agent: params.agent,
tokensUsed: result.tokens,
},
}
},
})
使用场景:
// 委托给 explore agent 进行代码探索
{
"tool": "task",
"input": {
"description": "Find authentication implementations",
"agent": "explore",
"prompt": "Search for all authentication-related code in the project"
}
}
// 委托给 general agent 进行复杂分析
{
"tool": "task",
"input": {
"description": "Analyze database schema",
"agent": "general",
"prompt": "Review the database models and suggest improvements"
}
}
5. 权限请求与用户交互
5.1 权限评估机制
5.1.1 权限检查流程
// packages/opencode/src/permission/index.ts
const ask = Effect.fn("Permission.ask")(function* (input: z.infer<typeof AskInput>) {
const { approved, pending } = yield* InstanceState.get(state)
const { ruleset, ...request } = input
let needsAsk = false
for (const pattern of request.patterns) {
const rule = evaluate(request.permission, pattern, ruleset, approved)
log.info("evaluated", { permission: request.permission, pattern, action: rule })
if (rule.action === "deny") {
return yield* new DeniedError({
ruleset: ruleset.filter((rule) => Wildcard.match(request.permission, rule.permission)),
})
}
if (rule.action === "allow") continue
needsAsk = true
}
if (!needsAsk) return
// 需要用户确认
const id = request.id ?? PermissionID.ascending()
const info: Request = { id, ...request }
const deferred = yield* Deferred.make<void, RejectedError | CorrectedError>()
pending.set(id, { info, deferred })
yield* bus.publish(Event.Asked, info)
return yield* Effect.ensuring(
Deferred.await(deferred),
Effect.sync(() => {
pending.delete(id)
}),
)
})
评估逻辑:
- 遍历所有模式:检查每个文件路径的权限
- 立即拒绝:如果任何模式被拒绝,直接抛出错误
- 跳过允许:如果所有模式都允许,无需询问
- 需要询问:如果有模式需要询问,暂停执行等待用户响应
5.1.2 权限规则匹配
// 示例:评估对 src/config.ts 的编辑权限
evaluate("edit", "src/config.ts", [
{ permission: "*", pattern: "*", action: "allow" },
{ permission: "edit", pattern: "*.env", action: "ask" },
])
// 匹配过程:
// 1. "*" matches "edit" AND "*" matches "src/config.ts" → allow
// 2. "edit" matches "edit" BUT "*.env" does NOT match "src/config.ts" → skip
// 结果:allow
5.2 交互式授权
5.2.1 权限请求事件
// packages/opencode/src/permission/index.ts
export const Event = {
Asked: BusEvent.define("permission.asked", Request),
Replied: BusEvent.define("permission.replied", z.object({
sessionID: SessionID.zod,
requestID: PermissionID.zod,
reply: Reply,
})),
}
前端监听:
// packages/app/src/context/permission.tsx
function PermissionProvider({ children }) {
const [pendingRequests, setPendingRequests] = useState([])
useEffect(() => {
const unsubscribe = bus.subscribe("permission.asked", (request) => {
setPendingRequests(prev => [...prev, request])
})
return unsubscribe
}, [])
return (
<PermissionContext.Provider value={{ pendingRequests, reply }}>
{children}
{pendingRequests.map(request => (
<PermissionDialog key={request.id} request={request} />
))}
</PermissionContext.Provider>
)
}
5.2.2 用户响应选项
export const Reply = z.enum(["once", "always", "reject"])
响应含义:
- once:仅此次允许
- always:总是允许(添加到 approved 列表)
- reject:拒绝(并取消同一会话中的其他待处理请求)
5.3 自动接受策略
5.3.1 信任机制
用户可以配置自动接受某些权限:
{
"permission": {
"edit": "allow",
"bash": {
"npm test": "allow",
"git status": "allow",
"*": "ask"
}
}
}
5.3.2 会话级记忆
// 用户选择 "always" 后
if (input.reply === "always") {
for (const pattern of existing.info.always) {
approved.push({
permission: existing.info.permission,
pattern,
action: "allow",
})
}
}
持久化:
// 会话结束时保存
yield* Database.use((db) =>
db.insert(PermissionTable)
.values({
project_id: ctx.project.id,
data: state.approved,
})
.onConflictDoUpdate({
target: PermissionTable.project_id,
set: { data: state.approved },
})
)
6. 错误处理与恢复
6.1 工具执行失败
6.1.1 错误类型
// 常见错误类型
class ToolExecutionError extends Error {
constructor(
public tool: string,
public params: any,
public cause: Error
) {
super(`Tool ${tool} failed: ${cause.message}`)
}
}
class FileNotFound
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐

所有评论(0)