乐于分享
好东西不私藏

Spring AI 应用上线前,先把大模型调用变成可观测链路

Spring AI 应用上线前,先把大模型调用变成可观测链路

很多 Java 团队接入大模型的第一版,往往只有三件事:配置 API Key,写一个 ChatClient,把结果返回给前端。

Demo 能跑,但一到真实项目就会遇到另一组问题:为什么这次回答慢了 8 秒?为什么同一个问题成本突然变高?为什么工具调用失败但业务日志里只有一句“模型返回异常”?为什么线上排查时看不到用户问题、模型名、token、traceId 之间的关系?

AI 应用上线的分水岭,不是“能不能调通模型”,而是“出问题时能不能解释这次调用发生了什么”。

Spring AI 的价值也不只是封装模型 API。更关键的是,它把 ChatClient、Advisor、ChatModel、EmbeddingModel、VectorStore、Tool Calling 这些 AI 调用环节纳入 Spring 的工程体系,其中可观测性就是很容易被低估的一环。

别只记录模型返回值

传统后端接口排查,一般看 HTTP 状态码、SQL 慢查询、线程池、Redis、MQ、traceId。AI 调用多了几个新的变量:

维度
普通后端接口
AI 调用
延迟来源
DB、RPC、锁、网络
模型推理、上下文长度、工具调用、检索
成本来源
机器资源、存储、带宽
输入 token、输出 token、模型单价
失败形态
超时、异常、空指针
内容不可控、工具参数错、上下文缺失
排查依据
日志、指标、链路追踪
日志、指标、trace、prompt 版本、token 使用量

所以,AI 应用不能只在日志里打印:

log.info("ai answer: {}", answer);

这类日志在开发阶段有用,但在生产环境很危险。它可能泄露用户输入、业务数据、内部知识库片段,甚至工具调用参数。

更合理的目标是:默认记录结构化、低敏、可聚合的信息;只有在受控环境里,才开启 prompt、completion、tool 参数等高敏内容。

一个更像生产项目的调用边界

下面这个例子不是完整 RAG 系统,只展示一个关键点:把一次 AI 调用包在业务可观测边界里,让它具备 trace、指标和业务标签。

Maven 依赖示意如下,版本请以项目实际 Spring Boot / Spring AI 版本为准:

<dependency>    <groupId>org.springframework.ai</groupId>    <artifactId>spring-ai-starter-model-openai</artifactId></dependency><dependency>    <groupId>org.springframework.boot</groupId>    <artifactId>spring-boot-starter-actuator</artifactId></dependency><dependency>    <groupId>io.micrometer</groupId>    <artifactId>micrometer-tracing-bridge-otel</artifactId></dependency><dependency>    <groupId>io.opentelemetry</groupId>    <artifactId>opentelemetry-exporter-otlp</artifactId></dependency>

application.yml可以先保守开启 tracing 和 actuator:

spring:  ai:    openai:      api-key: ${OPENAI_API_KEY}management:  endpoints:    web:      exposure:        include: health,info,prometheus  tracing:    sampling:      probability: 1.0  otlp:    tracing:      endpoint: http://localhost:4318/v1/traces

在业务代码里,不要把所有问题都推给框架自动观测。框架能帮你记录底层 AI 调用,但业务侧仍然需要补充“这次调用属于什么场景”。

@Servicepublic class AiAnswerService {    private final ChatClient chatClient;    private final ObservationRegistry observationRegistry;    private final MeterRegistry meterRegistry;    public AiAnswerService(ChatClient.Builder builder,                           ObservationRegistry observationRegistry,                           MeterRegistry meterRegistry) {        this.chatClient = builder.build();        this.observationRegistry = observationRegistry;        this.meterRegistry = meterRegistry;    }    public String answer(String question) {        return Observation                .createNotStarted("biz.ai.answer", observationRegistry)                .lowCardinalityKeyValue("scenario", "customer_support")                .observe(() -> {                    Timer.Sample sample = Timer.start(meterRegistry);                    try {                        return chatClient.prompt()                                .system("你是一个严谨的客服助手,只基于已知信息回答。")                                .user(question)                                .call()                                .content();                    } finally {                        sample.stop(Timer.builder("biz.ai.answer.latency")                                .tag("scenario", "customer_support")                                .register(meterRegistry));                    }                });    }}

