OpenCode

在代码仓库中分析、修改并运行检查的编程智能体
反馈(在 GitHub 新标签页打开)

主要技术:OpenCode、Effect、SQLite、Drizzle ORM

项目目录

项目简介

OpenCode 是一套基于 TypeScript 的开源编程智能体,提供终端、桌面和 Web 交互入口。开发者可以围绕现有仓库提出问题,让它搜索代码、解释调用关系、修改文件并运行检查命令,工具结果会回到会话中参与下一步判断。

例如,用户要求“找到首页按钮并修改颜色”,它会先搜索组件、阅读候选文件,必要时询问修改目标,再进行编辑。Plan 用于讨论方案,Build 用于实施;长任务还有会话持久化、上下文压缩与服务重启后的继续执行机制。

OpenCode 在终端搜索首页按钮并询问修改目标

项目亮点

  • 模式约束与实际授权分层: Plan 的提醒和默认编辑规则约束分析阶段;工具执行时再按动作、路径或命令检查权限,支持允许、拒绝和等待确认。工具可见与一次操作能否执行分别判断。
  • 输入受理与会话推进分离: 用户输入先进入持久待处理队列,再由会话执行器消费。同一会话的多次唤醒在进程内协调,避免仅依赖客户端连接维持任务。
  • 按模型预算压缩上下文: 每次请求前依据模型窗口、已知用量和新增内容判断是否压缩;采用原生压缩或任务摘要,并保留近期消息,溢出反馈与手动压缩走不同入口。
  • 重启后的状态结清与续跑: 通过持久执行标记识别遗留任务,将未完成工具记录标为中断,再重建模型输入继续。恢复采用至少一次语义,外部副作用仍需核对。

技术架构

交互与会话受理: 终端、桌面或 Web 客户端向服务提交输入;会话消息、待处理输入及执行状态保存在 SQLite,执行协调器负责唤醒和串行推进当前会话。

模型与工具执行: Runner 组装持久历史、模式提醒与工具定义,经模型适配层发起流式请求;工具注册、权限检查与文件/命令执行共同完成动作,结果写回会话,压缩服务控制后续请求规模。

代码位置 主要职责
packages/core/src/session/inbox.ts、packages/core/src/session/run-coordinator.ts 受理输入、协调会话执行
packages/core/src/session/runner/ 组装模型请求、处理流和工具结果
packages/core/src/plugin/plan.ts、packages/core/src/permission.ts 模式规则、动作授权与确认
packages/core/src/session/compaction.ts、packages/core/src/session/history.ts 压缩与模型上下文重建
packages/core/src/session/execution/restart.ts 重启后识别和恢复遗留执行

如何写进简历

补丁工坊 · SaaS 接口缺陷修复助手 | 企业软件场景示例

项目描述:面向企业软件研发团队的接口缺陷修复助手,结合缺陷说明、请求日志与现有用例推进代码排查、修改和检查,交付补丁及执行记录。

技术栈:TypeScript、OpenCode、Effect、SQLite、Drizzle ORM

主要工作:

  • SDK 接入与分析改码分层:通过 OpenCode SDK 嵌入修复会话,按仓库组织缺陷日志、源码与复现输入;结合 Plan / Build 及动作、路径权限划分分析与改码阶段,控制接口排查中的修改范围。
  • 持久输入与中断续接:以 SQLite 持久化 Inbox、消息事件和执行标记,通过会话级串行推进消化补充线索;进程恢复时识别遗留执行并结清中断工具状态,续接尚未完成的接口排查。
  • 上下文水位与长排查压缩:结合模型窗口、已知用量与新增消息估算压缩水位,用摘要和近期交互重建请求;在长日志排查中延续接口约束、已排除原因与待处理步骤,控制历史输出进入后续上下文的规模。
  • 复现工具与补丁交付:通过插件封装项目现有接口复现与检查入口,将退出状态、失败断言和日志位置回传修复会话;按仓库基线回收代码差异与检查记录,供研发审查补丁。

脚本医生 · 游戏脚本调试助手 | 游戏开发场景示例

项目描述:面向游戏研发的脚本调试助手,结合关卡触发条件、运行日志和脚本配置推进多轮排查,输出代码改动和本地检查结果。

技术栈:TypeScript、OpenCode、Effect、SQLite

主要工作:

  • 脚本调试与工具反馈循环:嵌入 OpenCode SDK,将关卡触发条件、运行日志和脚本配置交给调试会话,串联代码读取、文件修改与项目检查;按工具执行结果继续排查,并回收实际代码差异。
  • 调试输入持久化与串行推进:通过持久 Inbox 保存新增关卡日志与调试要求,在步骤边界优先处理 steer,空闲时再消费后续输入;用会话级唤醒合并和串行推进协调多次提交,让排查接续新线索。
  • 长调试上下文管理:按模型窗口与新增输入触发压缩,使用摘要和近期工具交互继续处理长运行日志;围绕触发条件、已尝试方案和下一步检查延续调试,减少过期输出反复占据输入窗口。
  • 脚本与命令执行边界:基于 allow、deny、ask 规则约束脚本目录、工程外路径和检查命令,运行时核对工具可用性;将脚本解析、检查输出与需人工进入游戏确认的结果分别交付。

