本地优先 AI 编程实战:用 Ollama + Aider + Docker 搭建数据不出域的编码智能体(附代码)

📅 发布时间:2026/9/30 18:20:11
本地优先 AI 编程实战:用 Ollama + Aider + Docker 搭建数据不出域的编码智能体(附代码)
1. 为什么要在内网跑编码智能体先说结论本地优先 AI 编程指的是把模型推理、代码读写、工具执行三件事全部放在你自己的机器或内网服务器上不经过任何外部 API。Ollama 负责跑模型Aider 负责当编码智能体Docker 负责把运行环境隔离起来。适合谁手里有 GPU 或 Apple Silicon 机器、对源码外流有顾虑、又不想放弃 AI 辅助编码效率的后端和运维同学。我见过太多团队的矛盾一边要求代码不能出内网一边又眼馋 AI 补全和重构的效率。云端编程助手确实好用但请求和上下文都要离开你的网络边界这在金融、政企、制造业研发场景里基本是一票否决。本地推理方案的价值就在这里——模型权重在你自己的磁盘上推理过程不调用任何外部端点仓库文件通过卷挂载进容器出站流量可以按需收紧。这篇文章交付的东西很具体一份可复制的 Docker Compose 配置、Ollama 模型拉取命令、Aider 接入本地端点的 settings 片段以及一次完整的代码补全加单元测试验证动作。你照着做能在一台内网机器上跑通一个数据不出域的编码智能体。技术章节会比拿 Key 的章节长得多因为真正的坑都在配置和排障里。需要说明的是本文的本地方案和 TaoToken 这类云端 API 聚合平台并不冲突。本地推理适合对数据边界有硬性要求的场景而当你需要更强的模型能力、又不想自己维护 GPU 集群时通过 https://taotoken.net/api 接入云端模型是另一条路。两条路可以并存按项目敏感度分流即可。2. Ollama 本地推理服务部署与模型拉取Ollama 是整个方案的推理底座。它的定位很简单把模型权重下载、加载、暴露成一个 HTTP 端点默认监听 127.0.0.1:11434。你不需要懂 CUDA 编译也不需要手写推理服务一条ollama pull就能把模型拉下来。先装 Ollama。Linux 上一行脚本搞定curl -fsSL https://ollama.com/install.sh | sh装完之后确认服务状态systemctl status ollama curl -s http://localhost:11434/api/tags第二条命令返回 JSON 说明服务已经起来了。接下来拉代码模型。Qwen2.5-Coder 系列是目前本地编码场景里比较均衡的选择32B 版本在 4 张 24G 卡上能跑单卡 24G 建议降到 14B 或 7B# 32B 版本首次下载约 18GB 权重 ollama pull qwen2.5-coder:32b-instruct # 单卡 24G 或内存有限的机器用 14B ollama pull qwen2.5-coder:14b-instruct # 验证模型可调用 curl -s http://localhost:11434/api/generate -d { model: qwen2.5-coder:14b-instruct, prompt: 用一行 python 读取文件 all.txt 并返回行数, stream: false } | python3 -c import sys,json;print(json.load(sys.stdin)[response])预期输出类似with open(all.txt) as f: print(len(f.readlines()))。如果这一步通了说明本地推理链路是活的。这里有个关键点Ollama 默认只监听 127.0.0.1也就是只有本机能访问。这本身是好事但容器里的 Aider 要访问它就需要打通容器到宿主的网络。有两种做法一种是用--add-host host.docker.internal:host-gateway让容器能解析到宿主另一种是把 Ollama 监听到0.0.0.0然后用内网 IP 访问。前者更安全因为不暴露端口到局域网。模型选择上给个对照模型量化后显存适用硬件编辑质量qwen2.5-coder:7b约 5G单卡 8G / M 系列日常补全够用qwen2.5-coder:14b约 9G单卡 24G重构、单测生成qwen2.5-coder:32b约 20G4×24G 或多卡复杂跨文件修改拉模型这一步必须在宿主机上做不要在沙箱容器里做。原因后面排障章节会讲——一旦你按最小权限收紧了出站规则容器内是拉不动模型的。3. Docker Compose 与 Aider settings 可复制配置这一节是全文的核心给你一份能直接用的 Docker Compose 配置以及 Aider 接入本地端点的 settings 片段。先说目录结构假设你的工作目录是/srv/local-coder/srv/local-coder/ ├── docker-compose.yml ├── Dockerfile.coder ├── settings.yml └── work/ # Agent 的输出目录先写Dockerfile.coder只装开源组件镜像来源可审计FROM python:3.12.1-slim RUN pip install --no-cache-dir aider-chat0.71.0 # 清掉可能存在的云凭证降低泄露面 RUN rm -rf /root/.aws /root/.config/gcloud 2/dev/null || true WORKDIR /workspace ENTRYPOINT [aider]然后是docker-compose.yml。这份配置做了几件事把宿主 Ollama 通过host-gateway暴露给容器、把业务仓库以只读方式挂载、把输出目录单独挂载可写、限制容器能力services: coder: build: context: . dockerfile: Dockerfile.coder image: local-coder:0.1 container_name: local-coder stdin_open: true tty: true extra_hosts: - host.docker.internal:host-gateway environment: - OLLAMA_HOSThttp://host.docker.internal:11434 - AIDER_CONFIG/workspace/settings.yml volumes: - /srv/repos/order-service:/workspace/order-service:ro - /srv/local-coder/work:/workspace/out - /srv/local-coder/settings.yml:/workspace/settings.yml:ro cap_drop: - ALL security_opt: - no-new-privileges:true deploy: resources: reservations: devices: - driver: nvidia count: all capabilities: [gpu]注意cap_drop: ALL和no-new-privileges这两个是防止 Agent 执行危险操作的基础。源仓库用:ro只读挂载Agent 只能改工作区副本污染不了源仓库。接下来是 Aider 的settings.yml这是接入本地端点的关键片段model: ollama/qwen2.5-coder:14b-instruct openai-api-base: http://host.docker.internal:11434/v1 openai-api-key: ollama edit-format: diff auto-commits: false read: - /workspace/order-service这里三个字段必须写全Base URL 指向 Ollama 的 OpenAI 兼容端点http://host.docker.internal:11434/v1Key 随便填一个非空字符串Ollama 不校验Model ID 用ollama/前缀加模型名。edit-format: diff让 Aider 先给 patch 等你确认auto-commits: false关掉自动提交避免 Agent 乱改历史。启动命令cd /srv/local-coder docker compose build docker compose run --rm coder \ --model ollama/qwen2.5-coder:14b-instruct \ --read /workspace/order-service如果你用的是 TaoToken 这类云端 API 作为补充settings 里的openai-api-base换成https://taotoken.net/apiKey 换成你在控制台生成的即可模型 ID 按文档填。本地和云端两套配置可以放在不同文件里按项目切换。4. 验证请求与单元测试闭环配置写完不算完得跑一次完整的代码补全加单元测试验证确认整条链路是通的。这一节给你一个可复现的动作序列。先准备一个待修改的小仓库。假设/srv/repos/order-service里有个calc.pydef add(a, b): return a b启动 Aider 后在交互界面里输入给 calc.py 增加一个 divide 函数除数为零时抛出 ValueError并补一个 pytest 单测Aider 会扫描仓库、调用本地 Ollama、生成 diff。因为设了edit-format: diff它会先把 patch 展示给你--- a/calc.py b/calc.py -1,2 1,8 def add(a, b): return a b def divide(a, b): if b 0: raise ValueError(division by zero) return a / b确认后Aider 把改动写到/workspace/out。接着验证单测。在容器里跑docker compose run --rm coder \ --model ollama/qwen2.5-coder:14b-instruct \ --message 为 calc.py 生成 pytest 测试文件 test_calc.py \ --yes生成的测试文件大致长这样import pytest from calc import add, divide def test_add(): assert add(1, 2) 3 def test_divide(): assert divide(6, 3) 2 def test_divide_by_zero(): with pytest.raises(ValueError): divide(1, 0)跑测试cd /srv/local-coder/work python -m pytest test_calc.py -v预期输出三行 PASSED。到这一步代码生成、文件写入、单元测试验证的闭环就跑通了全程没有一次外部 API 调用。再补一个出站校验脚本确认沙箱真的封住了外联# verify_egress.py import socket BLOCKED [8.8.8.8, 1.1.1.1, api.openai.com, github.com] ok True for host in BLOCKED: try: socket.create_connection((host, 443), timeout3) print(f[FAIL] 竟能连到 {host}出站规则未生效) ok False except (socket.timeout, socket.gaierror, OSError): print(f[PASS] {host} 已阻断) print(egress 校验通过 if ok else 请检查 iptables 规则)在容器内运行它全部 PASS 才算真正做到了数据不出域。这个脚本建议纳入 CI 定时任务持续验证。5. 常见报错排查401、local proxy failed、reading choices本地方案跑起来之后报错基本集中在几个地方。这一节按真实报错逐条排。报错一401 Unauthorized。这个在本地 Ollama 场景下通常不是真的鉴权失败而是 Aider 把请求发到了错误的端点。检查settings.yml里的openai-api-base是不是写成了http://host.docker.internal:11434少了/v1。Ollama 的 OpenAI 兼容端点在/v1路径下少了它就会返回 404 或 401。另外openai-api-key不能为空填ollama即可。报错二local proxy failed / connection refused。容器里访问不到宿主 Ollama。先确认宿主上curl http://localhost:11434/api/tags是通的再确认 compose 里有没有extra_hosts: host.docker.internal:host-gateway。如果宿主是 Linux 且 Docker 版本较老host-gateway可能不生效改用宿主内网 IP比如OLLAMA_HOSThttp://10.0.0.5:11434同时把 Ollama 监听到0.0.0.0。注意这样会暴露端口到局域网配合防火墙只放行容器网段。报错三Error reading choices / unexpected response format。这个多半是模型返回了非标准 JSON或者模型名写错了。Aider 期望 OpenAI 格式的choices数组如果 Ollama 那边模型没加载成功会返回错误结构。先单独测curl -s http://localhost:11434/v1/chat/completions -d { model: qwen2.5-coder:14b-instruct, messages: [{role: user, content: hi}] } | python3 -m json.tool如果这里报 model not found说明模型名不对用ollama list看准确名称。如果返回正常但 Aider 还报错检查 settings 里的 model 字段有没有ollama/前缀。报错四OAuth / authentication 相关。如果你之前用过云端 Aider 配置~/.aider.conf.yml里可能残留了云端 Key 或 OAuth token导致它优先走云端。清掉旧配置或者用AIDER_CONFIG环境变量显式指定本文的 settings 文件。报错五模型拉取中断。前面提过ollama pull必须在宿主机做。如果你先收紧了容器出站规则容器内是拉不动模型的。顺序是宿主拉模型 → 启动容器 → 收紧出站。排障时记住一个原则先分层定位。宿主 Ollama 通不通、容器到宿主通不通、Aider 配置对不对三层逐层验证比盲目改配置快得多。如果你在本地和云端之间切换云端那套的 Base URL 用 https://taotoken.net/apiKey 在控制台生成接入文档里有完整的模型 ID 列表对照着填就不会错。6. 本地与云端如何按场景分流跑通本地方案之后实际用起来会遇到一个现实问题本地 14B 模型在日常补全和单测生成上够用但遇到复杂跨文件重构、架构级设计时效果和顶级云端模型还是有差距。这时候不必二选一按项目敏感度分流就行。我的做法是分三档。核心业务仓库、涉及客户数据的模块一律走本地 Ollama数据不出域是硬约束。开源项目、个人练手项目、不敏感的脚本工具走云端 API模型能力更强、响应更快。介于两者之间的内部工具看代码里有没有硬编码的密钥或业务逻辑有就走本地。云端接入这块如果你不想同时维护多家厂商的 Key 和计费可以用 TaoToken 这类聚合平台统一管理。它的 API 地址是 https://taotoken.net/api兼容 OpenAI 格式Aider 里把openai-api-base指过去、Key 换成平台生成的就行。模型对话可以在 https://taotoken.net/models 试效果长期跑编码任务的话 Coding Plan 更划算接入文档在 https://taotoken.net/doc 有完整说明。需要强调的是本地方案和云端方案的安全边界不一样别混着用。本地容器里如果同时配了云端 Key一旦出站规则没封死敏感代码就可能顺着云端请求出去。所以本地容器的 settings 里只留 Ollama 端点云端配置放在宿主机的另一个配置文件里物理隔离。最后给个实用技巧把verify_egress.py挂到 cron 里每小时跑一次输出写到日志。这样即使有人误改了 iptables 规则你也能第一时间发现。本地优先不是口号是每一层都要有可验证的边界。