恒美微站 Logo 恒美微站
  • 首页
  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心
  • 联系我们

GenAI应用可观测性实战:OpenTelemetry埋点与Token成本治理

  • 首页
  • 资讯中心
  • /
  • GenAI应用可观测性实战:OpenTelemetry埋点与Token成本治理

相关资讯

小红书小程序抓包实战:mitmdump 拦截与 CSV 落库去重 2026/10/8 16:42:10
AI日报实战:TPU、智能体与Claude Code/Codex工具链配置指南 2026/10/8 16:42:10
QuickBlue:企业AI应用底座如何破解重复造轮子困境 2026/10/8 16:42:10

最新资讯

文本编辑快捷键效率指南:从VC6.0到VS2008的TaoToken配置实践
AI Agent Harness Engineering 容量规划与弹性扩展:TaoToken 统一 Key 通道下的并发压测与自动扩缩容配置
【收藏必备】ChatGPT拥抱MCP:一条Prompt实现全自动化,小白也能轻松上手|TaoToken统一Key接入实战
【学习笔记】框架层坍缩——LangChain 们正在被重新定义-14/15:从 LangGraph 到 Harness,Agent 编排的下一站在哪
【Bug已解决】OpenClaw 报错 plugin load failed: dependency tree corrupted 解决方案:用 TaoToken 统一 Key 通道排查依赖树
JavaEE二手书交易系统:Servlet+JDBC完整电商闭环实现

今日推荐

context-mode实战指南:从全量塞入到结构化裁剪与检索增强
大模型对话上下文管理实战:三种模式与Token优化
抖音用户主页视频数据爬虫详解:点赞、收藏、分享字段抓取与 TaoToken 统一 Key 配置

本周热门

MR25H40CDF + PIC18F65K40:工业记录仪高可靠存储实战
基于STM32的数控恒压恒流电源设计:从硬件到PID调参全解析
LT9211 MIPI重定时器原理与双路扇出实战指南

本月精选

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证
2026 大模型集体涨价:用 Python 做企业 Token 成本测算与选型避坑(附配置)

GenAI应用可观测性实战:OpenTelemetry埋点与Token成本治理

