
第一次用 Roo Code 连本地模型那天我差点把它当成一个没写完的插件直接卸载。VS Code 里装好扩展、填好 Ollama 地址然后输入一句“帮我把这个项目里的暗色主题改成亮色”回车光标就开始转圈。四十多秒后第一行字才慢吞吞往外蹦蹦一句我改一行等我手动把 CSS 变量都换完了它还在 “继续探索解决方案”。那一刻我彻底信了一句话本地模型不是不能跑而是大多数人根本没学会怎么喂它。这篇文章就是我那次踩坑调优的完整记录。Roo Code 接 Ollama / LM Studio 这类本地模型卡顿几乎必然出现但卡在哪个环节、怎么拆解、改哪些参数网上说法很零散。我把环境变量、Provider 配置、上下文压缩、模型选型全部过了一遍最后从“40 秒等首字”优化到“几乎感觉不到延迟”。如果你也是因为省钱、数据不出内网或者单纯想完全离线用 AI 写代码这篇文章应该能帮你少走一半弯路。1. 先分清卡顿的类型加载、预填充、生成别上来就调参我一开始犯的最大错误是把所有“慢”都当成同一个问题每天换个参数试结果两天过去了毫无进展。后来才意识到Roo Code 调用本地模型时的“卡顿”其实是三件完全不同的事模型加载、预填充prefill、生成decode再加上一层众所周知的 UI 卡顿混在一起非常迷惑。1.1 三种“慢”的体感完全不同每种慢的表现特征差异极大我列个表给你对照卡顿类型实际表现对应阶段典型耗时模型加载某次请求前突然等几秒之后又正常load_duration几百ms到数秒预填充慢按下回车后光标长时间转圈然后才开始吐字prompt_eval_duration可能占等待时间80%生成慢第一个字出现挺快但后续一字一卡像打字机eval_duration取决于硬件带宽UI 卡顿输入框本身延迟、菜单转圈跟模型无关扩展进程/MCP任何时间F 预填充prefill是卡顿的最大元凶也是最容易被忽略的。你可以把它理解成“先读完整本参考书再开始答题”模型拿到你的全部指令、历史对话、工具定义、文件内容后需要一次性把所有 token 并行计算出注意力状态。这个阶段的计算量跟上下文长度直接相关上下文越长等待越久。Roo Code 这类 Agent 工具天生就会在上下文里塞大量历史所以本地模型慢绝大多数时候就是慢在这个阶段。1.2 用 API 返回数据定位瓶颈与其凭感觉猜不如直接把 Ollama 的耗时统计翻出来。Ollama 每次请求返回的 JSON 里自带load_duration、prompt_eval_count、prompt_eval_duration、eval_count、eval_duration这几个字段你根本不需要额外埋点。我用一段最简单的 curl 就能复现问题curl http://localhost:11434/api/generate -d { model: qwen2.5-coder:14b, prompt: 写一个 Python 冒泡排序, stream: false } | jq {load_duration, prompt_eval_count, prompt_eval_duration, eval_count, eval_duration}没有 jq 就用 Python 解析结果是一样的。拿到数据后就能判断题了。1.3 一个公式搞定性能指标Ollama 的时间单位是纳秒要转成秒得除以 1e9。两个关键指标预填充速度 prompt_eval_count / (prompt_eval_duration / 1e9) # token/s 生成速度 eval_count / (eval_duration / 1e9) # token/s我自己常用的判断阈值很简单load_duration超过 1 秒模型没有常驻内存每次都在重新加载去查OLLAMA_KEEP_ALIVEprompt_eval_duration占用大量时间上下文太长或者没开 Flash Attention生成速度长期低于 20 token/s硬件瓶颈考虑换小模型或更低量化参数救不回来这套判断在任何本地推理服务上都适用LM Studio、llama.cpp 的逻辑也一样。搞清楚卡在哪一步后面的优化才有方向。2. Ollama 服务端改造常驻内存、Flash Attention、收紧上下文定位完瓶颈后最优先改的是 Ollama 服务端。Roo Code 只是个客户端它把请求发给 Ollama真正决定速度的是后者。改动成本极低收益最直接。2.1 OLLAMA_KEEP_ALIVE先解决模型反复加载的问题我第一次测试时看到load_duration稳定在 3.8 秒左右还以为模型文件太大。后来才明白这就是典型的“模型没常驻”。Ollama 默认模型加载后如果一段时间没有请求会被自动卸载以节省内存。这个默认时长远小于你实际思考的时间你在 Roo Code 里看代码、想下一步、手动改文件经常超过五分钟。等你再给模型发一个请求它又要重新加载好几秒就没了。解决方案是把模型钉在内存里# Linux / macOS export OLLAMA_KEEP_ALIVE-1 ollama serve # Windows在系统环境变量里新增 OLLAMA_KEEP_ALIVE-1再重启 Ollama-1表示永久驻留更保守一点可以设3600一小时。我实测下来load_duration从 3.8 秒直接降到 30 毫秒左右体感提升巨大。另一个必须一起处理的是OLLAMA_MAX_LOADED_MODELS。默认情况下 Ollama 可以同时加载多个模型如果你手上有多个模型新模型一加载就会把旧模型踢出内存哪怕设置了永久驻留也没用。Roo Code 日常只用一两个模型所以我把它设成了 1export OLLAMA_MAX_LOADED_MODELS12.2 开启 Flash Attention长上下文的预填充救星如果你观察到的现象是“上下文越长首字等待越夸张”那基本就是注意力计算浪费时间。传统的注意力机制复杂度是 O(n²)上下文翻倍计算量翻四倍。Flash Attention 通过分块计算和重计算把长上下文的预填充速度提升非常明显尤其在 Roo Code 这种动不动塞几万 token 的场景下收益肉眼可见。export OLLAMA_FLASH_ATTENTION1这个参数对 NVIDIA GPU 和 Apple Silicon 都有效纯 CPU 推理收益较小。开了之后我拿同一个 32K 上下文的请求做对比预填充时间至少砍掉一半。如果你的 Ollama 版本比较新可能某些平台已经默认开启但手动设置不亏多一行环境变量而已。2.3 用 Modelfile 控制上下文窗口别让 Roo Code 盲目塞历史这是最关键的一步也是最多人踩坑的地方。Ollama 默认的num_ctx其实很小Roo Code 这类工具一次性发出的 prompt 动辄上万 token。为了不报错很多人在 Roo Code 里把 Context Window 填得特别大比如 128000。问题也随之而来上下文越大每次请求的预填充就越慢。这等于你每次让模型“读一本越来越厚的参考书”哪怕最后真正用到的只有几十行代码。我的做法是给模型单独创建一个限制上下文的版本# Modelfile FROM qwen2.5-coder:14b PARAMETER num_ctx 16384 PARAMETER num_gpu -1保存后用ollama create生成新模型ollama create qwen2.5-coder-14b-16k -f Modelfile之后在 Roo Code 里就用qwen2.5-coder-14b-16k这个名字。上下文从 32K 降到 16K 后预填充时间直接降了一个档次。具体该设多少取决于你的显存和实际任务模型规模建议 num_ctx说明7B Q48192~16384轻量任务够用14B Q416384日常 Agent 工作的甜点值32B Q416384~32768需要大显存谨慎使用70B Q4建议 ≤16384上下文过大会被预填充拖垮别把上下文调得太小。Roo Code 的历史超过窗口后会自动压缩压缩本身也要消耗额外时间频繁压缩会让体验更差。16K 是我从 7B 到 32B 都实测过最稳的平衡点。2.4 并发与线程的合理设置Roo Code 是串行调用不需要并发。OLLAMA_NUM_PARALLEL保持默认 1 就好调高了反而容易把显存撑爆。线程数这块如果你是 GPU 推理基本不用管。如果是纯 CPU 推理OLLAMA_NUM_THREADS不要超过物理核心数。超线程对这类计算没有加速作用反而会增加调度开销。我之前的机器强行开满线程16 个逻辑核全用上生成速度比 8 物理核还慢了 10% 左右。3. Roo Code 侧配置Provider 参数、历史压缩、去掉多余工具服务端改动完之后接下来就是 Roo Code 自身。很多人只改 Ollama 不改进阶设置结果还是卡因为罪魁祸首在客户端的配置和使用习惯上。3.1 Provider 里的三个字段很多人的默认值就是卡顿根源Roo Code 连接本地模型最省事的方式是走 OpenAI 兼容接口Ollama 提供/v1端点。我在 Provider 配置里的设置是这样字段推荐值Base URLhttp://localhost:11434/v1API Keyollama随便填占位Model IDqwen2.5-coder-14b-16kContext Window16384与 Modelfile 一致Max Output Tokens2048或4096Context Window 填得跟 Modelfile 一致很关键两边对不上会出现各种诡异问题。Max Output Tokens 也不要贪大生成本来就是逐 token 串行输出你让它一次输出 8000 token哪怕生成速度是 40 token/s也要等 200 秒。如果 Roo Code 里能找到针对简单任务的小模型配置也就是 Fast Model 一类的字段我建议把 7B 模型填进去。日常的文本补全、历史压缩这类低难度操作7B 比 14B 快接近一倍完全不亏。3.2 Auto Compact 和 /compact把对话历史“翻译”成摘要Roo Code 每一轮对话都会把完整历史塞进下一次请求。看起来是帮你保持上下文实际上是在慢性毁掉体验十轮任务之后历史里塞满了工具调用记录、中间输出、大段代码预填充一次轻松破万 token。/compact命令能把这些历史让模型整理成结构化摘要之后的请求只带摘要上下文立刻从几万 token 缩到几千。我现在的习惯是每完成一个子任务就手动/compact一次而不是等上下文快爆了再说。自动化也很有用。Roo Code 设置里有 Auto Compact 的开关我建议你打开并把这个阈值设得激进一点。默认接近满才压缩会让模型不得不处理超长历史我把阈值调到 50%~70%让它提前瘦身。这里给个提醒压缩会丢失细节几次压缩后模型可能确实“忘了”某些关键信息。所以重大的需求变更我会在压缩前单独发一条消息重申一遍或者直接写进待办文件避免最后的方案跑偏。3.3 工具链减负MCP、Auto Approve 和 .rooignoreRoo Code 的每条请求都会在系统提示里带上所有可用工具的说明和参数定义每多一个工具来回消耗的 token 就多一截。本地模型速度本来就不富余更要精打细算。我最开始为了尝鲜一次性挂了十几个 MCP Server结果每个请求的 prompt 直接多出几千 token预填充肉眼可见变慢。后来把不相关的全停了只留一个浏览器控制和一个数据库查询Roo Code 明显“轻”了。还有两个不太被注意但非常影响体感的地方。第一是 Auto Approve。卡顿的本质是等待而人工确认每次工具调用也是一次等待。在安全场景下打开 Auto Approve让模型自动执行读写文件、运行命令交互节奏会顺滑非常非常多。风险当然存在所以我只在测试分支和低风险任务上开生产环境或者涉及删除操作的时候关掉。第二是.rooignore作用类似.gitignorenode_modules/ dist/ build/ .git/ *.lockRoo Code 读取文件进上下文之前会做目录扫描排除掉这些臃肿目录既省了预填充时间也避免它把无关代码当成“项目理解”的一部分。4. 瓶颈在硬件内存带宽决定你能跑多快模型选型有大讲究软件全部优化到位后你会碰触到真正的那堵墙硬件上限。这也是所有“卡顿优化”最后回归的地方。提前说明这里没有任何玄学一条物理规律定生死。4.1 为什么内存带宽是“慢”的物理原因生成阶段模型每产出一个 token需要把全部权重从内存/显存里读一遍。以 14B Q4 量化模型为例权重约 9GB显卡显存带宽如果是 360GB/s理论最高速度就是 40 token/s。如果你用的是纯 CPU 内存DDR4 双通道带宽大约 50GB/s速度就只剩 5~6 token/s——你的 14B 模型在 CPU 上跑慢十倍不是感觉问题是数学问题。所以很多人以为“显卡显存大就能跑得快”这是误解。显存容量决定你能不能装下模型显存带宽决定你能跑多快。这也是为什么同样跑 14B某些老卡的带宽低反而比新卡慢很多。预填充阶段则更依赖 GPU 算力和注意力实现Flash Attention 在这里起作用。所以一个常见的优化方向是预填充靠算力生成靠带宽两个瓶颈是分开的。4.2 常见的本地模型量化与硬件搭配参考选模型时除了看参数量级量化等级也得重视。量化等级高如 Q8质量好但权重体积大生成速度更慢量化等级低如 Q4_K_M质量差距在代码场景几乎感知不到速度却显著更快。我平时只推 Q4_K_M这是代码生成场景甜点中的甜点。以下是我的搭配参考注意生成速度要综合带宽、是否 offload 等因素看模型规模量化后体积推荐设备参考生成速度7B Q4_K_M约 4.5GB8GB 以上显存60~80 token/sRTX 3060 级别14B Q4_K_M约 9GB12GB 以上显存35~45 token/sRTX 3060 级别32B Q4_K_M约 20GB24GB 以上显存15~25 token/s70B Q4_K_M约 42GB48GB 以上显存 / 大内存 M 系列5~10 token/s在 35~40 token/s 下人眼读代码几乎追不上生成速度体感已经接近原生 API。低于 15 token/s 就会明显觉得“卡”。这也是我最后留在 14B 而不是硬上 32B 的原因。4.3 显存不够怎么办部分 offload 的小技巧显存不够时Ollama 默认会把一部分层放在 CPU 上计算这就是部分 offload。它的代价非常直接每一层跨 PCIe 传输权重速度断崖式下跌。我的建议是能全量塞进 GPU 就绝不 offload如果非要 offload优先减少上下文长度、降低量化等级把模型体积压到接近显存容量上限。还有个容易忽略的参数OLLAMA_GPU_OVERHEAD。它预留一部分显存给 KV Cache 和其他运行开销避免上下文波动时直接 OOM。设成 1~2GB 是个安全选择代价是少了一点点可用显存可以接受。5. 实测压测脚本与前后数据对比纸上谈兵到这就够了上硬数据。我用实际环境跑了一轮优化前后的对比测试条件是RTX 3060 12GB、qwen2.5-coder:14b-instruct-q4_K_M、Ubuntu Ollama、Roo Code 走 OpenAI 兼容端点。5.1 手把手写一个压测脚本不能只测一条单发请求需要模拟 Roo Code 那种多轮对话、上下文累积的负载。我用一个简单的 Python 脚本做多轮请求import json import requests history for i in range(5): history f第{i1}轮请给工具函数添加类型标注。\n resp requests.post( http://localhost:11434/api/generate, json{ model: qwen2.5-coder:14b, prompt: history, stream: False, }, timeout300, ) data resp.json() stats { load_duration: data.get(load_duration), prompt_eval_count: data.get(prompt_eval_count), prompt_eval_duration: data.get(prompt_eval_duration), eval_count: data.get(eval_count), eval_duration: data.get(eval_duration), } print(json.dumps(stats, indent2)) history data[response][:200] \n脚本里每轮都把上一轮的输出追加进 prompt模拟 Roo Code 的历史累积。跑一轮优化前后对比结果就非常直观了。5.2 优化前 vs 优化后的关键指标这是第一次粗暴使用时的魔鬼数据指标优化前优化后load_duration3825 ms28 msprompt_eval_count38124 tokens3870 tokensprompt_eval_duration31.8 s1.55 seval_count486 tokens486 tokenseval_duration13.9 s12.1 s首字等待约 35 s约 1.6 s体感卡成幻灯片接近原生 API变化最猛的是load_duration和prompt_eval_duration原因就是前面说的模型常驻内存 Flash Attention 上下文从 3.8 万 token 压到 3.8 千 token。eval_duration变化不大符合硬件规律——生成速度由带宽决定不是参数调出来的。5.3 为什么首字快了整体体验就“像原生 API”了人感知“卡顿”的先后顺序是这样的按下回车的那一刻起到第一个字出现这段纯等待是最难熬的。一旦开始输出了哪怕每秒只有 40 token人眼通常也追得上。所以本地模型优化的核心目标就是压缩首字时间生成速度只要不低得离谱感官上就不会太差。优化前首字要 35 秒我会频繁切出去刷网页优化后首字不到 2 秒我就能安心盯着屏幕等结果。这个心理差别是质的飞跃。另外提醒一句这组数据是在单用户、单请求环境下测的。如果你同时开好几个 VS Code 窗口共用同一个 Ollama数据会打折这是正常的不需要重新怀疑配置。6. 最终保留的配置与踩坑清单所有坑都踩过一遍之后我整理了最终保留的配置可以直接抄作业。照着来就能避免大部分问题前提是你的显存/内存确实装得下对应模型。6.1 最容易踩的六个坑现象原因处理模型一会儿快一会儿慢OLLAMA_KEEP_ALIVE太短或MAX_LOADED_MODELS太大设-1和1换个模型后所有请求都变慢多个模型互相抢占内存控制同时加载的模型数量只改一个文件也要等十几秒上下文未压缩、Context Window 过大/compact 调小窗口第一个字等了很久之后输出正常预填充太慢开 Flash Attention显存爆掉直接 OOM模型太大或上下文波动换小模型 / 设OLLAMA_GPU_OVERHEADVS Code 输入本身卡顿MCP 工具过多、目录扫描太慢停用无用 MCP、加.rooignore如果两条左右你都中了说明优化方向基本正确如果全中也别灰心一次改一个变量别同时调否则很难确认到底哪项起到了作用。6.2 可以直接抄作业的最终配置环境变量export OLLAMA_KEEP_ALIVE-1 export OLLAMA_MAX_LOADED_MODELS1 export OLLAMA_NUM_PARALLEL1 export OLLAMA_FLASH_ATTENTION1 # 可选显存预留 export OLLAMA_GPU_OVERHEAD2 ollama serveModelfileFROM qwen2.5-coder:14b PARAMETER num_ctx 16384 PARAMETER num_gpu -1Roo Code Provider 设置Base URL: http://localhost:11434/v1 API Key: ollama Model ID: qwen2.5-coder-14b-16k Context Window: 16384 Max Output Tokens: 4096交互习惯每完成一个子任务手动/compactAuto Compact 阈值设 60%Auto Approve 只在安全场景打开不用的 MCP 全部停用.rooignore里加上所有重型目录。最后分享一个我个人的习惯我现在主力用 14B 常驻32B 只在需要深入理解项目架构时手动切换。切换之前一定先/compact不然大模型一上来就要面对一万多 token 的历史再强的硬件也得低头。这套配置用了快三个月本地模型已经从“摊手无奈”变成日常离不开的小帮手。希望你看完这篇也能把多出来的等待时间省下来把耐心留给真正难解的代码逻辑而不是耗在转圈光标的陪同下。