[控场AI]
· 12 分钟阅读· 6,052 字

Claude Code 编排本地大模型实测:78分对76分

Claude Code 编排本地大模型实测:78分对76分

把本地模型当工人、Claude 当包工头拆解审查,比一次性喂大需求得分更高,成本仅 0.40 美元。

开发者用 RunPod 上的 RTX PRO 4000 跑 Qwen3.8-27B(Q4_K_M 量化),对比两种驱动方式:Claude 拆解任务、逐模块审查返工的编排模式,与把整份规格一次性交给本地模型的单次模式。结果编排模式拿到 78/78 和 77/78,单次模式仅 76、74、71。关键差异在提示长度——编排模式最大提示不到 8K token,单次模式冲到 32K 上限附近后表现急剧下滑,即使给到 90 轮反而拿到最低分。所有被审查抓出的缺陷都通过了可见测试,58 个隐藏测试才是真正的量规。审查也有漏网之鱼,命令过滤器不等于沙箱。整个实验五次运行共耗 42 分钟 GPU 时间,约 0.40 美元。结论是本地模型能胜任小型、规格明确的任务,真正让方案成立的是"写—审—改"的工作流程与一份精确的规格。

拆分工序让本地模型逆袭

用本地大模型跑 Claude Code,成本低、数据私有,但能力往往差一截。一位开发者在 RunPod 租了一张 RTX PRO 4000 Blackwell(24GB,$0.57/小时),用 Qwen3.8-27B Q4_K_M 模型驱动 llama.cpp,做了一个对照实验:让 Claude 当"包工头",把五个小模块分别派给本地模型写,写完后 Claude 逐份审查、退回重做;对照组则是把整份需求一次性丢给本地模型。

Claude Code using a local LLM

结果很干脆:编排模式下本地模型拿到 78/78 和 77/78;单次大需求模式只拿到 76、74、71(满分 78)。五个测试轮次共享同一张显卡、同一套量规、同一份隐藏测试集。

更关键的数字不在总分,而在"最大提示长度"。编排模式下没有任何一次任务提示超过 8K token;单次模式的最大提示冲到了 32,001 token,距离 32K 上下文上限只剩 800。上下文被塞满之后,模型开始抓不住关键信息——跑得最久的那次反而拿了最低分。

实验是怎么搭的

整体架构分两个角色。Claude 跑在开发者 Mac 上的 Claude Code 会话里,负责规划、写任务提示、审查代码、写探针验证、决定哪些要退回重做。本地模型是"工人",通过一个 Python 脚本 worker.py 调用,每次任务开一段无状态对话,配备四个工具:

  • list_files / read_file:限定在工作区目录内读取
  • write_file:同样限定路径,拒绝绝对路径、.. 和符号链接逃逸
  • run_command:只允许 python3 脚本、python3 -c、python3 -m unittest、ls、cat、pwd,且只能用相对路径

服务端启动命令只有一行:llama-server -m Qwen3.8-27B-Uncensored-Q4_K_M.gguf --host 127.0.0.1 --port 8080 -c 32768 -ngl 99 --jinja -fa on --alias local。监听本地端口,Mac 通过 SSH 隧道接入,不走 RunPod 的公网代理。

本地模型选的是 Qwen3.8-27B 的 Q4_K_M 量化版,文件 16.8GB,刚好塞进 24GB 显存。温度 0.2,关闭思考链,最大输出 4096 token。

Q4_K_M 与 llama.cpp 的量化机制。 Qwen3.8-27B 原始权重以 bfloat16 存储约需 54GB 显存,远超单张 24GB 显卡。Q4_K_M 是 llama.cpp 的 GGUF 量化方案代号:Q 代表量化,4 表示权重平均压到约 4 bit,K 指采用 k-quant 分块量化,M 表示中等混合精度(medium)。它的做法不是把所有张量一视同仁地砍到 4 bit,而是给注意力投影、前馈等对精度敏感的层保留更高的位宽,对较鲁棒的层压得更狠,最终模型文件压到 16.8GB,17B 级模型恰好能连 KV cache 一起塞进 24GB。量化必然带来困惑度上升,但对代码补全类任务,4 bit 的退化通常在可接受范围内,这也是本地部署最常见的选择。

温度 0.2 与关闭思考链。 温度控制采样随机性,0.2 接近贪心解码,让同一个提示多次运行输出高度一致——这对需要复现缺陷、稳定评分的实验很关键。关闭思考链则意味着模型不输出中间推理步骤再做答,直接补全工具调用和代码。这样省 token、降延迟,也避免了长推理链把宝贵的上下文预算吃掉,代价是复杂逻辑任务上的表现会打折,这也解释了为什么一个显式的审查环节如此重要。

为什么选一个有棱角的测试项目

开发者写了一个叫 minikv 的玩具项目:用纯 Python 实现的键值存储,五个模块层层依赖,每个模块都埋了一个容易翻车的角落。