发布时间:2026/10/8 16:47:11
GenAI应用可观测性实战:OpenTelemetry埋点与Token成本治理 1. 为什么 GenAI 应用需要一套专属的可观测性方案1.1 从传统微服务到 LLM 调用链监控对象变了做过几年后端监控的人对可观测性三件套——指标、日志、链路追踪——应该都不陌生。传统微服务里我们关心的是一个 HTTP 接口的 P99 延迟、数据库慢查询、下游服务超时率。这些指标有明确的物理含义耗时就是耗时错误码就是错误码一次请求要么成功要么失败边界非常清晰。但把同样的思路套到 GenAI 应用上会立刻发现不对劲。一次 LLM 调用从用户视角看只是问了一句话、等了几秒、拿到一段回答可中间发生的事远比一次普通接口调用复杂提示词被拼装、上下文被裁剪、模型被选中、Token 被消耗、流式响应被逐块吐回、可能还触发了工具调用或检索增强。这里面每一个环节都可能出问题而且问题的表现形式和传统服务完全不同。举个最典型的例子。传统接口超时你一眼就知道是下游慢了。但 LLM 调用慢可能是模型本身排队、可能是输入 Token 太长导致推理时间线性增长、可能是流式输出被网络卡住、也可能是你在应用层做了重试但没记录。如果你只盯着一个总耗时指标根本定位不到根因。这就是为什么 GenAI 应用需要一套专门的可观测性方案而不是把老一套监控直接搬过来。1.2 OpenTelemetry GenAI 规范解决了什么痛点OpenTelemetry 这几年在云原生可观测性领域基本成了事实标准它的核心价值是统一数据模型 厂商中立。而 GenAI 语义约定Semantic Conventions for GenAI是社区专门为生成式 AI 场景扩展出来的一套规范它把 LLM 调用、向量检索、Agent 执行这些新形态的操作抽象成了标准的 Span 和 Attribute。这套规范最实在的地方在于它定义了一批约定俗成的字段名。比如gen_ai.system标识模型提供方gen_ai.request.model标识请求的模型名gen_ai.usage.input_tokens和gen_ai.usage.output_tokens分别记录输入输出 Token 数。这意味着只要你的埋点遵循这套规范无论后端接的是哪家模型、前端用的是什么框架数据都能落到同一张表里做对比分析。我个人的判断是这套规范目前还在快速演进字段命名偶有调整但方向已经非常明确。早点按规范埋点后面迁移成本最低。如果你现在还在用自定义字段名记录 Token 用量等到要做跨模型成本对比时就会发现数据根本对不齐。1.3 谁最该关注这套方案不是所有用 AI 的团队都需要上这套东西。如果你只是调个 API 做个 demo那确实没必要。但以下几类场景我强烈建议尽早把可观测性搭起来多模型混用的应用同时接了多个模型提供方需要对比质量、延迟、成本没有统一埋点根本没法比。面向 C 端、有成本压力的产品Token 就是钱一次对话消耗多少、哪个功能最烧钱、有没有异常调用必须能实时看到。带 Agent 或 RAG 的复杂链路一次用户请求背后可能触发多次模型调用和检索链路一长出问题就是黑盒。需要做 SLA 承诺的商业服务你得能拿出数据证明你的响应时间和成功率。下面我会从整体设计、核心字段、实操埋点、成本治理、问题排查几个层面把这套方案拆开讲清楚。内容偏实战代码和配置都能直接抄。2. 整体设计思路把一次 AI 调用拆成可观测的链路2.1 核心思路以 Span 为单位组织 AI 调用OpenTelemetry 的基本单位是 Span一个 Span 代表一次有开始和结束的操作。在 GenAI 场景里我习惯把链路拆成这样的层级最外层是一个用户请求 Span比如chat.completion代表用户发起的一次完整对话。中间层是编排 Span比如agent.run或rag.pipeline代表应用内部的逻辑处理。最内层是模型调用 Span比如gen_ai.chat代表真正打到模型的那一次请求。如果涉及检索还会有gen_ai.embeddings或vector.search这样的 Span。这样拆的好处是当一次请求变慢时你能一眼看出是编排逻辑慢、检索慢还是模型本身慢。如果全塞进一个 Span 里就只能看到一个总耗时等于没拆。2.2 为什么选 OpenTelemetry 而不是自建埋点有人会问我自己在代码里打日志记录 Token 用量不就行了为什么要引入 OpenTelemetry 这么重的东西我踩过的坑是自建埋点前期确实快但很快就会遇到三个问题。第一是上下文传递。一次 AI 调用可能跨多个函数、多个协程甚至多个服务你要手动把 trace_id 一路传下去代码里到处是参数非常脏。OpenTelemetry 的 Context 机制自动帮你处理这件事。第二是数据格式统一。自建埋点每个人写的字段名都不一样今天叫input_token明天叫prompt_tokens做聚合分析时全是坑。规范化的字段名能省掉大量清洗工作。第三是后端可替换。OpenTelemetry 的 Collector 可以把数据导出到任意后端今天用这个平台明天换那个平台应用代码一行不用改。这个解耦价值在长期看非常关键。2.3 数据采集的三种典型架构实际落地时采集架构大致有三种我按复杂度从低到高列一下架构模式适用场景优点缺点SDK 直连后端小团队、单服务部署简单无中间件后端绑定扩展性差SDK Collector中等规模、多服务解耦、可做采样和脱敏多一层运维SDK Collector 网关大规模、多租户统一治理、限流架构复杂我一般推荐从第二种起步。Collector 这一层非常值得加因为它能在数据出应用之前做很多事过滤敏感字段、按比例采样、批量压缩、路由到不同后端。尤其是脱敏AI 应用的提示词里经常带用户隐私绝对不能原样上报Collector 的 processor 能在这一层统一处理。2.4 采样策略别让可观测性本身变成成本负担这一点很多人会忽略。AI 调用的 Span 数据量比普通接口大得多因为提示词和响应内容可能很长。如果全量上报存储成本会非常吓人。我的经验是分层采样错误请求全采出错的调用必须完整保留这是排查的命根子。慢请求全采超过阈值的请求全采用于性能分析。正常请求按比例采比如 10% 或 1%用于统计趋势。高价值用户全采付费用户或关键客户的请求优先保留。OpenTelemetry 支持基于 TraceID 的一致性采样能保证同一条链路要么全采要么全不采不会出现半截链路。这个特性在做根因分析时特别重要。3. 核心字段解析Token 成本治理的数据基础3.1 GenAI 语义约定的关键字段要把 Token 成本算清楚前提是埋点字段要规范。下面这张表是我实际项目里最常用的字段按用途分类整理字段名类型用途说明gen_ai.systemstring模型提供方标识用于区分来源gen_ai.request.modelstring请求的模型名称gen_ai.response.modelstring实际响应的模型可能被路由改写gen_ai.usage.input_tokensint输入 Token 数gen_ai.usage.output_tokensint输出 Token 数gen_ai.request.temperaturedouble采样温度gen_ai.request.max_tokensint最大输出限制gen_ai.response.finish_reasonsarray结束原因如 stop、lengthgen_ai.operation.namestring操作类型如 chat、embeddings这里要特别提醒一点input_tokens和output_tokens的计费单价通常不一样输出往往更贵。所以成本计算不能简单用总数乘一个单价必须分开算。我见过有团队图省事只记总量结果成本核算一直对不上账。3.2 输入 Token 的构成与优化空间输入 Token 不是简单等于你写的那句提示词。它通常包含这几部分系统提示词System Prompt历史对话上下文检索增强注入的文档片段用户当前输入工具调用的 schema 定义其中系统提示词和工具 schema 是固定开销每次调用都要付。如果系统提示词写得很长比如上千 Token那高频调用下这笔钱非常可观。我做过一个优化把系统提示词从 1200 Token 压到 400 Token在日均百万次调用的场景下一个月省下的成本相当可观。历史上下文是另一个大头。很多应用无脑把全部对话历史塞进去导致输入 Token 随对话轮次线性增长。合理的做法是做滑动窗口或摘要压缩只保留最近几轮加一个历史摘要。这个优化对成本的影响往往比换更便宜的模型还明显。3.3 输出 Token 的控制手段输出 Token 直接受max_tokens参数控制但很多人不敢设小怕回答被截断。我的经验是分场景设置分类、抽取类任务输出很短max_tokens设 256 足够。摘要类任务设 512 到 1024。长文生成才需要设大值但要配合流式输出。还有一个容易被忽略的点finish_reason如果是length说明回答被截断了这既影响体验又浪费了已生成的 Token。监控这个字段的比例能帮你判断max_tokens是否设置合理。如果截断率很高要么调大限制要么优化提示词让模型回答更简洁。3.4 成本计算的实操公式把字段埋好之后成本计算就是一道简单的算术题。核心公式单次调用成本 input_tokens × 输入单价 output_tokens × 输出单价但实际项目里要更细一些因为不同模型单价不同还可能涉及缓存命中折扣。我一般会在 Collector 或后端做一层聚合按gen_ai.request.model分组统计。下面是一个简化的聚合逻辑示例# 按模型聚合 Token 用量与成本 from collections import defaultdict # 单价表每千 Token 的价格示例值实际以官方为准 PRICING { model-a: {input: 0.001, output: 0.002}, model-b: {input: 0.0005, output: 0.0015}, } def aggregate_cost(spans): stats defaultdict(lambda: {in: 0, out: 0, cost: 0.0, calls: 0}) for span in spans: model span.get(gen_ai.request.model) in_tok span.get(gen_ai.usage.input_tokens, 0) out_tok span.get(gen_ai.usage.output_tokens, 0) price PRICING.get(model) if not price: continue cost in_tok / 1000 * price[input] out_tok / 1000 * price[output] s stats[model] s[in] in_tok s[out] out_tok s[cost] cost s[calls] 1 return stats这段代码看着简单但它是整个成本治理的地基。只有把每次调用的 Token 和成本都算清楚后面的优化才有依据否则就是拍脑袋。4. 实操埋点从零接入调用链追踪4.1 环境准备与依赖安装先明确一下技术栈假设。下面以 Python 应用为例因为大部分 AI 应用是 Python 写的。需要装的核心包pip install opentelemetry-api \ opentelemetry-sdk \ opentelemetry-exporter-otlp \ opentelemetry-instrumentation如果用的是某个 LLM 框架社区通常已经有对应的 instrumentation 包能自动埋点省去手写 Span 的功夫。但自动埋点覆盖的字段往往不全尤其是 Token 用量这种业务字段很多时候还是要手动补。我的建议是自动埋点打底关键字段手动补。4.2 初始化 TracerProvider初始化这一步有几个坑我一个个说。先看代码from opentelemetry import trace from opentelemetry.sdk.trace import TracerProvider from opentelemetry.sdk.trace.export import BatchSpanProcessor from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter from opentelemetry.sdk.resources import Resource resource Resource.create({ service.name: ai-chat-service, service.version: 1.2.0, deployment.environment: production, }) provider TracerProvider(resourceresource) exporter OTLPSpanExporter(endpointhttp://collector:4317, insecureTrue) provider.add_span_processor(BatchSpanProcessor(exporter)) trace.set_tracer_provider(provider) tracer trace.get_tracer(__name__)第一个坑是service.name必须设。不设的话后端里所有服务都叫unknown_service根本分不清谁是谁。第二个坑是导出器要用BatchSpanProcessor而不是SimpleSpanProcessor。Simple 是同步逐条发送高并发下会严重拖慢应用Batch 才是生产环境该用的。第三个坑是insecureTrue只在内部网络用跨网络一定要配 TLS。4.3 手动埋点给一次模型调用加上完整属性自动埋点能帮你生成 Span 骨架但 Token 用量、模型名这些关键属性往往要手动补。下面是一个完整的封装示例def call_llm(prompt, model, max_tokens512): with tracer.start_as_current_span(gen_ai.chat) as span: span.set_attribute(gen_ai.system, example-provider) span.set_attribute(gen_ai.request.model, model) span.set_attribute(gen_ai.request.max_tokens, max_tokens) span.set_attribute(gen_ai.operation.name, chat) try: response client.chat(promptprompt, modelmodel, max_tokensmax_tokens) usage response.usage span.set_attribute(gen_ai.usage.input_tokens, usage.input_tokens) span.set_attribute(gen_ai.usage.output_tokens, usage.output_tokens) span.set_attribute(gen_ai.response.model, response.model) span.set_attribute(gen_ai.response.finish_reasons, [response.finish_reason]) return response except Exception as e: span.record_exception(e) span.set_status(trace.Status(trace.StatusCode.ERROR, str(e))) raise这段代码有几个细节值得说。record_exception会把异常堆栈记进 Span比你自己拼字符串强得多。set_status标记错误状态后端才能正确统计错误率。还有一点finish_reasons是个数组因为有些模型支持多候选输出每个候选有自己的结束原因。4.4 上下文传播让跨服务链路不断裂如果 AI 调用发生在多个服务之间比如网关服务调用推理服务那必须做上下文传播否则链路会断成两截。HTTP 场景下OpenTelemetry 会自动注入和提取traceparent头前提是你用了对应的 instrumentation。手动场景下可以这样传from opentelemetry.propagate import inject, extract # 发送方把上下文注入到请求头 headers {} inject(headers) requests.post(http://inference-service/generate, headersheaders, jsonpayload) # 接收方从请求头提取上下文 ctx extract(request.headers) with tracer.start_as_current_span(handle_request, contextctx): ...我踩过的一个坑是用了异步框架但没配异步 instrumentation导致上下文在协程切换时丢失链路断断续续。解决办法是确保装了对应的异步支持包并在应用启动时正确初始化。4.5 敏感信息脱敏合规红线不能碰AI 应用的提示词和响应里极可能包含用户隐私、商业机密。这些内容绝对不能原样上报到可观测性后端。我的做法是在 Collector 层统一脱敏而不是在应用层因为应用层容易漏。Collector 配置里可以用attributesprocessor 删除或哈希敏感字段processors: attributes/redact: actions: - key: gen_ai.prompt action: delete - key: gen_ai.completion action: delete - key: user.email action: hash注意提示词和响应内容默认不要上报只上报 Token 数、模型名、耗时这些元数据。真需要看内容时走单独的、有权限控制的日志通道。5. Token 成本治理从看得见到管得住5.1 建立成本看板先量化再优化成本治理的第一步永远是看得见。我一般会搭一个看板至少包含这几个维度按模型分组的日均 Token 消耗和成本按功能模块分组的成本占比单次调用平均 Token 数趋势输入输出 Token 比例异常高消耗调用 Top N其中异常高消耗调用 Top N特别有用。我遇到过好几次线上事故都是某个用户或某个 bug 导致单次调用消耗了几十万 Token如果不监控这笔钱就悄悄烧掉了。5.2 缓存策略重复请求不必重复付费很多 AI 应用的请求其实高度重复尤其是客服、FAQ 这类场景。缓存能省下大量成本。缓存分几层精确缓存提示词完全一致直接返回缓存结果。语义缓存提示词语义相近用向量相似度匹配命中后返回缓存。提示词前缀缓存部分模型提供方支持前缀缓存相同的系统提示词部分可以打折。语义缓存要小心相似度阈值设太低会返回不相关的答案设太高又命中率低。我的经验是阈值从 0.95 起步根据实际命中质量慢慢调。同时一定要记录缓存命中率这个指标否则你不知道缓存到底有没有用。5.3 模型路由让合适的任务用合适的模型不是所有任务都需要最强的模型。分类、抽取、格式化这类简单任务用小模型完全够用成本可能只有大模型的十分之一。我一般会做一个路由层任务类型推荐模型档位理由意图分类小模型任务简单准确率够用信息抽取小到中模型结构化输出可控多轮对话中模型需要一定理解力复杂推理大模型需要强推理能力长文创作大模型质量优先路由层的关键是要有 fallback。小模型处理失败或置信度低时自动升级到大模型。这样既省了钱又保证了质量下限。5.4 提示词压缩省下的都是纯利润提示词压缩是我认为性价比最高的优化手段。几个具体做法精简系统提示词删掉冗余的礼貌用语和重复说明只留必要指令。上下文摘要长对话用摘要替代原文Token 能省一大半。检索结果去重和截断RAG 场景下检索回来的文档片段经常有重复去重后能省不少。工具 schema 精简只传当前任务可能用到的工具定义不要全量传。我做过一次对比测试同样的任务优化前平均输入 3500 Token优化后降到 1200 Token输出质量基本没变化。这个优化没有任何技术门槛纯粹是细心活但收益非常直接。5.5 预算与告警给成本装上刹车光优化不够还得有兜底。我一般会设几道防线单次调用 Token 上限超过阈值直接拒绝或截断。单用户日消耗上限防止单个用户刷爆预算。全局日消耗告警超过预算的 80% 触发告警。异常检测单次消耗超过历史 P99 的若干倍时告警。这些规则可以在应用层做也可以在网关层做。我倾向于放在网关层因为应用层容易被绕过网关层是统一入口更可靠。6. 常见问题与排查技巧实录6.1 Token 数对不上账怎么办这是最常见的问题。你记录的 Token 数和账单上的对不上可能的原因有几个计费口径不同有些提供方把系统提示词也算进去有些不算。缓存命中命中缓存的调用单价不同但 Token 数可能照记。重试未记录应用层重试了但没埋点导致少记。流式输出统计延迟流式场景下 Token 数可能在最后一个 chunk 才返回。排查思路是先对齐口径拿几条具体请求去和账单逐条核对。我一般会挑几条特征明显的请求比如超长输入、超长输出做人工核对确认口径一致后再看聚合数据。6.2 链路断裂的定位方法链路断裂表现为本该连成一条的 Span 变成了几条独立的。排查步骤检查是否所有服务都初始化了 TracerProvider。检查跨服务调用是否做了上下文传播。检查异步场景是否装了异步 instrumentation。检查是否有中间件如消息队列没配传播。我遇到过一次是因为用了自定义的线程池Span 上下文没跟着线程传过去。解决办法是用 OpenTelemetry 提供的上下文包装器把任务提交时手动带上上下文。6.3 采样导致关键数据丢失采样是为了省成本但采过头会把关键数据也采掉。我的经验是错误和慢请求的采样规则要单独配置优先级最高。采样决策要基于 TraceID 做一致性哈希保证整条链路一致。定期检查采样后的数据是否还能支撑排查需求。有一次我们采样率设得太激进结果线上出问题时发现错误请求也被采掉了排查时两眼一抹黑。后来改成错误全采问题立刻解决。6.4 常见问题速查表现象可能原因排查方向后端看不到数据导出器配置错误检查 endpoint 和网络连通性Token 字段为空未手动补埋点检查响应对象是否解析了 usage链路断成多段上下文未传播检查跨服务/异步传播配置成本统计偏高重试重复计数检查重试逻辑是否重复埋点看板数据延迟大批量导出间隔长调整 BatchSpanProcessor 参数敏感信息泄露未脱敏在 Collector 层加脱敏 processor6.5 几个我踩过的坑第一个坑一开始没设service.name结果所有服务数据混在一起排查时完全分不清。这个错误很低级但真的很容易犯。第二个坑把提示词原文上报了后来发现里面有用户手机号赶紧加了脱敏。这件事之后我养成了习惯任何上报字段先过一遍隐私审查。第三个坑采样率设成固定值没考虑流量波动。低峰期采样太少高峰期采样太多。后来改成动态采样根据流量自动调整。第四个坑只监控了成功调用的 Token忽略了失败调用。其实失败调用也可能消耗 Token比如输出到一半被截断这部分成本也要算进去。7. 我个人的一些实践体会这套方案落地下来最大的感受是可观测性不是一次性工程而是持续迭代的过程。一开始不用追求大而全先把最核心的字段埋上把成本看板搭起来能回答钱花在哪了这个问题就已经赢了一半。剩下的优化都是在这个基础上逐步展开的。另外一个体会是规范的价值会随时间放大。刚开始按 OpenTelemetry GenAI 规范埋点时可能觉得字段名又长又啰嗦不如自己起个短名字方便。但当你需要跨模型、跨服务、跨时间做对比分析时规范字段带来的便利是巨大的。数据这东西一旦格式乱了后面清洗的成本远超当初省下的那点功夫。最后分享一个小技巧在开发环境就把可观测性打开别等到上线才补。开发阶段数据量小调试埋点问题最容易。等上线后再补面对的是海量数据和线上压力排查成本高得多。我现在的习惯是新功能开发时埋点和业务代码一起写当成功能的一部分而不是事后补的附加项。

关于恒美微站

恒美微站专注于为个体商户、工作室提供极简自助建站服务,让每个人都能轻松拥有专业网站。

快速链接

  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心

服务项目

  • 可视化建站
  • 拖拽编辑
  • 主题定制
  • SEO 优化
  • 网站托管

联系方式

  • 📍 地址:北京市朝阳区建国路 88 号
  • 📞 电话:400-888-8888
  • ✉️ 邮箱:info@hmyw.cn
  • 🕐 时间:周一至周日 9:00-18:00

© 2024 恒美微站 hmyw.cn 版权所有 | 京 ICP 备 12345678 号