关于 ai-observability
面向使用 Spring AI 1.0+ 的开发者,整理 Micrometer 指标、审计日志与成本归因的 AI 可观测性实现指导,并指出 GA 版本 API 变更和日志注意事项。
主要功能
- 01Spring AI 内置指标配置
- 02自定义 AI 指标示例
- 03提示词与响应审计顾问
- 04Token 成本归因建议
- 05异步审计持久化指导
适用场景
- 01监测模型调用延迟与 Token 用量
- 02记录脱敏后的 AI 调用审计数据
- 03按模型用量估算运营成本
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ rrezartprebreza/ spring-boot-skills --skill ai-observability安装后在 Claude Code 中输入/ai-observability确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
agents/openai.yamlexamples/bad-ai-audit-advisor.javagood-ai-audit-advisor.javatemplates/AiAuditAdvisor.javaAiMetrics.java详细介绍
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>
Spring AI 内置可观测性
Spring AI 1.0+ 内置了 Micrometer 监测功能:
spring:
ai:
chat:
observations:
log-prompt: false # GA renamed include-prompt → log-prompt. true only in local dev (PII)
log-completion: false # GA renamed include-completion → log-completion
management:
metrics:
tags:
application: order-service
endpoints:
web:
exposure:
include: health,prometheus,metrics
自动生成的指标(OpenTelemetry GenAI 语义约定):
gen_ai.client.operation— 模型调用延迟,附带提供商和模型标签gen_ai.client.token.usage— token 数量(输入/输出/总计)spring.ai.chat.client— ChatClient 级别的操作计时器/跨度
自定义 AI 指标
@Component
@RequiredArgsConstructor
public class AiMetrics {
private final MeterRegistry meterRegistry;
private final Timer.Builder promptTimer = Timer.builder("ai.prompt.latency")
.description("LLM prompt latency");
private final Counter.Builder tokenCounter = Counter.builder("ai.tokens.used")
.description("Total tokens consumed");
public <T> T track(String operation, String model, Supplier<T> call) {
return Timer.builder("ai.prompt.latency")
.tag("operation", operation)
.tag("model", model)
.register(meterRegistry)
.record(call); // record(Supplier) — recordCallable throws checked Exception
}
public void recordTokens(String operation, String model, int inputTokens, int outputTokens) {
Counter.builder("ai.tokens.used")
.tag("operation", operation)
.tag("model", model)
.tag("type", "input")
.register(meterRegistry)
.increment(inputTokens);
Counter.builder("ai.tokens.used")
.tag("operation", operation)
.tag("model", model)
.tag("type", "output")
.register(meterRegistry)
.increment(outputTokens);
}
}
提示词/响应日志顾问
GA 版本替换了整个顾问 API:CallAroundAdvisor → CallAdvisor、AdvisedRequest →
ChatClientRequest、AdvisedResponse → ChatClientResponse,以及 Usage.getGenerationTokens() →
getCompletionTokens()。Agent 总是生成旧版本的 API——它在 1.0 中无法编译。
@Component
public class AiAuditAdvisor implements CallAdvisor {
private static final Logger log = LoggerFactory.getLogger(AiAuditAdvisor.class);
@Override
public ChatClientResponse adviseCall(ChatClientRequest request, CallAdvisorChain chain) {
String requestId = UUID.randomUUID().toString();
long start = System.currentTimeMillis();
log.info("[AI-AUDIT] requestId={} promptLength={}",
requestId, request.prompt().getUserMessage().getText().length());
try {
ChatClientResponse response = chain.nextCall(request);
long latency = System.currentTimeMillis() - start;
ChatResponse chatResponse = response.chatResponse();
if (chatResponse != null && chatResponse.getMetadata() != null) {
Usage usage = chatResponse.getMetadata().getUsage();
log.info("[AI-AUDIT] requestId={} latencyMs={} inputTokens={} outputTokens={}",
requestId, latency,
usage.getPromptTokens(), usage.getCompletionTokens()); // GA: not getGenerationTokens()
}
return response;
} catch (Exception e) {
log.error("[AI-AUDIT] requestId={} FAILED after {}ms", requestId,
System.currentTimeMillis() - start, e);
throw e;
}
}
@Override
public String getName() { return "AiAuditAdvisor"; }
@Override
public int getOrder() { return Ordered.LOWEST_PRECEDENCE; }
}
成本归因
- 将提供商价格存放在外部管理的配置中,并注明生效日期和货币。
- 使用用量元数据中返回的确切提供商模型标识符作为价格键。
- 遇到未知模型时应拒绝处理,而不是静默套用默认价格。
- 保留原始 token 用量,以便在价格变更后重新计算历史成本。
- 发票应优先使用提供商的计费导出数据;应用估算仅作为运维参考。
结构化 AI 审计日志(DB)
audit advisor 模板将遥测视为尽力而为。既要在调用独立异步 Bean 的代码周围捕获提交失败,也要处理其中的持久化失败:执行器已满或已停止时,任务可能在方法运行前就被拒绝。使用有界执行器并监控被拒绝的审计任务;若必须确保响应延迟不受审计存储影响,请勿使用 caller-runs。存储经过清理的错误类型,而不是提供商异常消息原文。强制合规审计记录需要可靠持久的投递机制,不能采用这种尽力而为的设计。
请参阅 Spring 任务执行与拒绝策略。
@Entity
@Table(name = "ai_audit_log")
public class AiAuditLog {
@Id @GeneratedValue(strategy = GenerationType.UUID)
private UUID id;
private String operation;
private String model;
private int inputTokens;
private int outputTokens;
private double estimatedCostUsd;
private long latencyMs;
private boolean success;
private Instant createdAt;
}
// Async to avoid blocking main flow
@Async
public void saveAuditLog(AiAuditLog log) {
auditLogRepository.save(log);
}
application.yml — 完整可观测性
management:
endpoints:
web:
exposure:
include: health,prometheus,metrics,info
metrics:
distribution:
percentiles-histogram:
ai.prompt.latency: true # enables P50/P95/P99
tracing:
sampling:
probability: 1.0 # 100% trace sampling in dev, reduce in prod
logging:
level:
org.springframework.ai: DEBUG # enable in dev only
注意事项
- Agent 实现了
CallAroundAdvisor/AdvisedRequest——它们已在 GA 版本中移除;请使用CallAdvisor/ChatClientRequest - Agent 调用了
usage.getGenerationTokens()——GA 版本将其重命名为getCompletionTokens() - Agent 在生产环境中记录完整提示词——为保护 PII,请保持
log-prompt: false - Agent 保存审计记录时跳过异步处理——务必使用
@Async避免影响延迟,并将@Async方法放在单独的 Bean中;在this上调用该方法会绕过代理并同步运行 - Agent 将 token 价格硬编码——应提取到配置中,因为价格会变动
- Agent 未在指标中统计失败的调用——请使用错误标签单独跟踪错误
评论
支持 Markdown