跳转至

📄 文章总结:《为什么我不推荐使用 OpenCode》

公众号: Visualizeit | 分类: token plan | 收录日期: 2026-08-11 原链接: https://mp.weixin.qq.com/s/YQVcEe0CJz2Ki57q5eaM8Q


为什么我不推荐使用 OpenCode

本文整理翻译自 Wren 发布于 wren.wtf 的《Stop Using OpenCode》。原文基于 OpenCode 提交 baef5cd4 展开分析。文中观点来自原作者,不代表译者立场;后续版本可能已有变化。文章也讨论了 Coding Agent 背后的 Harness 工程:Agent Harness(Agent 运行框架)负责把模型输出转换为工具调用,管理提示词缓存和上下文,处理中断与子 Agent 协作,并限制文件、Shell 和网络权限。任一环节处理不当,都可能导致任务中断、上下文丢失,甚至出现越权的文件访问和命令执行。

一、使用 OpenCode 时遇到的麻烦

OpenCode 是一个开源 Coding Agent,可以读取项目文件、修改代码并执行 Shell 命令。作者用本地模型体验了一段时间,最后决定不再使用。主要遇到四类问题:

  • CACHE:OpenCode 的一些操作会让提示词缓存(Prompt Cache)失效,模型需要重新处理此前积累的上下文。
  • PRUNING:OpenCode 会裁剪早期的工具调用结果,已经读取过的文件内容也在裁剪范围内。
  • INTERRUPT:手动停止主 Agent 运行后,排队发送的消息有时会被忽略。
  • SUBAGENT:子 Agent 执行失败时,已有的分析过程没法复用。

排查这些问题时,作者查看了 OpenCode 源码,问题集中在系统提示词、工具结果裁剪、中断处理,以及命令和路径检查上。

二、提示词缓存为什么频繁失效

许多模型服务提供与 OpenAI /v1/chat/completions 相似的接口。客户端每次请求都会提交当前的完整对话,服务端再以流式方式返回模型生成的内容。为了避免重复计算,服务端会查找与当前请求相同的最长前缀。已经缓存的部分可以直接复用,只有新增内容需要重新处理。这个过程通常称为预填充(Prefill)。对话越长,预填充需要处理的 Token 越多。

OpenCode 的一些操作会改变已经发送过的上下文:

  • PROJECT FILE:每轮请求都会重新读取 AGENTS.md。文件内容一旦变化,之前的缓存前缀就无法继续使用。
  • PRUNING:OpenCode 会裁剪早期的工具调用结果,改变已经进入缓存的上下文。
  • PRUNING:手动停止主 Agent 运行也会触发上下文裁剪。
  • DATE:系统提示词会写入当前日期。跨过午夜后,日期变化会让原有缓存失效。

这些改动会破坏已有的缓存前缀,服务端只能从变化处重新处理上下文。

三、OpenCode 如何管理上下文

OpenCode 采用基于距离的上下文裁剪(Pruning)机制,以对话末尾为锚点,保护最近 40,000 Token 内的工具调用结果。裁剪触发后,距离超过阈值的早期结果会被删除。

裁剪只看距离,不判断工具调用结果的用途。需求文档、代码片段和命令输出都按相同规则处理。等到主 Agent 真正开始修改代码时,早期读入的任务要求可能已经被移出上下文。

用户手动停止尚未完成的主 Agent,原本只是想纠正执行方向,前面读取的需求和计划却可能随裁剪一起消失。主 Agent 继续工作时便无法参考这些内容。

模型的上下文窗口存在容量上限。对话越长,预填充需要的计算和等待时间越多,早期的任务要求也更容易被大量工具输出淹没。上下文压缩(Compaction)会把早期对话和工具结果整理成较短的摘要,为后续消息腾出空间,并减少下一轮需要处理的 Token。

