mcpField
A
ai-observability

ai-observability

github.com/rrezartprebrezaクラウド・DevOps32889 回インストール6 日前に更新
リポジトリ

ai-observability について

Spring AI 1.0+を使う開発者向けに、Micrometerのメトリクス、監査ログ、コスト帰属の実装方法をまとめたスキルです。GA版のAPI変更や、プロンプト漏えい防止、監査処理の非同期化に関する注意点も扱います。

主な機能

  1. 01Spring AI内蔵メトリクスの設定
  2. 02カスタムAIメトリクスの例
  3. 03プロンプトと応答の監査アドバイザー
  4. 04トークンコスト帰属の指針
  5. 05非同期監査保存の指針

活用シーン

  1. 01モデル呼び出しの遅延とトークン量の監視
  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 — トークン数(入力/出力/合計)
  • 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() です。エージェントは古い 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; }
}

コストの割り当て

  • プロバイダーの料金は、適用開始日と通貨を含め、外部で管理する設定に保持します。
  • 料金は、使用状況のメタデータで返されるプロバイダーモデル識別子と完全に一致するキーで管理します。
  • 不明なモデルにはデフォルト料金を暗黙に適用せず、エラーにします。
  • 料金変更後に過去のコストを再計算できるよう、トークン使用量の生データを保持します。
  • 請求書にはプロバイダーの請求データを優先して使用します。アプリケーションによる推定値は、運用上の目安に限ります。

構造化 AI 監査ログ(DB)

audit advisor template はテレメトリをベストエフォートとして扱います。別の非同期 Bean の呼び出し時と、その内部での永続化時の両方で送信失敗を捕捉してください。executor が満杯または停止している場合、メソッドの実行前にタスクが拒否されることがあります。容量制限のある executor を使用し、監査タスクの拒否を監視してください。応答レイテンシーを監査ストレージから独立させる必要がある場合は、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

注意点

  • エージェントが CallAroundAdvisor/AdvisedRequest を実装する — GA で削除済みです。CallAdvisor/ChatClientRequest を使用してください
  • エージェントが usage.getGenerationTokens() を呼び出す — GA で getCompletionTokens() に名称が変更されました
  • エージェントが本番環境でプロンプト全体をログに記録する — PII を保護するため、log-prompt: false のままにしてください
  • エージェントが監査保存を非同期にしない — レイテンシーへの影響を避けるため、必ず @Async を使用し、@Async メソッドは別の Bean に配置してください。this 経由で呼び出すとプロキシを迂回し、同期実行されます
  • エージェントがトークン料金をハードコードする — 料金は変わるため、設定に切り出してください
  • エージェントが失敗した呼び出しをメトリクスに含めない — エラータグを付けて、エラーを別途追跡してください

コメント