新闻详情

新闻详情

首页 / 资讯中心 / 详情

Spring AI RAG全链路观测实战:基于观测云与OpenTelemetry的埋点与排查

发布时间:2026/9/30 10:27:41来源:尧图网络
Spring AI RAG全链路观测实战:基于观测云与OpenTelemetry的埋点与排查
1. 为什么RAG应用需要全链路观测做过Spring AI RAG项目的朋友大概率都经历过这种场景用户反馈知识库答非所问你打开日志一看只有一行干巴巴的ChatClient call completed in 2.3s至于检索命中了哪些文档、相似度分数是多少、重排序有没有生效、大模型到底收到了什么上下文一概不知。排查全靠猜优化全靠玄学。这就是RAG应用最典型的困境——链路太长黑盒太多。一个完整的RAG请求至少要经过用户提问 → 查询改写 → 向量化 → 向量检索 → 重排序 → 上下文组装 → Prompt渲染 → 大模型调用 → 结果后处理。中间任何一个环节出问题最终表现都是回答不准但根因可能完全不同。全链路观测要解决的就是这个问题把RAG的每一个环节都变成可量化、可追踪、可回溯的数据点。而观测云作为国内一体化观测平台天然支持Trace、Log、Metric三类信号的关联分析正好适合承载Spring AI RAG这种多阶段、跨组件、强时序的观测需求。这篇文章适合三类人看正在用Spring AI做RAG但被排查问题折磨的开发者、准备给AI应用上观测体系的架构师、以及想搞清楚RAG到底慢在哪、错在哪的技术负责人。我会从埋点设计讲到落地代码把踩过的坑一并交代清楚。2. 整体观测方案设计与选型思路2.1 为什么不用纯日志方案最朴素的做法是每个环节打一行日志用ELK或者Loki收集。我早期项目就是这么干的跑了两个月发现三个致命问题第一日志没有因果关系。一次RAG请求产生20条日志分散在不同线程、不同服务里你没法自动把它们串成一条链路。用户报障时你得靠时间戳人工对齐效率极低。第二日志无法聚合分析。你想知道过去一周检索命中率是多少、平均重排序耗时占比多少纯日志方案要么写复杂的正则要么根本算不出来。第三日志和指标割裂。检索延迟突然升高你想看同时段的向量库连接数、JVM GC情况得在两个系统之间来回切。所以我的结论很明确RAG观测必须以Trace为主线Log和Metric作为挂载点。Trace负责串联因果Log负责记录细节Metric负责趋势告警。2.2 观测云接入的技术选型观测云支持OpenTelemetry协议原生接入这对Spring AI项目非常友好因为Spring Boot 3.x本身就基于Micrometer Observation抽象可以无缝对接OTel。具体选型上我推荐这套组合组件选型理由追踪协议OpenTelemetry观测云原生支持Spring生态适配好埋点方式Micrometer Observation 手动Span自动埋点覆盖HTTP/DBRAG业务Span需手动数据传输OTLP over HTTP比gRPC更容易穿透企业网络策略日志关联TraceId注入MDC日志和Trace自动关联指标采集Micrometer Prometheus格式观测云支持自定义指标上报提示如果你的项目还在Spring Boot 2.x建议先升级到3.x否则Micrometer Observation的很多能力用不上埋点代码会写得很别扭。2.3 RAG链路的Span划分原则这是整个方案的核心。Span划分得好排查效率翻倍划分得烂Trace图看起来跟蜘蛛网一样。我的划分原则是按可独立优化、可独立失败的粒度切分。具体到Spring AI RAG我切成这样rag.request根Span一次完整的用户请求rag.query.rewrite查询改写如果有rag.embedding问题向量化rag.retrieve向量检索rag.rerank重排序如果有rag.context.assemble上下文组装rag.llm.call大模型调用rag.post.process结果后处理每个Span都要带上关键属性Attribute比如检索Span要带topK、hitCount、maxScore、minScoreLLM Span要带model、promptTokens、completionTokens。3. Spring AI RAG埋点实操从依赖到代码3.1 依赖配置与观测云接入先在pom.xml里加上必要的依赖。Spring AI本身基于Micrometer所以核心是OTel的bridgedependency groupIdio.micrometer/groupId artifactIdmicrometer-tracing-bridge-otel/artifactId /dependency dependency groupIdio.opentelemetry/groupId artifactIdopentelemetry-exporter-otlp/artifactId /dependency dependency groupIdio.micrometer/groupId artifactIdmicrometer-registry-otlp/artifactId /dependency然后是application.yml的配置这里有个坑要注意观测云的OTLP endpoint路径是/v1/traces和/v1/metrics别写错了management: otlp: tracing: endpoint: https://your-guance-endpoint/v1/traces headers: DF-API-KEY: ${GUANCE_API_KEY} metrics: export: url: https://your-guance-endpoint/v1/metrics headers: DF-API-KEY: ${GUANCE_API_KEY} tracing: sampling: probability: 1.0 observations: key-values: service.name: spring-ai-rag-demo deployment.environment: prod注意sampling.probability在生产环境别设1.0RAG请求的Trace数据量很大建议0.1~0.3但错误请求要强制采样这个后面讲。3.2 用Observation API包裹RAG各阶段Spring AI的ChatClient和VectorStore本身已经带了Observation但默认的Span粒度太粗我们需要手动加细粒度的业务Span。核心工具是ObservationRegistry注入后就能创建自定义ObservationService public class RagObservabilityService { private final ObservationRegistry registry; public RagObservabilityService(ObservationRegistry registry) { this.registry registry; } public T T observe(String name, MapString, String attrs, SupplierT action) { Observation obs Observation.createNotStarted(name, registry) .lowCardinalityKeyValues( KeyValues.of(attrs.entrySet().stream() .map(e - KeyValue.of(e.getKey(), e.getValue())) .toList()) ); return obs.observe(action); } }然后在RAG主流程里这样用public RagResponse ask(String question) { return ragObs.observe(rag.request, Map.of(question.length, String.valueOf(question.length())), () - { // 1. 向量化 float[] queryVector ragObs.observe(rag.embedding, Map.of(), () - embeddingModel.embed(question)); // 2. 检索 ListDocument docs ragObs.observe(rag.retrieve, Map.of(topK, 5), () - vectorStore.similaritySearch( SearchRequest.query(question).withTopK(5))); // 3. 组装上下文 String context ragObs.observe(rag.context.assemble, Map.of(docCount, String.valueOf(docs.size())), () - assembleContext(docs)); // 4. LLM调用 String answer ragObs.observe(rag.llm.call, Map.of(model, qwen-plus), () - chatClient.prompt() .user(u - u.text(PROMPT_TEMPLATE) .param(context, context) .param(question, question)) .call().content()); return new RagResponse(answer, docs); }); }3.3 关键业务属性注入光有Span还不够属性才是排查问题的关键。我总结了每个Span必须带的属性Span名称必带属性用途rag.retrievetopK, hitCount, maxScore, minScore, avgScore判断检索质量rag.rerankinputCount, outputCount, topScore判断重排序效果rag.context.assembledocCount, contextLength, truncated判断上下文是否超限rag.llm.callmodel, promptTokens, completionTokens, finishReason判断成本和截断rag.requestuserId, sessionId, questionHash关联用户和会话检索Span的属性注入要特别注意相似度分数是排查答非所问的第一手证据。如果maxScore低于0.6基本可以判定是检索没召回相关内容而不是LLM的问题。ListDocument docs vectorStore.similaritySearch(request); double maxScore docs.stream() .mapToDouble(d - d.getMetadata().get(score) ! null ? (double) d.getMetadata().get(score) : 0.0) .max().orElse(0.0); Span.current().setAttribute(rag.retrieve.maxScore, maxScore); Span.current().setAttribute(rag.retrieve.hitCount, docs.size());4. 观测云上的RAG看板与告警配置4.1 搭建RAG专属仪表盘数据上报之后观测云的控制台里就能看到Trace了。但默认视图对RAG不够友好我建议自建一个仪表盘包含这几块第一块请求概览。用rag.request的P50/P95/P99延迟做时序图叠加QPS曲线。这里有个经验值——RAG请求P95超过8秒用户体感就会明显变差需要优化。第二块阶段耗时拆解。把embedding、retrieve、llm.call三个Span的耗时做成堆叠柱状图。我实测下来正常情况LLM调用占60%~70%检索占15%~20%embedding占5%左右。如果检索占比突然飙升到40%八成是向量库出了问题。第三块检索质量趋势。用rag.retrieve.maxScore的均值做趋势线配合hitCount的分布。这条线是RAG健康的晴雨表一旦持续下滑说明知识库和用户问题的语义gap在扩大。第四块Token消耗。按模型维度统计promptTokens和completionTokens直接对应成本。4.2 关键告警规则观测云支持基于Trace和Metric的告警我配了这几条实测很管用检索命中率告警5分钟内hitCount0的比例超过10%说明大量问题检索不到内容可能是知识库覆盖不足或embedding模型退化。LLM调用超时告警rag.llm.call的P95超过15秒可能是模型服务限流或网络抖动。Token异常告警单次请求promptTokens超过8000说明上下文组装可能失控需要检查截断逻辑。错误率告警rag.request的error率超过2%这个不用多说。提示告警通知里一定要带上TraceId点进去直接跳到具体链路排查效率提升非常明显。4.3 日志与Trace的关联观测云的日志采集支持从MDC里提取TraceId。Spring Boot 3.x默认会把traceId放进MDC你只需要在logback配置里加上pattern%d{HH:mm:ss.SSS} [%thread] [%X{traceId:-},%X{spanId:-}] %-5level %logger{36} - %msg%n/pattern这样每条日志都带TraceId在观测云里点Trace就能直接看到关联日志不用再来回切系统。5. 常见问题与排查技巧实录5.1 Trace断链问题现象RAG主流程的Span都在但LLM调用的Span是独立的没挂在根Span下。原因Spring AI的ChatClient内部用了异步线程池Observation的上下文没传过去。解决用ContextExecutorService包装线程池或者手动传递Observation上下文Observation.Context context registry.getCurrentObservation().getContext(); executor.submit(ContextExecutorService.wrap(() - { // LLM调用 }, context));5.2 属性丢失问题现象Span上报了但自定义属性在观测云上看不到。原因属性值类型不对。观测云对Attribute的类型有要求double和long要区分清楚传了BigDecimal会静默丢弃。解决统一转成String或标准数值类型我一般用String.valueOf()兜底。5.3 采样导致错误丢失现象生产环境采样率0.1结果出错的请求没被采样排查时找不到Trace。解决用ObservationPredicate或自定义Sampler对error状态的Span强制采样Bean public Sampler customSampler() { return new Sampler() { Override public SamplingResult shouldSample(...) { if (hasError(parentContext)) { return SamplingResult.recordAndSample(); } return SamplingResult.drop(); } }; }5.4 排查速查表现象优先看什么常见根因答非所问retrieve.maxScore检索没召回或知识库缺内容回答截断llm.finishReasonmaxTokens设太小响应慢各Span耗时占比LLM慢或检索慢看占比定位成本高promptTokens趋势上下文太长需优化截断偶发失败error Span的exception向量库连接池或模型限流6. 我在实际项目中的几点体会第一观测不是上线后才做的事。我见过太多项目先跑起来再补埋点结果历史数据全丢了优化没有基线。RAG项目从第一天就该把Observation接上哪怕只埋根Span。第二属性比Span更重要。Span告诉你哪一步慢属性告诉你为什么慢。检索Span如果不带分数你永远不知道是召回问题还是排序问题。第三别追求100%采样。RAG的Trace数据量是普通CRUD的几十倍全采样既费钱又费存储。我的做法是正常请求10%采样错误请求100%采样关键用户100%采样。最后分享一个小技巧把用户反馈点赞/点踩也作为一个Span属性上报这样你就能在观测云上直接筛选被点踩的请求看它们的检索分数分布很快就能定位到知识库的薄弱环节。这个闭环一旦建立起来RAG的迭代效率会有质的提升。
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

