在喜马拉雅山脚离线运行 AI 编码智能体:OpenCode + Ollama + Qwen3.5 9B 实测
Coding From the Himalayas With an Offline AI Agent
Thomson Reuters 首席工程师 Rittike 作者实测在断网状态下于本地笔记本运行编码智能体完成修 bug。她用 OpenCode 1.19.4 搭配 Ollama 运行 Qwen3.5 9B(下载约 6.6–7.6 GB),在 Wi-Fi 关闭后让智能体修复 expense_total.py 中一行累加错误,五个测试全部通过,作者随后自行运行原测试验证。

当我们谈论远程工作时,很多东西都被我们视作理所当然,尤其是可靠的网络和电力。但有时候,远程工作意味着要在航班上、长途火车旅途中,或是网络很差甚至没有网络的地方完成工作。
一年前,我搬到了喜马拉雅山麓。我喜欢住在那里的宁静,但这也带来了自身的挑战,包括断网和停电。以前我没怎么考虑过的事情,现在成了规划工作的一部分。
我是Thomson Reuters 的首席工程师,大部分时间都在写代码和审查队友的代码。编程智能体已经是我日常工作流程的重要组成部分,这让我很好奇,这套流程中有多少可以在离线状态下继续使用。

在喜马拉雅山麓工作。我的工作区照片。
如果你也和我一样,既喜欢与科技打交道,又能在网络不总是配合的宁静偏远之地找到平静,那么这篇文章就是为你准备的。它也献给所有想知道编程智能体能否在关掉网络的情况下完全在自己的笔记本电脑上运行的人。我尝试在笔记本电脑上本地运行一个编程智能体,看看离线时究竟能完成多少工作。
离线能做些什么?
笔记本电脑上已有的项目为我们提供了 plenty 可供操作的内容。我们可以让智能体解释一个陌生的函数、排查一个失败的测试、重构一小段代码,或者帮助完成一个功能。当模型、项目依赖和测试工具都在本地可用时,这些任务可以离线运行。下载新包或在网上查找资料仍然需要联网。
OpenCode 的用武之地
OpenCode 是一个可以配合你的项目工作的开源编程智能体。你给它一个任务,它就可以读取文件、提出修改建议并运行命令,例如测试套件。在我们的设置中,它的权限提示让我们可以在放行之前审查命令和更改。
我选择 OpenCode 是因为它让我可以在终端中保留这套工作流程,同时连接到运行在我自己笔记本电脑上的模型。Ollama 负责运行模型;OpenCode 负责与项目交互。

Mac 上的 OpenCode,选择 Qwen3.5 9B 作为本地模型。
开放权重模型,包括 Qwen,使这成为可能:它们的权重,即用于生成响应的学习到的参数,可以下载并在本地运行。借助 Ollama 在同一台笔记本电脑上运行 Qwen,OpenCode 可以把请求发送到本地服务。我们先把所有东西下载好,然后断开网络进行编程任务。
如果你更喜欢有引导的编辑工作流程,Aider 是另一个选择;它同样支持 Ollama。为了保持设置和结果的一致性,我们在整个操作演示中都使用 OpenCode。

