aider 接本地 Ollama:num_ctx 每轮重算的四个坑

aider 接本地 Ollama 能用,装完到改出第一个补丁大约五分钟。但它的默认配置有三个地方必须动:每条请求都重算一次上下文窗口、每轮自动提交一次 git、整文件重写会顺手改动无关行。按本文改完,一次 110 行文件的全线替换只动 24 行目标行。

下面所有数字来自 M4 16GB 一台机器,aider 0.86.2,ollama 0.40.14,模型 qwen3:8b,全部实跑。没有跑过的部分会明确标出来。

装:一条命令四秒,装完先修 PATH

不要用 pip。aider 依赖 litellm、tree-sitter 语言包、tiktoken 一大堆,pip 会卡在解析。用 uv 装工具,走清华源:

export UV_DEFAULT_INDEX=https://pypi.tuna.tsinghua.edu.cn/simple
export UV_TOOL_DIR=~/tools UV_TOOL_BIN_DIR=~/tools/bin
time uv tool install aider-chat

实测 4.26 秒装完,226 个包,占 620MB。venv 用的是 Python 3.12,即使你机器上默认是 3.13 也一样。uv 的镜像配法见uv 国内加速那篇,这里只补一个坑:装完 uv 会提示 bin 目录不在 PATH,此时直接敲 aider 是 command not found,必须先 export。

export PATH=~/tools/bin:$PATH
aider --version
aider 0.86.2

接 Ollama:模型名、两个必加参数、一条无害告警

最小可用命令是这样,一次问答就退出:

aider --model ollama_chat/qwen3:8b \
  --yes-always --no-stream \
  --message "修复 median 函数:元素个数为偶数时返回中间两个数的平均值。只改 stats.py。" \
  stats.py < /dev/null

三个参数各有原因。--yes-always 跳过每步确认,脚本里不加会卡住等输入。--no-stream 让日志整段输出,方便事后 grep。< /dev/null 是因为非终端环境下 aider 会打印一条 Warning: Input is not a terminal (fd=0),重定向后它就直接走一次性模式。

首次启动有两条告警都不影响使用。第一条是 OLLAMA_API_BASE 未设置,aider 默认就连 127.0.0.1:11434,本地服务在跑就不用管。第二条是它自动往 .gitignore 里写了一行 .aider*,这是把对话历史文件排除出版本库,属于正常行为。

前缀这件事网上说法很乱。实测 ollama/qwen3:8b 和 ollama_chat/qwen3:8b 都能连上,都能改文件,同一类单文件小改两次都成功。区别只在底层走的接口不同,本轮只测了单文件小改,多轮工具调用的差异没有测到。

num_ctx 每条请求重算一次,这是最该知道的一条

aider 给 Ollama 发请求时会自己塞一个 num_ctx,公式在 models.py 里,就一行:

if self.is_ollama() and "num_ctx" not in kwargs:
    num_ctx = int(self.token_count(messages) * 1.25) + 8192
    kwargs["num_ctx"] = num_ctx

意思是:本次消息的 token 数乘 1.25,再加固定的 8192 余量。消息越长,窗口开得越大。这个值不是写死的模型默认值,是逐次计算的。

这里要和 Ollama 自己的口径分开看。Ollama 侧的 num_ctx 决定模型能装多少 token,超了会静默截断,具体表现见Ollama 上下文怎么设那篇的实测。aider 这一行做的是替客户端把值算好再传过去,两边都归它管。

验证办法是读 ollama 的启动日志。llama-server 的启动命令里带 -c,那就是实际生效的上下文长度。同一轮对话里连着抓四次,四个值全不一样:

日志里的 -c 按公式反解的 token aider 自报的发送量
9220 822 824
9004 649 同轮第二次请求
9573 1104 1.1k
9435 993 同轮第三次请求

反解公式是 (ctx – 8192) / 1.25,四组误差都在 3 个 token 以内,和 aider 自己报的发送量对得上。这条能解释很多现象。

第一个现象是模型反复重载。num_ctx 一变,Ollama 就要按新窗口重建 llama-server,实测四次启动分别耗 4.16 秒、2.78 秒、3.29 秒、3.05 秒。一轮对话三轮请求,就有三轮重新加载权重。

第二个现象是文件加多了上下文会暴涨。repo-map 默认吃 4096 token,加上附件文件内容,一条消息到 2 万 token 很常见,代入公式就是 33192 的窗口。qwen3:8b 在 16GB 机器上按每 1K 上下文 144MB 算(这个斜率在显存核算那篇里测过),光 KV 就要接近 2.9GB,加上 5.2GB 权重就到了换出边缘,一旦换出去,下次请求又得重载,连带影响常驻策略,换出与常驻的关系见keep_alive 那篇。

