终端里的 AI 编码代理:我的 OpenCode 实践与本地 / 线上模型选型

作者:npc001 · 2026-08-19T10:49:28.333486283+00:00

终端里的 AI 编码代理:我的 OpenCode 实践与本地 / 线上模型选型

事实依据:本文基于 OpenCode 官方文档(opencode.ai/docs)与本机真实接入配置撰写,撰写于 2026 年 8 月。

一、OpenCode 是什么

OpenCode 是一个开源的 AI 编码代理,主打终端(TUI)界面,也提供桌面应用与 IDE 扩展。它基于 AI SDK 与 Models.dev,官方声称支持 75+ 大模型供应商,同时原生支持接入本地模型。

上手只需两步:/connect 配置供应商,然后在项目目录运行 opencode。但真正决定体验上限的,是下面这些容易被忽略的工程细节——尤其当你把模型跑在本地。

二、我的接入配置:本地为主,云端兜底

我用 llama.cpp 的 llama-server 部署本地模型,服务跑在局域网一台 GPU 机器上,通过 OpenAI 兼容 API 暴露。项目根目录的 opencode.json 这样接入:

{
  "provider": {
    "local": {
      "npm": "@ai-sdk/openai-compatible",
      "name": "Local OpenAI-compatible",
      "options": { "baseURL": "http://192.168.1.3:8001/v1" },
      "models": {
        "unsloth/Qwen3.6": {
          "name": "unsloth/Qwen3.6",
          "modalities": { "input": ["text", "image"], "output": ["text"] },
          "limit": { "context": 128000, "output": 65536 }
        }
      }
    }
  }
}

几个要点:

  • llama.cpp 就是走 OpenAI 兼容协议。只要 llama-server 暴露了 /v1/chat/completions,一个 npm: "@ai-sdk/openai-compatible" 就能接上,背后是什么推理引擎完全透明。
  • 务必声明 limit.context / limit.output。官方 llama.cpp 示例就是这么配的——告诉 OpenCode 真实的上下文窗口上限,它才能合理规划,否则 agent 会在毫不知情下把上下文塞爆。
  • modalities 要如实声明。我的 Qwen3.6 支持图片输入,于是显式声明 input: ["text", "image"]——这样终端里拖入设计稿,模型才真正能读图。但这同时也带来了崩溃风险,见坑 2。

云端我只在 auth.json 里保留了一个 DeepSeek 作为兜底(deepseek-v4-flash / deepseek-v4-pro 等)。日常主力是本地模型,重活偶尔切到云端。

三、避坑:本地模型在真实工程里的四个坑

先说一个前提:下面这些问题不全是本地模型的锅。有些是本地推理资源的物理限制,有些其实是 OpenCode 产品/架构层面的特性——本地模型只是让它们更早、更频繁地暴露出来。每个坑我都会标注归因(本地特有 / 本地为主 / 混合),以及对策的实际效力(真解决 / 缓解 / 取决于环境)。产品层面的共性问题单独放在第四节「扩展」里讲。

坑 1:重复任务 / 复读机(本地为主)

症状是:

  • 重复描述同一任务:同一段任务描述被反复输出,却迟迟不执行。
  • 重复执行同一操作:连续多次调用同一个工具、运行同一条命令、修改同一文件同一位置,产出却没有任何推进。
  • 重复写入:同一段代码/文本被插入多遍。

深入看,根因分两层:

  • 架构层面(产品):agent 循环本质是"无状态编排 + 上下文回放",框架不维护独立的"任务状态",模型只能从不断变长的转录里反推自己做过什么。这触发了已知的 Lost in the Middle 现象——任何规模的模型在足够长的上下文里都会退化。这一层换云端也躲不掉,详见第四节扩展。
  • 模型大小决定阈值(本地):小模型指令遵循、工作记忆都更弱,注意力在更短的上下文长度上就开始稀释,所以复读来得更快更频繁;大模型只是把失忆阈值推得更远。这解释了为什么线上旗舰"看起来没这个问题"——它只是还没到自己的阈值。

所以换更大的模型只是推迟,架构级缓解才治本:

