让本地 27B 模型在 Agent 框架里真正干活:一次完整调优实录
引子
家里有台带 64G 内存的 Jetson 边缘计算设备(AGX Orin),上面跑着 Ollama,常驻一个 27B 的量化模型。想法很朴素:让这个本地模型接入 Agent 框架(我用的两个是 Hermes Agent 和 pi coding agent),替我干点离线的开发活。
但理想和现实之间隔着一道鸿沟:模型直接接上框架后,慢到不可用——一次 API 调用 31 分钟,一个任务跑几个小时出不来东西。这篇文章记录完整的调优过程:从定位根因、三个层面的参数修正,到最后用一套「强模型评审 + 弱模型回炉修复」的循环,让本地 27B 模型自主完成了一个 LocalSend 类应用的完整开发,并通过了 98/100 的评审验收。
如果你也在边缘设备上跑本地模型做 Agent 开发,这篇文章里的坑和数字应该能帮你省不少时间。
一、问题:模型不是不能干,是「思考」失控
先看一组对比数据。调优前,模型在 Agent 框架里执行一个开发任务:
1 | API call #1: latency=94.8s (20K 系统 prompt 首次 prefill) |
第二次调用生成了 13838 个 token,其中 9-13K 是模型内联的 <think> 推理文本。这类思考型模型(thinking model)默认在每次回复前先「想」——问题在于 Agent 场景下它想得太多、太发散,而边缘设备的生成速度只有 7-8 token/s,13000 个思考 token 就是 27 分钟纯烧在没人看的内心独白上。
关键认知:对于 27B 这个量级的模型 + 8 tok/s 的速度,关闭思考不是「降低质量换速度」,而是「去掉负收益部分」。它的长思考既不是高质量推理(27B 上限摆在那),又吞掉了绝大部分时间预算。
二、调优三板斧
1. 服务端:让模型常驻显存 + 开满上下文
Ollama 默认 5 分钟无请求就释放显存,下次请求要重新加载 18GB 的模型权重(一分钟起步)。对 Agent 这种「想一下、跑个工具、再想一下」的使用模式是灾难。
Docker compose override(Ollama 容器场景的标准做法):
1 | services: |
两个坑:
KEEP_ALIVE: "-1"(官方文档里的永久驻留写法)这个构建不认,报time: missing unit in duration "-1"。必须带单位,8760h即一年。OLLAMA_CONTEXT_LENGTH设 524288,运行时会被自动钳到模型 GGUF 的真实上限(本例 262144)。KV cache 用 q8_0 量化 + flash attention,262K 上下文在 64G 设备上放得下。
验证是否真的常驻:看容器日志里 runner ... duration=8760h0m0s,以及 ps aux | grep llama-server 确认进程带着 -c 262144 存活。
2. 关闭思考:三个客户端,三种姿势
这是整个调优的核心。Ollama 的 OpenAI 兼容接口支持顶层 reasoning_effort 字段(较新版本,对应上游 issue #25758):
1 | {"model": "...", "messages": [...], "reasoning_effort": "none"} |
实测效果(同一个编码问题):
| 配置 | 耗时 | 输出 token |
|---|---|---|
| 默认(思考开) | 35.6s | 316(含 883 字符推理) |
reasoning_effort: "none" |
21.4s | 187(零推理) |
但每个客户端把这个字段送进请求体的路径不一样,这是最容易踩的坑:
curl 直连:直接生效,response 里不再有 reasoning 字段。
Hermes Agent:它的 custom provider 对 Ollama 端点有专门处理——当 reasoning 配置为关闭时会自动发 reasoning_effort:"none" + extra_body.think:false。所以只需要在 config.yaml 里给模型配 override:
1 | agent: |
注意:不能用 hermes config set 设置带点号的模型名——dotted key 会被拆成嵌套 dict(qwen3: {8-27b-gguf-iq4_nl: ...}),必须用 python 直接改 YAML。
pi coding agent:这是最隐蔽的坑。模型定义里有个 reasoning: false 字段,我一开始以为这就是关闭思考——错了,它只是 UI 标记,不会向服务端发任何字段,模型照常思考。日志里的指纹一目了然:
1 | cmn common_reaso: activated, budget=2147483647 tokens |
正确的做法是在模型定义里加 samplingParams,pi 会把它合并进每个请求体:
1 | { |
改完后同样的「Reply with exactly: PI-OK」测试,服务端日志不再出现 activated,响应从 39 token 掉到 6 token。
通用验证方法:不管什么客户端,看 Ollama 容器日志 docker logs ... | grep common_reaso——activated 消失即关闭成功。
3. 客户端超时与上下文配置
8 tok/s 的速度需要配套放宽超时,否则 Agent 框架默认超时会把正常的慢生成掐断:
1 | providers.<ollama>: |
上下文窗口方面,Ollama 的 /v1/models 接口不返回真实 context length(报的是运行时默认值),框架按它算会误判窗口过小。需要在框架的 context 缓存里手动写入模型真实上限(262144)。
三、验收:怎么证明「能正常干活了」
调参只是手段,我用一个硬标准验收:让模型在 Agent 框架里自主开发一个 LocalSend 类应用(局域网文件互传:UDP 组播发现 + TCP JSON 握手 + 长度帧流式传输,纯标准库实现 + 完整测试 + README),然后由一个云端强模型(GLM-5.3)按 100 分制评审,≥95 分算通过。
这套「评审-修复回炉循环」是整个实验里最有价值的部分:
- 弱模型(本地 27B)自主完成项目初版
- 强模型(云端 GLM-5.3)评审,输出精确到
file:line的缺陷清单和分数 - 把缺陷清单喂回同一个弱模型会话(
hermes --resume),让它自己修 - 修完再审,循环直到达标
实际跑了四轮:
| 轮次 | 分数 | 主要缺陷 |
|---|---|---|
| 1 | 82 | serve 模式的发现任务从未被调度(功能性 bug)、断连不清理半文件 |
| 2 | 92 | 错误路径测试没测真实代码、死代码 |
| 3 | 94 | 自定义端口时发现协议广播错误端口、连接失败裸 traceback |
| 4 | 98 | 只剩 cosmetic 级别 |
最终 14/14 测试全绿,真实跨机传输验证:10MB 随机文件从另一台机器发过来,MD5 完全一致。整个过程(含四轮修复)约 4-6 小时,全程无人值守(哦不对,我盯了全程,但没写一行代码)。
pi 框架侧用同样流程做了第二个项目(markdown 笔记 CLI),两轮 89→96 分,同样达标。
几个实操层面的经验:
- 给弱模型的指令要带「效率规则」:明确要求「每个文件单独一次 write 工具调用」「不要在聊天里输出长篇规划」。否则它会把所有文件代码内联在一个回复里输出——一次生成 13K token,30 分钟就没了。
- max-turns 给足(50+),这类模型经常需要多轮试探才能收敛到一个 bug 的根因(比如同 IP 多播回环覆盖 peer 表这种问题,它修了三轮才对)。
- liveness watchdog 会误杀长调用:Agent 框架一般有「N 秒无进展就中断」的保护,8 tok/s 的生成很容易触发。中断了 resume 会话接着跑就行,上下文都在。
- 评审提示词要固定 rubric(我用的 40/25/15/10/10 五项),并明确告诉评审「已修复的缺陷不许重复扣分」,否则分数不收敛。
四、这台设备现在的定位
调优完成后,这个本地 27B + Agent 组合的可用画像很清晰:
能干的:代码审计走读(262K 上下文一次吃几万行)、中小工具项目开发(800 行级,含测试)、日志巡检摘要、按模板生成配置、批量文档翻译。适合挂 cron 离线跑,白天看结果。
不能干的:长输出任务(几千 token 的生成以 8 tok/s 计就是几分钟起步)、视觉类任务(纯文本模型)、需要深度推理的复杂架构决策(27B 上限,这次它对并发问题的直觉就不如强模型准)。
一句话总结:边缘设备上的本地模型做 Agent,先把「思考失控、显存反复加载、超时误杀」这三个结构性问题解决掉,剩下的就是选对任务类型——量大不急、输出中等、文本为主。
尾声
这次实验最有意思的发现是「评审-修复回炉循环」:强模型出精确缺陷清单、弱模型照单执行修复,两者配合的质量远超弱模型单打独斗。这个模式对所有「本地小模型 + 云端 API」混合部署的场景都适用——云端 API 只花在评审上(一次几分钱),重活累活全在本地。
毕竟,让 27B 自己想明白怎么修一个 bug 很难,但让它照着一张写好 file:line 的清单改代码,它还是很称职的。