固定 num_ctx:重启从三次降到一次,省不了时间

在项目根目录放一个 .aider.model.settings.yml,就能把窗口钉死:

- name: ollama_chat/qwen3:8b
  extra_params:
    num_ctx: 16384

官方 ollama 文档里给了这个写法,但示例只写了 ollama/ 前缀的模型名,抄到 ollama_chat/ 上要自己改名,名字必须和 --model 后完全一致,否则不生效。

同一任务做了对照:110 行文件、12 个函数、把上限从 1000 改成 5000,两轮参数完全一样,只差这个配置文件。

配置 runner 启动次数 总用时 发送 / 接收
动态 num_ctx 3 次 214 秒 3.6k / 2.3k
固定 16384 1 次 212 秒 3.6k / 2.3k

结论有点反直觉:重启次数确实从三次降到一次,总用时只差 2 秒,在噪声里。原因是权重还在页缓存里,重载一次两三秒,分摊到三分钟的生成里看不出来。

所以修 num_ctx 的目的不该是提速。它的价值在另外两处:一是大文件场景下把窗口钉住,避免 num_ctx 随文件膨胀冲到显存上限;二是让日志可复现,排查时不会因为窗口值每次不同而看花眼。想省时间,应该去减附件文件数量,那是数量级的事。

编辑格式:diff 只动目标行,whole 会多改 12 行

aider 给 Ollama 模型默认选的是整文件重写,日志里会写 with whole edit format。想改成补丁模式要显式指定:

aider --model ollama_chat/qwen3:8b --edit-format diff ...

同一个任务两种格式各跑一次,两者都一次成功,但 git 里的改动行数差得很明显:diff 格式改写 24 行,正好是 12 个函数各两行;whole 格式改写 36 行,多出的 12 行是三引号 docstring 被顺手改成了单引号。语义没坏,可代码评审时会多出一堆无关 diff。

这个差异的来源很直白。整文件重写要求模型把 110 行原样吐回来,它在复述过程中会顺手做点优化。补丁模式只要求它输出改动块,碰不到没提的行。

接收量也是同一个道理。同一个小文件,whole 格式的接收 token 是 1.2k 到 2.4k,diff 格式稳定在 1.6k 上下。文件越长,whole 的整份复述就越贵。

自动提交:每轮一条 commit,作者是你的本地模型

aider 默认每次落盘就 commit 一次。跑完六次任务,git log 里多了六条提交,例如 fix: return average of two middle elements for even-length lists in median function,还有 fix: adjust clamp upper bound to 255。message 是本地模型自己写的,中英混杂,风格随机。

在个人实验仓库里这很方便,在真实项目里会把历史搞得很脏。加一个参数就关掉:

aider --no-auto-commits ...

关掉之后它仍然改文件,只是不提交,改完你自己看 diff 再决定。团队协作场景建议直接写进配置文件,不要每次手敲。

三处真会卡住的地方

第一处是装完敲 aider 报 command not found。uv 把可执行文件放在工具目录的 bin 下,不写进 PATH 就用不了,这不是装失败。

第二处是脚本里调用时没有重定向 stdin。aider 检测到不是终端会打印告警,然后进入交互等待,在 CI 或定时任务里就表现为永久挂起。判断方法是看日志有没有停在 Input is not a terminal 那一行。

第三处是把整个项目一次性加进对话。repo-map 占 4096 token,每多一个文件就多一份整文件内容,num_ctx 跟着按 1.25 倍放大,生成时间会从 100 秒级掉到 200 秒以上。实测 6 个文件的小仓库跑一次三分钟,只加一个目标是 108 秒。用 --no-gitignore 之外的手段控制范围,比调参有用。

顺带说一个容量上限:110 行、1.8k token 的单文件,8B 模型一次改完没压力。超过 200 行后建议先拆文件,或者改用 diff 格式降低复述量。这个上限来自实测,不是估算。

FAQ

aider 一定要用 ollama_chat/ 前缀吗?

不必。实测 ollama/qwen3:8b 和 ollama_chat/qwen3:8b 都能连上本地服务并成功改文件。两者走的是不同接口,本轮没测出功能差异,但多轮工具调用这类场景建议用 ollama_chat,那是官方推荐的聊天接口。

本地模型跑 aider 要花钱吗?

不花。aider 只在云端模型时才计费并显示金额,本地推理只吃电和显存。它的成本体现在时间上:一次单文件小改 108 秒,一次多文件改动 212 秒,都包含模型加载和逐 token 生成。

8B 模型能扛多大的改动?

实测 110 行文件、24 处替换一次成功,接收 token 1.2k。越接近 200 行,整文件重写的失败率越高,因为输出长度逼近模型单次上限。超过这个规模就拆文件,或者切到 diff 格式。

全部文章归档与模型专题