ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

AI系统可观测性:从监控到洞察,构建大模型应用的运维基石

AI系统可观测性:从监控到洞察,构建大模型应用的运维基石 1. 从“黑盒”到“白盒”为什么AI时代必须谈可观测性最近和几个做AI应用开发的朋友聊天发现一个挺有意思的现象。大家聊起大模型、Agent、AI应用开发都头头是道但一谈到“这东西上线后怎么管”会议室里就突然安静了。一个朋友吐槽他们团队花了大半年搞了个智能客服Agent上线第一天就出了个“灵异事件”系统日志一切正常CPU内存也平稳但用户反馈的“答非所问”率飙升了30%。团队花了整整两天像侦探一样翻遍了代码、日志和监控面板最后才发现问题出在一个第三方知识库向量化服务的隐性降级上——服务本身没挂但返回的向量相似度阈值因为一次静默更新被调高了导致召回的相关内容质量骤降。这件事让我深刻意识到我们正处在一个关键的转折点上AI尤其是大模型驱动的复杂系统正在把传统的运维和监控体系推向极限。过去我们谈运维核心是“监控”Monitoring。监控就像给系统装了个仪表盘看CPU、内存、磁盘I/O、网络流量、请求成功率SLA。这些指标很重要但它们回答的是“系统是否在运行”以及“运行得是否健康”。对于传统的、确定性高的单体或微服务应用这套体系基本够用。服务挂了、接口超时、错误码飙升监控告警能第一时间告诉你“哪里病了”。但AI系统特别是引入了大模型、Agent工作流、RAG检索增强生成的应用其复杂性是指数级上升的。它的“病”不再是简单的“服务宕机”而是更微妙、更难以捉摸的“逻辑失常”或“性能退化”。比如你的大模型突然开始生成带有偏见或事实错误的内容但响应延迟和Token消耗量完全正常。你的AI Agent在执行一个多步骤任务时卡在了某个看似无关的外部API调用上但该API的HTTP状态码返回200。你的RAG应用召回的相关文档质量下降导致回答质量滑坡但向量数据库的QPS和延迟指标毫无波澜。这些问题传统的监控仪表盘是看不见的。你看到的是一片“绿色”但用户体验和业务效果却在“流血”。这就是为什么我们必须从“监控”进化到“可观测性”Observability。可观测性要回答的问题是“系统内部正在发生什么为什么” 它不再满足于已知的、预设的指标Metrics而是强调通过日志Logs、链路追踪Traces、指标Metrics这三大支柱尤其是前两者去探索未知的、非预设的问题。如果说监控是系统的“健康体检报告”那么可观测性就是一套完整的“实时全身CT扫描血液成分动态分析”它能让你穿透表象看到每一次AI推理的内部状态流转、决策依据和资源消耗。因此把可观测性称为AI时代的“操作系统”这个比喻非常精准。操作系统管理硬件资源为上层应用提供稳定、高效的运行环境。而在AI时代可观测性就是管理“AI智能”这个新型资源确保其行为可控、过程可信、结果可靠的基础平台。没有它再强大的AI模型也只是一个无法被理解和驾驭的“黑盒”其商业应用将充满风险和不确定性。接下来我们就深入拆解在AI系统的具体实践中如何构建和运用这套“操作系统”。2. AI系统可观测性的三大核心挑战与破局思路构建AI系统的可观测性远比传统系统复杂。我们不能简单地把Prometheus、Grafana、ELK栈一套就了事。必须首先理解AI系统独有的三大挑战才能设计出有效的观测方案。2.1 挑战一非确定性输出与“逻辑状态”的缺失这是AI系统与传统软件最根本的区别。一个Java微服务输入A经过确定的业务逻辑必然输出B。它的内部状态是清晰的数据库连接池大小、线程池队列长度、某个关键业务变量的值。但一个大模型输入同一个问题“今天的天气如何”两次的输出可能措辞不同甚至可能一次正确回答“请提供地点”另一次却开始胡言乱语。它的“内部状态”是什么是注意力权重分布是神经网络中间层的激活值这些对应用开发者来说过于底层且难以解读。破局思路定义并追踪“语义指标”和“业务工作流状态”。我们需要在传统的技术指标之上构建一层面向AI语义和业务逻辑的观测层。语义指标Semantic Metrics这需要将非结构化的文本输出转化为可量化的指标。例如回答相关性评分通过一个小型的评估模型或规则对AI的回答与问题进行实时相关性打分。毒性/偏见检测分数实时检测生成内容是否包含不安全信息。事实一致性检查对于RAG应用检查生成答案中的关键事实是否能在召回的文档中找到支撑。提示词Prompt注入检测监控用户输入是否试图劫持或误导系统指令。 这些指标需要被像传统指标一样采集、汇聚和告警。例如当“平均回答相关性”在10分钟内下跌超过20%时就应该触发告警。工作流状态追踪对于AI Agent或多步骤任务必须能完整追踪其“思考-行动-观察”的循环。这需要强大的分布式链路追踪Tracing能力并且追踪的Span跨度不能只是HTTP调用必须扩展为AI特有的操作agent.plan: Agent制定计划。llm.generate: 调用大模型生成。tool.search: 执行搜索工具。agent.decide: Agent基于结果做出下一步决策。 每个Span都必须携带丰富的上下文例如使用的提示词模板、调用的工具名称、工具输入/输出摘要、模型名称、消耗的Token数、思考链Chain-of-Thought的中间结果。这样当Agent卡住或做出错误决策时你可以像看一本侦探小说一样回溯它完整的“心路历程”。2.2 挑战二极高的维度与成本一次大模型API调用涉及数十个甚至上百个有意义的维度模型版本、提示词模板ID、用户ID、会话ID、输入Token数、输出Token数、生成耗时、是否使用了特定工具、召回文档的ID列表、流式输出的首个Token延迟……如果每个维度都进行组合查询和聚合数据量会爆炸存储和查询成本将无法承受。破局思路采用分层采样与智能数据降维。关键指标全量采集详细链路采样采集对于核心业务指标如总请求量、总Token消耗、总体响应延迟必须全量采集和监控。但对于包含完整思考链、工具调用细节的详细追踪数据可以采用采样策略。例如默认只记录1%的请求的完整追踪链路但当错误发生或某些关键指标异常时动态提高该会话或用户的采样率至100%。这需要在数据采集SDK中实现智能采样逻辑。设计可聚合的维度提前规划好核心的聚合维度。例如按“模型版本业务线”聚合Token成本按“提示词模板大类用户群体”聚合回答质量评分。避免在查询时进行临时的、高基数的维度组合。利用向量化与近似计算对于回答质量评估这类复杂计算不一定每次都用重型的评估模型。可以探索使用轻量级的文本嵌入模型将问答对转化为向量通过向量相似度来快速近似评估与历史“好答案”的偏差作为实时监控的代理指标。2.3 挑战三根因定位的复杂性在传统系统中根因定位往往沿着调用链或依赖关系图进行。数据库慢就查SQL和索引下游服务超时就找下游的问题。但在AI系统中问题可能是一个复合性故障。案例一个智能写作助手的回答质量下降。可能的原因链是1) 用户输入中包含了新的网络流行语数据分布漂移2) 导致提示词匹配到了次优的模板3) 该模板触发了对某个特定知识库的检索4) 该知识库的向量索引最近刚好重建过参数未调优5) 导致召回文档不相关6) 最终生成糟糕内容。 传统的监控只会看到知识库查询延迟正常第4步大模型API响应正常第6步。问题被完美地隐藏在了逻辑链条中。破局思路构建“因果图”与“AI运维知识库”。基于追踪链路构建服务依赖图不仅要画出微服务之间的调用图更要将大模型、向量数据库、评估服务、知识库更新流水线等AI特有组件以及它们之间传递的数据语义如“查询向量”、“召回文档列表”作为节点和边构建一个更丰富的“AI应用因果图”。关联多源数据将可观测性数据与AI研发环节的数据关联。例如当生产环境出现回答质量告警时能快速关联到最近是否发布了新的模型版本、是否更新了提示词库、是否上线了新的知识文档。这需要打通MLOps平台和可观测性平台。沉淀排查经验将每一次成功的问题排查过程转化为结构化的“剧本”或“诊断规则”存入知识库。例如“若‘回答相关性评分’下降且‘召回文档平均相似度’同时下降则优先检查向量索引近期的变更记录”。通过不断积累让系统具备一定的辅助诊断能力。3. 构建AI可观测性平台的核心组件与实操选型理解了挑战我们就可以着手搭建平台了。一个完整的AI可观测性平台可以自底向上分为四层数据采集层、数据处理与传输层、数据存储层、应用与可视化层。每一层的技术选型都需要考虑AI数据的特性。3.1 数据采集层埋点与SDK设计这是所有数据的源头设计好坏直接决定上层能力。你需要一个强大的AI可观测性SDK它应该尽可能无侵入或低侵入地集成到你的AI应用框架中如LangChain、LlamaIndex、Dify、FastAPI等。核心采集数据Metrics指标技术指标请求量、并发数、响应延迟总延迟、TTFT-首Token时间、Token间延迟、Token消耗输入/输出/总计、API调用错误率与错误类型。业务/语义指标如前文定义的回答质量评分、毒性分数、成本折算为金额等。这些需要你自定义采集。Spans追踪跨度用于记录一次AI工作流的完整轨迹。每个Span应包含trace_id,span_id,parent_id用于串联整个工作流。name操作名称如llm_completion,tool_execution,retrieval。attributes丰富的属性这是黄金数据区。例如{ llm.model: gpt-4-turbo, llm.provider: openai, prompt.template_id: customer_service_v2, input.token.count: 1250, output.token.count: 320, retrieval.top_k: 5, retrieved_doc_ids: [doc_123, doc_456], agent.workflow_step: 3 }eventsSpan期间发生的重要离散事件如“开始流式输出”、“收到工具执行结果”。status成功/失败及错误信息。Logs日志结构化日志记录关键决策点或异常情况。避免打印大段的提示词或生成内容而是记录其哈希ID或摘要详情应关联到Trace中。实操选型建议标准优先强烈建议采用OpenTelemetryOTel作为采集标准。OTel提供了统一的API、SDK和数据格式用于采集Metrics, Traces, Logs。社区已有一些针对LLM的OTel插件或实验性SDK如opentelemetry-llm可以大幅降低集成成本。自定义开发如果OTel生态的AI组件不成熟可以基于其API自行封装。核心是定义好一套统一的Attribute命名规范如llm.*,agent.*,retrieval.*确保跨团队、跨服务的数据一致性。关键埋点位置在你的AI应用框架中找到核心的执行钩子Hooks或中间件Middleware进行埋点。例如在LangChain中可以利用CallbackHandler在直接调用OpenAI API时可以封装一个带观测功能的客户端。3.2 数据处理、传输与存储层采集到的数据需要被高效地传输和存储。处理与传输通常使用OTel Collector作为代理它负责接收来自SDK的数据进行批处理、过滤、采样、加密然后导出到后端存储。OTel Collector配置灵活可以同时将数据发送到多个目的地。存储选型这是最大的挑战和成本中心。Traces数据数据量巨大关联查询复杂。Jaeger或TempoGrafana Labs开源是专门为追踪数据设计的存储支持高效的TraceID查询和依赖分析。云服务商也有托管方案。Metrics数据传统时间序列数据库如Prometheus仍然适用但要注意其高基数问题。VictoriaMetrics或M3DB在处理高基数维度方面表现更好。对于云原生场景Prometheus Thanos/Cortex是常见组合。Logs数据Loki因其出色的日志索引和与Grafana的原生集成成为当前的热门选择。它擅长存储和查询与Trace关联的日志。ELKElasticsearch, Logstash, Kibana栈功能强大但运维复杂成本也较高。一个实用的混合架构建议使用OTel SDK OTel Collector统一采集。Collector将Metrics 发送到VictoriaMetrics兼顾性能和成本。Traces 发送到Tempo或托管服务。Logs 发送到Loki。同时可以配置一个Pipeline将关键的Trace和Log信息聚合生成更高维度的业务指标再发送给VictoriaMetrics用于告警。3.3 应用与可视化层从仪表盘到智能洞察这是价值呈现的最后一公里。核心工具是Grafana因为它能很好地统一查询和展示来自不同数据源VictoriaMetrics, Tempo, Loki的数据。必须建设的核心仪表盘全局健康总览展示总请求量、平均响应延迟、总Token消耗、总成本、整体错误率、关键业务指标如平均回答质量分的趋势。这是你的“指挥中心”。成本分析面板按模型、按业务线、按用户群细分Token消耗和成本。设置预算消耗告警。这是控制AI支出的生命线。性能深度分析面板可以下钻查看不同模型版本、不同提示词模板的延迟分布P50, P90, P99、TTFT对比。用于评估模型升级或提示词优化的效果。质量与安全监控面板实时跟踪回答相关性、事实一致性、毒性检测等语义指标的走势。设置阈值告警。分布式追踪探索器集成Grafana的Tempo数据源提供强大的Trace查询界面。能够通过TraceID、属性如user_idxxx、错误状态等快速定位问题链路并一键关联查看对应的日志Loki。进阶向智能运维AIOps演进在基础可视化之上可以引入一些智能分析能力异常检测对关键指标如延迟、错误率、质量分应用异常检测算法如Prophet、DBSCAN实现更灵敏、更早的告警而不是依赖简单的静态阈值。根因分析RCA辅助当告警触发时系统能自动分析同时段发生变化的其他指标、关联的部署事件、知识库更新记录给出可能根因的排序列表缩短MTTR平均修复时间。数据驱动优化基于可观测性数据回答业务问题哪个提示词模板在大多数场景下效果最好、成本最低对于某类问题调用GPT-4是否比调用Claude 3.5 Sonnet性价比更高我们的Agent工作流中哪个工具调用最常失败或最耗时4. 实战指南为一个AI问答系统接入可观测性让我们以一个基于RAG的智能问答系统为例走一遍接入可观测性的核心步骤。假设系统使用FastAPI作为Web框架LangChain编排流程查询OpenAI和向量数据库。4.1 第一步定义观测指标与埋点方案在写代码之前先进行设计。召开一个包括研发、算法、运维、产品经理的会议确定大家关心什么。产品经理关心回答的准确率、用户满意度、热点问题分布。算法工程师关心不同召回策略如重排序的效果、Embedding模型的效果、大模型生成内容的稳定性。研发工程师关心接口响应时间、服务依赖健康度、Token消耗成本。运维工程师关心系统整体可用性、资源利用率、故障快速定位。输出一份《观测指标定义文档》至少包含指标类型指标名称描述维度Tags采集方式技术指标llm.request.duration大模型调用耗时model,provider,endpointSDK自动技术指标llm.token.usageToken使用量model,token_type(input/output)SDK自动业务指标rag.answer.relevance_score回答相关性评分 (0-1)query_type,session_id业务代码手动业务指标rag.retrieval.hit_rate检索命中率是否找到相关文档retrieval_strategy业务代码手动事件agent.tool.callAgent调用工具事件tool_name,statusSDK自动4.2 第二步集成OpenTelemetry SDK在FastAPI应用中集成OTel。# 安装必要的Python包 pip install opentelemetry-api opentelemetry-sdk opentelemetry-instrumentation-fastapi opentelemetry-instrumentation-requests# app.py 或类似的主应用文件中 from opentelemetry import trace from opentelemetry.sdk.trace import TracerProvider from opentelemetry.sdk.trace.export import BatchSpanProcessor, ConsoleSpanExporter # 先用控制台导出测试 from opentelemetry.sdk.resources import Resource from opentelemetry.instrumentation.fastapi import FastAPIInstrumentor import fastapi # 1. 创建TracerProvider并设置资源服务名等 resource Resource.create({service.name: ai-qa-service}) trace.set_tracer_provider(TracerProvider(resourceresource)) # 2. 设置Span处理器这里先输出到控制台生产环境换成OTLP导出器 tracer_provider trace.get_tracer_provider() span_processor BatchSpanProcessor(ConsoleSpanExporter()) tracer_provider.add_span_processor(span_processor) # 3. 创建FastAPI应用并自动注入Instrumentation app fastapi.FastAPI() FastAPIInstrumentor.instrument_app(app) # 现在你的FastAPI路由会自动创建Trace了4.3 第三步封装可观测的LangChain组件和工具这是最关键的一步需要为LangChain的组件添加详细的追踪信息。我们可以创建自定义的OpenTelemetryCallbackHandler。# otel_callbacks.py from opentelemetry import trace from langchain.callbacks.base import BaseCallbackHandler from typing import Any, Dict, List, Optional import json class OpenTelemetryCallbackHandler(BaseCallbackHandler): def __init__(self): self.tracer trace.get_tracer(__name__) self.current_span None self.span_stack [] # 用于管理嵌套的Span def on_chain_start(self, serialized: Dict[str, Any], inputs: Dict[str, Any], **kwargs) - None: 当一个链Chain开始运行时调用。 chain_name serialized.get(name, serialized.get(id, [unknown])[-1]) span self.tracer.start_span(fchain.{chain_name}) self.span_stack.append(span) self.current_span span # 记录输入注意脱敏不要记录完整提示词中的敏感信息 if inputs: self.current_span.set_attribute(chain.inputs_keys, str(list(inputs.keys()))) # 可以记录输入摘要或哈希 self.current_span.set_attribute(chain.inputs_summary, str(inputs)[:100] ...) def on_chain_end(self, outputs: Dict[str, Any], **kwargs) - None: 链结束时调用。 if self.current_span and self.span_stack: # 记录输出 if outputs: self.current_span.set_attribute(chain.outputs_keys, str(list(outputs.keys()))) self.current_span.end() self.span_stack.pop() self.current_span self.span_stack[-1] if self.span_stack else None def on_llm_start(self, serialized: Dict[str, Any], prompts: List[str], **kwargs) - None: 大模型调用开始时调用。 llm_name serialized.get(name, unknown_llm) span self.tracer.start_span(fllm.{llm_name}) self.span_stack.append(span) self.current_span span self.current_span.set_attribute(llm.prompts_count, len(prompts)) # 记录第一个提示词的开头部分用于调试 if prompts: self.current_span.set_attribute(llm.first_prompt_prefix, prompts[0][:200]) def on_llm_end(self, response, **kwargs) - None: 大模型调用结束时调用。 if self.current_span and self.span_stack: # 记录Token使用和生成内容摘要 if hasattr(response, llm_output) and response.llm_output: token_usage response.llm_output.get(token_usage, {}) self.current_span.set_attribute(llm.token_usage.input, token_usage.get(prompt_tokens, 0)) self.current_span.set_attribute(llm.token_usage.output, token_usage.get(completion_tokens, 0)) self.current_span.set_attribute(llm.token_usage.total, token_usage.get(total_tokens, 0)) if hasattr(response, generations): text response.generations[0][0].text if response.generations else self.current_span.set_attribute(llm.generation_summary, text[:100] ...) self.current_span.end() self.span_stack.pop() self.current_span self.span_stack[-1] if self.span_stack else None def on_tool_start(self, serialized: Dict[str, Any], input_str: str, **kwargs) - None: 工具调用开始时调用。 tool_name serialized.get(name, unknown_tool) span self.tracer.start_span(ftool.{tool_name}) self.span_stack.append(span) self.current_span span self.current_span.set_attribute(tool.input, input_str[:500]) # 限制长度 def on_tool_end(self, output: str, **kwargs) - None: 工具调用结束时调用。 if self.current_span and self.span_stack: self.current_span.set_attribute(tool.output, output[:500]) # 限制长度 self.current_span.end() self.span_stack.pop() self.current_span self.span_stack[-1] if self.span_stack else None def on_error(self, error, **kwargs): 发生错误时调用。 if self.current_span: self.current_span.record_exception(error) self.current_span.set_status(trace.Status(trace.StatusCode.ERROR, str(error)))然后在你的LangChain链构造时传入这个回调处理器from langchain.chains import RetrievalQA from langchain.chat_models import ChatOpenAI from .otel_callbacks import OpenTelemetryCallbackHandler otel_callback OpenTelemetryCallbackHandler() llm ChatOpenAI(modelgpt-4, temperature0) qa_chain RetrievalQA.from_chain_type( llmllm, chain_typestuff, retrieveryour_vectorstore.as_retriever(), callbacks[otel_callback] # 关键注入回调 )4.4 第四步配置OTel Collector并连接后端在生产环境中你需要运行一个OTel Collector并将SDK的数据导出到Collector再由Collector转发到后端存储。修改SDK配置导出到Collector# 替换之前的ConsoleSpanExporter from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter from opentelemetry.sdk.trace.export import BatchSpanProcessor otlp_exporter OTLPSpanExporter(endpointhttp://your-otel-collector:4317, insecureTrue) span_processor BatchSpanProcessor(otlp_exporter) tracer_provider.add_span_processor(span_processor)编写OTel Collector配置文件 (otel-collector-config.yaml)receivers: otlp: protocols: grpc: endpoint: 0.0.0.0:4317 http: endpoint: 0.0.0.0:4318 processors: batch: timeout: 10s send_batch_size: 1000 # 可以添加过滤、属性修改等处理器 exporters: logging: loglevel: debug prometheusremotewrite: endpoint: http://victoriametrics:8428/api/v1/write tempo: endpoint: tempo:9095 insecure: true loki: endpoint: http://loki:3100/loki/api/v1/push service: pipelines: traces: receivers: [otlp] processors: [batch] exporters: [tempo, logging] metrics: receivers: [otlp] processors: [batch] exporters: [prometheusremotewrite, logging] logs: receivers: [otlp] processors: [batch] exporters: [loki, logging]部署后端存储使用Docker Compose或Kubernetes部署VictoriaMetrics、Tempo、Loki和Grafana。Grafana需要配置好这三个数据源。4.5 第五步在Grafana中构建观测仪表盘数据打通后在Grafana中创建仪表盘。面板1全局概览使用VictoriaMetrics数据展示请求QPS、平均延迟、Token消耗速率、错误率。面板2成本分析编写PromQL按model标签聚合llm_token_usage_total并乘以各模型的单价需在采集时或查询时计算。面板3链路追踪使用Explore功能连接到Tempo数据源。你可以通过{span_namellm.*}这样的查询来查找所有大模型调用或者通过{status_codeERROR}来查找所有出错的Span。点击一个Trace可以清晰看到整个RAG流程接收请求 - 检索文档 - 调用LLM - 返回结果每一步的耗时、属性一目了然。面板4日志关联在Tempo的Trace详情页Grafana可以自动生成关联的Loki查询直接查看该Trace对应时间点、服务、TraceID下的所有相关日志实现无缝的日志追踪。4.6 关键注意事项与避坑指南采样策略是成本与细节的平衡全量采集所有Trace数据成本极高。务必在生产环境配置头部采样Head-based Sampling或尾部采样Tail-based Sampling。例如使用OTel Collector的probabilistic_sampler进行低比例如1%的随机采样同时对所有错误status_codeERROR的Trace进行100%采样。注意数据安全与隐私绝对不要在Span属性或日志中记录完整的用户输入、模型输出、检索到的文档内容等敏感信息。应记录哈希值、摘要或脱敏后的内容。可以在OTel Collector中配置attributesprocessor来过滤或脱敏敏感字段。定义清晰的属性命名规范团队必须统一Attribute的Key命名如llm.modeluser.idsession.id。混乱的命名会导致查询和聚合变得极其困难。建议制定并维护一个内部词典。监控可观测性系统自身你的可观测性平台本身也需要被监控。关注OTel Collector的CPU/内存、VictoriaMetrics/Tempo/Loki的磁盘使用量、写入延迟等。不要让寻找问题的工具本身成为问题。从小处着手迭代建设不要试图一次性覆盖所有指标和场景。先从最核心的1-2个AI应用、最关键的3-5个指标开始。跑通数据从采集到展示的全链路证明其价值。然后逐步扩展观测范围增加更复杂的语义指标和智能分析功能。构建AI可观测性体系是一个持续的过程而非一蹴而就的项目。它始于对AI系统复杂性的深刻认知成于细致的技术设计和持续的迭代运营。当你能够清晰地回答“我的AI应用为什么这样回答”时你才真正开始驾驭AI而不是被其不可预测性所困扰。这套“操作系统”将成为你AI战略中比模型本身更重要的基础设施和核心竞争力。
返回列表