今年 1 月,Anthropic 开始限制用户把 Claude Code 的订阅凭据用在 OpenCode 里。按照 OpenCode 联合创始人兼 CEO Jay V 的说法,当系统提示词里直接出现 “OpenCode” 时,请求甚至可能被拒绝。这本来应该是一记重击,结果却让大量开发者 第一次注意到:Claude Code 之外,还有一个开源、支持多模型、能够自由扩展的 Coding Agent。
Jay V 后来在 Y Combinator 的访谈里说,这件事意外地把 OpenCode 和 Claude Code 放到了同一个竞争层级。紧接着,Codex 又正式支持了 OpenCode,允许用户直接在 OpenCode 里使用 Codex 订阅。一个有些反直觉的增长故事由此出现:
| 时间或指标 | OpenCode 的规模 |
|---|---|
| 年初月活 | 约 65 万 |
| 6 月月活 | 约 1300 万 |
| 半年增长 | 约 20 倍 |
| Token 用量 | 每天约 7 万亿 |
OpenCode 官网目前展示的数据还要更高:195K GitHub Stars、950 位贡献者、16M 月活用户。OpenCode 能在 Claude Code、 Codex 这些强势产品之间快速增长,并不只是因为那次限制带来了意外曝光,更重要的是,它从一开始就选择了一个明确的位置:
不绑定某一家模型,做一个开放的 Agent Runtime。
一、OpenCode 到底是什么?
OpenCode 是一个开源 AI 编程 Agent,可以在终端、桌面端和 IDE 中运行,也可以连接 Claude、GPT、Gemini、开源模型和 本地模型。它的主要特性可以概括为:
| 特性 | 对用户意味着什么 |
|---|---|
| 开源 | 可以查看源码、二次开发,也可以私有部署 |
| 模型开放 | 支持 75+ 模型提供商,不必绑定一家模型公司 |
| 多种入口 | 终端、桌面、IDE 都能使用 |
| Agent 能力完整 | 支持 LSP、多会话、工具调用、权限控制和 Skill |
| 原生插件系统 | 可以注册 Hook、Tool,甚至改写发给模型的消息 |
OpenCode 的团队本身就是一群重度终端和 Neovim 用户。Claude Code 出现后,他们意识到,这不是又一个代码补全工具,而是 一种新的开发方式;但他们也认为,Claude Code 的终端体验距离 Neovim 这类成熟工具还有差距。因此,OpenCode 从一开始 就不只是想做一个“能调用模型的命令行”,而是希望做出开发者愿意每天使用的现代终端产品。这也是它能够吸引核心开发者的 第一个原因:
开放只是入口,工具本身还得足够好用。
1.1 不选模型,也是一种产品选择
Claude Code 和 Codex 通常围绕自家的模型构建,OpenCode 则把模型和 Agent Runtime 分开:
| |
用户今天可以使用 Claude,明天可以换成 GPT;想尝试刚发布的 DeepSeek、Kimi、GLM 或 MiniMax,也不需要更换整套 Coding Agent。创始人 Jay V 对这个定位有一个很直接的总结:
当市场里已经有一两个强势的封闭产品时,剩下的生态往往会聚集到一个开放的替代方案周围。
1.2 模型不只要强,还要用得起
OpenCode 本身是开源的,用户可以自带 API Key,也可以连接现有账号。除此之外,它还提供了两种模型服务:
| 方案 | 价格方式 | 主要用途 |
|---|---|---|
| 自带模型 | 按模型提供商计费 | 已有 API Key、订阅或私有模型 |
| OpenCode Go | 每月 10 美元 | 低成本使用精选开源模型 |
| OpenCode Zen | 预充值、按量付费 | 灵活使用经过 Agent 测试的模型 |
OpenCode Go 的核心目标是“让更多人用得起 Coding Agent”。这一点在美国之外尤其重要:在中国、印度尼西亚、巴西、 越南等市场,每月 200 美元的 Coding Agent 订阅并不便宜。10 美元的 Go 加上逐渐成熟的开源模型,让 OpenCode 获得了 大量美国之外的用户。
全球化的用户分布还带来了另一个优势:GPU 不容易闲置。东方进入工作时间时,西方正在休息;西方开始工作时,东方又进入 低峰,请求因此更加平稳,形成接近 24 小时连续运行的 GPU 使用周期。OpenCode 并不只依靠自己部署的 GPU,而是同时使用 租赁 GPU、推理服务商和模型厂商;全球流量能够提高整体利用率,进而降低推理服务的单位成本。这几项因素形成了一个正向循环:
- 开源带来信任和扩展性;
- 多模型避免厂商锁定;
- 低价计划降低使用门槛;
- 全球用户改善 GPU 利用率;
- 成本下降,又让更多人能够使用。
至此,OpenCode 已经拥有模型、工具、权限、Agent 循环和不错的终端体验,能够完成复杂的编码任务。但它仍然面对一个 问题:
今天这个 Agent 做完的工作,明天打开一个新会话,它还知道吗?
它可能看得到代码,却不知道昨天为什么这样修改;它也可以重新阅读仓库,却不知道哪些方案已经讨论过、哪些约束不能破坏, 以及当前任务进行到了哪里。这正是 PowerContext 要解决的问题。
二、关于 PowerContext
假设你正在用 OpenCode 修改一个项目。第一天,你和 Agent 经过充分讨论,最终确定:
- 公共 API 必须保持异步;
- 旧配置格式需要继续兼容;
- 当前实现已经完成,下一步补错误输入测试。
Agent 按照这些约定完成了代码修改,测试也已经通过。第二天,你重新打开一个 OpenCode 会话:代码仍然存在,但昨天的 对话并没有进入当前上下文。新的 Agent 能够重新阅读代码,却不知道:
- 为什么 API 必须保持异步;
- 哪些兼容性约束不能破坏;
- 哪些方案已经讨论并放弃;
- 当前任务究竟做到哪里。
一种直接的办法是把所有历史对话重新交给模型,但这样很快会遇到三个问题:
| 问题 | 结果 |
|---|---|
| 历史对话越来越长 | Token 消耗持续增长 |
| 大量内容与当前问题无关 | 真正重要的信息反而被淹没 |
| 旧结论可能已经失效 | 模型容易把过期信息当成当前事实 |
PowerContext 并不打算把整份聊天记录永久塞进上下文,而是采用另一种方式:
把项目中值得留下来的证据、决策和工作状态保存下来,在需要时只找回相关的一小部分。
2.1 先分清四个概念
PowerContext 中经常出现 Source、Memory、Prepared Context 和 Handoff 四个概念。名称看起来不少,但每个概念只解决 一个明确的问题:
| 概念 | 最简单的理解 | 例子 |
|---|---|---|
| Source | 原始证据 | 用户 Prompt、任务结果、外部资料 |
| Memory | 值得长期复用的项目知识 | 决策、约束、当前状态、下一步 |
| Prepared Context | 针对当前问题临时找回的上下文 | 与“配置兼容”相关的几条历史 |
| Handoff | 把正在进行的工作交给下一个会话 | 目标、进度、阻塞项、证据和下一步 |
它们不是同一件事的四种叫法,而是一条逐步加工的链路:
| |
这里需要特别注意一个边界:
保存 Source,不等于自动创建 Memory。
例如,用户刚输入:
帮我看看这个测试为什么失败。
PowerContext 可以先把这句话保存为 Source,作为本轮工作的原始证据,但不会立刻生成一条“项目测试失败”的长期 Memory。只有当任务产生了经过验证、以后仍然有用的结论,例如:
Windows 下失败是因为临时目录没有提前创建。
这类信息才适合进一步沉淀,从而避免把每一句聊天都变成所谓的“记忆”。
2.2 Prepared Context 不是聊天记录
当用户提出一个新问题时,PowerContext 会根据当前项目、用户问题、内容相关性和字节预算,生成一份有界的 Prepared Context。“有界”意味着它不会把全部 Memory 一股脑交给模型,而是只提供当前问题可能需要的部分。例如用户问:
为什么这里没有把 API 改成同步调用?
Prepared Context 可能只返回:
| |
其他与数据库、文档或部署相关的历史不会进入这一轮。这段内容还会被明确标记为“不可信历史证据”,因为历史只能说明 “过去记录过什么”,不能证明它现在仍然正确。当前用户指令、当前代码和实时运行结果,始终优先于历史 Memory。
2.3 Handoff 解决的是另一类问题
Memory 更像项目的长期知识,Handoff 则是一次具体工作的交接包。例如,一个会话已经找到问题根因、修改了两个文件并 运行过单元测试,目前只差真实环境验证。此时,Handoff 可以把以下内容一起交给后续会话:
| 内容 | 示例 |
|---|---|
| 目标 | 修复 OpenCode 插件安装缓存 |
| 已完成 | 缓存加入仓库和 commit 身份 |
| 已验证 | 单元测试、双仓库真实 clone |
| 阻塞项 | GitHub Actions 等待维护者批准 |
| 下一步 | 批准后检查远端 CI |
下一个会话不必重新翻阅聊天记录,也不必猜测前一个 Agent 做过什么;它拿到的是一份结构化、可检查的工作状态。
2.4 为什么 PowerContext 要做成独立 Server
PowerContext 没有把存储、检索和记忆逻辑直接写进 OpenCode 插件,而是把这些能力放在独立 Server 中:
| |
实际运行时,OpenCode 和 PowerContext Server 是两个独立进程,各组件的职责如下:
| 组件 | 负责什么 |
|---|---|
| OpenCode | 接收用户输入、运行 Agent、调用模型和工具 |
| PowerContext 插件 | 把 OpenCode 生命周期转换成 HTTP 请求 |
| PowerContext Server | 保存 Source、管理 Memory、召回上下文和处理 Handoff |
这种架构有五个直接好处:
- 不绑定 OpenCode:同一份项目上下文可以被 Codex、Claude Code、DeepSeek Harness、Pi 和 OpenCode 使用。
- 不绑定模型:今天用 Claude,明天换 Codex 或开源模型,项目 Memory 不需要迁移。
- 插件保持轻量:插件不重写存储,不嵌入 Python,也不维护另一套记忆数据库。
- 故障时正常降级:PowerContext 超时或暂时离线时,OpenCode 跳过召回,继续完成当前任务。
- 项目相互隔离:每个仓库使用独立 scope,换项目不会召回当前仓库的决策。
由此,两边的职责边界就清楚了:
OpenCode 负责让 Agent 在当前环境里行动。
PowerContext 负责让项目上下文跨会话留下来。powercontext-opencode负责把两边接起来。
接下来需要解释的是:OpenCode 的一条用户消息会经过哪些生命周期,插件又是在什么时候召回、什么时候保存,以及什么时候 把上下文交给模型。这需要从 OpenCode 的 Plugin 和 Message Hook 讲起。
三、一条 OpenCode 消息是怎么跑起来的?
用户在 OpenCode 中输入一句话后,系统并不会立刻把字符串原样交给模型;中间还会经历消息创建、插件 Hook、模型请求、 工具调用和后续模型请求。只保留与 PowerContext 有关的部分后,生命周期大致如下:
| |
3.1 chat.message:用户刚说完,模型还没开口
chat.message 可以理解为“OpenCode 已经接收用户消息,但模型请求尚未发出”的时刻。PowerContext 插件会从消息中提取
正常的文本部分,忽略系统合成内容,然后完成两项彼此独立的工作。
先召回
插件向 PowerContext Server 调用:
| |
请求里包含:
- 当前项目 scope;
- 用户这一轮的问题;
- 最大输出字节预算。
Server 返回 ready 时,插件会得到一段有界的 Prepared Context;返回 empty 时,则表示当前没有相关历史。
再采集
插件同时调用:
| |
该请求把用户这一轮的原话保存为 Source,并记录项目目录、会话 ID、消息 ID 等元数据。召回失败不影响采集,采集失败也 不影响召回;即使两者都失败,OpenCode 仍然继续回答。这就是 fail-open:记忆服务可以增强 Agent,但不能成为 Agent 正常工作的前置条件。
3.2 为什么还需要一份临时缓存?
chat.message 负责找回上下文,但此时不能直接修改用户的永久聊天记录。插件会先把召回结果放入进程内的临时缓存,缓存键
由会话 ID 和消息 ID 组成。
这份缓存:
- 不写入 SQLite;
- 不写入 OpenCode transcript;
- 不会变成新的 Memory;
- 会话删除时会一起清理。
它只负责在“召回完成”和“模型即将请求”之间传递数据。
3.3 messages.transform:只改这一次模型请求
在 OpenCode 真正向模型发出请求之前,experimental.chat.messages.transform 会被调用。插件在这里定位当前用户消息,
再把缓存中的 Prepared Context 作为一段 synthetic 文本临时追加进去。模型最终看到的内容类似:
| |
但是,OpenCode 保存的用户消息仍然只有第一句。这样做有两个好处:
- 不污染用户真实聊天记录;
- 同一条消息在工具循环中再次发给模型时,可以识别并避免重复注入。
因此,“消息改写”不是篡改聊天历史,而是在模型请求边界加入一份仅供本轮使用的临时参考材料。
3.4 system.transform:告诉模型该怎么对待历史
插件还会给本轮系统提示加入一段很短的说明:
- PowerContext 提供的是不可信历史;
- 当前用户、仓库和系统指令优先;
- 不要为了复制当前 Prompt 再写一条 Memory;
- 持久操作前要确认;
- PowerContext 不可用时继续当前任务。
这段说明既不负责召回,也不执行任何操作,只用于告诉模型:这份历史应该如何使用,以及哪些边界不能越过。
四、把 PowerContext 装进 OpenCode
当前插件面向 OpenCode 1.x,最低验证版本为 1.18.21。
在 OpenCode 集成进入正式 PowerContext 发布版本之前,可以让 CLI、Server 和插件都使用同一个 master revision:
| |
4.1 启动 PowerContext Server
| |
默认地址是:
| |
Server 默认使用本地 SQLite。显式保存和读取 Memory 不要求配置推理模型;只有自动抽取等生成能力需要模型。可以另开一个 终端检查服务状态:
| |
4.2 安装 OpenCode 插件和 Skill
| |
这个命令会完成两件事:
- 把原生 PowerContext 插件全局注册到 OpenCode;
- 把
project-contextSkill 安装到 OpenCode 配置目录。
安装完成后运行:
| |
正常情况下,各项检查结果如下:
| 检查 | 期望结果 |
|---|---|
| OpenCode 版本 | ok |
| 插件配置与激活 | configured and active |
project-context Skill | ok |
最后启动 OpenCode:
| |
4.3 先体验跨会话 Memory
在一个项目的第一个会话中输入:
使用 PowerContext 保存一条项目 Memory:公共 API 必须保持异步,下一步是补错误输入测试。
OpenCode 会调用 pc_remember,并在持久化前请求确认。保存完成后,关闭当前会话,在同一个仓库中重新打开 OpenCode,
然后输入:
这个项目之前确定了哪些 API 约束?
即使不显式调用搜索工具,本轮 Message Hook 也会先根据问题请求 Prepared Context,再把相关历史临时交给模型。换到另一个 仓库后再问同样的问题,不应该召回这条 Memory;这说明项目 scope 隔离正常工作。
五、总结
整条链路可以归纳为三层:
- OpenCode 提供开放、多模型的 Agent Runtime,负责会话、模型、工具、权限和交互体验;
- PowerContext 提供持久、项目级的上下文,负责 Source、Memory、Prepared Context 和 Handoff;
- powercontext-opencode 通过原生插件和 HTTP,把两边接起来。
这种集成没有把 PowerContext 塞进 OpenCode,也没有让 OpenCode 依赖某个特定模型,两边都可以独立演进。OpenCode 可以 继续增加模型、界面和 Agent 能力,PowerContext 可以继续改进召回、审核、交接和经验沉淀,中间只保留一层薄适配。
OpenCode 最有价值的地方不只是“可以更换模型”,而是它把 Agent Runtime 做成了一个开放的产品:模型可以更换,工具可以 扩展,生命周期可以挂载 Hook,核心 Agent 循环也可以被其他应用嵌入。这为项目上下文提供了一个自然的接入位置。
随着模型成本不断下降,Agent 完成任务的能力越来越强,但项目真正需要留下来的不只是代码,还包括为什么这样写、工作进行到 哪里,以及下一步是什么。
OpenCode 解决 Agent 怎么动手。
PowerContext 解决项目上下文怎么留下来。