李眉站在白板前:"AI 调用跟数据库不一样——每次调用都花钱。Token 用量、延迟、成功率,一个都不能少。Spring AI 已经内置了 Micrometer 集成,我们只需要配置。"
可观测性与指标监控
定义与作用
可观测性是 AI 应用上线后的"生命体征监测系统"。Spring AI 基于 Micrometer Observation API 自动为每次 AI 调用生成分布式追踪 Span 和指标,覆盖 Token 用量、调用延迟、错误率三个核心维度。
核心原理
图释:ChatClient 内部通过 Observation API 自动创建 Span → Micrometer Registry 收集指标 → 导出到 Prometheus + Grafana 实现可视化,或导出到 Zipkin/Jaeger 实现调用链追踪。
关键配置
spring:
ai:
observation:
include-usage: true # 记录 Token 用量
include-completion: false # 生产环境关闭,避免泄露敏感内容
include-prompt: false # 生产环境关闭
自动生成的指标
| 指标名称 | 类型 | 说明 |
|---|---|---|
spring.ai.chat.client.requests | Counter | AI 调用总次数 |
spring.ai.chat.client.operation.duration | Timer | 每次调用耗时(含网络) |
gen_ai.client.operation.duration | Timer | 生成式 AI 操作耗时 |
gen_ai.usage.input_tokens | DistributionSummary | 输入 Token 数量 |
gen_ai.usage.output_tokens | DistributionSummary | 输出 Token 数量 |
gen_ai.usage.total_tokens | DistributionSummary | 总 Token 数量 |
完整示例一:集成 Prometheus + Grafana
场景说明
李眉需要为飞翔科技智能客服搭建 AI 调用监控面板。
依赖引入
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
</dependency>
management:
endpoints:
web:
exposure:
include: prometheus, metrics, health
metrics:
export:
prometheus:
enabled: true
spring:
ai:
observation:
include-usage: true
关键代码
@RestController
public class ObservedChatController {
private final ChatClient chatClient;
private final MeterRegistry meterRegistry;
public ObservedChatController(
ChatClient chatClient,
MeterRegistry meterRegistry) {
this.chatClient = chatClient;
this.meterRegistry = meterRegistry;
}
@GetMapping("/observed/chat")
public String chat(@RequestParam String message) {
// ChatClient 内部自动生成 Span,无需手动编码
return chatClient.prompt()
.user(message)
.call()
.content();
}
@GetMapping("/observed/stats")
public Map<String, Object> stats() {
Map<String, Object> result = new HashMap<>();
// 查询 Token 用量
meterRegistry.find("gen_ai.usage.total_tokens")
.summary().ifPresent(s -> {
result.put("totalTokens", s.totalAmount());
result.put("avgTokens", s.mean());
});
// 查询调用延迟
meterRegistry.find("gen_ai.client.operation.duration")
.timer().ifPresent(t -> {
result.put("avgDurationMs", t.mean(TimeUnit.MILLISECONDS));
result.put("totalCalls", t.count());
});
return result;
}
}
Grafana 面板查询
# AI 调用 QPS
rate(spring_ai_chat_client_requests_total[1m])
# 平均 Token 消耗
gen_ai_usage_total_tokens_sum / gen_ai_usage_total_tokens_count
# P99 延迟(毫秒)
histogram_quantile(0.99,
rate(gen_ai_client_operation_duration_seconds_bucket[5m])) * 1000
完整示例二:自定义观测埋点
场景说明
大翔要求按"用户提问类别"分类统计 AI 调用量。
@Component
public class CategoryTaggingAdvisor implements RequestResponseAdvisor {
private final MeterRegistry meterRegistry;
public CategoryTaggingAdvisor(MeterRegistry meterRegistry) {
this.meterRegistry = meterRegistry;
}
@Override
public Prompt adviseRequest(Prompt prompt,
Map<String, Object> context) {
String userContent = prompt.getInstructions().stream()
.filter(m -> m.getMessageType() == MessageType.USER)
.map(Message::getContent)
.findFirst().orElse("");
// 简单分类(实际可用 AI 做更精准的分类)
String category = categorize(userContent);
// 按类别计数
Counter.builder("ai.chat.category")
.tag("category", category)
.register(meterRegistry)
.increment();
return prompt;
}
private String categorize(String text) {
if (text.contains("课程") || text.contains("学分"))
return "course";
if (text.contains("费用") || text.contains("学费"))
return "financial";
return "general";
}
@Override
public ChatResponse adviseResponse(ChatResponse response,
Map<String, Object> context) {
return response;
}
@Override
public String getName() { return "category-tagging"; }
}
运行结果
访问 /actuator/prometheus 输出:
ai_chat_category_total{category="course"} 1523.0
ai_chat_category_total{category="financial"} 421.0
ai_chat_category_total{category="general"} 3056.0
在 Grafana 中可配置饼图,直观展示各类别调用占比。
易错场景与面试考点
易错场景一:生产环境开启 include-prompt 导致敏感信息泄露
# ❌ 错误:生产环境记录完整 Prompt
spring:
ai:
observation:
include-prompt: true # Prompt 可能含用户隐私数据
问题分析
追踪系统中的 Span 标签对运维人员可见,开启 include-prompt 会将用户问题和检索上下文记录到 Zipkin/Jaeger,造成隐私泄露。
# ✅ 正确:生产环境只记录 Token 用量
spring:
ai:
observation:
include-usage: true
include-prompt: false
include-completion: false
面试高频题
Q1:Spring AI 如何实现可观测性?
基于 Micrometer Observation API,自动为
ChatClient.call()和ChatModel.call()生成 Span,命名规则为spring.ai.<provider>.<action>(如spring.ai.openai.chat)。Span 包含模型名称、Token 用量、调用状态等标签,可通过 Prometheus/Grafana 导出。
Q2:如何估算 AI API 调用成本?
通过 Micrometer 的 Token 用量指标:输入 Token × 输入单价 + 输出 Token × 输出单价。利用 DistributionSummary 统计每日总 Token 量,乘以对应模型的价格即可估算日成本。Grafana 面板可配置成本折线图。
本章小结
- Spring AI 自动生成 AI 调用 Span 和指标,零侵入
include-usage开启 Token 用量监控,include-prompt生产环境必须关闭- 支持 Prometheus + Grafana 可视化,Zipkin/Jaeger 分布式追踪
- 自定义 Advisor 可注入业务维度的观测埋点