拓冰建站拓冰建站
首页 / 资讯中心 / 正文

应用上线后的持续观察

应用上线后的持续观察将大模型LLM与 Spring Boot 框架集成并引入检索增强生成RAG、向量检索以及上下文编排Prompt Orchestration后系统的整体可观测性面临全新挑战。与传统微服务架构中明确的数据库查询或 RPC 调用链路不同AI 增强型后端服务包含 Embedding 向量化、向量数据库 Top-K 检索、Prompt 上下文拼接、模型流式 Token 吐出以及 Tool Calling 函数多轮交互等多个异步与长连接环节。任何一个子环节的异常如向量数据库索引劣化导致检索延迟飙升或者大模型生成陷入死循环都会直接影响上游服务的稳定性。利用 Spring Boot 3.x 提供的Micrometer Observation统一观测体系打通日志Logs、指标Metrics与链路追踪Traces是保障线上 AI 服务稳定运行的关键手段。1. Spring Boot 3.x 源码级观测体系Micrometer Observation 原理拆解从 Spring Boot 3.x 开始官方重构了可观测性底层抽象推出了Micrometer ObservationAPI。该设计将原先分散的 MetricMicrometer与 TraceMicrometer Tracing / OpenTelemetry收口统一通过状态机模式的ObservationContext管理一次操作的完整生命周期。在 AI 业务场景中标准 HTTP 拦截器无法记录“Prompt Token 数量”或“向量匹配相似度”等领域特定指标。通过扩展ObservationHandler源码接口可以实现对 AI 链路上特有属性的明确捕捉package com.example.ai.observability; import io.micrometer.observation.Observation; import io.micrometer.observation.ObservationHandler; import io.micrometer.core.instrument.MeterRegistry; import io.micrometer.core.instrument.Counter; import io.micrometer.core.instrument.Timer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; import java.util.concurrent.TimeUnit; Component public class AiServiceObservationHandler implements ObservationHandlerObservation.Context { private static final Logger log LoggerFactory.getLogger(AiServiceObservationHandler.class); private final MeterRegistry meterRegistry; public AiServiceObservationHandler(MeterRegistry meterRegistry) { this.meterRegistry meterRegistry; } Override public boolean supports(Observation.Context context) { // 仅处理 AI 领域的特定 Context return context instanceof AiObservationContext; } Override public void onStart(Observation.Context context) { AiObservationContext aiContext (AiObservationContext) context; log.info([TraceID: {}] 开始执行 AI 编排链路: operation{}, aiContext.getTraceId(), aiContext.getOperationName()); } Override public void onStop(Observation.Context context) { AiObservationContext aiContext (AiObservationContext) context; long durationMs context.getDurationNs() / 1_000_000; // 上报耗时指标至 Prometheus Timer.builder(ai.operation.duration) .tag(operation, aiContext.getOperationName()) .tag(model, aiContext.getModelName()) .register(meterRegistry) .record(durationMs, TimeUnit.MILLISECONDS); if (aiContext.getPromptTokens() 0) { Counter.builder(ai.tokens.prompt.total) .tag(model, aiContext.getModelName()) .register(meterRegistry) .increment(aiContext.getPromptTokens()); } if (aiContext.getCompletionTokens() 0) { Counter.builder(ai.tokens.completion.total) .tag(model, aiContext.getModelName()) .register(meterRegistry) .increment(aiContext.getCompletionTokens()); } log.info([TraceID: {}] AI 链路完成: duration{}ms, promptTokens{}, completionTokens{}, aiContext.getTraceId(), durationMs, aiContext.getPromptTokens(), aiContext.getCompletionTokens()); } Override public void onError(Observation.Context context) { AiObservationContext aiContext (AiObservationContext) context; Throwable error context.getError(); log.error([TraceID: {}] AI 链路发生异常: operation{}, error{}, aiContext.getTraceId(), aiContext.getOperationName(), error ! null ? error.getMessage() : Unknown); meterRegistry.counter(ai.operation.errors, operation, aiContext.getOperationName()).increment(); } }配套的AiObservationContext用于在线程上下文中安全传递元数据package com.example.ai.observability; import io.micrometer.observation.Observation; public class AiObservationContext extends Observation.Context { private final String operationName; private final String traceId; private String modelName unknown; private int promptTokens 0; private int completionTokens 0; public AiObservationContext(String operationName, String traceId) { this.operationName operationName; this.traceId traceId; } public String getOperationName() { return operationName; } public String getTraceId() { return traceId; } public String getModelName() { return modelName; } public void setModelName(String modelName) { this.modelName modelName; } public int getPromptTokens() { return promptTokens; } public void setPromptTokens(int promptTokens) { this.promptTokens promptTokens; } public int getCompletionTokens() { return completionTokens; } public void setCompletionTokens(int completionTokens) { this.completionTokens completionTokens; } }2. 智能检索与知识增强链路的 OpenTelemetry 手动埋点在 RAG 知识检索场景中将用户查询向量化并提交至向量数据库如 Milvus 或 PGVector检索 Top-K 结果是关键阶段。使用 OpenTelemetry API 手动创建 Span能明确在 Trace 视图中展现向量检索在总执行耗时中的占比。package com.example.ai.service; import com.example.ai.observability.AiObservationContext; import io.micrometer.observation.Observation; import io.micrometer.observation.ObservationRegistry; import io.opentelemetry.api.trace.Span; import io.opentelemetry.api.trace.Tracer; import org.springframework.stereotype.Service; import java.util.List; Service public class KnowledgeRagService { private final ObservationRegistry observationRegistry; private final Tracer openTelemetryTracer; public KnowledgeRagService(ObservationRegistry observationRegistry, Tracer openTelemetryTracer) { this.observationRegistry observationRegistry; this.openTelemetryTracer openTelemetryTracer; } public ListString searchContext(String queryText) { String currentTraceId Span.current().getSpanContext().getTraceId(); AiObservationContext aiContext new AiObservationContext(vector_search, currentTraceId); aiContext.setModelName(text-embedding-3-small); return Observation.start(rag.vector.search, () - aiContext, observationRegistry) .observe(() - { Span span openTelemetryTracer.spanBuilder(milvus_vector_query) .setAttribute(query.length, queryText.length()) .startSpan(); try (var scope span.makeCurrent()) { // 模拟执行向量计算与检索 Thread.sleep(120); span.setAttribute(vector.results.count, 3); span.setAttribute(vector.top1.score, 0.89); return List.of(文档片段 1: JVM 内存结构..., 文档片段 2: ZGC 低延迟原理...); } catch (Exception e) { span.recordException(e); throw new RuntimeException(e); } finally { span.end(); } }); } }通过这一层封装每一次向量检索的查询长度、结果数及 Top-1 匹配得分都会自动绑定至对应 OpenTelemetry Span 的 Attribute 中。3. 日志、指标与 Trace 的联动排查实战服务部署上线后统一日志中心如 Loki将收集包含trace_id的结构化日志。当遇到大模型响应过慢的诊断诉求时可使用 CLI 工具直接查询特定阈值的异常链路# 检索过去 15 分钟内耗时超过 2000ms 的向量检索 Trace 日志 logcli query {appai-spring-boot-service} | rag.vector.search | duration 2s \ --since15m --limit20 --outputraw | jq {traceId: .traceId, duration: .durationMs, promptTokens: .promptTokens}在 Prometheus 监控后台通过 PromQL 监控全局 Token 消耗速率与各环节的延迟分位数为大模型服务商配额与并发限流提供依据# 计算过去 5 分钟各模型 Completion Token 的消耗速率 (Tokens/sec) sum(rate(ai_tokens_completion_total[5m])) by (model) # 统计 RAG 向量检索 P99 延迟趋势 histogram_quantile(0.99, sum(rate(ai_operation_duration_bucket{operationvector_search}[5m])) by (le))配合 Grafana 配置包含首字延迟TTFT、Token 吞吐量、向量数据库 P99 延迟与报错计数的监控面板建立指标异常触发自动告警的联动机制实现 AI 增强型 Spring Boot 应用的工程化可观测性落地。
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门