这段代码的重点不是“多包一层 Observation”,而是把 AI 调用从不可解释的外部请求,变成业务链路里的一段 span。以后排查问题时,你至少能知道:这次客服问答接口慢,是业务逻辑慢,还是模型调用慢;是所有场景都慢,还是只有 customer_support 这个场景慢。

具体 API 可能会随 Spring AI、Spring Boot、Micrometer 版本变化,实际项目中应以官方文档为准。

生产环境最该看的不是回答文本

AI 调用的可观测指标,不建议一开始做得很复杂。第一版重点盯住 5 类信号:

  1. 请求量

区分不同业务场景、模型、租户或功能入口,但不要把用户 ID、问题原文这类高基数字段放进 tag。

2. 延迟

至少拆出接口总耗时和模型调用耗时。如果接了 RAG,还要拆出向量检索、重排序、模型生成。

3. 失败率

不要只统计异常。模型返回空内容、结构化输出解析失败、工具调用参数不合法,也应该算失败。

4. token 使用量

token 是 AI 应用成本的核心变量。一次请求贵不贵,不看字符数,要看输入 token、输出 token、上下文拼接策略和模型选择。

5. 工具调用结果

如果使用 Tool Calling 或 MCP 工具,必须记录工具名、调用状态、耗时、失败原因。不要默认记录工具入参和返回值,里面很可能有订单号、手机号、内部系统数据。

这里有个很实际的判断:如果你的团队还没有办法回答“哪个功能最耗 token、哪个模型最常超时、哪个工具调用失败最多”,那这个 AI 应用还不适合大规模推广。

prompt 和 completion 不是普通日志

Spring AI 官方文档里对可观测性有一个很关键的安全边界:prompt、completion、tool 参数、tool 结果这类内容默认不应该随便导出。原因很简单,它们经常包含敏感信息。

很多团队排查问题时喜欢打开完整日志,短期看很方便,长期会变成合规风险。尤其是企业知识库、客服、合同、财务、医疗、政务类场景,用户输入和检索片段本身就是数据资产。

更稳妥的做法是分层处理:

开发环境可以临时开启详细日志,用于调 prompt 和工具参数。

测试环境可以采样记录脱敏后的 prompt 摘要,并绑定 prompt 版本。

生产环境默认只记录结构化指标、traceId、模型名、场景、耗时、token、错误码。确实需要追查内容问题时,通过审批、采样、脱敏和短期留存来处理。

AI 工程化不是把所有信息都记下来,而是知道哪些信息应该长期保留,哪些信息只能在受控条件下短暂出现。

第一版不用追求“大而全平台”

很多团队一聊 AI 可观测性,就想做一个完整平台:prompt 管理、评估集、成本中心、模型路由、用户反馈、RAG 召回分析全都上。

这当然是正确方向,但第一版容易做重。对大多数 Java 后端团队来说,更实用的路径是:

先把 Spring Boot Actuator、Micrometer、Tracing 接好,让 AI 调用进入现有监控体系。

再定义少量业务指标,比如 scenario、model、result,避免一开始就设计复杂标签。

然后补 token 成本统计,把“感觉贵”变成“哪个功能、哪个模型、哪类请求贵”。

如果用了 RAG,再继续拆检索耗时、召回数量、重排序耗时、最终上下文长度。

如果用了工具调用,再把工具成功率、耗时、失败原因纳入面板。

这样做的好处是不会打断原有系统建设。AI 应用不是一个孤岛,它仍然运行在 Spring Boot、网关、认证、日志、监控、告警、成本核算这一整套后端工程体系里。

真正成熟的 AI 应用,不是回答看起来更聪明,而是当它慢、贵、错、不可控的时候,团队能沿着 trace 和指标一步步定位到原因。对 Java 开发者来说,这正是熟悉的工程能力,只是现在排查对象从 SQL、RPC、缓存,扩展到了 token、prompt、工具调用和模型响应。

相关学习资料