mcpField
A
ai-observability

ai-observability

github.com/rrezartprebreza云与 DevOps32889 次安装6 天前更新
代码仓库

关于 ai-observability

面向使用 Spring AI 1.0+ 的开发者,整理 Micrometer 指标、审计日志与成本归因的 AI 可观测性实现指导,并指出 GA 版本 API 变更和日志注意事项。

主要功能

  1. 01Spring AI 内置指标配置
  2. 02自定义 AI 指标示例
  3. 03提示词与响应审计顾问
  4. 04Token 成本归因建议
  5. 05异步审计持久化指导

适用场景

  1. 01监测模型调用延迟与 Token 用量
  2. 02记录脱敏后的 AI 调用审计数据
  3. 03按模型用量估算运营成本

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/rrezartprebreza/spring-boot-skills --skill ai-observability

安装后在 Claude Code 中输入/ai-observability确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

agents/
openai.yaml
examples/
bad-ai-audit-advisor.java
good-ai-audit-advisor.java
templates/
AiAuditAdvisor.java
AiMetrics.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