模块陷阱
store.pyTTL 取整方式、过期键仍留在字典里
txn.py事务里删掉的键必须以"不存在"返回,即使 store 里还在
protocol.py引号规则、报错顺序、KEYS 不接受参数
aof.py最后一行被截断要忽略,空行要保留
session.py重放日志时不能把重放的行再写一遍;事务要缓冲自己的日志行

测试集共 78 个 unittest 用例:20 个工人能看到,58 个隐藏。评分脚本从测试文件取分母而非从提交物取,沙箱只复制五个规格模块,用 -I 隔离模式跑 Python——造假 unittest.py 或篡改汇总结果都没用。

编排模式怎么运转

五个模块按依赖顺序拆成任务。第一阶段三个独立模块并行发出,最慢的 87 秒完成;Claude 随后逐份对照规格审查,用 python3 -c 一行命令探测边界情况,发现问题就退回。最多两轮返工,超了就按现状评分。

每个任务提示包含:模块的规格原文、所依赖模块的接口、测试命令,以及最关键的一句——"最终评分使用你看不到的额外测试,所以请严格按需求实现"。

返工提示只写三样东西:缺陷是什么、怎么复现、规格原文怎么说的。不提示原因。

单次模式则把整份规格一次性喂进去,五个模块全塞进一个提示,工具相同、可见测试相同,预算 90 轮。

无状态对话与提示累积。 文中反复提到的"最大提示长度"是理解整篇实验的钥匙。本地模型走的是无状态调用:worker.py 每次任务开一段全新对话,模型不记得上一轮做过什么,全部上下文靠提示一次性喂进去。在编排模式下,每个工人只拿到自己那个模块的规格和依赖接口,提示天然短小;而单次模式把五个模块全塞进一个提示,之后每一轮工具调用的结果又被追加回同一个对话,上下文单调膨胀,到第 90 轮时逼近 32K 上限。这是"无状态 + 累积式提示"结构带来的直接后果,而不是模型本身变笨。

--jinja 与工具调用。 本地模型要能像 Claude 一样调用 list_files、write_file 这类工具,服务端必须把工具定义按模型自身的对话模板渲染进提示,并正确解析模型输出的结构化调用。--jinja 让 llama-server 使用 GGUF 内嵌的 Jinja 模板来完成这一步。若模板不匹配,模型会输出格式错乱的工具调用,整个编排工作流就无法运转。这也是为什么工具调用能力对"本地模型当工人"这件事是硬门槛,而不只是模型分数高低的问题。

审查环节抓住了什么

所有被审查抓出来的缺陷都通过了可见测试——这正是隐藏测试存在的意义。

模块缺陷第1轮第2轮
protocol.pyKEYS 命令完全没实现一轮修复一轮修复
txn.py已删除/过期的键仍被视为存在一轮修复一轮修复
txn.py外层事务删除的键在内层嵌套事务中仍可见未出现返工轮次耗尽仍未修复
session.py启动时重放日志把每行又写回去,文件每次重启翻倍第二轮修复一轮修复
session.py内层 ROLLBACK 保留了该事务的日志行第二轮修复未出现

最典型的是重放日志的 bug。三次单次模式运行全部带着这个 bug 交付,两次编排模式的工人也写出了同样的代码。区别只在于:Claude 读了 __init__,看到重放循环调用了 execute(),而它知道 execute() 会写日志。这是一个很容易写出、也很容易看出的 bug——前提是有人在看。

开发者的一句话总结很到位:本地模型会告诉你它干完了,但重放 bug 还在那里。

隐藏测试与可见测试的分离。 这是本实验最重要的评测设计。工人能看到的 20 个测试用来引导开发,评分真正依据的是 58 个隐藏用例。如果只有可见测试,模型会精确地针对它写代码——文中那几个缺陷全部通过了可见测试就是明证:可见测试是模型的目标函数,隐藏测试才是真正的量规。评分脚本从测试文件而非提交物取分母、沙箱只复制五个规格模块、用 -I 隔离模式跑 Python,都是为了堵死"造假 unittest.py 或篡改汇总结果"这条路。一个宽松的可见测试集对本地模型几乎是负资产,它教会模型通过检查而非满足规格。

为什么审查者能看出工人看不出的 bug。 重放日志的 bug 在单次模式下三次运行全部逃逸,因为工人只盯着自己当前要写的函数;Claude 读了 __init__,看到重放循环调用了 execute(),从而联想到 execute() 会写日志。这是典型的跨模块视线问题:缺陷的成因和后果分散在不同文件里,只有通读过一遍整体结构的人才能把两点连起来。审查环节的价值不在于更聪明,而在于它站在一个能看见全貌的观察点上。

审查也有漏网之鱼

