insight Latent Space Tue, 27 Oct 2026 正文 ok 2063 words

喜马拉雅山脚下的离线编程:用 OpenCode、Ollama 与 Qwen 打造离线 AI Coding Agent

英文标题:Coding From the Himalayas With an Offline AI Agent

本文介绍了如何在完全断网的极端环境下,利用开源工具链搭建并运行本地离线 AI Coding Agent。作者结合 OpenCode 框架、Ollama 运行时以及 Qwen3.5 9B 开源权重模型,在笔记本电脑本地完成了代码读取、错误诊断、单测执行与自动化 Bug 修复全流程。评测与实践表明,现代轻量级开源大模型已具备出色的工具调用与代码生成能力,能够脱离云端独立运作。这为差旅移动办公、偏远地区开发以及对数据隐私和断网合规有严苛要求的开发者与团队提供了切实可行的解决方案。
打开原文相关 8.8 · 机会 1
media6.5 KB media10.7 KB media4.9 KB media388.4 KB media634.9 KB media59.2 KB media50.5 KB media2.7 KB media27.2 KB media9.7 KB media25.2 KB

中文

当我们谈论远程办公时,很多事情都被视作理所当然,尤其是稳定的网络和电力供应。但有时,远程办公意味着要在航班上、漫长的火车旅途上,或者在几乎没有网络信号的偏远角落完成工作。

一年前,我搬到了喜马拉雅山脚下生活。我热爱这里的宁静,但这同时也带来了独特的挑战,包括频繁的网络与电力中断。过去未曾过多考虑的事情,如今成了规划工作的一部分。

我在汤森路透(Thomson Reuters)担任首席工程师,大部分时间都在编写代码以及审查同事的代码。Coding Agent 已经成为我日常工作流程中不可或缺的一部分,这让我产生了一个好奇:在离线状态下,我究竟还能保留多少这样的工作流?

在喜马拉雅山脚下工作。我的工作台照片。

如果你和我一样,既热爱与技术打交道,又在网络并不总是配合的偏远静谧之地寻找内心平静,那么这篇文章正是为你准备的。它也适用于任何想知道**Coding Agent 是否能完全在笔记本电脑上运行(且彻底断开网络)**的人。我在自己的笔记本上尝试本地运行了一个 Coding Agent,以探究在离线状态下究竟能完成多少实际工作。

我们在离线状态下能做什么?

只要项目已经保存在你的笔记本电脑上,我们能做的事情其实相当多。我们可以让 Agent 解释一个陌生的函数、排查失败的测试用例、重构一小段代码,或者协助完成某个功能。只要模型、项目依赖和测试工具在本地完备,这些任务完全可以离线运行。当然,下载新依赖包或在网络上搜索资料依然需要网络连接。

OpenCode 的定位

OpenCode 是一款能够直接与你的项目配合工作的开源 Coding Agent。你只需给它分配一个任务,它就能读取文件、提出修改建议,并运行诸如测试套件等终端命令。在我们的配置中,它的权限确认提示允许我们在批准执行之前审查所有命令与代码变更。

我之所以选择 OpenCode,是因为它允许我将工作流保留在终端中,同时连接到在本地笔记本上运行的模型。Ollama 负责模型的运行与调度;OpenCode 则负责与项目文件的交互。

运行在我的 Mac 上的 OpenCode,已选择 Qwen3.5 9B 作为本地模型。

包括 Qwen 在内的开源权重模型让这一切成为可能:它们的权重参数(用于生成响应的训练参数)可以被下载到本地并离线运行。通过让 Ollama 在同一台笔记本上运行 Qwen,OpenCode 可以将其请求直接发送给本地服务。 我们先下载所需的一切,随后断开网络进行编码任务。

如果你更喜欢有引导式的代码编辑工作流,Aider 是另一个不错的选择,它同样支持 Ollama。为了保证本次演练的环境配置与结果一致,我们全程使用 OpenCode。

OpenCode 操作项目文件,并将模型请求发送到同一台笔记本上的 Ollama。Ollama 在本地运行 Qwen。

选择开源模型

