很多 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 类信号:
-
请求量
区分不同业务场景、模型、租户或功能入口,但不要把用户 ID、问题原文这类高基数字段放进 tag。
-
延迟
至少拆出接口总耗时和模型调用耗时。如果接了 RAG,还要拆出向量检索、重排序、模型生成。
-
失败率
不要只统计异常。模型返回空内容、结构化输出解析失败、工具调用参数不合法,也应该算失败。
-
token 使用量
token 是 AI 应用成本的核心变量。一次请求贵不贵,不看字符数,要看输入 token、输出 token、上下文拼接策略和模型选择。
-
工具调用结果
如果使用 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、工具调用和模型响应。