第二组编排运行丢了一分,而且丢得不冤。规格要求:当参数为空、含空白或双引号时才加引号。工人额外给含反斜杠的值也加了引号,format_command 输出 SET k "a\\\\b",而隐藏测试期望 SET k a\\b。两种形式解析回来一样,所以往返检查通不出问题——要抓住它得逐字符检查输出,审查没有覆盖到。

还有嵌套事务的 bug 在第二轮返工耗尽后仍残留,但 58 个隐藏测试里没有覆盖这个场景,所以没扣分。审查找到了测试看不见的缺陷,测试找到了审查漏掉的缺陷。两者都不是完整的检查手段。

给的轮次越多,单次模式表现越差

第一轮实验最容易被质疑的地方是预算不公平:编排用了 88 轮,单次只用了 40 轮。于是这次给单次模式 90 轮,其中一次还关掉了重复保护以允许用满。

结果它用满了所有轮次,拿了五组中最低的 71/78。结束时提示长度达到 32,001 token,距离 32,768 上限仅差 800,因为每一轮都把工具结果追加进对话。更短的两次运行分别失败 2 个和 4 个隐藏测试,这次失败了 7 个,包括短运行能通过的事务日志测试。

同样一个模型,绳子给长了,代码反而更差。 开了保护的那两次分别在 23 轮和 11 轮自行停下,成绩更好。

差距的真正来源不是编排模式获得了更多轮次,而是每一轮都发生在一个短小、聚焦的对话里,且有一个审查者决定下一轮该聊什么。

实际踩到的坑

命令过滤器不是沙箱。 一次返工中工人花了 20 轮里的 12 轮反复跑同一个检查脚本,每次换个文件名:/tmp/aof1.txt 到 /tmp/aof12.txt。绝对路径藏在 python3 -c 字符串里,过滤器解析不到,于是在开发者 Mac 的 /tmp 下写了 11 个文件。而且因为每轮文件名不同,没有两轮完全一致,重复保护从未触发。工人的代码是以你的身份、在你的机器上运行的——工作区里别放重要的东西。

finish: done 只是模型的看法。 每个带着缺陷结束的任务,回来时都附带一份兴高采烈的总结说"所有测试通过"。确实通过了——可见的那些。

max-turns 意味着你得自己读代码。 三次工人调用在编排运行中耗尽轮次,其中一次留下的代码通过了可见测试但藏了两个缺陷。状态只告诉你它停了,不告诉你它留下了什么。

第二次返工必须说清楚改哪里。 第一次 session 返工(缺陷+复现+规格)什么都没改;第二次点名了方法并描述了修复方向,七轮就完成了。这是编排者做了更多工作,和"Claude 只负责审查"的说法有出入。

命令白名单与操作系统级沙箱的区别。 文中"命令过滤器不是沙箱"这一点值得强调。这里的防护是在 Python 层用字符串匹配和参数校验实现的:只放行 python3、ls、cat 等命令,拒绝绝对路径和 ..。但模型只要把绝对路径藏在 python3 -c 的字符串里,过滤器就解析不到。真正的沙箱应当在操作系统层施加限制——容器、chroot、单独的用户身份、只读挂载、资源限额——让即便命令被放行,越界写入也物理上无法发生。字符串过滤永远追不上模型的想象力,它只挡得住意外,挡不住任何有目的的绕过。实验里工人往开发者 Mac 的 /tmp 写了 11 个文件,这正是缺少 OS 级隔离的代价。

这个方案的边界

开发者自己列出了几条诚实的限制:

  • 每种条件只跑两次,样本太小。 模型不是确定性的,同样的提示,单次模式三次分别拿了 76、74、71。差距在每一对比较中都成立,但别把小数点后一位当真。
  • 第二轮的审查不是完全盲测。 在第二轮之前,给单次模式评分时 Claude 瞥见过隐藏测试的简短失败摘要。虽然第二轮审查仍按同样的探针和规格阅读标准执行,但笔记看到了就是看到了。
  • 规格写得精确,审查才有依据。 规格模糊,审查能抓到的东西就少。部分收益来自精确的规格——这一点对人也成立。
  • 编排者的成本没有计入。 Claude 的 token 才是贵的,上面的比较只是工人侧的成本。
  • 上下文从来不是编排模式的约束。 最大提示不到 8K,这个项目只证明了拆解能让模型远离上下文上限,没有证明它能救活一个塞不进 32K 的代码库。

值得带走的结论

本地模型做小型、规格明确的任务已经够用。开发者说得很直接:"我会用它写 Python 或 Bash 自动化脚本,但不会让它碰我的数据库。"

整个会话——两种条件、五次运行——一共消耗 42 分钟 GPU 时间,约 0.40 美元。真正让方案成立的,是那个审查环节。

换句话说,如果要用本地模型省钱,你真正购买的不是模型能力,而是一套"写—审—改"的工作流程,以及一个足够精确的规格说明。 本地模型负责打字,Claude 负责思考,而缺陷死于审查。",,

分享:

相关推荐