Qwen 提供了多种不同的尺寸。我希望选择一款既能在本地流畅运行,又具备 Agent 工具调用能力的模型。作为背景参考,以下是 Qwen 官方 Model Card 中公开的两个 Qwen 尺寸以及另一个开源权重模型的基准评测数据。

LiveCodeBench v6 用于测试编程问题的代码生成能力。BFCL-V4 则用于评估函数调用(Function Calling)能力——这在模型需要挑选合适工具并传递正确参数时至关重要。两项评测均为得分越高表现越好。

模型 LiveCodeBench v6 BFCL-V4
Qwen3.5 4B 55.8 50.3
Qwen3.5 9B(本文使用) 65.6 66.1
gpt-oss-20b 74.6 未报告

上表的分数源自 Qwen Model Card 的官方报告,并非来自本笔记本电脑的实测。“未报告”表示原数据源未提供该项分数。

在本次演练中,我们选择使用 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 的内存。在我们的 OpenCode 64k 上下文配置下可能需要更多,因此请确保为上下文窗口和其它正在运行的应用程序留出足够空间。

OpenCode 在基准测试中的表现

OpenCode 不仅仅在小型 Demo 中运行良好,也在专业测试中接受了检验。研究预印本论文《The Scaffold Effect in Coding Agents》在 50 项 Terminal-Bench Pro 任务上,使用两款云端模型对 OpenCode、Goose 和 OpenHands-SDK 进行了对比。下表为该论文 Table 2 中报告的通过率。

Coding Agent 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(Terminal A)。