{
  "agent": {
    "build": { "steps": 30, "temperature": 0.2 },
    "plan": { "temperature": 0.1 }
  },
  "permission": { "doom_loop": "ask" }
}
  • 真解决——steps 限制最大迭代轮数:官方文档明确这是"为想控制成本的用户设一个 agentic 操作上限",达到上限后强制转纯文本总结。它能终止死循环,但不能防止重复发生——上限内该复读还是复读,它是安全阀不是解药。
  • 真解决——缩短"回放"的上下文:失忆源于上下文太长,压缩就对症。compaction/prune 自动压缩转录、裁掉旧工具输出(配置见坑 3);explore/scout 子代理把上下文隔离成小块、只回传摘要;把大任务拆成单步指令,每步独立重推,模型无需记住更早的步骤。这是治本手段,代价是压缩会丢细节(以信息换长度)。
  • 缓解——先 Plan 再 Build:把计划"钉"在上下文里,执行时模型照单走,减少即兴重推。对复读有抑制作用,但不保证。
  • 缓解——调低 temperature:减少发散有真实作用,但对"复读"这类结构性失败效果有限。遇到循环立刻中断并用 /undo 回退,才是实际最可靠的操作手段。
  • 取决于环境——doom_loop 权限:它触发"卡住恢复提示",但提示发出去后,小模型能不能顺着走出来取决于模型本身——可能有用,也可能只是再复读一轮。

坑 2:多图输入导致 llama.cpp 崩溃(本地特有)

本地模型接了 image modality 后,有一个很现实的崩溃场景:一次上传几张图片,llama-server 直接崩掉。原因通常是视觉输入的 embedding 与 KV cache 急剧膨胀,撑爆了显存/上下文,llama.cpp 没有优雅降级就直接 OOM。这是纯本地问题——云端多模态按需扩容,不存在这个瓶颈。先声明:下面的对策没有一条能保证不崩。

{
  "attachment": {
    "image": {
      "auto_resize": true,
      "max_width": 2000,
      "max_height": 2000,
      "max_base64_bytes": 5242880
    }
  }
}
  • 真解决(彻底回避)——限制图片数量 + 多图切云端:一次只给一两张关键图;密集多图理解直接切 DeepSeek 等云端多模态。这是唯一"确保不崩"的办法,代价是放弃本地多模态。
  • 缓解——auto_resize 预压缩图片:发送前把超限图片缩放/降体积(默认 2000×2000、base64 5MB),减少视觉 token → 降低 KV 压力,能降低崩溃概率,但不保证不崩——如果 OOM 出在视觉投影层或显存本身紧张,压图也救不了。
  • 取决于环境——limit.context 留余量、--ctx-size / --n-gpu-layers:都是真实参数,能腾出显存/上下文,但怎么调取决于你的显存与模型大小,没有银弹数值,需要对着崩溃日志逐项试。

坑 3:多窗口互相影响,上下文同时过载(本地为主)

另一个我反复踩的坑:opencode 同时开多个窗口会话,它们会互相影响——多个 agent 并行对着同一个本地推理服务器吐 token,显存和 KV cache 被瓜分,最终"同时出现上下文窗口过大",表现为多个会话一起变慢、报上下文溢出或干脆崩溃。

归因同样是两层:本地层面是共享同一块 GPU 的物理限制(云端按需扩容,不存在);产品层面是各窗口各自维护独立上下文、没有统一预算协调(这条换云端也存在,见第四节)。

{
  "compaction": {
    "auto": true,
    "prune": true,
    "reserved": 10000
  }
}
  • 真解决——控制并发窗口数 + 窗口间任务隔离:本地模型算力是共享的,同一时刻主力窗口最多两三个、每个窗口只专注一件事。这是釜底抽薪,但对"你就是要并行干活"的场景等于没用。
  • 缓解——开启自动压缩:compaction.auto(默认 true)在上下文将满时自动把长会话压成摘要;prune: true 裁掉旧的工具输出省 token;reserved 预留 buffer 避免压缩瞬间溢出。它能把"溢出"推迟,但压缩丢细节,是以信息换长度。
  • 取决于环境——limit.context:告诉模型真实上限,它才不会被并发请求推爆;但模型会不会严格遵守这个上限,依赖模型本身的指令遵循能力。

坑 4:中途停摆与挂起(混合)