《RAD Studio 13.2》 [DELPHI 13.2] [官方原版ISO] 下载 2026/9/30 11:29:42

《RAD Studio 13.2》 [DELPHI 13.2] [官方原版ISO] 下载

RAD Studio 13.2(代号 Florence Update 2)已于2026年9月17日由 Embarcadero 正式发布,核心围绕编译器性能跃升、现代平台深度适配、大型项目开发效率、AI 生态融合四大方向完成全面升级,是 13 Florence 系列的里程碑式正式版本 。…

阅读更多 →
智诺方AI|论文引用部分怎么处理?降重优化时的保护技巧 2026/9/30 11:29:34

智诺方AI|论文引用部分怎么处理?降重优化时的保护技巧

智诺方AI|论文引用部分怎么处理?降重优化时的保护技巧,智诺方ai官网www.znfai.cn 微信公众号搜一搜 智诺方ai 参考文献引用是论文必不可少的组成部分,很多同学在降重、降AIGC改写的时候踩坑:直接把引用段落丢进AI改写&…

阅读更多 →
Java类加载过程梳理,一篇搞定2万字详解 2026/9/30 11:29:20

Java类加载过程梳理,一篇搞定2万字详解

引言:为什么要深入理解类加载很多 Java 工程师写了多年业务代码,对集合、并发、Spring 等框架使用得炉火纯青,但一被问到「类的加载过程是怎样的」「双亲委派机制为什么这么设计」「什么场景会打破双亲委派」时,往往只能说出一两个…