面试题与回答

3 题
  1. Plan Mode 与执行模式的本质区别及工具权限差异是什么?

    展开收起

    核心回答

    OpenCode 的 Plan 和 Build 都可以读取代码、调用模型和使用工具,区别在于任务目标及有效工具权限:Plan 用于分析和形成方案,默认限制普通文件编辑;Build 用于实施,但具体操作仍要通过权限检查。切换模式改变的是 Agent 配置和行为约束,并不会自动把进程变成操作系统级只读沙箱。

    实现上有三层:模式提醒告诉模型当前应做什么;工具注册表过滤被整体禁用的工具;真正读取、编辑或执行命令时,再按动作和资源调用 Permission.assert()。最后这一层决定操作是否允许、需要确认,或直接拒绝。

    Plan 默认限制了什么

    在 OpenCode 2.1.2 中,Plan 插件为 plan Agent 追加普通 edit 拒绝规则,并为用户主目录下的 .opencode/plan 计划目录保留编辑例外。提示词同时要求默认在对话中讨论计划,只有用户明确要求时才保存计划文件;实现任务应由用户切换 Agent 后继续。Plan 配置与提醒。

    例如用户要求“先分析重构方案”,模型可以读取调用链、整理涉及文件;按默认 Plan 编辑规则,直接改普通源码文件会被拒绝,而计划目录是明确例外。切换到 Build 后,默认 Agent 可以按配置执行工具,仍需检查当前会话和目标资源的权限。Build 定义。

    这里的 edit 限制不能推成“任何途径都无法写文件”:Shell 命令、插件和外部工具有各自的动作权限,最终还取决于合并后的配置。若要求严格只读,需要同时收紧这些入口;文件系统或进程隔离承担的又是另一层职责。

    为什么隐藏工具还不够

    Tool.snapshot() 会根据权限过滤被整体禁用的工具,但可见性无法表达某次调用的全部条件。同一个编辑工具可能只允许操作计划目录,因此工具仍可见,普通源码路径却不允许写。工具可见性过滤。

    实际授权以动作和资源匹配规则。规则求值采用最后匹配项;权限服务合并 Agent 与会话规则后,先检查明确拒绝,再处理保存的允许规则及需要确认的请求。deny 返回拒绝,allow 继续,ask 则建立待处理请求并等待决定。这样模型即使提出了一个可见工具的调用,也不能只凭工具名取得任意资源的访问权。规则匹配、权限求值与确认。

    权限检查怎样落到真实操作

    • 读取文件:读取工具先调用 FileAccess.authorizeRead(),解析目标路径,必要时检查工作目录外访问,再以 read 动作和目标资源调用 Permission.assert(),通过后才交给读取器。读取入口、路径与读取授权。
    • 编辑文件:编辑工具解析匹配位置、准备差异预览,再用 edit 动作对目标路径申请权限;通过后还会检查匹配是否唯一等编辑条件,才进入写入。权限允许与补丁可正确应用是两个判断。编辑授权。
    • 执行命令:Shell 工具扫描命令和涉及目录,检查外部目录访问,并按命令资源申请权限;确认后还会重新核对工作目录,再启动进程。因此允许 read 不等于允许执行所有命令,禁止 edit 也不会自动禁止所有可能写文件的命令。Shell 执行前检查。

    模式切换与方案取舍

    切换 Agent 时,Plan 插件注入进入或退出提醒,并在后续上下文中补齐缺失提醒,避免压缩后模型仍受旧模式提示影响。它负责行为连贯性,具体动作仍由权限服务判断。模式提醒维护。

    这种设计能复用同一套模型与工具,在分析和实施之间调整能力范围,也支持路径级例外。代价是需要逐个工具落实授权,并分别验证直接编辑、Shell 写入、外部目录和模式切换;不能只看到界面显示 Plan 就认定所有副作用已被隔离。

    参考资料

  2. 上下文压缩在什么条件下触发?

    展开收起

    OpenCode 2.1.2 的会话执行器在每一步模型请求之前检查自动压缩,也支持模型拒绝超长请求后的恢复,以及用户主动提交的压缩请求。三种入口共用压缩服务,但触发条件不同。

    自动压缩依据模型预算

    SessionRunner 先加载当前会话,再调用 compaction.compact({ reason: "auto", ... })。压缩服务用模型的 input 上限计算水位,没有独立输入上限时使用 context 窗口。

    该版未显式设置 buffer 时,默认预留窗口的 15%;窗口至少为 32k 时,预留量还不低于 16k Token。例如窗口为 100k 时,默认水位为 84k。这是一版源码的策略,不能照搬为所有模型的最佳阈值。

    当前大小优先以同一提供商返回的有效用量作基准,再估算此后新增的消息与工具结果;缺少有效用量时,估算系统指令、工具定义和消息。模型窗口未知时不会自动按比例触发,只能依赖手动操作或服务端溢出反馈。刚完成压缩时,执行器先重建请求;原生压缩产生的不透明窗口,也要等新用量反馈后再判断,避免拿旧计数连续压缩。

    溢出恢复与手动压缩

    当请求实际被判断为上下文溢出时,执行器尝试 reason: "overflow" 的压缩;成功后重新加载历史发起请求,并限制同一步的再次溢出恢复。压缩请求自身过大时,还会逐步降低目标大小,无法再缩减则返回失败,不无限重试。

    自动压缩关闭时,普通水位检查和溢出恢复都会跳过压缩;用户的手动请求仍可进入压缩服务。因此,“关闭自动压缩后,超长时一定会自动兜底”并不符合这版实现。

    压缩后怎样继续任务

    支持原生压缩的模型走提供商原生路径;其他模型生成包含目标、要求、决定、工作状态和下一步的摘要,并按 keep 保留近期上下文。该版 keep 默认预算为 15k Token,与触发水位分别配置。

    普通摘要通过消息标识关联保留片段,后续会话历史据此重建请求。这样既能减少反复携带旧过程,又保留近期任务细节;是否丢失约束仍需检查摘要及后续执行,不能仅凭压缩成功状态判断。

  3. Session 中断后重新启动时如何恢复上下文?

    展开收起

    核心回答

    OpenCode 是能够阅读仓库、修改文件和执行命令的编程助手。以该版 TypeScript 会话实现为例,它把用户输入受理、会话执行和模型输入重建分开:先将输入写入持久待处理队列,按会话串行推进步骤;服务重启后检查遗留执行标记,核对未结束的工具记录,再从持久消息重建上下文继续。

    这恢复的是任务的推进依据。它不会接回旧模型请求的进程现场,也不会仅凭会话恢复就撤销或去重所有文件、命令和外部系统的副作用。

    输入先持久受理,再唤醒执行

    Session.prompt 先按输入 ID 核对已有记录,再准备并受理输入,之后才唤醒执行器。resume: false 可以只受理而暂不执行。相同会话、相同输入类型和相同 ID 的重试沿用第一次受理的内容,因此重新发送并不意味着替换原输入。

    待处理输入保存在 session_inbox 中;投递时消费待处理行,并在同一事件事务中插入可见消息。这样,恢复时可以区分“已经受理、尚未投递”和“已经成为会话消息”的输入,而不依赖客户端是否收到了响应。

    重启标记与执行协调各管一件事

    执行器在开始执行时,随开始事件的事务写入持久执行标记。正常完成、失败或用户主动中断会释放标记;服务关闭导致的中断保留标记,进程突然退出也可能留下它。

    受管理服务启动时,恢复扫描识别这些遗留会话,持久增加恢复尝试次数,再安排继续执行;超过预算则记录失败。这个标记用于识别未结束的执行,不是分布式租约。

    同一会话的多次唤醒由进程内协调器合并,同一会话串行推进,不同会话可以并发。重启恢复假定旧进程已经退出,不能据此允许多个进程共享数据库并同时接管同一会话。

    先结清遗留记录,再重建模型输入

    Runner 开始推进时,会把遗留的 streaming/running 工具记录标为中断失败,把遗留的执行中压缩记录标为压缩失败。这段处理让后续模型能看到调用没有取得正常结果,避免历史永远停在“执行中”;它没有证明实际动作未发生,也没有恢复那个动作的执行现场。

    之后每一步重新选择会话上下文并加载持久历史,再进行一次模型流式请求。工具调用及结果进入会话记录,下一步从更新后的历史继续,而不是依赖一个只在内存里保存消息的无限工具循环。

    长历史采用压缩时,本地摘要路径会保留任务目标、要求、决定、工作状态和下一步等内容,并通过 keepID 引用近期消息;历史加载再取回这些近期消息组成文本,与摘要及后续消息一起重建模型输入。原生模型压缩还有模型兼容条件,不能将任意压缩结果直接用于任意模型。

    哪些状态仍要另行核对?

    最关键的情况是:工具已经修改文件或完成外部请求,但进程在结果落库前退出。恢复后即使工具记录被标成中断,动作也可能已经发生。输入 ID 的受理幂等与会话串行,都不能替代业务动作的幂等和结果核对。该版重启恢复明确采用至少一次的执行语义。

    同样,遗留后台 Shell 的取消或完成通知,是对后台任务状态的处理;不能把通知当成任意子进程、外部服务或业务资源已经恢复的证据。文件快照与显式回退也不能撤销任意命令的外部副作用。

    验证时应分别在输入受理后、工具动作完成但结果未写入时、模型输出中途和压缩过程中终止进程,再检查恢复后的输入数量、消息顺序、工具状态及实际文件或业务结果。还要区分用户主动停止与服务退出,确认不会把用户已取消的任务自动重启。