
在实际的 AI 应用开发和运维中响应延迟和 Token 消耗是两个直接影响成本、性能和用户体验的核心指标。一个看似简单的对话接口背后可能涉及大模型推理、上下文管理、网络传输等多个环节任何一个环节的波动都会体现在延迟和 Token 消耗上。如果缺乏有效的监控手段你可能会在用户抱怨“卡顿”时束手无策或者在收到天价云服务账单时才发现 Token 消耗异常。因此构建一套可观测性体系对 AI 服务的延迟和 Token 消耗进行精细化监控与分析是保障服务稳定性和成本可控性的关键。本文将围绕如何解密 AI 服务的响应延迟与 Token 消耗监控带你从零搭建一套基于 OpenTelemetry 和 ClickHouse 的监控解决方案。这套方案不仅能告诉你服务“慢不慢”、“贵不贵”更能帮你定位“哪里慢”、“为什么贵”。我们将从核心概念入手逐步完成环境准备、数据采集、存储分析、可视化展示的全流程并深入探讨生产环境中可能遇到的典型问题及其排查路径。无论你是负责 AI 应用后端开发的工程师还是关注服务性能和成本的运维人员都能通过本文获得一套可直接落地的实践方案。1. 理解监控的核心延迟与 Token 消耗的观测维度在深入技术实现之前我们必须先厘清要监控的对象究竟是什么以及为什么传统的监控指标如 CPU、内存不足以覆盖 AI 服务的特殊需求。1.1 响应延迟不仅仅是“快”与“慢”对于 AI 服务尤其是大语言模型LLM接口响应延迟是一个多维度指标。它不仅仅是客户端从发起请求到收到完整响应所经历的总时间。为了有效定位瓶颈我们需要将其拆解总响应时间从客户端发送第一个字节到收到最后一个字节的时间。这是用户直接感知的延迟。首 Token 时间从请求发出到收到模型生成的第一个 Token或第一个有效数据块的时间。对于流式响应这个指标至关重要它决定了用户需要等待多久才能开始看到内容。Token 生成速率在流式响应中后续 Token 的生成间隔时间。这反映了模型推理的持续吞吐能力。网络传输时间请求和响应在网络上传输所消耗的时间特别是在客户端与网关、网关与模型服务之间。排队与调度时间在高并发场景下请求在负载均衡器或服务队列中等待被处理的时间。只监控总响应时间就像只知道病人发烧却不知道是哪里发炎。拆解后的指标能帮助我们快速判断延迟是出在模型推理本身、网络传输还是服务调度上。1.2 Token 消耗成本与效率的量化标尺Token 是大多数按使用量计费的 AI 服务如 OpenAI API、Azure OpenAI的核心计费单元。监控 Token 消耗直接关系到运营成本。输入 Token 数用户 Prompt 以及系统指令、上下文历史等消耗的 Token 总数。输出 Token 数模型生成的回答所消耗的 Token 总数。总 Token 数输入与输出 Token 之和通常是计费的直接依据。Token 使用效率一个衍生指标例如“每输出 Token 的平均响应时间”或“单位成本Token下的回答质量评分”。这有助于评估不同模型或参数配置的性价比。异常高的 Token 消耗可能源于Prompt 设计不当导致重复或冗余信息、上下文管理漏洞导致历史对话无限累积、或遭遇了试图耗尽资源的恶意请求。1.3 为什么需要 OpenTelemetry 和 ClickHouse传统的监控系统如 Prometheus Grafana擅长处理指标Metrics但对于追踪Traces和日志Logs的处理尤其是需要关联分析和灵活查询海量明细数据时往往力不从心。OpenTelemetry是一个云原生、厂商中立的可观测性框架。它统一了 Metrics、Traces、Logs 三种信号的采集、生成和导出标准。通过 OpenTelemetry我们可以用一套 SDK 和 API在应用代码中无侵入或低侵入地埋点收集包括函数调用链、耗时、自定义属性如input_tokens,model_name在内的全链路追踪数据。ClickHouse是一个高性能的列式 OLAP 数据库。它特别适合存储和查询海量的时序数据和事件数据。OpenTelemetry 收集的 Trace 数据天然带有时间戳且包含大量维度属性标签。ClickHouse 能够以极高的压缩比和查询速度支持我们对这些数据进行灵活的聚合分析如按模型、按用户、按时间段统计平均延迟和 Token 消耗和明细查询如查找某次特定慢请求的完整调用链。二者的结合为我们提供了从细粒度数据采集到强大数据分析的完整能力栈。2. 环境准备与核心组件部署接下来我们将搭建一个最小化的监控环境。这个环境包含数据采集端、存储分析端和可视化端。2.1 基础环境与组件规划我们假设你有一个 Linux 服务器或本地开发环境并已安装 Docker 和 Docker Compose。整个架构包含以下组件示例应用一个简单的 Python Flask 应用模拟调用 AI 服务。它将集成 OpenTelemetry SDK 进行埋点。OpenTelemetry Collector负责接收、处理和导出应用上报的追踪数据。我们将使用 Docker 运行其开源版本。ClickHouse存储 OpenTelemetry 数据的数据库。我们使用官方 Docker 镜像。Grafana数据可视化平台用于从 ClickHouse 中查询数据并绘制监控图表。我们将使用 Docker Compose 来编排和管理这些服务。首先创建一个项目目录并编写docker-compose.yml文件。2.2 编写 Docker Compose 编排文件在项目根目录下创建docker-compose.ymlversion: 3.8 services: # OpenTelemetry Collector otel-collector: image: otel/opentelemetry-collector-contrib:latest command: [--config/etc/otel-collector-config.yaml] volumes: - ./otel-collector-config.yaml:/etc/otel-collector-config.yaml ports: - 4317:4317 # OTLP gRPC 接收端口 - 4318:4318 # OTLP HTTP 接收端口 - 8889:8889 # 健康检查/指标端口 depends_on: - clickhouse networks: - observability-net # ClickHouse 数据库 clickhouse: image: clickhouse/clickhouse-server:latest ports: - 8123:8123 # HTTP 接口 - 9000:9000 # Native 客户端接口 volumes: - clickhouse_data:/var/lib/clickhouse - ./clickhouse-config.xml:/etc/clickhouse-server/config.d/custom.xml:ro environment: CLICKHOUSE_DB: otel CLICKHOUSE_USER: otel_user CLICKHOUSE_PASSWORD: otel_password ulimits: nofile: soft: 262144 hard: 262144 networks: - observability-net # Grafana 可视化 grafana: image: grafana/grafana:latest ports: - 3000:3000 environment: - GF_SECURITY_ADMIN_PASSWORDadmin volumes: - grafana_data:/var/lib/grafana - ./grafana-provisioning:/etc/grafana/provisioning depends_on: - clickhouse networks: - observability-net # 示例 Python 应用 (将在后续步骤中构建和配置) demo-ai-app: build: ./demo-app ports: - 5000:5000 environment: - OTEL_EXPORTER_OTLP_ENDPOINThttp://otel-collector:4317 depends_on: - otel-collector networks: - observability-net volumes: clickhouse_data: grafana_data: networks: observability-net: driver: bridge这个配置定义了一个网络observability-net让四个服务能够相互通信。注意demo-ai-app服务引用了./demo-app目录进行构建我们稍后会创建它。2.3 配置 OpenTelemetry CollectorCollector 是数据管道的中枢。我们需要创建其配置文件otel-collector-config.yaml定义如何接收、处理和导出数据。receivers: otlp: protocols: grpc: endpoint: 0.0.0.0:4317 http: endpoint: 0.0.0.0:4318 processors: batch: # 批处理提高写入效率 timeout: 1s send_batch_size: 1024 memory_limiter: # 内存限制防止 OOM check_interval: 1s limit_mib: 512 spike_limit_mib: 256 exporters: clickhouse: endpoint: tcp://clickhouse:9000 database: otel username: otel_user password: otel_password ttl_days: 7 # 数据保留7天按需调整 timeout: 5s logs_table_name: otel_logs traces_table_name: otel_traces metrics_table_name: otel_metrics # 映射 OpenTelemetry 数据到 ClickHouse 表结构 logs_table_columns: - name: Timestamp type: DateTime64(9) - name: TraceId type: String - name: SpanId type: String - name: Body type: String traces_table_columns: - name: Timestamp type: DateTime64(9) - name: TraceId type: String - name: SpanId type: String - name: ParentSpanId type: String - name: TraceState type: String - name: SpanName type: String - name: SpanKind type: String - name: ServiceName type: String - name: Duration type: Int64 - name: Attributes type: Map(String, String) # 关键将属性存储为 Map便于查询 - name: Events type: String - name: Links type: String service: pipelines: traces: receivers: [otlp] processors: [memory_limiter, batch] exporters: [clickhouse] metrics: receivers: [otlp] processors: [memory_limiter, batch] exporters: [clickhouse] logs: receivers: [otlp] processors: [memory_limiter, batch] exporters: [clickhouse]这个配置的关键点在于exporters.clickhouse部分它定义了如何将数据写入 ClickHouse特别是将 Span 的Attributes字段定义为Map(String, String)类型这让我们可以方便地通过键值对查询我们自定义的监控属性如input_tokens。2.4 配置 ClickHouse 和 Grafana为了简化我们使用一个基本的 ClickHouse 自定义配置clickhouse-config.xml主要调整时区。yandex logger levelinformation/level consoletrue/console /logger listen_host0.0.0.0/listen_host timezoneAsia/Shanghai/timezone /yandexGrafana 需要配置数据源。创建目录grafana-provisioning/datasources/和文件grafana-provisioning/datasources/clickhouse.yamlapiVersion: 1 datasources: - name: ClickHouse type: grafana-clickhouse-datasource access: proxy url: http://clickhouse:8123 jsonData: defaultDatabase: otel port: 8123 username: otel_user secure: false server: clickhouse tlsSkipVerify: false secureJsonData: password: otel_password editable: true至此基础设施的配置已经完成。接下来我们创建最关键的部分——集成了 OpenTelemetry 埋点的示例 AI 应用。3. 实现可观测的 AI 应用示例我们的示例应用是一个 Flask Web 服务它提供一个/chat接口。为了模拟真实场景我们会在这个接口中创建多个 Span追踪片段并记录关键的延迟和 Token 消耗属性。3.1 创建应用项目结构在项目根目录下创建demo-app目录并进入mkdir -p demo-app cd demo-app创建以下文件Dockerfile应用容器构建文件。requirements.txtPython 依赖列表。app.py主应用文件。3.2 编写 Dockerfile 和依赖文件Dockerfile内容FROM python:3.9-slim WORKDIR /app COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt COPY . . CMD [python, app.py]requirements.txt内容Flask2.3.3 opentelemetry-api1.21.0 opentelemetry-sdk1.21.0 opentelemetry-exporter-otlp-proto-grpc1.21.0 opentelemetry-instrumentation-flask0.42b0 opentelemetry-instrumentation-requests0.42b0我们安装了 Flask 和 OpenTelemetry 的核心 SDK、OTLP gRPC 导出器以及针对 Flask 和 requests 的自动埋点工具Instrumentation。自动埋点可以帮我们捕获 HTTP 请求/响应的基础信息。3.3 编写核心应用代码app.py是核心我们将详细解释每一步。import random import time from flask import Flask, request, jsonify 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.instrumentation.flask import FlaskInstrumentor from opentelemetry.instrumentation.requests import RequestsInstrumentor # 1. 设置 TracerProvider trace.set_tracer_provider(TracerProvider()) # 2. 创建 OTLP 导出器指向 Collector 的 gRPC 端口 # 注意OTEL_EXPORTER_OTLP_ENDPOINT 环境变量在 docker-compose 中已设置 otlp_exporter OTLPSpanExporter() # 3. 创建批处理器并添加到 TracerProvider span_processor BatchSpanProcessor(otlp_exporter) trace.get_tracer_provider().add_span_processor(span_processor) # 4. 创建 Flask 应用并启用自动埋点 app Flask(__name__) FlaskInstrumentor().instrument_app(app) RequestsInstrumentor().instrument() # 如果内部有 HTTP 调用如真正调用 OpenAI API也需要 # 获取一个 Tracer tracer trace.get_tracer(__name__) def simulate_llm_call(prompt): 模拟调用大语言模型。 在实际项目中这里应替换为真实的 OpenAI、Azure OpenAI 或本地模型的调用。 # 模拟处理时间50ms 到 3s 之间 process_time random.uniform(0.05, 3.0) time.sleep(process_time) # 模拟 Token 消耗输入 Token 基于 prompt 长度输出 Token 随机 # 这是一个非常简化的模拟真实情况需根据模型 API 返回计算 input_tokens len(prompt) // 4 # 粗略估算 output_tokens random.randint(10, 500) return { response: f这是对‘{prompt[:20]}...’的模拟回答。, input_tokens: input_tokens, output_tokens: output_tokens, process_time: process_time } app.route(/chat, methods[POST]) def chat(): 主要的聊天接口。 我们将手动创建 Span 来记录关键的业务操作和属性。 # 从请求中获取数据 data request.get_json() user_message data.get(message, ) user_id data.get(user_id, anonymous) # 为整个请求创建一个顶级 Span with tracer.start_as_current_span(chat_request) as request_span: # 为当前 Span 设置属性Attributes # 这些属性会作为键值对存储到 ClickHouse 的 Map 字段中是后续查询分析的基础 request_span.set_attribute(http.route, /chat) request_span.set_attribute(user.id, user_id) request_span.set_attribute(request.message_length, len(user_message)) # 模拟一个前置处理步骤比如请求验证或上下文加载 with tracer.start_as_current_span(preprocess): time.sleep(0.01) # 模拟耗时 # 可以在这里添加更多属性 current_span trace.get_current_span() current_span.set_attribute(preprocess.step, validation_ok) # 模拟调用 LLM这是最核心、最耗时的步骤 with tracer.start_as_current_span(llm_inference) as llm_span: llm_result simulate_llm_call(user_message) # 记录 LLM 调用的关键指标 llm_span.set_attribute(llm.input_tokens, llm_result[input_tokens]) llm_span.set_attribute(llm.output_tokens, llm_result[output_tokens]) llm_span.set_attribute(llm.total_tokens, llm_result[input_tokens] llm_result[output_tokens]) llm_span.set_attribute(llm.process_time_ms, llm_result[process_time] * 1000) # 转毫秒 # 可以记录模型名称、温度等参数 llm_span.set_attribute(llm.model, simulated-gpt-4) # 模拟后处理步骤比如格式化响应 with tracer.start_as_current_span(postprocess): time.sleep(0.005) # 在顶级 Span 中记录总 Token 数也可以在 Collector 或查询时聚合 total_tokens llm_result[input_tokens] llm_result[output_tokens] request_span.set_attribute(request.total_tokens, total_tokens) # 构建响应 response_data { reply: llm_result[response], usage: { input_tokens: llm_result[input_tokens], output_tokens: llm_result[output_tokens], total_tokens: total_tokens } } return jsonify(response_data) if __name__ __main__: app.run(host0.0.0.0, port5000, debugFalse) # 生产环境务必关闭 debug代码关键点解释初始化 OpenTelemetry设置了TracerProvider和 OTLP 导出器数据将通过 gRPC 发送到otel-collector:4317。自动埋点FlaskInstrumentor会自动为每个 Flask 路由创建 Span记录 HTTP 方法、状态码、路径等信息。这省去了大量手动工作。手动埋点我们使用tracer.start_as_current_span()手动创建了业务层 Spanchat_request,preprocess,llm_inference,postprocess。这形成了清晰的调用链。记录自定义属性通过span.set_attribute()方法我们将业务核心指标llm.input_tokens,llm.process_time_ms和上下文信息user.id附加到 Span 上。这些属性是后续在 ClickHouse 中分析延迟和 Token 消耗的核心数据。模拟耗时使用time.sleep模拟不同阶段的处理时间方便我们观察延迟分布。3.4 启动并验证整个系统回到项目根目录运行以下命令启动所有服务docker-compose up -d这个命令会拉取镜像并启动clickhouse,otel-collector,grafana和demo-ai-app四个服务。等待几分钟让服务完全启动后我们可以进行验证测试应用接口curl -X POST http://localhost:5000/chat \ -H Content-Type: application/json \ -d {message: 请解释一下量子计算的基本原理。, user_id: user_123}你应该会收到一个包含模拟回答和 Token 用量的 JSON 响应。多执行几次此命令以生成一些数据。检查数据是否进入 ClickHouse# 进入 ClickHouse 容器 docker-compose exec clickhouse bash # 在容器内连接客户端 clickhouse-client --user otel_user --password otel_password --database otel # 查询 traces 表看是否有数据 SELECT count(*) FROM otel_traces; SELECT SpanName, Attributes[‘llm.total_tokens’] as tokens, Duration FROM otel_traces WHERE SpanName ‘llm_inference’ LIMIT 5;如果查询能返回数据并且Attributes字段中包含我们设置的llm.total_tokens等键值说明数据链路已经打通。4. 在 Grafana 中分析延迟与 Token 消耗数据已经存储到 ClickHouse下一步是通过 Grafana 创建可视化的监控仪表盘。4.1 配置 Grafana 数据源与查询打开浏览器访问http://localhost:3000使用admin/admin登录。导航到Configuration-Data Sources应该能看到之前通过 Provisioning 配置好的ClickHouse数据源状态应为OK。新建一个 Dashboard然后添加一个 Panel。4.2 绘制平均响应时间与 P99 延迟图表我们首先关注延迟。在新建的 Panel 中编辑 SQL 查询。查询1各接口平均响应时间与 P99 延迟按分钟聚合SELECT $timeSeries as t, SpanName, avg(Duration) / 1000000 as avg_duration_ms, -- Duration 单位是纳秒 quantile(0.99)(Duration) / 1000000 as p99_duration_ms FROM otel_traces WHERE $timeFilter AND SpanName IN (chat_request, llm_inference) -- 筛选关键 Span GROUP BY t, SpanName ORDER BY t, SpanName$timeSeries和$timeFilter是 Grafana ClickHouse 插件的宏会自动替换为时间范围和聚合间隔。Duration是 OpenTelemetry Span 自带的字段单位是纳秒需要转换为毫秒以便阅读。quantile(0.99)计算 P99 分位数反映长尾延迟。在 Visualization 中选择Time series图表并为avg_duration_ms和p99_duration_ms分别设置图例。这样就能看到不同 Span 的延迟趋势和尖峰。4.3 绘制 Token 消耗统计图表Token 消耗是我们的成本指标。由于我们将 Token 数记录为 Span 的 Attribute查询时需要从 Map 结构中提取。查询2总 Token 消耗趋势按小时聚合SELECT $timeSeries as t, sum(cast(Attributes[llm.total_tokens] as Int64)) as total_tokens_consumed FROM otel_traces WHERE $timeFilter AND SpanName llm_inference AND Attributes[llm.total_tokens] ! GROUP BY t ORDER BY t这个查询按时间聚合了所有llm_inferenceSpan 的总 Token 数可以直观看到成本消耗的速度。查询3按用户统计 Token 消耗 TopNSELECT Attributes[user.id] as user_id, count() as request_count, sum(cast(Attributes[llm.input_tokens] as Int64)) as total_input_tokens, sum(cast(Attributes[llm.output_tokens] as Int64)) as total_output_tokens, sum(cast(Attributes[llm.total_tokens] as Int64)) as total_tokens FROM otel_traces WHERE $timeFilter AND SpanName llm_inference AND Attributes[user.id] ! GROUP BY user_id ORDER BY total_tokens DESC LIMIT 10这个查询使用Table可视化可以快速识别出 Token 消耗最大的用户用于成本分摊或异常检测例如是否存在某个用户脚本在疯狂调用。4.4 创建关联分析仪表盘将上述几个图表再加上诸如“每秒请求数RPS”、“各阶段耗时占比桑基图或饼图”、“错误率通过 Span 状态码”等面板组合在一个 Dashboard 中。这样运维和开发人员就能在一个页面看到全局健康状况请求量、错误率。性能瓶颈哪个阶段llm_inference还是preprocess是延迟的主要贡献者P99 是否异常成本驱动Token 消耗是否与请求量匹配是否有异常的高消耗用户或时段5. 生产环境关键问题排查与优化将监控系统部署到生产环境后你会遇到各种实际问题。以下是基于此架构的典型问题排查清单。5.1 数据采集与传输问题问题现象可能原因检查方式处理建议Grafana 中查不到数据1. 应用未正确上报数据。2. Collector 未运行或配置错误。3. ClickHouse 表不存在或写入失败。1. 检查应用日志确认 OpenTelemetry SDK 初始化无报错。2. 检查 Collector 容器日志 (docker-compose logs otel-collector)。3. 登录 ClickHouse检查otel.otel_traces表是否存在及是否有数据。1. 确认应用环境变量OTEL_EXPORTER_OTLP_ENDPOINT指向正确的 Collector 地址。2. 检查 Collector 配置文件中exporters.clickhouse的连接参数。3. 确保 Collector 和 ClickHouse 网络互通。数据延迟高或丢失1. Collector 批处理 (batchprocessor) 缓存过大或超时设置过长。2. 网络抖动或 ClickHouse 写入压力大。1. 查看 Collector 的batchprocessor 相关指标如batch_size_*。2. 检查 ClickHouse 的system.metrics表关注InsertedRows、DelayedInserts。1. 调整batchprocessor 的timeout和send_batch_size在延迟和吞吐间权衡。2. 考虑对 ClickHouse 进行分片集群部署或使用更强大的硬件。Span 属性如 Token 数查询为空1. 应用代码中set_attribute的键名与查询语句中的键名不匹配大小写敏感。2. 属性值类型不是字符串而查询时直接当字符串处理。1. 在 ClickHouse 中执行SELECT Attributes FROM otel_traces LIMIT 1 FORMAT Vertical查看准确的键名。2. 检查代码确保属性值被正确转换为字符串OpenTelemetry SDK 通常会自动处理。1. 在代码中定义属性键名的常量避免拼写错误。2. 在查询时使用cast(Attributes[‘key’] as Int64)等进行类型转换。5.2 查询性能与存储优化问题问题现象可能原因检查方式处理建议Grafana 图表加载缓慢1. 查询涉及全表扫描没有利用索引。2. 聚合时间区间过大数据量太多。1. 在 ClickHouse 中使用EXPLAIN分析查询语句。2. 检查 Grafana 面板的 Query Inspector查看查询耗时。1. 为otel_traces表创建合适的索引例如对Timestamp、SpanName、ServiceName和常用的 Attribute 键如mapKeys(Attributes)创建索引。2. 在 Grafana 中设置合理的自动分组间隔 ($interval)避免查询过细粒度数据。ClickHouse 磁盘空间增长过快1. 数据保留策略TTL未生效或设置过长。2. 采集的数据量过大如采样率 100%。1. 检查表结构SHOW CREATE TABLE otel.otel_traces确认 TTL 设置。2. 估算每日数据增量。1. 在 Collector 配置或 ClickHouse 表结构中缩短 TTL如从 30 天改为 7 天。2. 在应用端或 Collector 端配置采样策略如头部采样、尾部采样只保留部分有代表性的 Trace。5.3 应用埋点与指标定义问题问题现象可能原因检查方式处理建议无法区分不同模型的性能所有 LLM 调用都记录在同一个llm_inferenceSpan 下没有区分模型标识。查看llm_inferenceSpan 的 Attributes是否包含llm.model等字段。在调用不同模型 API 时在对应的 Span 上设置model、api_version等属性。无法计算 Token 消耗单价监控数据只记录了 Token 数没有记录调用的具体模型和单价信息。检查数据中是否包含足够用于成本计算的维度。1. 在 Span 属性中记录llm.model和llm.deployment如果使用 Azure。2. 在 Grafana 中通过查询关联外部定价表或定义变量计算估算成本。首 Token 时间无法监控当前埋点只记录了整个推理过程的耗时没有细分。检查 Span Events 或是否创建了更细粒度的子 Span。如果 AI 服务 API 支持返回首 Token 时间如 OpenAI 的response.created和首个 chunk 的时间差应将其作为一个单独的属性或 Event 记录到 Span 中。6. 生产环境最佳实践与扩展方向基于以上实践和问题排查经验以下是部署此类监控系统到生产环境的建议。6.1 监控系统自身的高可用监控系统不能成为单点故障。对于生产环境OpenTelemetry Collector应部署为集群并使用负载均衡器如 Nginx将应用流量分发到多个 Collector 实例。配置应通过配置中心管理。ClickHouse考虑部署为多副本集群如 2 分片 2 副本使用Distributed表来保障数据可靠性和查询负载均衡。Grafana同样可以多实例部署前端通过负载均衡暴露。6.2 实施有效的采样策略100% 采集所有 Trace 数据对高流量服务是不现实的。需要实施采样头部采样在请求入口如网关或 Collector决定是否采样。例如每秒最多采集 100 条 Trace。尾部采样先采集所有数据但只持久化满足特定条件如延迟大于 1s、包含错误、特定用户的 Trace。这需要 Collector 的tail_samplingprocessor。速率限制在应用 SDK 端配置采样率如 10%。6.3 定义清晰的告警规则监控的目的之一是及时发现问题。在 Grafana 中或使用独立的告警系统如 Alertmanager定义告警延迟告警当llm_inference的 P95 延迟连续 5 分钟超过 2 秒时告警。错误率告警当 HTTP 状态码非 2xx 的比例超过 1% 时告警。成本异常告警当某个用户或模型的 Token 消耗速率在短时间内激增超过历史平均的 3 倍标准差时告警。数据链路告警监控 Collector 向 ClickHouse 的写入速率如果持续为 0则说明数据链路中断。6.4 扩展监控维度当前方案主要关注延迟和 Token。可以进一步扩展集成 Metrics使用 OpenTelemetry Metrics SDK 记录更轻量级的指标如当前活跃请求数、队列长度等这些指标更适合用 Prometheus 存储和告警。关联业务日志在 Span 中记录唯一的TraceId并将此 ID 打印到应用日志中。这样当发现一个慢请求时可以通过TraceId在日志系统中找到该请求所有的详细调试日志。监控外部依赖如果 AI 服务调用真正的云 API如 OpenAI使用opentelemetry-instrumentation-requests可以自动捕获这些外部 HTTP 调用的延迟和状态帮助你区分是自身网络问题还是上游服务问题。通过以上步骤你不仅搭建了一套监控系统更建立了一套理解、观测和优化 AI 服务性能与成本的完整方法论。从核心概念到环境搭建从代码埋点到可视化分析再到生产环境的排错与优化这套以 OpenTelemetry 和 ClickHouse 为核心的方案提供了从数据采集、存储到分析的强大灵活性和可扩展性能够伴随你的 AI 应用一起成长应对日益复杂的运维挑战。