OpenCode 与项目文件交互,并把模型请求发送到同一台笔记本电脑上的 Ollama。Ollama 在本地运行 Qwen。
选择一个开源模型
Qwen 有不同的大小。我想要一个可以在本地运行、又能使用智能体工具的模型。作为参考,以下是Qwen 的模型卡中报告的两种 Qwen 规格和另一个开放权重选项的已发布结果。
LiveCodeBench v6 测试模型在编程题上的代码生成能力。BFCL-V4 评估函数调用,这在模型需要选择工具并给出正确参数时很重要。两项基准分数越高越好。
模型 | LiveCodeBench v6 | BFCL-V4 |
|---|---|---|
Qwen3.5 4B | 55.8 | 50.3 |
Qwen3.5 9B(本文使用) | 65.6 | 66.1 |
gpt-oss-20b | 74.6 | 未报告 |
上表分数来自 Qwen 的模型卡片,并非这台笔记本上的实测结果。“未报告”表示来源未提供该分数。
本教程使用 Qwen3.5 9B。它公布的编程和工具调用成绩强于 4B 版本,因此是合适的起点。该表格并不衡量笔记本速度或整个 OpenCode 工作流;我们的离线文件编辑和测试才是检验标准。
模型大小与内存
模型 | 大约下载体积 |
Qwen3.5 4B | 3.3–4.0 GB |
Qwen3.5 9B(本文使用) | 6.6–7.6 GB |
Qwen3.5 27B | 17–20 GB |
下载体积不等于 RAM 需求。Voltti 为 6.6 GB 的 Qwen3.5 9B 构建预留了 12–16 GB 内存。我们的 64k token OpenCode 配置可能需要更多,因此要为上下文和其他应用留出空间。
OpenCode 在基准测试中的表现
OpenCode 的测试也不限于小示例。研究预印本 The Scaffold Effect in Coding Agents 使用两个云端模型,在 50 个 Terminal-Bench Pro 任务上比较了 OpenCode、Goose 和 OpenHands-SDK。以下是表 2 中报告的通过率。
编程智能体 | Qwen3.6 Plus | MiniMax M2.5 |
Goose | 48% [34–62%] | 38% [24–52%] |
OpenCode | 50% [36–64%] | 46% [32–60%] |
OpenHands-SDK | 50% [36–64%] | 46% [32–60%] |
括号中为 95% 置信区间。作者提醒,大多数通过率差异在统计上并不显著。
该研究还报告了 OpenCode 的 token 用量明显更高:每解决一个任务的 token 消耗约为 Goose 的 40 倍。这些运行使用云端 API 和与我们不同的模型,因此无法预测本地 Qwen3.5 9B 的表现。
初始设置
趁你还能访问互联网,按照 GitHub README 中的完整说明进行设置。它涵盖安装工具、下载项目以及安装 OpenCode 1.19.4。该项目包含在本地使用 Qwen 所需的示例、测试和配置。完成之后,在项目文件夹中保持终端 A 开启。
开始之前,先了解 opencode.json 的作用会有帮助。这是 OpenCode 的项目配置文件。在我们的文件中,model 和 small_model 都选择了 Qwen3.5 9B,baseURL 指向运行在 http://127.0.0.1:11434/v1 的 Ollama。只启用了本地提供方。它还会在编辑文件或运行 shell 命令前先询问,并屏蔽了 Web 工具。完整配置在代码仓库中。在离线测试时我们仍会断开网络。
我们将用终端 A 运行 OpenCode 和测试。在终端 B 中,启动 Ollama 并保持其运行。这条命令让模型服务保持在 localhost(同一台 Mac 内部的地址),并禁用 Ollama 的云端功能。
OLLAMA_HOST=127.0.0.1:11434 OLLAMA_NO_CLOUD=1 OLLAMA_CONTEXT_LENGTH=65536 ollama serve回到终端 A,下载 Qwen 并等待下载完成。我们会在断开互联网之前先确认智能体能够响应。
ollama pull qwen3.5:9b从一个费用计算器开始
该示例添加了五笔支出,其中包含一笔退款。正确的总额是 25.50。在终端 A 中运行起始程序。
python3 expense_total.py
起始程序打印出 8.00,即最后一笔支出的金额。
该函数在每次迭代时都会覆盖 total。在让智能体修复它之前,先运行现有的测试。
python3 -m unittest -v
三个用例失败:小数运算、退款和示例支出。空输入和单项输入已经通过。
使用现有测试为智能体提供了具体的目标,也让我们能够独立于其解释来验证结果。
联网状态下试用 OpenCode
联网状态下,直接从项目文件夹打开 OpenCode。--pure 选项会禁用外部插件。项目配置将主模型和辅助模型都指向 Ollama;稍后断开网络才使这次运行成为离线运行。
.tools/node_modules/.bin/opencode --pure检查是否已选中 Qwen3.5 9B local。在 OpenCode 中输入这条诊断提示。
读取 expense_total.py 并运行测试。在不编辑任何文件的情况下解释这个 bug。

OpenCode 在选中 Qwen3.5 9B local 的情况下接收到了诊断提示。
OpenCode 读取了源代码并搜索测试。它最初提议使用 pytest,尽管该项目使用的是 Python 内置的 unittest。权限提示让我有机会在允许之前检查该命令。对于下一个任务,我会在提示中写明确切的测试命令:python3 -m unittest -v。

智能体找到了测试,并请求运行测试命令的权限。
一旦本地设置响应,就退出 OpenCode。让 Ollama 继续在终端 B 中运行。
/exit关闭互联网
关闭 Wi-Fi,如果连接了以太网或手机网络共享,也一并断开。本地主机仍然可用,因为请求始终留在 Mac 内部。

