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、工具调用和模型响应。

相关推荐
Claire_881 分钟前
基于知识图谱构建的学习资料分类整理方案:以多模态检索与智能生成为例
人工智能·powerpoint
花椒技术5 分钟前
一个人已经有 Agent 了,我们为什么还要建设统 Agent 平台
人工智能·agent·ai编程
心平气和量大福大19 分钟前
android-实例-蒲公英-更新与安装-5-签名与生成APK
android·java·开发语言
Olafur_zbj23 分钟前
【AI】CUDA的新编程模型:tile编程模型 的好处
人工智能
Linguwen1 小时前
外贸GEO01|GEO是什么?生成式引擎优化,AI时代的新流量密码
人工智能
952361 小时前
Spring-cloud配置中心
java·spring·spring cloud·微服务
物质波波波1 小时前
WS-RPE:面向边缘物理AI实时特征值计算的硬件工作窃取调度器与冗余PE激活架构
人工智能·fpga开发·架构·系统架构·硬件架构
起个名字好难啊这也被占用了1 小时前
LangGraphjs可中断可恢复的AI工作流
人工智能·ai编程
@Mr_LiuYang1 小时前
状态栏动态上下文信息追加到Agent --《深入理解 AI Agent :设计原理与工程实践》实验2-8
人工智能·大模型·动态上下文·状态栏信息追加