本地模型另一个高频问题,是我经历过的两种"卡死"。要先把它们区分开,因为不是同一个问题、解药也不同:

  • 中途停摆(本地为主):agent 跑着跑着突然不往下推了——既不报错也不产出。通常是一次推理请求迟迟没有返回(llama-server 忙于长序列解码、或流式响应中途断了),而客户端还在干等下一个数据块。
  • 挂起后输入无效(产品为主):启动服务后整个会话被挂住,终端输入不再响应,只能手动停掉进程再重开。这更像 OpenCode 进程自身的问题(TUI 假死),换云端模型同样可能发生,详见第四节。
{
  "provider": {
    "local": {
      "options": {
        "timeout": 120000,
        "chunkTimeout": 30000
      }
    }
  }
}
  • 真解决——timeout / chunkTimeout(仅针对"中途停摆"):文档明确"无新 chunk 到达即中止请求"。这能真解决网络等待导致的无限挂起——但注意,它把"挂起"变成"报错",让你能重试,不修复服务端本身。
  • 无法靠配置解决——"挂起后输入无效":如果挂的是网络等待,上面的超时有效;但如果挂的是 opencode 进程本身,任何客户端配置都救不了,只能 kill 掉重启。可以这样区分:挂起时 curl http://192.168.1.3:8001/v1/models 还通 = 服务端正常、是进程假死;不通 = 服务端先卡死。
  • 缓解——配好服务端:超时频繁触发时回去看 llama-server——单次请求是不是太长、--ctx-size 是否过大拖慢解码、显存是否已被多窗口占满(回到坑 3)。

四、扩展:哪些"坑"其实是 OpenCode 产品 / 架构的限制

有些问题我在使用中一度归咎于"本地模型不行",后来发现换成云端旗舰模型照样存在——它们其实是 OpenCode 产品/架构层面的特性,本地模型只是把触发时机提前、把出现频率放大。判断标准就一句话:换云端模型后,这个问题是否仍然会发生?

1. 无状态回放架构 → 长上下文失忆

agent 循环不维护独立的"任务状态",模型只能从不断变长的转录里反推自己做过什么。这是坑 1 复读问题的架构根因,任何模型在足够长的上下文里都会退化(Lost in the Middle)。区别只在于:云端大模型阈值高、平时感知不到;本地小模型阈值低、日常就能撞上。对策就是坑 1 里的 steps、压缩与子代理隔离——这不是"配置能根治"的问题,而是架构使然,只能靠工程实践补偿。

2. 会话不沉淀为知识 → 新会话重新踩坑

模型没有跨会话记忆,当次会话的经验和教训留不在任何地方,新会话又从零开始。这不是本地模型的坑,是产品没有"自动学习"机制——/share 只生成分享链接,compaction 的摘要只留在当前会话内,都不会沉淀给新会话。所以知识持久化只能靠人主动做,OpenCode 提供的通道:

  • 真解决——经验回写 AGENTS.md 并提交:官方钦定的跨会话知识通道。每次会话结束,把踩的坑、有效的配置、让模型出错的指令习惯补进项目根目录 AGENTS.md 的 Gotchas 节,新会话启动时会自动读入——等于带着上次的教训开工。配合 /init 可原地完善,不用手写格式。
  • 真解决——个人经验放全局 ~/.config/opencode/AGENTS.md:与项目无关的通用教训放这里,所有会话、所有项目生效。
  • 真解决——把高频流程固化成命令/子代理:重复性的工作流写进 .opencode/commands/ 或 .opencode/agents/,把"过程知识"固化在文件里,下次一句话即可复用。
  • 缓解——instructions 引用独立经验文件:用 opencode.json 的 instructions 字段挂 docs/opencode-lessons.md(支持 glob 和远程 URL),模块化维护、团队共享。
  • 无法自动解决——别指望工具自己攒经验:落不落盘完全取决于你有没有写。AGENTS.md 就是 agent 的"永久记忆",你越勤于回写,新会话踩坑越少。

3. 多窗口各自独立上下文,无统一预算协调

坑 3 里被"同时过载"的那部分,底层是产品问题:每个窗口各自维护一份完整上下文,OpenCode 不做跨会话的上下文预算协调。云端靠服务端按需扩容掩盖了这一点;本地只有一个 GPU,立刻暴露。对策只能是坑 3 的"窗口纪律"——控制并发数、任务隔离。

4. TUI 假死 / 挂起输入无效(疑似产品 bug)

坑 4 里"输入无效只能 kill 进程"的部分,更可能是 OpenCode 客户端自身的问题而非模型。它不区分本地/云端,配置也救不了,只能杀掉重启。这类问题建议到 GitHub 提 issue,而不是在配置里找解药。

小结:本节列的四条,都是"换模型解决不了"的问题。与其反复折腾模型,不如认清它们是产品边界,用工程实践(AGENTS.md 沉淀、窗口纪律、及时重启)去补偿。