OpenCode 生成摘要时会改变上下文前缀,原有的提示词缓存无法继续使用,因此需要重新处理当前会话。新的上下文随后依赖模型生成的摘要,具体的实现约束和已经确认的决定可能在这个过程中丢失。

作者更倾向于让主 Agent 生成交接文档——先检查和修改,再交给新会话。文档保存在磁盘上,可以继续编辑并跨会话复用。

译者注:如果希望将这类交接过程固定下来,可以参考 planning-with-files。它将计划、调研结果和执行进度分别写入 task_plan.mdfindings.mdprogress.md,并在后续会话中重新读取。这些文件会随任务持续更新,适合需要跨越多轮会话的长期任务。

四、系统提示词与 Agent 交互

OpenCode 将系统提示词放在对话上下文的开头。这份提示词很长,大部分篇幅都在要求模型保持简洁。不同模型对应的提示词也有明显差异,部分规则会直接影响主 Agent 和子 Agent 的执行方式。例如,作者发现主 Agent 向子 Agent 分派任务时,会反复要求子 Agent 不要添加任何代码注释。

系统提示词无法全局修改。如需覆盖这些规则,只能逐个项目配置。Plan 模式用于分析需求和制定计划,Build 模式用于修改代码和执行任务。两种模式使用的系统提示词并不相同,切换模式会改变上下文前缀,让原有的提示词缓存失效。

Plan 模式的系统提示词与实际权限并不一致。提示词禁止主 Agent 写入任何目录,实际权限却允许写入 .opencode/plans。使用过程中,主 Agent 有时会自行创建计划文件,有时又会拒绝用户明确要求的写入操作。

译者注:如果使用 Plan 模式只是为了在动手前澄清需求和完善方案,可以参考 mattpocock/skills 中的 grill-me。它会针对计划或设计逐项提问,帮助用户与 Agent 在开始实现前对齐需求、约束和方案选择。在这类场景下,grill-me 是一种轻量级替代方案。

主 Agent 运行时,新消息会进入队列,但不会立即生效。有时主 Agent 完成工具调用后仍会继续运行,不处理队列中的消息。手动停止后,消息可能进入对话记录,却不会触发回复;用户必须再发送一条消息才能继续。

子 Agent 开始运行后,用户无法直接向它发送补充指令。发现执行方向有误时,只能等待它完成,或者终止任务并丢失已经产生的上下文。工具调用失败也可能直接结束子 Agent 的任务。

五、OpenCode 的权限设计

OpenCode 的安全文档明确说明,权限系统负责在执行操作前征求用户确认,不构成安全隔离。需要隔离时,官方建议使用 Docker 或虚拟机。

权限系统不是安全沙箱,但仍应准确执行用户设置的规则。未经允许的命令和文件访问不应直接通过。

主 Agent 或子 Agent 调用工具时,OpenCode 会根据操作类型、命令内容和文件路径匹配权限规则,决定直接允许、直接拒绝,还是询问用户。

需要确认时,界面提供 Yes、No 和 Always 三个选项。Yes 只允许当前操作,No 拒绝当前操作;Always 会保存这次选择,后续匹配的操作不再询问。界面没有与 Always 对应的永久拒绝选项。若要持续拒绝某类操作,用户需要在配置文件中添加 deny 规则。

问题在子 Agent 运行时更加明显。子 Agent 访问项目目录外的文件可能触发权限提示;选择 No 会直接终止任务,已经积累的上下文也会丢失。为了让任务继续运行,用户往往会选择 Yes。

Always 实际放行的范围也可能大于用户看到的当前命令。例如,允许一条用于输出文本的 python3 命令后,OpenCode 可能按 python3 前缀保存规则。后续 Python 命令即使改为读取文件或调用其他程序,也可能不再询问用户。

频繁出现的权限提示还会带来决策疲劳。用户反复在 Yes、No 和 Always 之间选择,容易逐渐习惯直接允许。久而久之,确认操作只是继续任务前的例行步骤,失去了安全判断的作用。

