上周三晚上十一点,我把一台装着单张24G显卡的机器挪到阳台上——风扇全速转起来那个噪音,屋里实在待不住人——然后给它挂上持续24小时的推理负载,回屋睡觉。第二天同一时间去收数据,屏幕上留下的是一条完整的显存曲线、一条温度曲线、一份两万多行的请求日志,以及几个足够写进复盘文档的坑。这篇东西就是那次实验的完整记录,从显存怎么算、负载怎么设计,到半夜三点到底发生了什么。
先把名词交代清楚。OpenClaw是一个开源的智能体运行时框架,你可以把它理解成给大模型装上一圈"手脚":能调用工具、读写本地文件、维护多轮任务状态、在失败时自己重试。千问3.5-9B是我这次选来当"大脑"的模型,9B 参数量,中文表达扎实,量化之后单卡能塞得下。压力测试这件事,说白了就是把这套组合按在椅子上连续干活一天一夜,看它会不会中途罢工、变慢、失忆或者开始胡说八道。
适合谁看:手上有一张消费级大显存卡、想在自己机器上跑智能体、又不想等上线之后才被 OOM 和延迟打脸的同行;也适合已经在跑但总觉得"跑一会儿就变慢"、找不到原因的人。全程没有云服务、没有多卡集群,就是一台机器、一张卡、24小时。
1. 这次压测要回答的三个问题
动手之前我先给自己划了范围。压测最怕的是"什么都想测",最后跑了一天,数据一堆,结论一个没有。所以我把目标收敛成三个能落地的问题,其他一律砍掉。
1.1 OpenClaw 在这条链路里到底干什么活
很多人对智能体框架的想象停留在"套一层 prompt 让它调用函数",实际跑起来远比这复杂。OpenClaw 在整条链路里承担的是调度与状态维护:它要决定这一轮是直接回答、还是去调工具;要维护对话历史和中间结果;要在工具返回异常时判断是重试还是换路径;还要控制整个任务最多走多少步,防止无限循环。
这些动作每一个都会往推理服务端发请求,而且请求长度极不均匀——一次工具调用可能只发 200 个 token,一次带完整上下文的总结可能塞进去 7000 个 token。这直接决定了压测的请求体不能是固定长度的假数据,必须模拟真实的任务流。我在设计负载时专门做了一份"任务样本池",后面第 3 节会细说。
另外还有个容易忽略的点:框架本身的 CPU 和内存开销。推理跑在 GPU 上,但 OpenClaw 的进程在 CPU 侧要解析 JSON、拼装上下文、写日志。24 小时下来,如果这块有内存泄漏,表现不是显存爆,而是系统内存缓慢爬升,最后被系统直接杀掉进程。这也是我这次重点盯的一项。
1.2 为什么偏偏选 9B 这个档位
参数量的选择本质上是一道成本与能力的平衡题。3B 以下的模型在工具调用格式的遵循度上经常出问题——你让它输出结构化参数,它给你一段散文,重试三次才对,实际吞吐被重试吃掉了。30B 以上的模型单卡放不下,得上量化到很低的档位,质量掉得厉害,而且显存余量小到没法开并发。
9B 这个档位是我实测下来性价比曲线的拐点:指令遵循基本稳定,中文长文本不塌,Q4 量化后权重只占五个多 G,剩下的显存足够撑起一个有意义的并发数。换句话说,它让我能在一个真实可用的配置下测出有参考价值的数据,而不是在极限边缘测出一堆"理论上可行"的结论。
这里有个判断标准值得分享:选模型不要看榜单分数,要看在你实际任务上的重试率。我拿二十条典型任务跑了一遍,统计每个候选模型需要重试几次才能产出合法结构,9B 这个档位的重试率大概在 8% 左右,再小一号的模型直接飙到 30% 以上,那点省下来的显存完全被重试抵消了。
1.3 我明确不测的东西
为了不跑偏,有几件事我提前排除在外。第一,不做多卡分布式,这次就是单卡,因为绝大多数个人和小团队的实际部署形态就是单卡。第二,不做模型微调相关的测试,压测针对的是推理稳定性,不是训练。第三,不测极限并发下的质量崩塌,那是另一个话题,需要人工标注才能评估,24 小时自动化跑不出来。
还有一条:不追求跑出最高分。压测的价值不在于把机器压到冒烟,而在于找到"能长期稳定运行的那个点"在哪里。所以我全程盯的是稳态表现,尖峰阶段只是短暂试探,不把机器按在极限上烤二十个小时——那样测出来的数据没有部署参考价值。
2. 显存账本:把 24G 掰成八瓣花
这一节是我认为整篇最有价值的部分。很多人部署失败不是因为不会装环境,而是一开始就没算清楚显存去哪了,装完发现差 2G,然后开始瞎调参数碰运气。我把这笔账完整算一遍。
2.1 权重、KV Cache、激活值三笔开销
显存开销可以拆成三大块,缺一不可。
第一块是模型权重。9B 参数在不同精度下的占用差别很大,粗略按"参数量 × 每参数字节数"估:
| 量化档位 | 每参数字节 | 权重占用(9B) | 质量损失感受 |
|---|---|---|---|
| FP16 | 2.0 | 约 18 GB | 基准,无损失 |
| Q8_0 | 约 1.06 | 约 9.6 GB | 几乎无感 |
| Q5_K_M | 约 0.72 | 约 6.5 GB | 轻微,长文偶有跳字 |
| Q4_K_M | 约 0.62 | 约 5.6 GB | 可接受,复杂推理略降 |
| Q3_K_M | 约 0.50 | 约 4.5 GB | 明显,不建议生产用 |
第二块是 KV Cache,也就是上下文缓存,这块最容易被低估。它的计算方式是:每 token 每层的缓存字节数 = 2(K 和 V 各一份)× KV 头数 × 头维度 × 每元素字节数。以 9B 级别模型常见的配置(32 层、4 个 KV 头、头维度 128、FP16 存储)来算,每层每 token 是 2 × 4 × 128 × 2 = 2048 字节,乘 32 层就是每 token 约 64 KB。
那么单个 8192 token 的请求,KV Cache 就要吃掉 8192 × 64KB ≈512 MB。如果同时跑 16 个这样的请求,就是8 GB。这个数字非常吓人——它和权重是一个量级的开销,但很多人部署时只算了权重。
第三块是激活值和框架开销,包括推理引擎的调度结构、CUDA 上下文、临时缓冲区。这部分不好精确算,经验值按 1.5 到 2.5 GB 预留比较稳妥。
三笔加起来:Q4_K_M 权重 5.6 GB + 16 并发 × 8K 上下文的 KV 8 GB + 框架开销 2 GB ≈15.6 GB,24G 卡上还剩 8G 多,能扛住请求长度的波动。如果换成 Q8_0,权重 9.6 + KV 8 + 2 = 19.6 GB,只剩 4G 余量,长请求一来就容易 OOM。
注意:上面这个算法假设 KV Cache 全部按 FP16 存。有些推理引擎支持 KV 量化到 INT8,能把这部分砍一半,但会带来轻微的质量损失,长上下文下尤其明显。我这次没用,因为要留一个干净的基线。
2.2 量化档位的取舍
算完账,Q4_K_M 基本是唯一合理的选择。但这里有个细节值得说:同样是 Q4,不同实现的质量差异比想象中大。我做过一个粗糙的对比,用同一批包含数字提取和格式转换的任务,Q4_K_M 的准确率比重一点的 Q5 档低大概 3 到 5 个百分点,但在纯生成类任务上几乎看不出区别。
所以我的策略是按任务分流:结构化输出、参数提取这类对精度敏感的任务,把温度压到 0.1 以下,减少随机性带来的偏差;开放式的写作、总结类任务,正常温度跑。这样在同一个模型上兼顾了两类需求,不用为了少量任务去堆更高的精度。
还有个实操细节:量化文件的分片方式会影响加载速度。单文件加载比多分片快,但如果显存紧张需要部分卸载到内存,分片反而更灵活。我这次是全量上卡,所以选了单文件,冷启动从点击到能接受请求大概 12 秒。
2.3 上下文长度与并发的乘积关系
这是整篇里我最想强调的一条经验:上下文长度和并发数是相乘关系,不是相加。你把上下文从 4K 提到 8K,看起来只是"能处理更长的文档",实际上显存开销翻倍;如果并发数不变,等于把安全余量直接吃掉一半。
我给自己定的规矩是:先定并发,再定上下文。因为并发决定了服务能承受多少同时到达的请求,这直接关系到用户体验;上下文长度则可以根据实际任务分布来裁剪。
具体做法是统计任务样本池里的输入长度分布。我统计下来,90% 的请求在 3000 token 以内,只有不到 3% 会超过 7000。所以最终配置是:上下文上限设 8192,但调度上按 4096 分配 KV 空间,超长请求单独排队。这样大部分请求走快车道,长请求走慢车道,整体显存占用降下来了,用户体验反而更稳。
3. 负载曲线设计:让 24 小时不是简单重复
如果 24 小时都用同一个并发数压,测出来的东西意义有限——真实的负载从来不是一条直线。它会爬坡、会平稳、会突然来一波尖峰、会掉下来。所以我把整条曲线设计成五个阶段。
3.1 五个阶段的节奏安排
| 阶段 | 时间窗 | 并发数 | 目的 |
|---|---|---|---|
| 基线 | 0-2h | 1 | 建立单请求性能基准,排除冷启动影响 |
| 爬坡 | 2-8h | 1→16 线性递增 | 找吞吐拐点和延迟开始劣化的临界点 |
| 稳态 | 8-18h | 16 恒定 | 长时间稳定性,盯显存泄漏和温度 |
| 尖峰 | 18-22h | 16→32 脉冲 | 短时过载表现,看恢复能力 |
| 收尾 | 22-24h | 16→1 递减 | 观察资源释放是否干净 |
这个设计的关键在爬坡阶段。线性递增的好处是能看到延迟曲线的形状:一开始请求稀疏,延迟基本等于单次推理时间;并发加上去之后,请求开始排队,延迟上升;到某个点会突然陡增,那就是拐点。拐点之前的并发数才是"安全并发",之后的都是拿延迟换吞吐。
我这次实测的拐点在18 到 20 并发之间。16 并发时 P95 延迟还在 4 秒左右,到 20 并发就跳到 7 秒以上,24 并发直接突破 12 秒。所以稳态阶段我把并发定在 16,留了一点余量。
3.2 请求体怎么构造才像真实业务
这一块如果做假了,整个压测的数据就废了。我用的是任务样本池随机抽取 + 长度分层的方式。
样本池里放了五类任务:短问答(100-300 token 输入)、工具调用(300-800,带结构化参数)、文档摘要(2000-4000)、多轮对话续写(累计 1000-6000)、长文档分析(5000-8000)。比例按真实使用分布来配,大概 40% 短问答、25% 工具调用、20% 摘要、10% 多轮、5% 长文档。
输出长度也要控制。统一按 256 token 上限跑会导致数据好看但不真实——实际业务里输出长度是波动的,有时候一句话就结束,有时候要写上千字。我设成按任务类型随机 64 到 1024 token,并且开启流式输出,这样才能真实测到首 token 延迟(TTFT)这个关键指标。
还有一个细节:请求之间要有间隔抖动。如果所有请求严格同时发出,测出来的是理论峰值;真实的用户请求是泊松分布的,有疏有密。我在请求生成器里加了 50 到 500 毫秒的随机间隔,让到达曲线更接近真实流量。
3.3 采样频率与数据落盘
采样频率这道题很多人会做错。采太密,数据量大到没法分析,而且采集本身消耗资源影响被测对象;采太疏,尖峰被平滑掉,什么都看不见。
我的设置是分层的:系统层指标(显存、温度、功耗、利用率)每 2 秒采一次,因为变化快,需要细粒度;服务层指标(吞吐、延迟分位)每 10 秒聚合一次,按滑动窗口算;应用层指标(任务完成率、重试次数、token 消耗)每个请求记录一条,最后汇总。
落盘策略上,我用了原始日志 + 分钟级聚合双写。原始日志保留全部请求明细,方便事后追查单个异常;聚合数据用来画曲线,文件小、加载快。24 小时下来,原始日志大概 2.3 GB,聚合文件不到 5 MB。
提示:日志一定要写到独立磁盘或者至少独立分区。我第一次跑的时候日志和模型文件在同一个盘,写到第 14 小时磁盘满了,推理服务直接报错退出,白跑半天。
4. 监控链路搭建:看不见的指标等于没有
压测做到一半最怕的情况是:机器变慢了,但你不知道从什么时候开始慢的、慢在哪个环节。所以监控必须提前搭好,而且要在正式跑之前先空跑半小时验证采集正常。
4.1 系统层:显存、温度、功耗、降频
这四项是硬件健康的晴雨表。显存看的是绝对值 + 增长斜率,绝对值判断是否接近上限,斜率判断有没有泄漏——如果稳态阶段显存每小时涨 200 MB,24 小时就是 4.8 G,必爆。
温度要分核心温度和热点温度看。核心温度是芯片整体的,热点是局部最热区域的,后者通常高 10 到 15 度。判断是否降频要看频率是否随温度下降,而不是只看温度数字。我这次设定核心 83 度、热点 95 度为警戒线,超过就开始记录降频事件。
功耗这一项很多人不采,但它其实很有用:功耗异常下降往往意味着降频或者部分单元空闲,是一个综合信号。我这次全程功耗稳定在 330 到 350 W 之间,只有在收尾阶段并发降下来之后才明显下降,说明中间没有出现异常的降频。
4.2 服务层:吞吐、延迟分位、队列深度
吞吐用每秒生成的 token 数衡量比用请求数更准,因为请求长度差异大。延迟必须看分位数,平均值毫无意义——平均 3 秒可能是 95% 的请求 1 秒加 5% 的请求 40 秒。
我盯的三个数是:TTFT 的 P50 和 P95、端到端延迟的 P95 和 P99、队列深度。队列深度这个指标特别有用,它是并发压力的直接体现,队列一长说明处理不过来,接下来延迟必然劣化。
实测稳态阶段(16 并发):TTFT 的 P50 约 0.35 秒,P95 约 1.2 秒(长 prompt 拉高了尾部);端到端 P95 约 4.1 秒,P99 约 8.7 秒;队列深度基本在 0 到 2 之间波动。这组数字我认为是可以接受的,用户的感知延迟主要落在首 token 上。
4.3 应用层:Agent 循环、工具调用、token 账本
这一层是智能体特有的,普通推理服务不需要,但跑 OpenClaw 就必须盯。
循环步数是第一位的。智能体最危险的失败模式是陷入"调工具 → 结果不满意 → 再调 → 还不满意"的死循环,一次任务烧掉几万 token,既慢又贵。我给每个任务设了硬上限:最多 8 步,超过强制终止并返回中间结果。
工具调用成功率是第二位的。如果某个工具持续失败,框架会不停重试,表现出来就是吞吐虚高但任务完成率低。我按工具维度统计成功率,跑下来文件读写类 99.2%,结构化解析类 96.5%,后者偏低是因为有些输入本身格式不规范。
token 账本是第三位,记录每个任务消耗的输入输出 token。这个数据直接决定了成本估算,也能暴露出异常任务——比如某个任务消耗了平均值的 20 倍,那大概率是循环没刹住。
5. 24 小时实录:每个阶段发生了什么
前面都是准备工作,这一节是真正跑起来之后的情况。我按时间段把关键现象和判断记下来,附上当时的处理动作。
5.1 0-2 小时:基线阶段
刚启动的前 20 分钟有个明显现象:首次请求特别慢,TTFT 超过 8 秒。这不是性能问题,是模型权重和计算图第一次加载,之后就走缓存了。所以基线段我特意留了 20 分钟预热,不计入统计。
预热完成后,单请求(并发 1)的表现相当稳定:短问答的 TTFT 在 0.18 到 0.25 秒之间,输出速度大概 68 到 75 token/s;长文档分析(7000 token 输入)的 TTFT 涨到 2.4 秒左右,因为要先把整个上下文编码一遍。这个 2.4 秒是后面所有延迟分析的基准线。
显存占用在这个阶段稳定在 8.1 GB 左右(权重 5.6 + 单请求 KV + 开销)。温度爬升到 62 度后基本不再涨,风扇转速 55%。一切正常。
5.2 2-8 小时:爬坡阶段
线性爬坡从并发 2 开始,每 12 分钟加 1,到第 8 小时刚好到 16。这条曲线跑出来信息量很大。
并发 1 到 6 这一段,吞吐几乎线性增长,延迟基本不变。说明这个区间内请求根本不需要排队,来一个处理一个。
并发 7 到 12,吞吐继续涨但斜率变缓,TTFT 的 P95 从 0.4 秒涨到 0.9 秒。这是因为批处理开始起作用了——多个请求被合到同一个批次里算,单次计算时间变长但总吞吐提高,属于正常现象。
并发 13 到 16,吞吐增长明显放缓,延迟开始有感知。16 并发时总吞吐大概 420 token/s,而 12 并发时是 380 token/s,多 4 个并发只换来 10% 的吞吐提升,延迟却涨了 60%。这就是典型的投入产出比恶化区间。
显存占用在这个阶段从 8.1 GB 爬到 19.7 GB,涨幅主要来自 KV Cache。这里有个值得注意的细节:显存不是均匀上涨的,而是阶梯式跳变——因为 KV 空间是按块分配的,每满一块才分配新的一块。所以看到曲线突然跳高 512 MB,不用紧张,那是正常的块分配。
5.3 8-18 小时:稳态阶段
最考验人的十个小时。这段时间并发恒定 16,理论上所有指标应该走平。
实际情况是:前 4 小时完全平稳,各项指标波动都在 5% 以内。第 5 小时开始出现一个缓慢的趋势——显存每小时涨 120 MB 左右。虽然涨得慢,但方向明确,不是随机波动。
我当时的处理是继续观察而不是立刻重启。因为重启会打断测试,而这个涨幅在 10 小时内总共只有 1.2 GB,不会立刻爆。结果到第 12 小时,涨幅自己停了,显存稳定在 21.3 GB,一直保持到稳态结束。
事后分析,这 1.2 GB 大概率是碎片而不是泄漏。原因是请求长度不均匀,长短请求交替导致 KV 块分配释放不规整,产生了无法复用的空洞。这不算 bug,但提醒了一件事:跑长时间服务,显存要留 15% 以上的余量给碎片,不能算到刚刚好。
温度方面,稳态阶段核心温度在 74 到 78 度之间波动,热点 89 到 92 度,风扇 88% 到 95%。频率全程没有下降,说明散热压住了,没触发温度墙。功耗稳定在 345 W。
5.4 18-22 小时:尖峰阶段
这一段是故意找麻烦。从 16 并发开始,每 15 分钟来一次 3 分钟的 32 并发脉冲,一共四次。
第一次脉冲的反应很典型:并发一加上去,队列深度从 2 秒内冲到 28,TTFT 的 P95 直接飙到 14 秒,端到端 P99 突破 30 秒。更关键的是恢复时间——脉冲结束后,队列需要大概 90 秒才排空,指标回到正常水平。
这个 90 秒的恢复时间是有意义的。它说明服务有一定的缓冲能力,短时过载不会导致雪崩,但如果尖峰持续时间超过 3 分钟,队列会积压到不可接受的程度。所以生产环境里,过载保护必须在队列深度超过阈值时就开始拒绝或降级,而不是等延迟已经烂了再动手。
第二次到第四次脉冲,表现基本一致,没有出现累积恶化——说明资源释放是干净的,没有因为过载留下后遗症。这一点比峰值数据更重要。
5.5 22-24 小时:收尾阶段
并发从 16 逐步降到 1,重点观察两件事:显存能不能还回去,延迟能不能回到基线。
显存方面,从 21.3 GB 降到 8.4 GB,但没有回到初始的 8.1 GB,差了 300 MB 左右。这个差值一直保持到测试结束。我的判断是正常的运行时缓存——推理引擎会保留一些预分配的缓冲区不释放,为了下次请求能快速响应。300 MB 在 24G 卡上占 1.2%,可以接受。
延迟方面,回到并发 1 之后,TTFT 是 0.21 秒,比基线的 0.18 到 0.25 中位数还略好一点。说明长时间运行没有导致性能衰退,这是个好消息。
整个 24 小时累计处理请求约 8.7 万个,生成 token 约 2100 万。电费按 350 W 平均功耗算,24 小时 8.4 度,不到六块钱。
6. 六个坑与排查速查表
这一节是踩坑记录。有些是这次新遇到的,有些是老问题在新场景下的变种,都值得记下来。
6.1 显存只涨不跌
这是最常见的问题,但原因不止一种,得分开看。
第一种:真的有泄漏。特征是线性稳定增长,重启就恢复,跑久了必爆。定位方法是看增长斜率是否恒定,如果每小时固定涨 X MB,那基本确定。常见来源是框架层的引用没释放,或者请求对象被全局缓存持有。
第二种:碎片累积。特征是有涨有停,达到某个值后稳定。这是长短请求混合导致的,处理办法是调整 KV 块大小,或者定期做一次"整理"(清空缓存重新预热)。
第三种:长请求堆积。特征是与请求长度分布强相关,长请求多的时候涨得快。这种不是问题,是正常现象,只要总量在预算内就行。
判断顺序建议:先看斜率是否恒定(区分泄漏和碎片),再看是否与请求长度相关(区分堆积),最后再动代码。
6.2 长上下文下的性能断崖
这个问题我这次专门测了。从 2K 上下文逐步加到 8K,观察 TTFT 的变化。
结果是非线性的:2K 到 4K,TTFT 从 0.6 秒涨到 1.1 秒,接近线性;4K 到 6K,涨到 2.0 秒,开始超线性;6K 到 8K,直接跳到 3.8 秒。最后这一段涨幅明显不正常。
原因是注意力计算的复杂度随长度呈平方增长,而且长上下文会撑爆缓存层级,导致显存访问效率下降。实践上的应对办法不是优化算法,而是在应用层做分块:超过 5K 的文档先切分成段落分别处理,再合并结果。这样虽然多花几次调用的时间,但总延迟反而更低,而且质量更稳定——长上下文里模型容易"忘记"中间部分的内容。
6.3 温度墙与降频
消费级卡在持续满载下很容易撞温度墙。我这次没撞上,但做了压力试验:把风扇手动锁到 60%,跑 30 分钟,核心温度冲到 86 度,频率从 2.5 GHz 掉到 2.1 GHz,吞吐直接降了 18%。
这个实验说明一件事:散热余量就是性能余量。如果机箱风道不好,或者环境温度高,你可能在 12 小时之后就进入降频状态,但因为没有监控频率,只感觉"越跑越慢",找不到原因。
实操建议:跑长时间任务之前,先做一次 30 分钟满载测试,记录稳定后的温度和频率。如果核心温度超过 80 度,先解决散热再谈压测,否则数据没意义。我这次把机器挪到阳台、拆了侧板、加了一个辅助风扇,稳定温度比放屋里低了 9 度。
6.4 Agent 空转烧 token
这是智能体特有的坑,也是最烧钱的。表现是某个任务迟迟不结束,token 消耗快速上涨。
我这次抓到一次:一个文件解析任务,因为输入格式不规范,工具返回了错误信息,框架判断"重试可能有不同结果",连续重试了 11 次,消耗了 1.8 万 token 才被步数上限拦住。这 11 次重试全部是无效的,因为输入本身就有问题,重试多少次都一样。
改进办法有两个:一是给重试加条件,同样的错误连续出现两次就判定为不可恢复,直接失败返回;二是给单个任务的 token 消耗设上限,超过就终止。这两条加上之后,后续 12 小时没有再出现类似情况。
6.5 磁盘与日志
前面提过一次,这里展开说。日志写满磁盘是个低调的杀手,因为它不会立刻报错,而是先让写入变慢,然后服务开始异常,最后进程被杀。
我的教训是第 14 小时磁盘满了,推理服务直接崩。事后算了一下,原始请求日志每条平均 28 KB(包含完整 prompt 和输出),8.7 万条就是 2.3 GB,加上框架自己的运行日志、系统日志,总共 4 GB 多。如果盘小,撑不过一天。
处理办法:日志按小时切分,超过阈值的旧日志自动压缩归档;关键指标单独走一条轻量通道,不跟详细日志混在一起。这样即使详细日志出问题,监控数据还在。
6.6 问题速查表
| 现象 | 最可能原因 | 快速验证 | 处理 |
|---|---|---|---|
| 显存线性上涨 | 引用泄漏 | 重启后是否恢复 | 排查框架层缓存 |
| 显存阶梯跳变 | KV 块分配 | 跳变是否规律 | 正常,无需处理 |
| 显存涨到某值停住 | 碎片累积 | 长短请求比例 | 调块大小或加余量 |
| 延迟突然整体上升 | 温度降频 | 看频率曲线 | 改善散热 |
| 延迟随长度非线性涨 | 注意力平方复杂度 | 分段测 TTFT | 应用层分块 |
| 队列深度持续大于 10 | 过载 | 看并发数 | 限流或降级 |
| 任务不结束且 token 暴涨 | 重试死循环 | 看步数统计 | 加重试条件和上限 |
| 服务无故退出 | 磁盘满或内存不足 | 看系统日志 | 日志分离、限制内存 |
| 吞吐高但完成率低 | 工具持续失败 | 按工具统计成功率 | 单工具隔离重试 |
7. 压测之后:这套组合能上什么场景
跑完这一天,我对"9B 模型 + 智能体框架 + 单张 24G 卡"这个组合的能力边界有了比较清楚的认识。这里说说我的判断,都是基于实测数据的,不是拍脑袋。
7.1 可以直接用的场景
个人或小团队的知识库问答。16 并发、P95 延迟 4 秒左右,支撑十几个人同时用完全够。8K 上下文能塞进一份中等长度的文档,配合分块检索效果更好。这类场景不需要极致延迟,稳定比快更重要。
本地化的文档批处理。比如批量提取合同要素、批量生成摘要、批量做格式转换。这类任务可以串行跑,用不到并发,单请求 70 token/s 的速度处理一千份文档大概几个小时,晚上挂机第二天收结果。数据不出本地这一点对很多场景是刚需。
开发阶段的智能体调试。在自己机器上跑,改一行代码就能看到效果,不用等云端部署。这个价值其实很大——智能体的行为调优需要大量试错,反馈循环越短越好。
7.2 必须加护栏的场景
面向外部用户的服务。这台机器的能力上限就在 16 到 20 并发之间,超过就雪崩。如果对外开放,必须在前置层做限流、排队、降级,不能让请求直接打到推理服务。而且要有熔断机制,队列深度超过阈值就快速失败,别让用户干等 30 秒。
长文档处理。8K 上下文是个硬边界,超过就得切分。而且前面测过,6K 以上性能断崖明显,实际可用区间是 5K 以内。如果业务里有大量长文档,要么上更大的上下文配置(但这会吃掉并发),要么在应用层做好分块和结果合并。
高精度结构化输出。Q4 量化的模型在参数提取上还有 3 到 5 个百分点的损失,而且偶尔会漏字段。如果业务对准确率要求极高,要么上更高精度的量化(牺牲显存和速度),要么加一层校验和重试。
成本敏感的场景。这一点容易被忽略。本地跑看起来不要钱,实际上电费、硬件折旧、维护时间都是成本。如果每天只是偶尔用几次,云端按量付费反而更划算。本地的优势在于数据不出门和长期高频使用,不在于绝对便宜。
跑完这次测试,我最大的感受是:压测的价值不在于跑出多高的数字,而在于让你知道边界在哪里。知道 16 并发是安全线、知道 5K 上下文是性能拐点、知道显存要留 15% 给碎片,这些具体的数字比任何"性能优异"的结论都有用。它们让你在设计方案的时候不用猜,可以直接按数据来配。
还有一个体会是关于"稳定"这件事的定义。跑一小时不崩不叫稳定,跑一天不崩、延迟不劣化、显存不失控、任务完成率不掉,才叫稳定。而要验证这些,除了老老实实挂机 24 小时,没有捷径。