
1. 项目概述为什么我们需要“可观测”的AI应用如果你正在开发或已经上线了一个基于大语言模型比如Google的Gemini的AI应用那么下面这个场景你一定不陌生月底收到云服务商的账单发现AI API的调用费用远超预期但你却说不清具体是哪个功能、哪个用户、甚至哪次对话消耗了最多的Token。或者产品经理跑来问“最近用户反馈说AI回答的质量好像下降了有数据能证明吗”你只能两手一摊凭感觉猜测是Prompt设计问题还是模型本身波动。这正是当前AI应用开发从“玩具”走向“产品”过程中最普遍的痛点黑盒与失察。我们调用一个强大的模型API输入一段文本得到一段输出但中间发生了什么消耗了多少成本输出质量如何这些关键信息往往被淹没在简单的请求-响应日志里。“可观测的AI应用”就是要解决这个问题。它不仅仅是传统的应用性能监控APM更是将AI模型调用这一核心业务逻辑的成本、性能、质量三大维度进行深度埋点、采集、分析与可视化让开发者对AI应用的状态了如指掌。本次我们聚焦于Google Gemini 3.5模型探讨如何为其构建一套从埋点到监控的完整可观测方案。选择Gemini 3.5作为示例是因为它作为Google的旗舰模型在性能、成本和多模态支持上具有代表性但其监控的核心理念与方法可以平移到任何基于API的AI模型如GPT、Claude等。核心目标有两个一是精准监控每一次API调用的Token消耗实现成本透明与优化二是量化评估模型输出的质量为效果优化提供数据支撑。2. 核心需求解析成本、质量与性能一个都不能少构建可观测体系首先要明确我们要观测什么。对于AI应用尤其是对话或内容生成类应用需求可以归结为三个核心支柱。2.1 成本透明化Token消耗的精细化追踪Token是AI模型世界的“计价单位”。对于Gemini这类按Token计费的模型成本控制直接等同于Token消耗管理。精细化追踪意味着我们需要回答以下问题总量与趋势我的应用每天/每周消耗多少Token趋势是上升还是下降维度下钻这些Token消耗在哪些用户、哪些会话、哪些功能模块上哪个Prompt模板最“费钱”输入输出占比在总消耗中输入Prompt和输出Completion各占多少优化Prompt减少输入Token或限制生成长度减少输出Token哪个性价比更高异常消耗识别是否存在异常的“Token泄漏”比如因程序BUG导致循环调用或因用户输入超长文本导致的非预期高消耗。没有这些数据成本优化就是盲人摸象。我们的埋点系统必须能捕获每一次Gemini API调用的usage_metadata从中提取prompt_token_count和candidates_token_count并与业务上下文用户ID、会话ID、功能标签关联。2.2 质量可度量超越主观感受的评估体系“回答质量下降了”——这是一个非常主观的判断。我们需要将其转化为可度量的指标。对于文本生成类应用质量评估可以从多个维度展开基础性能指标响应延迟Latency、每秒处理请求数TPS、成功率非业务错误如网络超时、鉴权失败。业务相关指标内容安全性响应是否触发了Gemini内置的安全过滤器SafetyRatings触发了哪个类别如仇恨言论、危险性频率如何内容相关性AI的回答是否紧扣用户问题这通常需要结合业务逻辑设计评估规则或引入人工评估样本。格式遵从性如果要求AI以JSON格式返回它是否每次都正确遵循了用户反馈信号用户是否给出了“点赞”、“点踩”的明确反馈这些是宝贵的监督信号。大模型特有指标例如输出结果的“困惑度”Perplexity需额外计算但可间接反映流畅度或通过小型评估模型对输出进行打分。质量监控的目标是建立基线Baseline。当指标发生漂移时如平均响应长度骤变、安全拦截率上升我们能第一时间收到警报并追溯到具体的会话和交互内容从而快速定位是Prompt问题、模型更新问题还是用户输入分布发生了变化。2.3 性能与稳定性保障这更接近传统的应用监控范畴但对AI应用同样关键可用性Gemini API的可用性是否达到SLA要求我们自身的服务调用API的成功率如何延迟P50、P90、P99的响应延迟是多少哪些功能的延迟最高延迟是否与输入Token长度强相关限流与重试是否频繁触发API的速率限制Rate Limit我们的重试策略是否合理重试是否导致了额外的成本或延迟将成本、质量、性能三方面的数据关联起来分析才能产生真正的洞见。例如你可能会发现某个高延迟的接口其输入Prompt非常冗长高成本且生成的内容用户点赞率很低低质量。这个洞察就能直接指导优化重构Prompt在降低成本、提升速度的同时可能也改善了质量。3. 技术架构设计从埋点到可视化的全链路方案一套可观测体系离不开稳定、高效的技术架构。我们的设计需要兼顾实时性、扩展性和对业务代码的低侵入性。3.1 核心组件与数据流一个典型的可观测架构包含以下组件数据流向清晰[AI 应用] - [埋点 SDK/装饰器] - [消息队列 (如 Kafka)] - [流处理/ETL (如 Flink)] - [时序数据库 (如 Prometheus)] [OLAP 数据库 (如 ClickHouse)] - [可视化 (如 Grafana)] |- [日志系统 (如 ELK)] 用于原始日志追溯埋点采集层这是最关键的一环需要集成到应用调用Gemini API的代码处。推荐使用装饰器Decorator或面向切面编程AOP的方式对API调用函数进行无侵入式包装。这样所有对generative_model.generate_content的调用都会被自动捕获关键信息。数据传输层采集的数据不应直接写入数据库以免对应用性能造成冲击。使用异步消息队列如Kafka进行解耦。埋点代码只需将数据快速发送到Kafka后续处理由下游系统负责。数据处理层消费Kafka中的数据流进行清洗、聚合、丰富如关联用户信息、计算衍生指标如计算单次调用成本。可以使用Flink这样的流处理框架进行实时聚合得到每分钟的Token消耗、平均延迟等指标同时将明细数据写入ClickHouse这类适合OLAP分析的数据库供下钻查询和离线分析。存储与查询层时序数据库Prometheus存储聚合后的核心指标如每秒Token数、请求率、错误率用于告警和实时监控仪表盘。OLAP数据库ClickHouse/Druid存储详细的调用日志支持按任意维度用户、会话、功能、模型版本进行快速分组查询和统计分析。日志系统ELK Stack存储完整的请求和响应原文需脱敏用于问题排查和深度分析。可视化与告警层使用Grafana从Prometheus和ClickHouse中读取数据构建监控大盘。配置告警规则当Token消耗突增、错误率升高或延迟超标时通过钉钉、企业微信等渠道通知负责人。3.2 埋点SDK设计要点设计埋点SDK时需要考虑以下关键点确保其健壮性和可用性低侵入性与易用性理想情况下开发者只需几行代码或一个配置即可开启监控。例如提供一个observe_ai_call的装饰器。# 示例使用装饰器进行埋点 from gemini_observability import observe_ai_call observe_ai_call(function_namegenerate_product_description, user_id_extractorlambda req: req.user_id) def call_gemini_for_description(prompt_text, user_context): # 原有的Gemini API调用逻辑 model genai.GenerativeModel(gemini-1.5-pro) response model.generate_content(prompt_text) return response.text上下文传播必须能够将一次调用的上下文Trace ID、Span ID与业务信息用户ID、订单ID、会话ID关联起来。这通常需要集成分布式追踪体系如OpenTelemetry。采样与降级全量采集所有请求的完整请求/响应体可能数据量巨大。需要支持采样策略例如只对1%的请求存储完整内容或当系统负载高时自动降级为只采集元数据。异步与非阻塞埋点数据上报必须是非阻塞的绝不能影响主业务请求的响应时间。采用内存队列后台线程发送到Kafka是常见做法。敏感信息处理在记录Prompt和Response时必须有严格的脱敏机制避免将用户隐私数据或公司机密写入日志。注意在设计之初就要考虑好数据Schema的版本兼容性。一旦字段定义发布再修改的成本会很高。可以为每条数据增加一个schema_version字段。4. 实操为Gemini API调用注入可观测性让我们进入实战环节。假设我们有一个使用Google Generative AI Python SDK的Flask应用。我们将分步实现对其Gemini调用的监控。4.1 步骤一创建可观测性装饰器我们首先创建一个核心的装饰器它负责包装Gemini的调用方法。# observability/decorator.py import functools import time import logging from typing import Dict, Any, Optional, Callable import google.generativeai as genai # 假设有一个发送数据到Kafka的客户端 from .kafka_client import send_observation_event class GeminiObservability: def __init__(self, kafka_topic: str ai_api_observability, default_tags: Dict[str, str] None): self.kafka_topic kafka_topic self.default_tags default_tags or {} def __call__(self, func: Callable): 装饰器主逻辑 functools.wraps(func) def wrapper(*args, **kwargs): # 1. 记录开始时间初始化观测数据 start_time time.time() observation { timestamp: start_time * 1000, # 毫秒时间戳 model: unknown, function_name: func.__name__, tags: self.default_tags.copy() } # 2. 尝试从参数或上下文中提取业务信息这里需要根据实际项目调整 # 例如假设被装饰函数的第一个参数是prompt文本第二个参数是包含user_id的context if len(args) 1 and isinstance(args[1], dict): observation[tags][user_id] args[1].get(user_id, anonymous) # 3. 执行被装饰的原始函数 try: response func(*args, **kwargs) observation[status] success except Exception as e: observation[status] error observation[error_message] str(e) # 仍然抛出异常不影响原有业务逻辑 raise finally: # 4. 计算耗时 end_time time.time() observation[latency_ms] int((end_time - start_time) * 1000) # 5. 提取Gemini特有的用量信息 if observation[status] success and hasattr(response, usage_metadata): observation[prompt_tokens] response.usage_metadata.prompt_token_count observation[completion_tokens] response.usage_metadata.candidates_token_count observation[total_tokens] response.usage_metadata.total_token_count # 提取模型名称 observation[model] getattr(response, _model_name, unknown) # 6. 可选提取安全评级 if hasattr(response, candidates) and response.candidates: safety_ratings response.candidates[0].safety_ratings observation[safety_blocks] any(r.blocked for r in safety_ratings) # 7. 异步发送观测数据到Kafka避免阻塞 # 在实际生产中这里应该使用一个缓冲队列和后台线程 try: send_observation_event(self.kafka_topic, observation) except Exception as e: logging.error(fFailed to send observability event: {e}, exc_infoTrue) return response return wrapper # 创建一个全局单例装饰器实例 gemini_observe GeminiObservability(default_tags{app_name: my_ai_product, env: production})4.2 步骤二在业务代码中应用装饰器现在我们可以在调用Gemini的业务函数上使用这个装饰器。# services/ai_service.py import google.generativeai as genai from observability.decorator import gemini_observe # 配置Gemini API Key (应从环境变量读取) genai.configure(api_keyos.environ.get(GEMINI_API_KEY)) class AIService: gemini_observe # 只需添加这一行 def generate_chat_response(self, prompt: str, user_context: dict) - str: 生成聊天回复 model genai.GenerativeModel(gemini-1.5-pro) # 可以在这里添加系统指令或更复杂的Prompt工程 full_prompt f你是一个有帮助的助手。请根据用户问题提供简洁、准确的回答。 用户问题{prompt} response model.generate_content(full_prompt) return response.text gemini_observe def analyze_sentiment(self, text: str, user_context: dict) - Dict: 分析文本情感 model genai.GenerativeModel(gemini-1.5-pro) prompt f请分析以下文本的情感倾向以JSON格式返回包含sentimentpositive/negative/neutral和confidence0-1之间的浮点数字段。 文本{text} response model.generate_content(prompt) # 这里可以添加JSON解析和格式验证的逻辑验证结果也可以作为质量指标上报 # 例如observation[tags][output_format_valid] True/False return parse_json_response(response.text)通过这种方式所有被装饰的Gemini调用都会自动生成包含丰富上下文的观测数据并异步发送到消息队列。业务代码几乎无需改动实现了低侵入性的埋点。4.3 步骤三数据处理与指标计算下游的数据处理服务如Flink作业会消费Kafka中的原始事件进行实时聚合。# 简化的Flink作业伪代码展示聚合逻辑 from pyflink.datastream import StreamExecutionEnvironment from pyflink.datastream.connectors import KafkaSource import json env StreamExecutionEnvironment.get_execution_environment() # 1. 从Kafka读取数据 source KafkaSource.builder()...build() ds env.from_source(source, ...) # 2. 解析JSON事件 parsed_ds ds.map(lambda event: json.loads(event)) # 3. 按分钟和功能维度聚合Token消耗 keyed_ds parsed_ds.key_by(lambda x: (x[function_name], get_minute_window(x[timestamp]))) aggregated_ds keyed_ds.reduce( lambda a, b: { total_prompt_tokens: a[total_prompt_tokens] b.get(prompt_tokens, 0), total_completion_tokens: a[total_completion_tokens] b.get(completion_tokens, 0), request_count: a[request_count] 1, error_count: a[error_count] (1 if b[status] error else 0) } ) # 4. 将聚合结果写入Prometheus或ClickHouse aggregated_ds.add_sink(...)这个流处理作业会实时产出诸如“每分钟generate_chat_response功能消耗了多少Prompt Token和Completion Token”的聚合结果。5. 构建监控仪表盘与设定告警有了数据我们需要一个直观的界面来查看。使用Grafana我们可以轻松搭建监控大盘。5.1 核心监控面板设计一个完整的AI应用可观测大盘通常包含以下几个视图成本总览视图图表1Token消耗趋势按输入/输出折线图显示最近24小时/7天总Token消耗并用不同颜色区分Prompt Token和Completion Token。图表2Top N 功能Token消耗排名柱状图显示消耗Token最多的几个业务功能。图表3预估成本曲线根据总Token数 * 单价估算每日API调用成本并与预算线对比。统计卡今日累计Token数、今日预估成本、平均每次调用Token数。质量与性能视图图表4请求成功率与错误类型分布用SLO仪表盘显示成功率并用饼图展示各类错误网络错误、速率限制、内容安全拦截等的比例。图表5API响应延迟分布P50, P90, P99折线图监控延迟变化特别是P99长尾延迟。图表6内容安全拦截率显示触发Gemini安全过滤的请求比例变化突增可能意味着有恶意用户或Prompt设计问题。图表7用户反馈趋势如果埋点了用户点赞/点踩可以展示正面/负面反馈的比例和趋势。下钻分析视图这是一个灵活的表格或日志查看器允许运维或开发人员输入特定的Trace ID、用户ID或时间范围查询到该次或该组调用的所有明细原始Prompt脱敏后、模型响应、Token用量、耗时、安全评级等。这通常直接查询ClickHouse中的明细表。5.2 关键告警规则设定监控不是为了事后查看而是为了事前预警。以下是一些必须设置的告警规则成本类告警规则1过去1小时内总Token消耗超过平时同期水平的200%。规则2单个功能在10分钟内的Token消耗速率异常激增可通过环比或同比检测。规则3每日预估成本即将超过日预算的80%。质量与性能类告警规则4API调用成功率在5分钟内低于99.5%。规则5P95响应延迟连续5个采样点如每1分钟一个点超过设定的阈值如2秒。规则6内容安全拦截率在1小时内超过5%基线需根据业务情况设定。业务类告警规则7用户负面反馈点踩率在短时间内显著上升。告警通知应发送到对应的团队频道并附带关键信息如异常的功能名、关联的用户ID如有、相关的Trace ID方便快速定位。6. 常见问题与实战避坑指南在实际落地过程中你会遇到各种预料之外的问题。以下是我从多个项目中总结出的经验与避坑点。6.1 数据一致性与丢失问题问题埋点事件发送到Kafka失败或下游处理作业崩溃导致数据丢失监控视图出现缺口。解决方案生产端重试与本地缓存埋点SDK在发送失败时应有指数退避重试机制。对于极端情况可以将事件暂存到本地磁盘文件由另一个进程异步重试发送。消费端确保Exactly-Once语义Flink作业应开启检查点Checkpoint并选择支持事务的Kafka连接器确保数据不被重复处理或丢失。设置数据完备性监控在Grafana中增加一个面板监控事件流的速率。如果速率突降至0立即告警。6.2 高基数维度导致的查询性能下降问题如果你把每个user_id都作为一个标签Tag打到Prometheus指标里当用户量达到百万级时会导致Prometheus序列爆炸存储和查询性能急剧下降。解决方案区分指标与维度在Prometheus中只存储需要实时告警和查看的高层聚合指标如按功能聚合的Token数。将明细数据特别是高基数维度用户ID、会话ID的数据存放在ClickHouse中用于下钻分析。对标签进行预处理例如不对单个用户ID打标而是对用户进行分群如“新用户”、“VIP用户”、“高风险用户”按群组打标。6.3 Token计算差异与对账问题你自己统计的Token数与Google Cloud账单后台统计的数可能存在细微差异。长期累积可能导致对账不准。解决方案信任官方元数据始终以API返回的usage_metadata中的数字为准进行记录和计费。建立对账机制定期如每天从Google Cloud的Billing API拉取指定时间范围的详细用量报告与你监控系统记录的聚合总量进行比对。允许存在极小比例如0.1%的差异可能源于API内部的四舍五入或统计时间窗口微差如果差异持续较大需要检查埋点是否有遗漏或重复。6.4 长上下文Long Context的成本监控盲区问题Gemini等模型支持超长上下文如100万Token。如果应用使用了长上下文缓存Conversation Buffer每次调用虽然只发送了最新的用户消息但模型实际处理的是整个缓存的历史对话消耗的Token远多于本次输入的Token。简单的输入输出统计会严重低估成本。解决方案深入集成SDK需要深入研究Gemini Python SDK看是否能钩取Hook到模型实际接收到的完整Prompt Token数。有时这需要更底层的拦截。客户端估算如果无法从API直接获取需要在客户端使用与模型匹配的Tokenizer如tiktokenfor GPTGemini可能需要找对应方法对缓存的完整对话历史进行Token估算并将这个估算值作为一个补充指标上报。虽然不精确但比完全忽略要好。6.5 监控系统自身的稳定性和成本问题可观测系统本身也可能成为故障点和成本中心。埋点数据量过大导致Kafka集群压力大、ClickHouse存储成本高昂。解决方案采样策略对于Trace级别的明细数据尤其是包含完整请求/响应体的实施采样。例如100%采集元数据Token数、延迟但只对1%的请求存储完整内容。数据生命周期管理为不同数据设置不同的TTL生存时间。Prometheus中的聚合数据保留7-30天ClickHouse中的明细数据保留30-90天原始日志保留7天。定期清理过期数据。监控监控系统为你的可观测流水线Kafka lag、Flink checkpoint时长、数据库CPU也设置基础监控确保它健康运行。构建可观测的AI应用不是一个一蹴而就的项目而是一个需要持续迭代的工程实践。从最核心的成本和质量指标开始逐步丰富维度、优化架构、完善告警。当你能清晰回答“钱花在哪了”和“效果怎么样”这两个问题时你的AI应用就从实验室原型真正向一个可靠、可控的商业产品迈进了一大步。这套方法论和工具链不仅是针对Gemini更是任何希望将大模型能力产品化的团队所必须搭建的基础设施。