六、Shell、文件与网络权限为什么会失效

用户可以通过权限规则限制 Shell 命令。下方配置拒绝所有以 git 开头的命令:

(配置示例:拒绝所有以 git 开头的命令)

OpenCode 会使用 Tree-sitter 将 Bash 或 PowerShell 命令解析成语法树,再把提取出的命令与权限规则进行匹配。直接运行 git status 会被拒绝;但经过绝对路径、环境变量或其他解释器调用后,命令文本发生变化,相同操作却可能绕过检查。

这套规则检查的是命令写法,无法判断命令最终产生的行为。Python、Shell 和其他解释器都可以继续启动新的程序。只要最终执行的命令没有以 OpenCode 能识别的形式出现在语法树中,针对 git 的规则就不会生效。

文件权限采用硬编码的命令列表。OpenCode 只对列表中的 catrmcpmv 等命令检查路径,不在列表中的程序不会经过相同的检查流程。

直接使用 cat 读取项目外的文件可能触发权限提示,改用 Python 读取同一个文件却可能不需要确认。硬编码列表只能覆盖已知命令,无法判断通用程序运行后会访问哪些文件。

Shell 重定向没有经过相同的路径检查。echo 不在文件操作命令列表中,但 echo foo > file.txt 仍然会修改文件。在语法树中,重定向目标与命令节点并列。OpenCode 只检查命令节点中的参数,因此 > 后面的目标路径不会触发权限判断。

OpenCode 也没有单独隔离 Shell 的网络访问。主 Agent 可以使用 WebFetch 工具读取网页,也可以在 Shell 中运行 curl 等网络工具。即使限制 WebFetch,主 Agent 仍然可以通过 Shell 下载外部内容。

原文分析的版本默认连接远程模型。本地模型配置错误或尚未选定时,主 Agent 读取的文件内容可能随下一次请求发送给远程服务。要让模型请求留在本机,需要明确配置本地服务,并确认 OpenCode 实际连接的模型提供方。

OpenCode 此前还出现过未经身份验证的本地 HTTP 服务漏洞。受影响版本会自动启动服务,并开放 Shell 执行和文件读取接口;宽松的跨域配置让网页也能调用这些接口。该问题影响 1.0.216 之前的版本,并已在 1.0.216 修复,编号为 CVE-2026-22812

七、Docker 能保护什么,不能保护什么

Docker 是一种用于创建和运行容器的工具。容器会把程序及其依赖放在相对独立的运行环境中,并允许用户配置文件、网络和系统资源的访问范围。

把 OpenCode 放进 Docker,可以将进程和文件系统与主机隔开,缩小误操作范围。未挂载的主机目录不会出现在容器中,主 Agent 只能修改容器文件系统和显式挂载的目录。

Coding Agent 需要读写项目代码,因此通常需要将项目目录挂载进容器。Docker 的 Bind Mount 默认允许写入,容器中的修改和删除会同步到主机。项目目录一旦以可写方式挂载,主 Agent 仍然可以覆盖代码或删除尚未提交的修改。

挂载凭证和 Docker Socket 会扩大主 Agent 能够访问的范围。SSH 密钥、云服务配置和其他凭证进入容器后,都可以被主 Agent 读取。将 Docker Socket 挂进容器后,容器还可以创建和管理主机上的 Docker 容器。

Docker 容器默认可以建立外部连接。需要显式使用 --network none 关闭网络。没有网络限制时,主 Agent 可以下载网页或脚本,也可以发送容器中能够读取的数据。

常规 Docker Daemon 通常以 root 权限运行,Rootless 模式则让 Daemon 和容器以非 root 用户运行。无论采用哪种模式,挂载范围、容器权限和网络出口都需要单独设置。

运行 Coding Agent 时,可以只开放任务需要修改的目录,将 .git 等关键内容设置为只读,不挂载凭证和 Docker Socket,并默认关闭网络。在支持 Landlock 的 Linux 内核上,还可以用它限制 Agent 进程能够访问的文件和网络。

