从零搭建企业级LLM监控系统:成本追踪与性能分析实战
发布时间:2026/10/1 19:17:40来源:尧图网络
过去大半年我一直在做一件事给公司的 LLM 应用搭一套企业级监控系统把成本追踪和性能分析从“靠猜”变成“看得见”。起因很朴素——财务拿着上个月的模型调用账单来问我这笔钱具体花在哪个业务线、哪个用户、哪次对话上了我答不上来运维又说接口响应忽快忽慢但没人能说清楚是模型推理慢、网络抖动还是 Prompt 太长导致的。如果你也在搞大模型应用大概率迟早会撞上这两堵墙模型调用费用高速增长但你算不清钱去哪了接口时快时慢但你定位不了瓶颈在哪。这篇文章不聊模型训练也不聊 Prompt 调优就聚焦一件事从零开始搭一套真正能用的 LLM 监控系统。我会给出完整的数据采集思路、自建方案的落地步骤、关键指标的计算口径以及我在生产环境里踩过的那些坑。适合正在负责 LLM 应用落地、被账单和性能问题困扰的工程师也适合想给团队建立一套规范监控体系的技术负责人。1. 先看清账本为什么 LLM 应用必须上监控很多人一开始觉得给 LLM 调用做监控不就是记个日志吗等真的上了生产才知道完全不是一回事。1.1 单次调用很便宜放量之后吓死人GPT-4o 刚出来那会儿单次调用几百 token 可能只要几厘钱做 Demo 的时候完全没感觉。但一旦接进生产系统几个业务线同时调用一天几万次请求一个月账单直接飙到六位数。而且更麻烦的是我当时的账单只有总额没有任何维度告诉你这笔钱是聊天机器人花的还是知识库问答花的哪个用户消耗最多。这就是 LLM 应用和传统服务最大的区别传统 API 的成本是相对固定的服务器成本、带宽成本而 LLM 的成本和每次请求的输入输出 token 强相关。用户多问一句废话Prompt 多塞一段上下文成本就上去了。如果你不做 token 级别的追踪成本失控是必然的。1.2 通用监控工具看不懂 LLM 的“语言”可能有人会说公司又不是没有 Prometheus 和 Grafana直接埋点不就行了问题在于传统监控只能告诉你“这个接口响应了 3 秒”但它解释不了为什么是 3 秒是网络传输慢还是模型推理慢是输入 Prompt 太长导致 prefill 耗时高还是输出 token 太多导致 decode 时间长模型在生成过程中有没有命中缓存缓存命中率是多少这些信息藏在 token、usage、模型版本、上下文长度这些字段里通用监控工具根本拿不到。你需要的是一套能理解 LLM 调用结构的系统——把每次调用拆成输入、输出、缓存、时延、成本几个维度去记录和分析。1.3 监控的四个层次成本、性能、质量、安全按我自己的实践LLM 监控可以分成四层。成本层每次调用的 token 消耗、缓存命中、金额按项目/用户/会话归因。性能层首 token 时延、端到端时延、生成速度、并发吞吐、错误率。质量层回答是否相关、是否遵循指令、RAG 引用是否准确。安全层Prompt 注入、敏感信息泄露、输出内容合规。大部分团队一开始只需要把成本层和性能层做扎实因为这两层直接决定了业务能不能持续跑下去。质量层和安全层可以后续逐步加。我的经验是监控系统不要一上来就追求大而全。先把“每次 LLM 调用的成本归因”和“关键性能指标的可视化”跑通让财务和运维都闭嘴再谈高级分析。本篇文章也重点围绕这两个方向展开。2. 监控系统的数据基座从一次 API 请求里能捞到什么聊完动机直接进入技术核心。搭建监控系统的第一步不是选工具而是定义数据模型。你得先想清楚一次 LLM 调用到底该抓哪些字段2.1 把每次 LLM 调用当成一条结构化事件我习惯把 LLM 调用想象成快递包裹每个包裹都有一张面单记录从哪里发、到哪里去、重量多少、运费多少。LLM 调用也一样必须有一条“面单”把所有关键信息都带上。一条完整的 LLM 调用记录至少要包含以下几类字段。维度字段用途调用基础request_id、API Key、模型名称、模型版本唯一标识一次调用快速检索Token 明细input_tokens、output_tokens、cache_read_tokens、cache_write_tokens成本核算的核心原料时延拆解ttft_ms、tpot_ms、total_ms、queue_ms定位慢在哪个环节状态信息status_code、error_type、retry_count错误分析与可用性统计上下文信息prompt_tokens含检索上下文、max_tokens、temperature分析 Prompt 长度对成本和时延的影响业务标签project、user_id、session_id、feature_name成本归因与行为分析流量信息is_streaming、source_ip、region判断场景差异流式/非流式这张表里的每个字段后面都会变成报表里的一个筛选条件。要特别强调project、user_id这些业务标签——没有这些标签你只能回答“花了多少钱”有了它们你才能回答“谁花的钱、为什么花这么多”。2.2 Trace、Metric、Log 三种信号怎么配合标准的可观测性体系讲究三信号协同LLM 监控也不例外。Trace链路追踪记录一次用户请求从网关到 LLM 网关再到模型服务的完整链路特别适合 RAG 场景能看出检索耗时、Prompt 拼接耗时、模型推理耗时分别占多少。Metric指标对上面提到的时延、token 数量、错误率做聚合统计生成趋势图和告警规则。Log日志保存完整的调用记录和业务标签用于事后排查和成本明细导出。我实际落地的做法是Trace 负责链路可视化Metric 负责实时监控和告警Log 负责明细查询。三者共用同一个request_id这样一旦指标异常可以顺着 Trace 找到链路再点进 Log 看细节整个排查链路是通的。2.3 一个最小可用的调用记录结构下面是我在生产环境里用的简化版 JSON 结构你可以直接参考。{ request_id: req_8f3a2b9c1d, timestamp: 2025-01-18T10:23:45.123Z, project: chatbot, user_id: user_1024, session_id: conv_5599, model: gpt-4o, model_version: 2024-11-20, streaming: true, usage: { input_tokens: 845, output_tokens: 312, cache_read_tokens: 0, cache_write_tokens: 0 }, latency: { ttft_ms: 580, tpot_ms: 15.2, total_ms: 3230, queue_ms: 40 }, status: { code: 200, error_type: null, retries: 0 }, meta: { prompt_length: 1200, max_tokens: 1024, temperature: 0.3 } }这一条 JSON 看起来简单但它承载的能力很强大按project筛能算各业务线成本按user_id筛能找到 Top N 消耗用户按model筛能对比不同模型的成本和时延按timestamp筛能看出一天之内的消耗波动曲线。在真正写采集代码之前先把这份 JSON 结构设计好后面对接所有数据源都会顺畅很多。3. 自建还是选开源方案选型与一条可落地的采集链路数据模型定了接着要回答一个务实的问题监控系统自己搭还是用现成的这取决于团队规模、预算和数据合规要求。我把几个常见选择放在一起对比一下。3.1 商业监控平台与开源方案怎么选目前市面上主流的 LLM 观测工具不少我实际接触过的有 LangSmith、Helicone、Langfuse、Phoenix以及基于 OpenTelemetry 的自建链路。它们各有侧重点。方案类型优势劣势LangSmith商业/SaaSPrompt 调试和 LangChain 集成体验好数据出网私有化成本高Helicone商业/SaaS代理网关模式接入简单成本统计直观深度定制能力有限Langfuse开源/可自托管完整支持 Trace、成本、评分、Prompt 管理需要团队自己运维基础设施OpenTelemetry 自建开源/完全自控数据完全在自己手里和现有监控体系统一需要大量工程开发工作我给团队的建议很简单如果是小团队、快速验证先用 Helicone 这类网关代理第二天就能看到成本报表如果公司已经有成熟的 Prometheus/Grafana/Loki 体系并且数据不能出内网那就在 OpenTelemetry 基础上扩展 GenAI 语义约定自建一套。3.2 我选择的自建路线FastAPI 中间件 结构化日志我的场景比较典型后端是 FastAPI统一走一个大模型网关封装所有业务线都通过这个网关调用模型。所以我选择了“中间件 结构化日志 ClickHouse 存储”的路线理由有三中间件模式不用改各业务线的代码侵入性最小。使用结构化日志作为采集通道天然兼容现有日志体系。ClickHouse 的列式存储特别适合这种“写多读少、按时间范围聚合”的查询模式。下面是一个 FastAPI 中间件的简化示例核心功能是调用业务处理函数从响应对象里提取 usage、latency然后写一条日志。from fastapi import Request from starlette.middleware.base import BaseHTTPMiddleware import time import json import logging logger logging.getLogger(llm_monitor) class LLMCallMiddleware(BaseHTTPMiddleware): async def dispatch(self, request: Request, call_next): start time.perf_counter() try: response await call_next(request) except Exception: # 异常也要记录方便统计错误率 raise finally: duration_ms (time.perf_counter() - start) * 1000 # 这里需要从 response/request 中提取 usage 等信息 # 因为不同框架的结构不同通常的做法是把 usage 放在 # 请求上下文或自定义响应头里然后在中间件里读取。 log_payload { request_id: request.headers.get(X-Request-ID, ), path: request.url.path, duration_ms: round(duration_ms, 2), timestamp: time.time() * 1000, } logger.info(json.dumps(log_payload))这段代码只是一个骨架真正生产环境里usage信息往往不在中间件能直接看到的地方。我的做法是在网关内部把 model、usage、latency 细节放到一个上下文对象里由网关调用链路的最后一环统一记录而不是在 HTTP 中间层硬捞。这个细节后面在踩坑部分会详细讲。3.3 存储与可视化选型存储方面如果你预算和技术储备都没有压力ClickHouse 是最好的选择如果只是想先跑起来PostgreSQL 也能扛住中小体量的写入。可视化层面我用的还是 Grafana。它不是专门为 LLM 设计的但通过定制 Panel完全能做出 Token 消耗趋势图、各业务线成本占比饼图、模型时延分位数图。关键在于上游数据质量要好下游展示只是水到渠成的事。提示不要把存储层做得太复杂。能先跑通“采集 - 存储 - 看板”闭环比一开始就堆一堆技术组件更重要。很多项目死在过度设计上。4. 成本追踪的颗粒度从每美元账单追踪到每个用户成本追踪是我做这套系统最初的目的也是我认为最有价值的部分。做的时候会发现这里面的坑比想象中多得多。4.1 Token 计价口径看懂模型的“运费规则”聊成本先得看懂计费规则。不同模型的计费逻辑差很多我用自己的理解做个类比LLM 的计费像快递运费首重输入 token一个价续重输出 token一个价有的还有“同城优惠”缓存命中。以通用的计算公式为例单次调用成本 input_tokens × 输入单价 output_tokens × 输出单价 cache_read_tokens × 缓存读取折扣单价 cache_write_tokens × 缓存写入单价不同厂商对缓存的定义差异巨大。有的模型是自动开启 Prompt 缓存同一个前缀的内容第二次调用就能命中有的需要显式传入缓存控制参数还有的模型默认不缓存需要你单独开。我踩过的坑是早期拿 OpenAI 的计费逻辑去套某国产厂商的模型导致成本统计整整偏了 20%。4.2 多模态与复杂场景的 Token 折算如果只是纯文本对话token 统计还算简单。但到了 2025 年多模态调用越来越普遍图片输入、音频输入、文档解析全都按 token 折算价格。不同厂商对图片尺寸的 token 折算算法完全不同有的按宽高像素折算有的按图片张数折算还有的按图像质量参数折算。我的建议是别自己试图硬编码这些计费规则否则模型一更新、价格一变你的折算代码就废了。正确的做法是维护一张“模型计价配置表”把模型的输入单价、输出单价、缓存单价、多模态折算规则都放进去每次厂商更新价格就只改配置表不碰监控代码。4.3 多维归因给每次调用打上“业务身份证”成本追踪的高级阶段是从“总额监控”进化到“多维归因”。要做到这一点必须在调用入口注入业务标签。我在网关层定义了三个强制标签所有业务线必须传project业务线名称例如 chatbot / rag_qa / content_analysis。user_id最终用户 ID方便识别大户和异常刷量。feature功能点名称例如 “文档问答”“摘要生成”。有了这三个标签成本看板可以做成下钻模式总成本 - 按项目 - 按用户 - 按会话。财务要数据时我直接导出一张表某项目、某用户的平均每次对话成本、Top 10 高消耗请求明细一清二楚。4.4 预算告警别等账单炸了才发现成本监控不能只做事后分析必须做事前预警。我配置了三级告警日级别告警当天消耗环比前一天增长超过 50% 时触发。小时级别告警每小时的消耗速率超过设定的阈值时触发。单次告警单次调用的成本超过某条线比如一次请求花了 5 美元时立即告警。告警推给企业微信或钉钉机器人一旦出现异常立刻下钻看是哪个项目、哪个用户在搞事情。这个机制帮我提前发现过两次事故一次是某业务线代码死循环反复调用模型另一次是测试环境忘了关把全量数据都送进模型跑批处理了。5. 性能分析TTFT、Token 吞吐和缓存命中率才算数说完了钱再来看性能。讲性能之前先说一个通用观点用户口中的“慢”是一个极其笼统的形容词。做监控的人必须把它拆解成可量化的指标。5.1 首 token 时延TTFT和逐 token 输出时间TPOT对于流式输出的 LLM 应用有两个指标最核心TTFTTime To First Token从发起请求到收到第一个 token 的时间。它反映了模型 prefill 和网络往返的速度是用户感知“响没响应”的关键。TPOTTime Per Output Token生成每个输出 token 的平均耗时也可以理解为生成速度。它反映的是模型 decode 阶段的性能。端到端总时延大致可以拆成total_ms ≈ queue_ms ttft_ms output_tokens × tpot_ms举个例子某次流式调用输出 300 个 tokenTTFT 是 0.8 秒TPOT 是 0.02 秒即每秒 50 token那么总时延约等于 0.8 300 × 0.02 6.8 秒。用户感知就是“等了好久才开始出字后面速度还挺快”。这种情况优化方向应该是降低 TTFT比如缩短 Prompt 前缀、开启缓存而不是压缩输出 max_tokens。反过来如果 TTFT 很短但 TPOT 很慢说明模型生成速度跟不上可能需要换更快的推理引擎或拆分请求。5.2 分位数比平均值更重要刚开始做监控的时候我看的是“平均时延”后来发现完全被平均骗了。某个时间段内 90% 的请求只要 1 秒剩下 10% 的请求要 20 秒平均值是 2.9 秒表面看还行但实际体验已经崩了。真正要看的指标是 P95、P99。我建议所有性能看板都按分位数展示重点盯 P99因为 P99 的尾部时延才是用户骂娘的地方。5.3 缓存命中率一个被低估的双料指标缓存命中率常常被忽略但它同时影响成本和性能。Prompt 缓存命中后onput token 价格通常只有原价的十分之一而且因为不需要重新处理整个 PromptTTFT 也会明显下降。我在实际系统里看到过一个数据开启 Prompt 缓存后某知识库问答接口的 TTFT 从 1.2 秒降到了 0.4 秒单次成本下降了 35%。所以我把“缓存命中率”做成一个独立看板按模型和项目两个维度展示。哪里命中率高说明 Prompt 复用做得好哪里命中率低就要去看看是不是 Prompt 前缀不稳定导致的。5.4 并发和错误率照看的两个“暗角”时延之外并发吞吐和错误率也是不能漏的。并发吞吐意味着系统能同时撑住多少路模型调用。很多模型网关对 Token 速率有峰值限制超过会返回 429 限流错误。我见过一个团队网关配置不当明明模型侧资源够却被自家的客户端连接池掐住了脖子并发一高全被 429。这个只有通过错误码和连接池监控才能发现。错误率则要细分 error_type是超时、限流、模型审核拒绝还是服务端 500。每一种错误的处理策略都不一样。我建了一个简单的分类表429 做指数退避重试5xx 做定时重试内容审核类错误直接返回给业务。6. 生产环境踩过的坑量错了成本也量错了性能接下来这部分可能是全文最有价值的部分。我把自己在从零搭建 LLM 监控系统过程中踩过的六个坑每条都写清根因和解法供大家参考。6.1 流式响应默认拿不到 usage成本统计直接少一大截第一次上生产时我发现很多流式请求的成本算出来是 0当场懵了。后来排查才发现OpenAI 的流式接口默认不返回 usage 字段需要在请求里显式加stream_options{include_usage: True}并且要在流结束的最后一个 chunk 里才能取到。更麻烦的是不同模型厂商的流式 usage 返回机制完全不同。有的没有这个参数有的放在了响应头里有的需要额外调一次查询接口。我的应对办法是在网关层统一封装建立“模型适配器”为每个模型写一个“从流中提取 usage”的处理器把差异收敛到网关内部上层业务无感。风险提示如果你们团队用的是 LangChain 或 LlamaIndex务必检查底层是否自动处理了流式 usage 提取。我遇到过框架版本升级后这个行为变了导致成本统计突然整体偏低的情况。6.2 deployment name 和 model name 不一致计价对不上Azure OpenAI 是最典型的例子。网关里看到的是MyGpt4Deployment但实际计价必须用底层模型gpt-4o。如果直接拿 deployment name 去匹配价格表要么查不到要么匹配到错误的型号成本归类完全乱掉。解法是在网关层维护一个 deployment - model 的映射表采集日志时同时记录两个字段计价时统一以底层 model 为准。6.3 上下文缓存计费规则没有对齐成本统计偏差 20%前面提到过不同模型的缓存计费规则差异巨大。有一次我把某一国产模型的“手动缓存”当成了“自动缓存”导致缓存命中的请求全部按全价计算成本虚高。后来逐条对比实际账单才发现缓存读取的单价只有输入单价的十分之一但必须显式传缓存控制参数才生效。我的经验是每当接新模型先拿一个固定 Prompt 反复调十次逐条核对厂商账单里的金额和你的统计金额误差超过 1% 就说明计费口径有问题一定要在正式上监控前对清楚。6.4 采样统计导致成本失真千万不能靠“抽样”为了省存储早期我想过只记录 10% 的调用日志。结果某天一个预算超支的请求正好没被采样到事后根本查不出是谁在烧钱。这个坑的本质是成本追踪不能采样必须全量。性能和链路可以按需采样但成本明细必须一条不落。因为成本归因要精准到每个用户、每次调用任何采样都会让追责和核算失去意义。解决办法是分级存储成本明细存全量存 ClickHouseTrace 和原始 Prompt 按业务需求采样存对象存储。6.5 LLM-as-judge 评估质量的隐形成本加了质量评估之后我发现成本又悄悄涨了一截。原因是用一个大模型去评估另一个大模型的输出每次评估都要额外消耗几十万 token。如果你拿贵模型做 judge一个月下来评估成本可能占模型总成本的 5% 到 10%。看板里必须单独列一个“评估成本”维度否则业务方会来质疑“这个月模型费用怎么涨了”。能省则省的做法是用小模型做定期抽样评估而不是实时全量评估。6.6 连接池配置不当压测时全被 429 掐住性能优化到最后发现瓶颈不在模型本身而是网关到模型服务端的 HTTP 连接池太小。高并发时大量请求在等空闲连接TTFT 飙升随后触发限流返回 429。这个问题在监控里表现为模型侧 CPU 还没吃满网关侧耗时曲线已经起飞。解决方式是压测时同时盯“连接池使用率”和“429 比例”两个指标动态调整 max_connections 和 max_keepalive_connections让连接池的规格和业务峰值匹配。7. 把监控数据用起来从成本归因到模型治理监控系统建好之后真正拉开差距的是能不能把这些数据变成业务决策。最后分享几个我验证过有效的“数据消费”方式。7.1 每周固定做一次“模型开销体检”我每周末会把本周的 token 消耗、成本分布、性能分位数跑一遍对比上周数据输出一份简短报告。重点关注三件事哪条业务线的成本环比增长超过 20%原因是什么。哪个用户的消耗异常有没有刷量或死循环风险。哪个模型的 P99 时延恶化是否需要和模型厂商沟通。这份报告我压缩到一页纸直接发给相关业务线负责人。不需要他们看懂技术细节只需要他们知道自己的消耗排名和优化空间。7.2 用成本数据反向驱动模型选型监控数据最有意思的价值是能验证模型选型是否合理。我有一次发现某个内容分类业务用 GPT-4o单次输入 token 不到 500输出也就 100 多但每次调用成本挺高换成便宜模型完全够用。替换之后该业务线成本直接下降了 80%准确率几乎没变。这种决策在监控系统建立之前是没人敢做的——大家不敢换模型是怕效果变差有了数据支撑后风险就完全可控了。7.3 用 Token 分布反推 Prompt 优化token 消耗分布也能反映很多问题。如果某个功能的输入 token 中 80% 是系统 Prompt 和检索上下文而用户真正输入只有 20%那么这个功能的成本大头其实在你的检索策略上。可以尝试压缩系统 Prompt、精简检索文档、设置上下文窗口上限成本立刻降一截TTFT 也会随之改善。我把这类优化总结成一个循环监控发现问题 - 归因定位 - 优化 Prompt 或模型 - 再监控验证效果。没有监控这个闭环根本走不起来。7.4 个人体会做这套系统前前后后花了大概三周踩坑的时间比写代码的时间多得多。但我始终觉得LLM 应用和传统应用最大的区别是它的成本和性能都太不透明了不搭监控就像蒙着眼开车只能靠翻车才知道路况。先在网关层把数据采集做扎实再建立一张能让财务看得懂的成本看板最后逐步加上性能和 SLO这套路确实走得通。
网站建设高端定制企业官网