编程模型要看激活参数,不看总参数:本地跑智能体先算显存

一、参数表给了两个数,本地部署要算三笔账
据公开报道,Recursive 发布了首款开放权重模型 Flint,面向编程 Agent,总参数约 398 亿、每 Token 激活约 25 亿。两个数差着一个以上数量级,这类稀疏结构的意图很清楚:用总参数换容量,用激活参数控制单个 Token 的计算量。
于是最常见的误读也来了——“每 Token 只激活二十几亿参数,那本地随便一台机器就能跑”。这句话把两笔账合成一笔。朴素但正确的分法是:总参数决定权重要不要常驻内存,激活参数决定处理每个 Token 时的计算与访存量。前者是你必须一次付清的空间,后者是你按量支出的时间。
而本地跑编程智能体真正的第三笔账,两者都不在里头:上下文带来的注意力键值缓存。它既不属于权重也不属于算力,却常常在长会话里超过前两者之和。三笔账分开算,才能得出”这台机器能不能干这件事”的结论。这一篇只把这三笔摊开,不评价它与任何模型的效果高低,也不给任何未经我们实测的吞吐数字。
二、一张估算表:公式先行,数字自己填
权重、键值缓存、运行时余量三块,各自由不同变量决定,也各自有不同的压缩手段。下表是我们做本地预算时的模板,把参数填进去就能得出一个下限,再与硬件实际容量比较。
| 组成 | 由什么决定 | 随什么增长 | 可压缩的手段 |
|---|---|---|---|
| 权重驻留 | 总参数量 × 每参数字节数 | 常数,与会话无关 | 降低位宽、分层卸载到内存、按层重新计算 |
| 注意力键值缓存 | 层数、键值头数、每头维度、上下文长度、并发数、位宽 | 随上下文与并发线性增长 | 分组键值结构、键值缓存量化、滑动窗口或摘要、限制并发 |
| 中间激活与运行时 | 实现与批大小 | 随批大小与最长序列变化 | 限制批、控制单条最长序列 |
| 框架与上下文余量 | 运行时、分词器、内核与调度开销 | 相对固定,但不可忽略 | 固定版本、减少并行实例数量 |
两点必须强调,否则估算会系统性偏低。第一,位宽不是模型属性而是部署选择,同一份权重在一种位宽与另一种位宽下的驻留可以差出数倍;而注意力层与嵌入往往保留较高精度,把整个模型按一个位宽统一折算会低估。第二,键值缓存的公式里,上下文长度和并发数是乘在一起的关系,单条会话看着不多,几条同时在线就吃满——很多”能跑”的结论死在这一项,因为测试只跑了一条。
关于稀疏结构还有一处容易漏:激活参数小,只意味着每 Token 前向经过的权重少;但如果实现把全部专家权重都驻留在显存里,显存占用仍按总参数算。省下的是访存与计算量,不是空间。要真正省空间需要额外的卸载或路由策略,而那会引入新的延迟来源。
三、编程智能体为什么把瓶颈推到长上下文上
通用问答场景里,上下文长度是产品参数;编程智能体场景里,它是负载本身的性质,而且很难压。原因有四条,都是结构性的。
- 仓库内容天然大块。一次跨文件改动要读进若干完整文件,而代码的信息密度高,截断中间就丢失定义与调用关系。
- 报错栈与构建输出是长文本。编译器与测试框架的输出动辄几百行,全都要作为工具结果进入上下文。
- 多轮工具调用只增不减。每一次读取、每一次编辑结果都要留在历史里,否则下一轮无法知道自己改了什么。
- 工具定义与系统提示本身不短。可调用工具的参数模式会被序列化进前缀,工具越多前缀越长。
后果是关键量变了:权重是常数,键值缓存随长度增长。会话足够长时,第二项会追上并超过第一项,此时决定”能不能跑”的不再是模型多大,而是你愿意支持多长的上下文、同时接几路并发。据此我们给本地部署定两条硬判据:一是按上下文长度分布的较高分位数算峰值,不按平均值——平均数字永远好看,事故都发生在长会话;二是把”最大允许上下文”做成显式配置项而非隐式行为,超限时的动作(截断、摘要、拒绝、转云端)必须提前选定并可观测。
四、激活参数小不等于延迟低
延迟要拆成三段看,三段各自的瓶颈不同,优化手段互不通用。
- 首 Token 之前:主要是预填充,成本随输入长度增长,注意力部分还随长度非线性上升。长上下文场景里这一段常常主导体验,而它和激活参数量关系最弱。
- 逐 Token 生成:解码每步都要读一遍本次用到的权重与全部历史键值缓存,属于访存受限。带宽决定这一段,算力决定不了。
- 总时长:等于前两段乘上输出长度,而编程任务的输出(补丁、重构、解释)普遍很长。
稀疏结构在这三段里各带一个代价:路由让批内样本走不同专家,实际并行的工作被拆碎,批大小的收益不如稠密模型直观;专家分布在不同设备时需要额外的集合通信;键值缓存量化省了空间但可能先影响长上下文的正确性。这些都不构成”能不能用”的结论,只说明一件事——延迟必须由你在目标硬件上、用目标上下文长度分布测出来,不能从参数表推。我们内部禁止用”参数少所以快”这类推断去做任何容量承诺。
五、本地跑还是调接口,三条判据
这件事我们不做站队,按判据走。
| 判据 | 倾向本地 | 倾向接口 | 需要留的证据 |
|---|---|---|---|
| 数据能不能出域 | 代码与缺陷单被限制在网内、有客户隔离要求 | 内容可外发且已有合规通道 | 数据流向说明与审批记录 |
| 并发与延迟要求 | 用量稀疏、可容忍较长首包 | 峰值集中、要求稳定尾延迟 | 长度分布与尾延迟实测 |
| 谁负责升级与回滚 | 有人跟版本、能自建回归集 | 希望由服务方滚动升级 | 回滚演练记录与模型版本登记 |
私有代码库通常是提出这件事的理由,但真正决定成败的是后两条:本地一旦人少、任务多,扩容就不是软件问题;而模型升级若没有一套固定任务集做回归,换权重等于换产品,没人敢动。接口方案反过来——升级不由你控制,因此要约定版本固定与弃用通知窗口,否则某天命中率或行为发生变化,你连对比基线都没有。
六、验收一个开源编程模型的四个动作
按下表顺序做,每一步都有明确产出,缺产出不算验收完成。
| 动作 | 怎么做 | 通过判据 | 常见失败 |
|---|---|---|---|
| 固定任务集上先跑 | 覆盖缺陷修复、跨文件改动、测试补齐、代码解释、重构五类,题目与期望结果冻结版本 | 首次可合入率有基线数字,且可复现 | 用临时找的例子测,结果无法比较 |
| 记录工具调用质量 | 统计参数格式错误率、非法路径写入、编辑未落地、重试次数 | 错误率与重试次数被单独记录,而非只看最终答案 | 只看最终补丁,掩盖过程中的反复试错 |
| 看长上下文退化 | 同一任务在短历史与长历史下各跑一遍,比较结论是否漂移 | 明确给出开始退化的长度区间 | 只测短例子,上线后在长会话翻车 |
| 验证多语言多框架覆盖 | 按团队实际技术栈分桶统计,不合并平均 | 每个桶都有单独数字 | 平均后掩盖某类语言几乎不可用 |
工具调用质量这一项值得单独说:编程智能体的失败大多不是”答错”,是”改错地方””改完编译不过””把上一轮的修改又覆盖一遍”。这些只有把每轮动作写进日志才能归因,字段最少要有一轮次、调用名、参数摘要、是否成功、失败原因分类、上下文长度。日志齐了,四类动作的结论才互相印证。
洹拓在做这类本地化交付时,报价前只会问三张纸:上下文长度分布、并发与尾延迟要求、固定任务集与回归记录。三张纸凑不齐,模型参数表读得再细也没用——真正决定成败的那笔账,是长上下文那一块会一直变长的内存。