资讯动态

大模型推理历史数据:采集、存储与分析实战指南

发布时间:2026/10/19 9:27:12 来源:尧图企业网站定制
做推理服务也有一阵子了今天想聊聊推理历史数据这件事。很多人一说大模型注意力全放在模型训练、微调、Prompt工程上可真把模型部署到线上以后最让人头疼的问题是线上推理到底发生了什么。GPU有没有吃满用户为什么感觉变慢了哪个业务线一个月烧掉了80%的token预算这些问题光靠拍脑袋和看监控大屏是回答不了的必须回到一条条真实的请求记录里去找答案。大模型推理历史数据说白了就是一次请求从进来到返回整个生命周期里留下的各种痕迹请求时间、输入输出token数、排队耗时、预填充耗时、解码耗时、显存占用、显存峰值、命中缓存情况、错误码、模型版本、业务标签等等。这些数据单看一条没什么但积少成多之后就能拼出一张完整的线上运行图。今天我就围绕这个东西把采集、存储、分析、优化和避坑从头到尾讲一遍适合正在做推理服务开发、SRE和数据工程的人参考。1. 大模型推理历史数据都包含哪些信息1.1 一次推理请求的完整数据轨迹一条提示词发到推理服务之后不是一个瞬间就出结果的。在它返回第一个token之前背后要走完网络接入、鉴权、排队、预填充、逐步解码、后处理、返回响应这一整段链路。每一段都会产生数据也都值得记录。网关层会记下请求到达时间、来源IP或租户标识、请求路径、鉴权结果、响应码和耗时。这里的数据主要服务于外部观测和访问控制。推理引擎层会记下更细的信息比如排队等待时间、批量大小、输入token数、预填充耗时、生成token数、解码耗时、总耗时、GPU编号、显存峰值、是否命中了前缀缓存、是否因超长被截断、有没有报错。业务层则可以根据实际场景补充用户标识、业务线、应用类型、会话ID、使用了哪个模型版本、Prompt的文本摘要等。把这些字段串起来你就能回答很多运营层面的问题哪个业务的Prompt最长、哪个模型版本的P99延迟最高、某次故障波及了多少租户、扩容到底应该扩在哪个GPU池子。我见过不少团队一开始只记录了请求数和平均延迟两个指标后来线上出了偶发超时排查了整整两天最后才发现是某个特殊构造的Prompt触发了超长解码而这在平均值里根本看不出来。如果从一开始就把每一条请求的关键轨迹记录下来这种问题定位就是一条SQL的事。1.2 历史数据为什么值得你认真对待你可能觉得推理服务能跑就行日志有没有无所谓。但如果线上服务出了问题或者你想把成本压下来历史数据就是唯一的证据。推理历史数据的价值集中在三块。第一是故障排查任何偶发性问题离了日志和链路数据都很难复盘尤其是GPU显存溢出、引擎重启这类看似随机的问题。第二是性能优化延迟和吞吐的瓶颈到底在排队、预填充还是解码只有把耗时拆开统计才能知道。比如你发现P99解码耗时远高于P50说明有部分请求的生成长度异常可能触发了长输出也可能是并发抖动。第三是容量和成本规划没有历史水位数据你根本不敢拍板该上几块卡该做多少冗余只能靠猜。另外历史数据也是模型迭代的客观裁判。每次上线新版本除了看评测集分数更要看线上同流量下延迟、错误率、输出长度分布的变化。这些判断没有历史日志支撑就是拍脑袋。2. 推理历史数据如何采集和存储2.1 三条采集路径网关、引擎、业务采集路径决定了数据覆盖的完整度。我只说最核心的三条。路径一是入口网关。通常推理服务前面都会有一层API网关负责鉴权、限流、路由和响应记录。在这里可以把每一条请求的基础信息打成结构化日志包括请求ID、来源标识、访问的模型名、耗时、状态码。如果网关层有日志但没结构化尽快改造否则后续分析会非常痛苦。路径二是推理引擎本身。现在主流的推理框架都会暴露性能和统计接口也能通过各种方式把日志接到标准输出或者外部收集器。你需要关心的字段主要是排队等待时长、预填充耗时、单步解码耗时、生成token数、显存占用情况、缓存命中率。注意这一层的数据往往只在进程内部可见必须主动透传出来。路径三是业务侧埋点。这条最容易被忽略但对分析业务场景价值最大。比如你在请求体里带上业务线标识、用户群标签、会话上下文长度分析时就能区分“是某个大客户把系统拖慢”还是“全局真实负载升高”。建议在入口处定义一个统一的透传协议比如在HTTP Header里加一个业务ID字段网关和引擎把它透传到日志里。三条路径的数据最好通过同一个请求ID串联起来。如果没有全局的请求ID后面做链路分析时会非常痛苦我后面会专门讲这个坑。2.2 日志Schema设计的要点日志字段不是越少越好但也绝对不是越全越好。关键在于先想清楚你要回答什么问题再决定要留哪些字段。就推理场景来说下面这个JSON格式的日志示例基本保留了所有核心要素同时又不冗余{ request_id: req_20250614_000123, timestamp: 2025-06-14T10:15:30.123Z, model_name: demo_model, model_version: v2.3.1, business_line: finance_ai, user_tag: u_8f3k2a, input_tokens: 156, output_tokens: 234, queue_wait_ms: 12, prefill_time_ms: 45, decode_time_ms: 880, total_latency_ms: 945, gpu_id: 0, batch_size: 8, cache_hit: true, error_code: 0 }设计Schema时有几个要点时间戳必须统一使用UTC并且带毫秒甚至微秒精度。跨时区、跨机器的日志如果格式不统一后面做窗口聚合会直接错乱。数值字段单位要固定。耗时统一用毫秒token数统一用实际token数而不是字符数。不同团队很容易混入微秒、秒、字符数这种不同口径。user_tag、business_line这类字段要提前做好脱敏和映射不能直接把明文手机号、邮箱写进日志。error_code要约定一套内部码0代表成功非0代表具体错误类型比如显存不足、长度超限、超时、引擎异常。我早期在设计Schema时没把model_version放进去结果就是线上同时跑了三个模型版本出问题后完全不知道流量是哪一版接的。后来补上了这个字段排查效率高了很多。2.3 存储分层热数据、温数据、冷数据采集到的数据不能一股脑全塞进同一个存储成本和查询性能都会失控。我的做法是按数据温度分层。热数据指最近几小时的原始日志和指标主要用于实时监控和快速排障一般放在时序数据库或类时序的存储里保留1到7天。这一层的特点是写入量大、查询窗口短、对读写速度要求高。温数据指最近几周到几个月的明细数据主要用于性能分析、成本核算、容量规划可以放到列式存储或数据仓库中保留90天到一年。这一层的数据已经经过了清洗、补全、去重并且通常做成分区表按天或按月分区查询基本是扫描一个时间范围。冷数据指超过一年的归档数据目的是满足审计、长周期趋势回溯等需求可以压缩后放到对象存储里存成列式文件比如Parquet格式。查询频率很低甚至可以不做在线索引。分层不是一句空话。我见过一个团队把全量明细日志堆在一个系统里结果每天新增几十GB数据一个月后查询慢得像蜗牛存储费用还在飙升。后来改成“热数据7天、温数据90天、汇总数据永久保留”的策略查询速度上去了账单也降下来了。3. 从历史数据里挖出关键性能指标3.1 延迟指标P99比平均值更值得看推理服务的延迟是最直观的用户体验指标但很多人只会看平均延迟。平均延迟一旦被长尾请求拉高就很容易掩盖真相。比如某天平均延迟只多了20毫秒你可能觉得没什么但P99可能已经翻倍。用历史数据算百分位延迟是一种很基础但极有效的做法。假设你有一张日志表其中total_latency_ms是每次请求的总耗时那么用如下SQL就能把P50、P95、P99都看一遍SELECT percentile_cont(0.50) WITHIN GROUP (ORDER BY total_latency_ms) AS p50, percentile_cont(0.95) WITHIN GROUP (ORDER BY total_latency_ms) AS p95, percentile_cont(0.99) WITHIN GROUP (ORDER BY total_latency_ms) AS p99 FROM inference_logs WHERE ts now() - interval 1 hour;在排查性能问题时你应该按“输入token区间”和“输出token区间”做分组汇总因为不同长度的请求延迟差异巨大混在一起看没有意义。比如同样一批请求输出长度在50个token以内的P99可能是0.8秒而输出长度在500个token以上的P99可能到8秒这两类请求的体验问题完全不同。只看延迟还不够最好把延迟分解成排队等待、预填充、解码三个阶段这样你能定位到瓶颈是调度不足还是长输出拖垮了解码阶段。我见过一个服务P99高得吓人分析后才发现90%的时间耗在排队等待因为并发池配小了GPU明明很闲但请求进不来。3.2 上下文长度和显存占用怎么挂钩大模型推理时显存消耗与序列长度是强相关的。每多一个token就会多一份对应的KV Cache和激活值。通过历史数据你可以精确算出现有流量对显存的要求为卡型选型和并发数配置提供依据。最直接的做法是对input_tokens和output_tokens做分桶统计分析。把请求按输入长度分成0-1K、1K-2K、2K-4K、4K-8K之类的区间再看每个桶里请求数占比、平均延迟、显存峰值。如果发现大部分请求集中在2K-4K那么你就可以把最大并发和最大序列长度配置得更激进一点。你还可以把显存峰值和上下文长度生成散点图确认在哪个长度区间显存开始突增。举例来说某次统计数据里输入长度2K到4K的请求显存峰值明显跳变原因是该区间正好触发了更大的Batch合并窗口导致KV Cache总量暴涨。这个现象在平均值曲线里完全看不出来。计算显存本身也有公式可循。以推理引擎在运行时的KV Cache占用为例它大致等于层数乘以层内头数乘以头维度乘以序列总长度乘以字节数再乘以批量大小。你不需要记住公式但你要知道批量大小和序列总长度是影响显存的两个乘数因子。历史数据能帮你反推出这两个因子的实际分布从而找到平衡吞吐和稳定性的调度参数。3.3 KV Cache命中率优化空间的藏宝图大模型生成过程里已处理过的历史token会以键值对形式缓存下来也就是常说的KV Cache。如果两条请求的前缀完全相同就可以复用前面的缓存大幅节省预填充阶段的算力开销。这个命中率的高低直接决定了你的推理服务到底有多“聪明”。历史日志里需要专门记录一列cache_hit或者cache_prefix_length。比如请求复用到了128个token的缓存记为true表示预填充阶段只处理了后续部分。没有这个字段你根本算不出缓存收益。统计命中率可以这样做SELECT model_version, avg(CASE WHEN cache_hit THEN 1.0 ELSE 0 END) AS cache_hit_ratio, sum(input_tokens) AS total_input_tokens FROM inference_logs WHERE ts now() - interval 1 day GROUP BY model_version;如果命中率长期偏低你就要考虑是不是请求公共前缀太少或者提示词模板本身不够统一。命中率偏高的场景通常是有大量多轮对话或固定系统提示词的业务比如客服助手、文档问答、代码补全助手。实际优化中不只是二值的命中与否更值得看的是命中前缀长度分布。有的请求虽然没有完整命中但命中了前64个token也能减少不少计算量。把这部分统计出来才能客观评估缓存策略值不值得投入。3.4 错误码与异常请求的关联分析线上没有不出错的服务推理系统常见的异常有显存溢出、长度超限、推理引擎重启、超时、并发限制等。如果你只盯着成功率很容易忽略某些带倾向性的故障。我建议把所有错误码做成一张维度表每个错误码对应一个描述然后在日志中记录error_code。之后你就可以做关联分析比如按时间窗、按模型版本、按业务线、按输入长度区间统计错误分布。举一个实际例子某次故障后分析日志发现某个业务线的错误码中长度超限的占比从2%涨到了20%。再细看是因为该业务新上了一个自动摘要功能经常发送长文档。没有历史数据你怎么找都想不到是上游功能变更导致的。还有一次是显存溢出的错误码集中在某个GPU编号上而且都发生在同一个时间段。后来一查是因为某个推理进程的内存碎片化已经被历史数据反复提示过了只是当时没人去认真看。错误分析时要特别注意不同错误码的时间戳可能不同是请求进入时报错还是生成中途报错直接影响分析口径。最好在error_code之外再加一个error_stage字段用来标注错误发生阶段。4. 用推理历史数据反哺系统优化4.1 基于历史趋势做容量规划容量规划最忌讳的就是拍脑袋。你不知道峰值是多少你就不知道该买多少GPU也不知道要不要在晚间开启额外的缓存节点。历史数据的强项就是能画趋势图。从日志中按分钟聚合QPS和GPU利用率你可以很清楚地看到一天内的波峰波谷也能看到周末和工作日的差异。有了这个趋势你可以制定规则根据过去30天的P99 QPS预测未来7天的峰值需求。在预测峰值前30分钟提前扩容GPU实例或预热缓存池。当QPS连续15分钟低于水位阈值时自动缩容节省成本。扩容决策不能只看QPS因为推理请求消耗的资源差异很大。更好的做法是统计“模型总吞吐负载”比如每分钟处理的总token数结合当前GPU池子的总算力来反推是否够用。4.2 语义缓存与提示词压缩的收益测算大模型推理的成本大头集中在预填充和解码阶段。如果能把完全相同的请求直接命中缓存返回成本可以降到原来的十分之一甚至更低。语义缓存是最近很火的解法它的原理是把请求转成向量再在向量数据库里找相似请求看是否可以直接复用结果。但做语义缓存之前你得先回答值不值得做。历史数据能给你答案统计完全重复和高度相似的请求比例。如果占比较低做缓存收益有限如果占比较高那就要尽快做。提示词压缩也类似。你可以从历史日志中找出平均输入长度偏长的业务分析压缩后是否能显著减少预填充耗时。我做过一次压测复核某个业务平均输入长度5000多token其中一半是历史对话记录压缩后预填充时间降低了40%。但要注意压缩带来的质量损失需要单独评测不能只看性能。4.3 模型路由和新版本灰度评估线上不同业务对模型能力的需求并不一样。有些是简单分类、抽取用小模型足够有些是复杂推理、长文写作必须用大模型。如果所有流量都打到最大的模型上成本会非常难看。用历史数据做模型路由核心是建立请求特征和效果指标之间的关联。比如你可以在日志里记录每类业务的实际输出长度、延迟和人工反馈分然后评估小模型在该场景下的通过率。通过率达标就把这类请求路由到小模型。新版本上线前的灰度评估更能体现历史数据的作用。把新旧两个版本的日志按请求ID关联直接对比同一批请求在输出长度、耗时、错误率上的差异。我所在团队上线新版时通过历史数据发现新版本在长输入场景下decode速度反而变慢了于是把长输入流量继续留在旧版本避免了线上事故。4.4 成本分摊与配额管理大模型推理往往是企业内部多个团队共享的公共服务。每个团队消耗的token数、GPU时长和延迟都不同如果不定量月底对账就是一笔糊涂账。历史日志里加上业务线字段后就可以按业务线汇总SELECT business_line, count(*) AS request_count, sum(input_tokens output_tokens) AS total_tokens, sum(gpu_id IS NOT NULL) AS total_gpu_slots, avg(total_latency_ms) AS avg_latency_ms FROM inference_logs WHERE ts now() - interval 30 days GROUP BY business_line ORDER BY total_tokens DESC;有了这个表内部成本结算、配额限制、优先级策略都变得有依据。比如给某个业务线设置月度token上限一旦达到80%就开始告警而不是等账单出来再互相推锅。配额管理还可以结合历史用量做弹性调整。有的业务旺季消耗高有的业务平时消耗低按固定配额不合理。用历史趋势估算下个月的预期用量按预期用量加20%的余量分配配额方案更符合实际。5. 推理历史数据实践中的常见坑与排障5.1 时间戳口径不一致排查了三天才发现如果你同时采了网关和推理引擎的日志尤其要注意它们的时间戳是不是同一套标准。我遇到过网关用本地时间引擎内部用了UTC两者相差八个小时结果流量高峰和GPU利用率高峰永远对不上。查了三天才在日志样本里发现多个不一致。解决办法也很简单统一在入口侧生成时间戳并让所有下游组件透传这个时间字段不要在各层自行打时间。日志Schema里明确约定timestamp必须是毫秒级UTC所有组件不得自行转换。同时部署监控探针定期检查跨系统时间偏差。5.2 采样率设太高长尾问题全被掩盖日志量一大很多人会选择只保留1%的采样日志。但推理服务的故障恰恰更容易发生在长尾请求上。你采样之后那些罕见的错误码、超长输出、显存峰值可能全部被丢掉。我的建议是核心指标和错误日志全量保留非核心流水日志可采样。也就是说基础的成功请求可以按10%采样但错误、超时、长输出比如超过预设阈值必须是100%记录。抽样规则里引用的阈值来自对历史数据的观察不能拍脑袋。另外采样时不要只按请求数均匀采样可以考虑分层采样。比如保证每个业务线、每个模型版本、每个输入长度区间里都有一定量的样本。这样才能保证统计分析偏差不大。5.3 存储成本失控从一个月几块钱到一个月几千块大模型推理日志一天就能产生几十GB如果保留三个月原始数据存储成本快速增长是必然的。但历史数据又不能随便删所以关键在设计和治理。我的做法原始日志只保留7天超过后写入归档存储并压缩。聚合指标如P50、P95、P99延迟token数分布错误码计数按天汇总保留一年。对日志中不必要的字段做裁剪比如去掉请求体原文只留token数和摘要信息。使用列式存储和压缩格式能压缩掉70%的成本。5.4 隐私与合规历史数据不是留得越久越好历史数据里往往包含用户输入的内容这些内容可能有隐私风险。你不能为了分析方便就把用户对话无限制存下来。合规上建议按最小必要原则采集凡是不做分析、不用于排障的字段一律不采。比如请求体原文除非业务明确要求审计否则不必保留只保留脱敏后的摘要。用户ID也应做不可逆哈希后才进入日志不能出现明文手机号。数据保留期限也要明确。比如原始请求日志保留7天聚合数据保留1年超出期限自动删除。这条规则要在存储层就落地不能靠人工删。保存期限策略要写进文档并定期检查是否执行到位。我在实操中还养成一个习惯每次上线新功能、变更日志Schema之前先在预发环境里跑一遍样例日志确认字段值和单位都正确再上生产。别小看这一步它能省掉后续多少头疼的分析时间。说到底大模型推理历史数据这件事技术门槛并不高但它非常考验工程习惯。把每条日志的细节管好把每次分析的结论沉淀下来你的推理服务就越来越有“数”可依。等某天线上真的出故障时你会发现历史上存的每一条记录都在帮你还原一个本该早就看见的真相。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