Level1Techs实验揭示BF16/FP8/INT4量化在本地Agent任务中的失败模式差异,量化选择直接影响工具调用成功率和JSON输出正确性。
前 40 分钟工具调用一切正常。本地 agent 运行着一个被你量化压缩到刚好能塞进 GPU 的 27B 模型,正在读取一份很长的日志文件、调用工具、编辑文件。然后它吐出了格式错误的 JSON,然后它调用了一个根本不存在的工具,然后它开始循环。你检查了 Spring Boot 的依赖注入、检查了 MCP 服务配置、检查了你的 prompt 模板。全都没问题。于是你把锅甩给了模型:"这个 27B 就是不够聪明,跑不了 agent 任务。"
其实有相当大的概率模型本身没问题,是你的推理栈在作祟。Level1Techs 本周发布了一项深度技术实验,目前在 Hacker News 上获得了 381 分、142 条评论,它精确地测量了这种故障模式,而这些数字足以改变每个用本地模型跑 agent 的团队选择量化格式的方式(讨论帖在此)。
开始之前先完整披露:下面的实验由 Level1Techs 作者在其自有硬件上完成,并非我所为。我自己在 Spring Boot 服务背后跑本地模型,和大多数人一样,选量化格式的标准是"多大能塞进我的显存"。这篇文章是我试图把他们的测量结果转化成你可以直接执行的决策,因为量化厂商宣称的指标与 agent 工作负载实际表现之间的鸿沟是巨大的。
这个实验设置比你通常看到的量化声明要严谨得多。作者取 Qwen3.6-27B 的官方 BF16 检查点——一个密集型混合模型,共 64 层,三层线性注意力(Gated DeltaNet)加一层全注意力的循环模式——跑在一张 RTX PRO 6000 Blackwell GPU 上,绑定了某个 pinned 的 vLLM nightly build。eager 执行,无 CUDA graphs,无投机解码,无前缀缓存。每次只改变一个变量。
工作负载比硬件更重要。作者没有用合成的"大海捞针"测试,而是回放了一段从真实 agent 工作流中截取的约 100K token 上下文,里面全是真实的工具调用和实际的工作产物。正如作者所说,没有人能针对这个 prompt 做基准最大化,也没有人能围绕它校准自己的量化,因为任何公开训练集或基准集中都没有这段内容。
测量方法值得真正理解,因为它解释了你的 agent 为什么会在聊天基准测试永远暴露不了的方式上出问题。每当第 32 个 prompt token 时,他们会以 BF16 捕获完整词表 logits,随后在 FP64 下比较概率分布。核心指标是"top-1 翻转":某个配置与基线相比,在贪婪地选择下一个 token 时产生了不同结果的位置。每个配置都针对同一段强制 token 历史进行评估,因此比较在数学上是受控的。当 2% 的下一 token 决策发生翻转时,微小的分歧会随着每个生成的 token 不断累积,直到你到达一个完全不同的位置。
第一个测试是最令人不安的一个。权重相同、GPU 相同、所有条件相同,唯独 prefill 阶段 vLLM 选择的全注意力后端不同:FlashAttention 2、FlashInfer 或 Triton Attention。而 64 层中只有 16 层使用了这个可切换的后端。
在前几千个 token,所有后端对下一个 token 的选择是一致的。再往后进入 prompt 深处,它们开始出现分歧,而且分歧出现在随 prompt 内容而变化的簇中,并非随上下文长度平滑增长。为排除随机性,同一个后端被跑了多次:logits 在每次运行中逐比特一致。分歧纯粹来自于不同的矩阵乘法实现以不同方式计算了相同的数学。
再读一遍这句话。同一个模型文件,在同一块 GPU 上,仅因为注意力 kernel 换了,就在长上下文情况下产生了可测量的不同下一 token 选择。光是那个 nightly vLLM 容器就绑定了 734 个 Python 包,每个包都有自己的 bug 和怪癖。你本地的栈是穿越那座代码大山的一条特定路径,而模型作者发布基准数字时走的并不是同一条路。
第二个测试的标题值得印在每篇本地 LLM 教程上:"为什么你的 LLM 在 40k token 之后智商断崖式下跌"。这次权重保持 BF16,只对 KV 缓存做了量化——那个随着每个上下文 token 增长而不断膨胀的过去 keys 和 values 的缓存。
结果:使用 INT8 KV 缓存时,足够多的 top token 在工具调用期间发生了翻转,作者得以复现一个完全可重现的工具调用错误。BF16 基线顺利完成了任务。INT8 缓存最终设法恢复了。INT4 KV 缓存则完全没有恢复。
这是对任何跑 agent 的人最具可操作性的单个发现。聊天应用很少超过几千 token 的上下文,所以一个让你的有效上下文长度翻倍的量化 KV 缓存看起来像是白捡的免费收益。但 agent 工作负载驻留在 40K、80K、100K token 的位置,正是 KV 缓存误差累积的地方,而且故障模式精准地落在了 agent 所依赖的结构化输出上:工具调用语法。
如果你本地 agent 在短会话中正常、在长会话中崩溃,而你又在跑量化 KV 缓存来省内存,那很可能你已经找到了 bug。
重头戏来了,对比同一模型的五个版本:
BF16 基线:官方 Qwen/Qwen3.6-27B 检查点,未量化
官方 FP8:Qwen 自家发布的 Qwen3.6-27B-FP8,E4M3 权重,128x128 块级动态 FP8 激活值
INT8 W8A16:TheHouseOfTheDude 的社区量化,静态对称 INT8 权重 + BF16 激活值,无校准数据集
NVIDIA NVFP4:NVIDIA 官方发布的 Qwen3.6-27B-NVFP4,一个 FP8 和 4-bit 目标的混合检查点
AWQ INT4 W4A16:社区 AWQ 量化,4-bit 权重,group size 32,在一份披露的"STEM and Agentic"数据集上校准
KV 缓存对五个版本都保持 BF16,以隔离权重精度。用作者的话说,结局"一旦你看到就相当可预测",但可预测不等于营销宣传所说的那样:
社区 INT8 量化击败了所有选手,包括模型亲爹自家发布的第一方 FP8。作者将其高保真度归因于 W8A16(激活值保持 BF16)以及 GDN 投影层未做量化这两点。
NVIDIA 的 NVFP4 成绩垫底,在 88K 上下文时约 50% 的 token 发生了翻转。半数贪婪下一 token 决策与基线不同。
这些失败不是表面文章。两个 4-bit 选项(NVFP4 和 AWQ INT4)都没能正确关闭它们的工具调用,还搞砸了一个 Cisco CLI 语法检查,执行了 show run 而正确命令应该是 show arp。FP8 和 INT8 都完成了正确的调用。
最后这条就是 agent 开发者需要了解的全部。50% 的 token 翻转率不是说模型答错了一半的常识问题。它意味着在长上下文 agent 场景中,你正在运行的模型在可衡量地不同于你下载的那个模型,而且这种分歧集中在 agent 最脆弱的地方:结构化工具输出。
有一个值得正视的注意事项:在这轮特定运行中,vLLM 将 GPU 路径归类为缺乏原生 FP4 支持,回退到了通过 Marlin kernel 做 weight-only FP4 压缩。所以这个特定的 NVFP4 数字反映的是那条运行时路径,不一定等同于各处原生 FP4 硬件算术。更广泛的教训在这个注意事项之外依然成立:不附带完整运行时上下文,量化宣称的数字毫无意义。
帖子还解释了 HF 模型卡是如何蒙混过关的。量化模型卡经常宣传与参考模型之间低到离谱的 KL 散度。作者的警告很直接:不披露参考检查点、完整运行时环境、评估文本、校准数据、上下文长度、采样位置、KL 方向、词表截断方式以及聚合方法,你根本无法解读那个数字。很多人搞错了。
在短对话 prompt 上测得的 KLD 数字,对 90K token 工具调用流量下的行为毫无说明意义。这相当于推理栈版的微基准测试——根本碰不到你的实际工作负载。
下面是我会交给任何在 Spring AI、LangChain4j 或纯 OpenAI 兼容客户端背后跑本地模型的人的一份决策清单:
默认选用 W8A16 INT8 或第一方 FP8 来跑 agent 工作负载。在这次大乱斗中,两者都在 90K+ token 下保持了工具调用能力。4-bit 权重量化适合聊天、摘要和批量分类,不适合必须在处理了一小时上下文后吐出精确工具语法的 agent。
KV 缓存对 agent 保持 BF16。量化 KV 缓存节省的内存是真实的,但测试 2 表明代价落在长会话中的工具调用上。如果你必须量化 KV 缓存,INT8 还能恢复而 INT4 不行;把 INT4 KV 视为对 agent 不合格。
不要相信模型卡上的 KLD 数字。如果方法论未披露,那个数字就是装饰品。
用真实工作负载测试,不用 prompt 测试。重放一段捕获的 50K-100K token agent 会话,包含工具调用,并与短会话的行为做 diff。作者的核心建议在此适用:三五个 prompt 的零样本测试不是 agent 任务的类比。
锁定你的推理栈,并把版本变更视为行为变更。如果逐比特一致的重复运行在不同注意力后端之间仍然会分歧,那 vLLM 一个小版本升级就足以悄无声息地改变你 agent 的行为。锁定版本,升级后重新跑你的回放测试。
对照模型卡检查采样器设置。一个来自帖子的附带小发现:如果你家的推理模型在 thinking 输出里无限循环,你的 temperature 大概太低了。模型卡上写明了预期的设置,照着使用就行。
一个让人不舒服的总结:当你的本地 agent 在长会话中变得"更笨"时,模型可能是无辜的。量化格式、KV 缓存精度以及你的栈选择的特定 kernel,全都在悄无声息地重塑它的决策,而只有回放你的实际工作负载才能告诉你偏差有多大。
量化是内存与保真度之间的权衡,而 Level1Techs 的数据首次为类似本地 agent 实际所做的工作负载的这个权衡给出了真实数字。INT8 W8A16 社区量化可以击败第一方 FP8。NVIDIA 的 4-bit 发布在 88K 上下文时翻转了一半 token 并搞坏了工具调用。KV 缓存被悄悄证明是长上下文杀手。任意一条都可能是"这个模型跑不了 agent"与"这个模型跑 agent 没问题"之间的分水岭。
我每周写关于 Java、Spring Boot 和实用 AI 工程的文章。订阅免费。
你有没有把本地模型当作 agent 使用过,在长会话中遇到了神秘的工具调用失败?你跑的是什么量化格式,这有没有改变你的看法?