用本地模型搭建断网编程助手
文章介绍如何组合 OpenCode、Ollama 和 Qwen,配置并运行离线编程 Agent。
AI 深度解读
断网后,AI 编程助手还能不能读代码、改文件、跑测试?一位住在喜马拉雅山脚下的工程师,把 OpenCode、Ollama 和 Qwen3.5 9B 装进同一台笔记本,关闭网络后完成了一次修复。答案是能,但证据也很明确:这是一个改动只有一行、已有测试的小型 Python 项目。
这次尝试值得看的地方,是作者把本地模型、工具调用、断网检查和人工验收串成了完整流程。对需要在高铁、航班或网络不稳定时继续写代码的人,它提供了一条可以复现的路径,也划清了目前不能据此下结论的范围。
离线能不能干活,取决于项目准备得有多完整
作者在 Thomson Reuters 担任 Principal Engineer,日常工作包括编码和代码审查。搬到喜马拉雅山脚下后,网络和电力中断成了工作安排的一部分,于是他开始验证:平时依赖的 coding agent,能有多少工作留在笔记本里完成?
他的方案分为三层:
- OpenCode 负责读取项目、提出修改、执行测试,并通过权限提示让人检查操作。
- Ollama 负责在本机运行模型,向 OpenCode 提供本地接口。
- Qwen3.5 9B 负责理解任务、生成修改和选择工具。
模型权重就是模型生成回答所需的已学习参数。开放权重让这些参数可以提前下载,之后在本机运行。Aider 也支持 Ollama,但作者全程使用 OpenCode,以保持配置和实验过程一致。
离线工作的前提,是模型之外的项目依赖、测试工具和必要文档也已经在本地。
解释函数、调查失败测试、小范围重构,都可能满足这个条件。安装新包、查询网页仍然需要网络。作者的示例只使用 Python 标准库,因此避开了临时下载依赖的问题;一般项目能否照搬,要先检查自己的测试和运行流程。
配置成本地接口,还需要真正断网验证
作者使用 OpenCode 1.19.4。项目配置文件 opencode.json 中,model 和 small_model 都选择 Qwen3.5 9B,baseURL 指向 http://127.0.0.1:11434/v1,只启用本地 provider。编辑文件、运行 shell 命令需要确认,网页工具则被禁用。
下面的启动命令让 Ollama 监听本机地址,关闭云端功能,并设置上下文长度:
OLLAMA_HOST=127.0.0.1:11434 OLLAMA_NO_CLOUD=1 OLLAMA_CONTEXT_LENGTH=65536 ollama serve
要看清其中的 OLLAMA_NO_CLOUD=1 和 OLLAMA_CONTEXT_LENGTH=65536:前者控制云端功能,后者会影响内存需求,并不是随手填大的免费额度。
联网时先执行 ollama pull qwen3.5:9b 下载模型,再从项目目录运行 .tools/node_modules/.bin/opencode --pure。--pure 用于禁用外部插件。作者先确认这套配置可以响应,再用 /exit 退出 OpenCode,保留 Ollama 服务。
随后关闭 Wi-Fi,并断开可能存在的网线或手机共享网络。断网不会影响 localhost,因为请求始终留在同一台 Mac 内部。
作者做了两项检查:执行 curl --noproxy '*' --fail http://127.0.0.1:11434/api/tags,本地服务仍返回已下载的模型;执行 curl --noproxy '*' --connect-timeout 5 --max-time 10 --head https://example.com,外部请求报出 Could not resolve host。
这组检查支持的是当时的运行状态:本地接口可用,外部域名解析失败。作者也要求在后续编码任务中保持所有外部连接断开。单靠配置文件里写了本地地址,证据还不够完整。
五个测试通过,证明了什么?
实验对象是一个费用计算器,输入包含五笔费用,其中有一笔退款。正确总额是 25.50,但执行 python3 expense_total.py 时,程序输出最后一笔费用 8.00。
原因很直观:循环每处理一笔金额,就覆盖一次总额,没有累加。
作者先运行 python3 -m unittest -v。五个测试中,小数运算、退款和示例费用三个用例失败,空输入和单项输入已经通过。这给 agent 提供了明确目标,也留下了独立验收的依据。
联网诊断时,OpenCode 曾提出使用 pytest,但项目实际使用 Python 内置的 unittest。权限提示让作者有机会检查命令。这个小插曲很实用:模型能找到测试,不代表它会自动选对执行方式,提示词里直接给出测试命令更稳妥。
真正断网后,作者重新启动 OpenCode,要求修复 expense_total.py,保持测试不变、不安装任何东西,并运行测试解释修改。agent 提出的修复是:
total += Decimal(amount) # after
要检查的就是 +=:原先的 total = Decimal(amount) 每次覆盖总额,改后才会把每笔金额累加进去。
录像展示了提出修改、请求编辑许可和运行测试的过程。随后作者退出 OpenCode,自己再次执行原来的测试命令,五个测试全部通过;保存后的源文件包含修复,测试文件保持不变。样例程序此时应返回 25.50。
这次实验验证了离线的「读文件、改代码、跑测试」闭环,但没有验证大型仓库里的持续开发能力。
项目还提供两个练习:examples/csv-report/csv_report.py 要完成按产品分组、统计数量与收入、输出报告的函数;examples/log-analyzer/log_analyzer.py 要修复错误计数和最慢请求选择。两者各有七个测试,但作者明确说,尚未完成它们的离线 agent 实验,不能把它们算成已通过的案例。
我的判断
这篇最有价值的部分,是验证过程清楚。原始测试先失败,修改后通过,作者又手动复查,而且没有允许 agent 改测试。相比只展示一句「任务完成」,这样的证据更容易判断修复是否成立。
模型选型和性能结论则需要收着看。作者参考了 Qwen 模型卡中的 LiveCodeBench v6 编码测试,以及 BFCL-V4 函数调用测试,认为 Qwen3.5 9B 比 4B 更适合作为起点。但给定原文没有提供具体分数,不能补出差距;这些榜单也没有测这台笔记本的速度。
内存同样不能只看下载体积。原文引用 Voltti 的预算:一个 6.6 GB 的 Qwen3.5 9B 构建需要预留 12–16 GB 内存,本文的 64k-token 配置可能需要更多。具体硬件、响应延迟、耗电和长任务稳定性,原文没有说清。离线推理也没有解决作者提到的停电问题。
关于 OpenCode,作者引用的预印本《The Scaffold Effect in Coding Agents》比较了 OpenCode、Goose、OpenHands-SDK,在 50 个 Terminal-Bench Pro 任务上使用两个云模型,报告带有 95% 置信区间,并提醒多数通过率差异在统计上不明确。OpenCode 每个已解决任务的 token 用量约为 Goose 的 40 倍。这值得关注,但不能直接推导本地 Qwen3.5 9B 的表现。
与云端 agent 相比,这套方案提供了断网继续工作的可能;与直接在本地聊天窗口粘贴代码相比,OpenCode 多了一层文件和命令操作。是否值得承担模型下载、内存占用和配置成本,要看你是否经常需要这种工作方式。至于大仓库、陌生框架和复杂功能,目前仍是待验证的问题。
能拿来做什么
- 出行前,把仓库、依赖、测试工具和必要文档准备齐,并用离线时相同的命令、模型与配置启动一次。前端或 Node 项目也应先确认已有依赖足够支撑目标任务。
- 给 agent 一个边界清楚的小任务,明确测试命令,并要求保持测试不变、不安装新依赖。作者误选
pytest的经历说明,具体命令比「帮我测一下」更可靠。 - 验收时退出 agent,自己检查 diff、确认测试文件未变,再手动运行原有测试。把模型的解释当作线索,把可检查的改动和测试结果作为依据。
- 评估本地方案时,同时记录失败尝试、人工介入、内存压力和完成速度,再决定是否扩大任务范围。先复现这个一行修复,再尝试 CSV 报告,比直接拿大型项目做结论更有参考价值。