为连通性检查关闭了 Wi-Fi。
在终端 A 中,检查 Ollama 的本地 API。
curl --noproxy '*' --fail http://127.0.0.1:11434/api/tags
Ollama 仍然响应,并列出了已下载的 qwen3.5:9b 模型。
然后尝试一个外部地址。
curl --noproxy '*' --connect-timeout 5 --max-time 10 --head https://example.com
外部请求失败,提示 Could not resolve host。
这些检查确立了录制时的实际情况:本地服务有响应,外部查询失败。在运行编码任务期间,保持所有外部连接断开。
离线修复 bug
在终端 A 中用同样的直接命令重新启动 OpenCode。
.tools/node_modules/.bin/opencode --pure让智能体修复这个 bug。
Fix the bug in expense_total.py so the existing tests pass. Don't change the tests or install anything. Run the tests and explain your fix.录像显示 Qwen 提出了一行代码的修改,请求编辑权限并运行了测试。这个改动很小,很容易检查。
total = Decimal(amount) # beforetotal += Decimal(amount) # after
录制到的编辑与测试循环。Qwen 提出修复方案,请求批准,并成功运行了五项测试。
智能体完成后,在运行你自己的检查之前退出 OpenCode。
/exit然后我们在终端 A 中自己运行了测试。
python3 -m unittest -v
手动检查显示全部五项测试通过。
保存的源代码中包含 total += Decimal(amount),测试文件与起始版本相比没有变化。修复后的代码现在会累加每笔金额。运行示例程序应返回 25.50。
还有两个编码任务可以尝试
我在配套项目中还包含了另外两个练习:一个 CSV 报告和一个日志分析器。我尚未完成它们的离线智能体运行,因此上面展示的结果来自支出示例。这些是用相同设置接下来可以尝试的任务。
CSV 报告示例从一个未完成的函数开始。让 OpenCode 按产品对行进行分组、计算数量和收入,并生成一份可读的报告。它带有示例数据,以及七项覆盖分组、小数运算、排序和空输入的测试。
按照 examples/csv-report/csv_report.py 的 README 完成它。保持测试不变,不要安装任何东西。运行这个示例的测试并解释你做了哪些修改。
日志分析器已经可以运行,但在错误计数和选择最慢请求方面有 bug。它的七项测试还覆盖了格式错误的行和空输入。
按照 examples/log-analyzer/log_analyzer.py 的 README 修复它。保持测试不变,不要安装任何东西。运行它的测试并解释你的修复。
每个演练都包含确切的测试命令和预期结果。在相同的本地设置下运行这些练习,然后检查改动和测试输出。
网络不可用时的有效做法
把项目和模型都准备好。下载其依赖项,并将你需要的文档保存在磁盘上。我们的示例使用 Python 标准库,因此它们的测试不需要下载软件包。
在联网时把确切的配置跑一遍。离线尝试时使用相同的命令、模型和配置,这样所需的运行时组件就已经可用。
把项目的测试命令告诉智能体。我们的智能体起初建议用 pytest,尽管这个示例使用的是 unittest。明确的测试命令可以减少不必要的弯路。
让任务保持专注并检查结果。要求修复一个 bug 或实现一个小功能,审查 diff,并亲自运行测试。保留原始测试,这样测试通过仍然能检验预期的行为。
留意内存压力。模型下载只是内存开销的一部分。较大的上下文和其他打开的应用会减少模型可用的空间。
同样记录下需要帮助的尝试。一份有用的演练应该展示权限提示、重试和失败,以及最终通过测试的输出。
我从离线尝试中学到的
关闭 Wi-Fi 后看到测试通过,回答了我的第一个疑问:我可以在没有网络连接的情况下使用编码智能体读取本地文件、进行修改并运行检查。我仍然审查了修改并亲自运行了原始测试。保持这些测试不变为我提供了一种检查结果的有效方式。
在断网或旅途中使用这种设置时,我会从小而自包含的任务开始。在连接消失之前,项目及其所需的一切都必须已经在笔记本电脑上。这个单行修复并不能告诉我模型处理大型代码库或陌生框架的能力如何。CSV 报告是下一个有用的检验:它要求智能体完成一个函数,因此比单行修复有更多内容可以审查。
来源: Latent Space · latent.space