在开始之前,了解 opencode.json 的作用很有帮助。这是 OpenCode 的项目配置文件。在我们的配置文件中,model 和 small_model 均指定为 Qwen3.5 9B,baseURL 指向本地 Ollama 实例(http://127.0.0.1:11434/v1)。配置中仅启用了本地 Provider。同时,它在修改文件或执行 Shell 命令前会主动请求许可,并禁用了网络工具。完整配置文件已包含在仓库中。在此之后,我们依然会在离线测试时彻底断开网络。

我们将使用终端 A 运行 OpenCode 及测试命令。在终端 B 中,启动 Ollama 并保持后台运行。以下命令将模型服务绑定在 localhost(Mac 本地回环地址),并禁用了 Ollama 的云端特性。

bash OLLAMA_HOST=127.0.0.1:11434 OLLAMA_NO_CLOUD=1 OLLAMA_CONTEXT_LENGTH=65536 ollama serve

切回终端 A,下载 Qwen 模型并等待下载完成。在拔掉网线前,我们需要先确认 Agent 能够正常响应。

以费用计算器为例

示例项目需要对包含退款(Refund)在内的 5 笔开销进行累加求和,正确总金额应为 25.50。在终端 A 中运行初始程序:

初始程序输出了 8.00,这是最后一笔开销的值。

该函数在每次循环迭代时错误地直接覆写了总额。在要求 Agent 修复之前,先运行现有测试套件。

结果显示有 3 个测试用例未通过:浮点高精度计算、退款处理以及样本开销汇总。空输入与单项输入则已通过测试。

借助现有的单元测试,能够给 Agent 提供一个具体明确的目标,同时也让我们有手段独立于 Agent 的自我阐述来客观验证其修改结果。

联网状态下的初次验证

在联网状态下,直接从项目根目录启动 OpenCode。使用 --pure 选项禁用所有外部插件。项目配置会将主模型与辅助模型均指向本地 Ollama;稍后主动切断网络才是实现真正离线运行的关键。

bash .tools/node_modules/.bin/opencode --pure

检查并确认已选中本地的 Qwen3.5 9B 模型。在 OpenCode 中输入以下诊断 Prompt:

Read expense_total.py and run the tests. Explain the bug without editing any files. (阅读 expense_total.py 并运行测试。解释该 Bug,先不要修改任何文件。)

OpenCode 接收诊断 Prompt,并已选中本地 Qwen3.5 9B。

OpenCode 随后读取了源代码并检索了测试用例。它最初建议使用 pytest,尽管本项目使用的是 Python 内置的 unittest。权限提示机制给予了我先检查命令再授权执行的机会。在下一个任务中,我会在 Prompt 里直接给出明确的测试命令:python3 -m unittest -v。

Agent 找到了测试代码,并请求运行测试命令的执行权限。

确认本地搭建环境能正常响应后,退出 OpenCode。保持终端 B 中的 Ollama 服务持续运行。

彻底断开网络

关闭 Wi-Fi,并拔掉以太网线或断开手机热点共享。由于请求完全保留在 Mac 内部,localhost 依然可以正常通信。

关闭 Wi-Fi 进行连通性检查。

在终端 A 中,验证 Ollama 本地 API 的可用性:

bash curl --noproxy '*' --fail http://127.0.0.1:11434/api/tags

Ollama 正常响应,并列出了已下载的 qwen3.5:9b 模型。

随后尝试访问外部网址:

bash curl --noproxy '*' --connect-timeout 5 --max-time 10 --head https://example.com

外网请求失败,提示 Could not resolve host(无法解析主机)。

这些检查确切证明了当时的状态:本地服务响应正常,而外部域名解析彻底失败。在接下来的编码任务执行期间,始终保持断网状态。

离线修复 Bug

使用完全相同的纯净命令在终端 A 中再次启动 OpenCode:

bash .tools/node_modules/.bin/opencode --pure

要求 Agent 修复该 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. (修复 expense_total.py 中的 Bug,使现有测试通过。不要改动测试文件,也不要安装任何东西。运行测试并解释你的修复方案。)

过程记录显示,Qwen 提出了一行代码的修改方案,请求编辑权限,并随后执行了测试套件。修改非常克制精简,极易人工审查:

python total = Decimal(amount) # before

total += Decimal(amount) # after

记录的编辑与测试周期:Qwen 提出修改方案、请求审批,并成功运行并通过了 5 项测试。

Agent 完成工作后,先退出 OpenCode,再进行我们自己的手动验证。

随后我们在终端 A 中自行执行了单元测试:

手动检查显示所有 5 项测试全部通过。

保存后的源码文件确认包含了 total += Decimal(amount),而测试文件自始至终未被改动。修复后的程序现已能够正确累加每一笔金额。

English / 原文

When we talk about working remotely , we take a lot of things for granted, especially reliable internet and power. But sometimes, working remotely means trying to get something done on a flight, during a long train ride, or somewhere with little or no network.

I moved to the foothills of the Himalayas a year ago. I love the peace of living here, but it also comes with its own challenges, including network and power outages. Things I didn’t think about as much before are now part of planning my work.

I work as a Principal Engineer at Thomson Reuters , where I spend most of my time coding and reviewing my teammates’ code. Coding agents are already a big part of my everyday workflow, which made me curious about how much of that workflow I could keep using offline.

Working from the foothills of the Himalayas. Photo of my workspace.

If you’re someone like me, who enjoys working with technology but also finds peace in quiet, remote places where the network doesn’t always cooperate, this article is for you. It’s also for anyone who has wondered whether a coding agent can run entirely on their laptop, with the internet switched off. I tried running a coding agent locally on my laptop to see how much work I could actually get done offline.

What can we do offline?

A project already on your laptop gives us plenty to work with. We can ask an agent to explain an unfamiliar function, investigate a failing test, refactor a small piece of code, or help finish a feature. These tasks can run offline when the model, project dependencies and test tools are available locally. Downloading a new package or looking something up on the web will still need a connection.

Where OpenCode fits

OpenCode is an open-source coding agent that works with your project. You give it a task, and it can read files, propose edits and run commands such as the test suite. In our setup, its permission prompts let us review commands and changes before allowing them.

I picked OpenCode because it lets me keep that workflow in the terminal while connecting to a model running on my own laptop. Ollama handles running the model; OpenCode handles the interaction with the project.

OpenCode on my Mac, with Qwen3.5 9B selected as the local model.

Open-weight models, including Qwen, make this possible: their weights, the learned parameters used to generate responses, can be downloaded and run locally. With Ollama running Qwen on the same laptop, OpenCode can send its requests to a local service. We download everything first, then disconnect the internet for the coding task.

Aider is another option if you prefer a guided editing workflow; it also supports Ollama. We are using OpenCode throughout this walkthrough so that the setup and results stay consistent.

OpenCode works with the project files and sends model requests to Ollama on the same laptop. Ollama runs Qwen locally.

Choosing an open-source model

Qwen comes in different sizes. I wanted a model we could run locally that could also use an agent’s tools. For context, here are published results for two Qwen sizes and another open-weight option, as reported in Qwen’s model card.

LiveCodeBench v6 tests code generation on programming problems. BFCL-V4 evaluates function calling, which matters when a model needs to choose a tool and give it the right arguments. Higher scores are better on both.

Model LiveCodeBench v6 BFCL-V4
Qwen3.5 4B 55.8 50.3
Qwen3.5 9B (used here) 65.6 66.1
gpt-oss-20b 74.6 Not reported

The above table scores reported in Qwen’s model card, not measurements from this laptop. “Not reported” means the source does not provide that score.

We’re using Qwen3.5 9B for this walkthrough. Its stronger published coding and tool-calling results than the 4B variant made it a useful starting point. The table doesn’t measure laptop speed or the whole OpenCode workflow; our offline file edits and tests are the check for that.

Model size and memory

Model Approx. download
Qwen3.5 4B 3.3–4.0 GB
Qwen3.5 9B (used here) 6.6–7.6 GB
Qwen3.5 27B 17–20 GB

Download size isn’t a RAM requirement. Voltti budgets 12–16 GB of memory for a 6.6 GB Qwen3.5 9B build. Our 64k-token OpenCode configuration may need more, so leave room for context and other apps.

How OpenCode performed in benchmarks

OpenCode has also been tested beyond small demos. The research preprint The Scaffold Effect in Coding Agents compared OpenCode, Goose and OpenHands-SDK on 50 Terminal-Bench Pro tasks using two cloud models. Here are the pass rates reported in Table 2.

Coding agent 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%]