阅读更多 →
局域网聊天程序课设全攻略:C/S架构、Socket与粘包拆包实践 2026/9/30 11:29:11

局域网聊天程序课设全攻略:C/S架构、Socket与粘包拆包实践

简介:这是一份计算机网络课程设计《局域网聊天程序》的完整设计说明书,面向软件工程、网络工程等专业学生,也适合需要完成P2P通信类课设的初学者参考。文档以C#为编程语言,基于Visual Studio 2010开发环境,围绕基于P2P…

阅读更多 →
Python局域网聊天程序开发:socket编程与TCP三次握手实战指南 2026/9/30 11:29:09

Python局域网聊天程序开发:socket编程与TCP三次握手实战指南

简介:这份计算机网络课设资料以P2P(点对点)技术为核心,完整呈现局域网聊天程序的设计与实现过程,面向计算机及相关专业的学生,可用于课程设计、毕业设计或Socket编程入门参考。文档围绕需求分析、总体设计、…

阅读更多 →
从赵灵儿的五气朝元,看 ABAP 如何让一组业务对象恢复运转 2026/9/30 11:29:08

从赵灵儿的五气朝元,看 ABAP 如何让一组业务对象恢复运转

仓库已经补录了库存,销售订单却仍然停在交付冻结状态。这种情况在企业系统里并不少见。订单能否继续履约,往往还取决于信用状态、价格、主数据和后续交付条件。修好其中一处,业务未必就能走通。直到几处关键状态重新协调,整张订单才像恢复了元气。 这与赵灵儿的五气朝元有…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