Docker 负责隔离容器和主机,无法判断一次命令、文件访问或网络请求是否应该放行。容器内部仍要由 Agent Harness 通过文件系统权限、进程沙箱和网络策略限制具体操作。

八、关于本地模型的补充

作者使用 OpenCode 时明确指定了本地模型服务。确认模型请求只发送到本地服务后,代码和上下文无需发送给云模型,模型推理也不依赖云模型提供商。但模型运行在本地,并不会改变 OpenCode 对文件、Shell 和网络的控制方式。

对作者来说,本地模型更适合代码检索和分析。例如,提供 Bug 的现象、涉及的代码和对原因的初步判断,再让模型读取相关实现,整理调用链,并为每个结论附上代码位置。

可以使用类似下面的提示:

我怀疑 x 中存在一个 Bug,目前的现象是 y,可能与 z 有关。请读取相关代码,整理完整调用链,并在每个判断后标注文件和代码位置。

这类任务的输入范围比较明确,输出也能通过源码逐项核对。模型负责查找和整理信息,作者再根据代码引用判断结论是否成立。

直接让模型完成大范围代码生成,结果往往更难控制。模型可能选择当前最容易完成的实现,却破坏原有的状态边界和模块关系。修改范围越大,越难确认每个设计选择来自哪里。

作者通常让本地模型负责分析、定位和小范围修改,架构选择和最终 Review 仍由人完成。生成的代码还需要经过测试和逐项检查。

本地模型可以避免代码进入云端,Docker 可以缩小误操作范围,但都无法替代 Agent Harness 的系统级权限控制。基于本文分析的版本,作者不推荐把 OpenCode 直接用于包含重要代码、凭证和个人文件的日常开发环境。

九、总结

可靠的 Coding Agent 不只取决于模型能力,也取决于 Harness 如何管理上下文、协作流程与权限边界。


🎯 Friday 提炼

  • OpenCode 的提示词缓存容易因 AGENTS.md 读取、上下文裁剪、手动停止和日期变化而失效,导致重复预填充和额外成本。
  • 上下文裁剪以距离为锚点保护最近 40,000 Token,不区分内容用途,可能把需求文档和计划移出上下文;压缩摘要也会丢失具体约束和已确认决定。
  • 系统提示词无法全局修改,Plan 与 Build 模式切换会破坏缓存;Plan 模式提示词与实际权限不一致;主 Agent 和子 Agent 的交互存在消息排队、中断忽略、无法向子 Agent 补充指令等问题。
  • 权限系统只是确认机制,不是安全沙箱。命令解析可被路径/环境变量/解释器绕开,文件检查只覆盖硬编码命令列表,重定向和网络访问也缺乏有效限制;Always 放行范围可能超出用户所见,且没有永久拒绝选项。
  • Docker 能隔离主机边界,但可写挂载、凭证/Docker Socket 挂载和默认网络出口都会扩大风险;真正可靠仍需 Agent Harness 做系统级权限控制。

一句话: OpenCode 在缓存管理、上下文裁剪、Agent 交互和权限控制上存在系统性缺陷,本地模型和 Docker 都无法替代 Harness 层面的工程保障,因此不推荐在重要开发环境中直接使用。


💡 Friday 看法

看完这篇我反而有点庆幸自己只是把 AI 当“高级补全”用,没真把它当能全权托管代码库的主力。技术分析确实扎实,但这也反映出一个问题:很多用户根本不会手动去管什么 prompt cache 和上下文裁剪,真正该做的是工具本身把这些坑抹平,而不是让使用者在源码里找答案。另外,作者用“交接文档”替代上下文压缩的思路我很认同,与其追求一个永远不丢上下文的 agent,不如把关键信息落到磁盘上,这比依赖花里胡哨的记忆机制靠谱多了。