The brackets show 95% confidence intervals. The authors caution that most differences in pass rate were not statistically clear.

The study also reported substantially higher token use for OpenCode : about 40 times Goose’s tokens per solved task. These runs used cloud APIs and different models from ours, so they do not predict how local Qwen3.5 9B will perform.

Initial setup

While you still have access to the internet, follow the complete setup in the GitHub README. It covers installing the tools, downloading the project, and installing OpenCode 1.19.4. The project includes the examples, tests and configuration for using Qwen locally. Once that is ready, keep Terminal A open in the project folder.

Before starting, it helps to know what opencode.json does. This is OpenCode’s project configuration. In our file, model and small_model both select Qwen3.5 9B, and baseURL points to Ollama at http://127.0.0.1:11434/v1. Only the local provider is enabled. It also asks before editing files or running shell commands, and blocks the web tools. The full configuration is in the repository. We still disconnect the network for the offline test.

We’ll use Terminal A for OpenCode and our tests. In Terminal B, start Ollama and leave it running. This command keeps the model service on localhost, an address inside the same Mac, and disables Ollama’s cloud features.

OLLAMA_HOST=127.0.0.1:11434 OLLAMA_NO_CLOUD=1 OLLAMA_CONTEXT_LENGTH=65536 ollama serve

Back in Terminal A, download Qwen and wait for the download to finish. We’ll check that the agent responds before disconnecting the internet.

Starting with an expense calculator

The example adds five expenses, including a refund. The correct total is 25.50. Run the starting program in Terminal A.

The starting program prints 8.00, the value of the last expense.

The function replaces its total on every iteration. Before asking the agent to fix it, run the existing tests.

Three cases fail: decimal arithmetic, refunds and the sample expenses. Empty and single-item input already pass.

Using existing tests gives the agent a concrete target. It also gives us a way to check the result independently of its explanation.

Trying OpenCode while online

While online, open OpenCode directly from the project folder. The --pure option disables external plugins. The project configuration points both the main and helper model at Ollama; disconnecting the network later is what makes the run offline.

.tools/node_modules/.bin/opencode --pure

Check that Qwen3.5 9B local is selected. Enter this diagnostic prompt in OpenCode.

Read expense_total.py and run the tests. Explain the bug without editing any files.

OpenCode receives the diagnostic prompt with Qwen3.5 9B local selected.