五、最佳实践

1. 用 /init 建立并提交 AGENTS.md

/init 会扫描仓库,自动生成一份简明扼要的 AGENTS.md,涵盖构建/测试命令、架构、工程约定与坑点。它相当于把"项目背景"一次性喂给模型。关键:把它提交到 Git。 这样团队共享,CI 里的 agent 也能获得一致的项目认知。个人偏好放 ~/.config/opencode/AGENTS.md,并可从 Claude Code 迁移兼容 CLAUDE.md。

2. 把探索交给子代理

内置子代理 explore(只读检索代码库)与 scout(只读调研外部依赖)。大任务先 @explore 摸清结构,再由主 agent 汇总。你还能用 markdown 自定义子代理(放 .opencode/agents/ 或 ~/.config/opencode/agents/),例如只读的 code-reviewer(permission: edit: deny)。

3. 用 MCP 补齐 agent 的能力边界

模型再强,不接外部系统也只是"会写代码的编辑器"。我的配置里接了两个 MCP:远程的 mcp_backend(http://localhost:9500/mcp/api)接入后端服务,本地的 mcp_browser(node 直接启动)做浏览器自动化。注意 mymcp_* 通配符可在权限里一次禁用某个 MCP 的全部工具。

4. 权限安全:别用默认全放行

OpenCode 默认允许所有操作而无需确认。生产环境建议收紧:

{ "permission": {
    "edit": "ask",
    "bash": { "*": "ask", "git status *": "allow" }
} }

bash 支持 glob,git push、rm -rf * 高危命令可按需拦截。本地模型指令遵循弱,权限这道闸更重要——它能把"重复任务"造成的破坏挡在确认环节。

5. 理解配置层级与其他开关

配置文件是合并而非覆盖,优先级从低到高:远程(组织默认)→ 全局 → 自定义 → 项目 → .opencode/ → 托管。所以个人习惯放全局、项目规范放仓库根的 opencode.json 并入库。值得开启的还有:formatter(自动格式化)、lsp(类型感知)、自定义 /command、small_model(廉价模型干标题生成等轻活)。

六、本地模型 vs 线上模型:我的实测对比

基于我用 Qwen3.6(本地 llama.cpp)与 DeepSeek(云端)的实际体感:

维度本地模型(llama.cpp + Qwen3.6)线上模型(DeepSeek 等)
代码质量够用,但复杂重构与多文件联动明显吃力更强,长上下文与跨模块改动更稳
工具调用稳定性偶发不遵循工具格式,需要重试稳定,agent 流程更顺
失败模式复读频繁、多图 OOM、并发资源争抢复读阈值高、无 OOM、并发按需扩容
多模态支持但脆弱,多图易崩依模型而定,通常更稳
数据隐私全部留在局域网,合规无忧源码需发送到第三方
依赖依赖局域网 GPU 服务器,离了内网不可用仅需外网,走到哪都能用
成本硬件一次性投入,用量免费按 token 计费,高频使用累积可观

注:表中的"失败模式"差异只是阈值与频率的差异——产品/架构层的四条限制(第四节)对本地和云端是一视同仁的。

我的选型结论

  • 主力开发:本地模型足够覆盖日常 CRUD、单文件改动、读代码,且无限试错不要钱——但前提是把第三节四个坑的对策配好,并接受第四节产品边界的客观存在。
  • 重型任务(大规模重构、跨模块架构调整、密集多图理解):切到云端 DeepSeek 或更强的商业模型。
  • OpenCode 允许为每个 agent 单独指定 model,于是最实用的混合方案是:主 agent 用云端强模型做实现,explore/scout 这类高频只读子代理切本地模型省钱。

七、结语

OpenCode 的价值不在于"多一个 AI 面板",而在于 AGENTS.md 规则、Plan/Build 双模式、可编排的子代理、MCP 与细粒度权限——这些把它的定位从"聊天工具"变成了"可配置的工程 agent"。接本地模型尤其如此:steps 卡复读、attachment 压图片、compaction 管上下文、limit 划边界、timeout 断挂起,再配合 AGENTS.md 把经验沉淀到下一个会话。但也要分清哪些问题值得折腾配置、哪些是产品边界——本地模型为你省下 token 费,同时也把这些产品限制照得最清楚。模型选型没有银弹:要隐私与免费选本地,要质量与稳定选线上,而真正高效的团队大多像我一样走"本地为主、云端兜底"的混合路线。