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 应用不能只在日志里打印:

XML 复制代码
log.info("ai answer: {}", answer);

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

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

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

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

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

XML 复制代码
<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:

XML 复制代码
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 调用,但业务侧仍然需要补充"这次调用属于什么场景"。

XML 复制代码
@Service
public 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、工具调用和模型响应。

相关推荐
小糯米6011 小时前
C语言 自定义类型:联合和枚举
java·c语言·开发语言
basketball6161 小时前
AI Infra 硬件体系与编程模型:6. Warp 调度器详解
人工智能
weixin_523185321 小时前
Java基础知识总结(二):JVM内存结构与变量生命周期
java·开发语言·jvm
我有2只猫1 小时前
LabelStudio二次开发
人工智能·python·django·ocr
多年小白1 小时前
AI 日报 - 2026年6月7日
人工智能·量子计算
我是大猴子1 小时前
连接池+虚拟线程
java
技术小结-李爽1 小时前
【工具】如何认识Maven
java·maven
前端的阶梯1 小时前
如何节省你的token,请看CodeGraph
前端·人工智能·后端
小碗羊肉1 小时前
【RabbitMQ高级】如何保证消息的可靠性?
java·rabbitmq·java-rabbitmq