OpenCode read the source and searched for the tests. It initially proposed pytest, although this project uses Python’s built-in unittest. The permission prompt gave me a chance to inspect the command before allowing it. For the next task, I would include the exact test command in the prompt: python3 -m unittest -v.

The agent finds the tests and requests permission to run a test command.

Once the local setup responds, exit OpenCode. Keep Ollama running in Terminal B.

Turning off the internet

Turn Wi-Fi off and disconnect Ethernet or phone tethering if either is connected. Localhost still works because the request stays inside the Mac.

Wi-Fi switched off for the connectivity check.

In Terminal A, check Ollama’s local API.

curl --noproxy '*' --fail http://127.0.0.1:11434/api/tags

Ollama still responds and lists the downloaded qwen3.5:9b model.

Then try an external address.

curl --noproxy '*' --connect-timeout 5 --max-time 10 --head https://example.com

The external request fails with Could not resolve host.

These checks establish what happened at the time of capture: the local service responded and the external lookup failed. Keep all external connections disconnected while running the coding task.

Fixing the bug offline

Start OpenCode again in Terminal A using the same direct command.

.tools/node_modules/.bin/opencode --pure

Ask the agent to fix the 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.

The recording shows Qwen proposing a one-line change, requesting edit permission and running the tests. The change is small, so it is easy to inspect.

total = Decimal(amount)   # before


total += Decimal(amount)  # after

Recorded edit and test cycle. Qwen proposes the fix, requests approval and runs five tests successfully.

After the agent finishes, exit OpenCode before running your own checks.

We then ran the tests ourselves in Terminal A.

The manual check shows all five tests passing.

The saved source contains total += Decimal(amount), and the test file is unchanged from the starter. The fix now accumulates each amount. Running the sample program should return 25.50.

Two more coding tasks to try

I’ve included two more exercises in the companion project: a CSV report and a log analyzer. I haven’t completed their offline agent runs yet, so the results shown above are from the expense example. These are the next tasks to try with the same setup.

The CSV report example starts with an unfinished function. Ask OpenCode to group rows by product, calculate quantities and revenue, and produce a readable report. It has sample data and seven tests covering grouping, decimal arithmetic, ordering and empty input.

Complete examples/csv-report/csv_report.py using its README. Keep the tests unchanged and don't install anything. Run this example's tests and explain what you changed.

The log analyzer already runs but has bugs in error counting and selecting the slowest request. Its seven tests also cover malformed lines and empty input.

Fix examples/log-analyzer/log_analyzer.py using its README. Keep the tests unchanged and don't install anything. Run its tests and explain your fix.

Each walkthrough contains the exact test commands and expected results. Run the exercises with the same local setup, then inspect the changes and test output.

What helps when the internet is unavailable

  • Prepare the project as well as the model. Download its dependencies and keep the documentation you will need on disk. Our examples use Python’s standard library, so their tests do not need package downloads.

  • Launch the exact setup once while online. Use the same command, model and configuration for the offline attempt, so that required runtime components are already available.

  • Give the agent the project’s test command. Ours initially proposed pytest even though this example uses unittest. A clear test command reduces unnecessary detours.

  • Keep the task focused and check the result. Ask for one bug fix or a small feature, review the diff, and run the tests yourself. Keep the original tests so that a passing result still checks the intended behavior.

  • Watch memory pressure. The model download is only part of the memory cost. A large context and other open apps can reduce the room available to the model.

  • Record the attempts that need help too. A useful walkthrough should show permission prompts, retries and failures, as well as the final passing output.

What I learned from trying this offline

Seeing the tests pass after turning Wi-Fi off answered the first question I had: I could use a coding agent to read a local file, make a change and run checks without an internet connection. I still reviewed the edit and ran the original tests myself. Keeping those tests unchanged gave me a useful way to check the result.

I’d start with small, self-contained tasks when using this setup during an outage or a journey. The project and everything it needs have to be on the laptop before the connection disappears. This one-line fix doesn’t tell me how well the model would handle a large repository or an unfamiliar framework. The CSV report is the next useful check: it asks the agent to complete a function, so there is more to inspect than a one-line repair.