跳到正文
cccskills
免费在 GitHub 公开

ai-observability

Use when adding monitoring, metrics, logging, or tracing to Spring AI or LLM integration code. Covers token tracking, latency measurement, cost estimation, and prompt/response logging. Use when user mentions AI monitoring, token costs, or LLM observability.

中文简介正在准备中。当前显示原文说明。

集成工具OpenAI
查看安装方法

包含的文件(5)

  • SKILL.md6.0 KB
  • examples/bad-ai-audit-advisor.java1.8 KB
  • examples/good-ai-audit-advisor.java2.4 KB
  • templates/AiAuditAdvisor.java3.0 KB
  • templates/AiMetrics.java2.3 KB

SKILL.md(原文)

安装前可以查看交给 AI 代理的指令内容。

AI Observability

Dependencies

<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 Built-in Observability

Spring AI 1.0+ includes built-in Micrometer instrumentation:

spring:
  ai:
    chat:
      observations:
        include-prompt: true       # log prompt content (disable in prod for PII)
        include-completion: true   # log response content
management:
  metrics:
    tags:
      application: order-service
  endpoints:
    web:
      exposure:
        include: health,prometheus,metrics

Auto-generated metrics:

  • spring.ai.chat.client.operation.seconds — latency histogram
  • spring.ai.chat.client.token.usage — token counts (input/output/total)

Custom AI Metrics

@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)
            .recordCallable(() -> call.get());
    }

    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);
    }
}

Prompt/Response Logging Advisor

@Component
public class AiAuditAdvisor implements CallAroundAdvisor {

    private static final Logger log = LoggerFactory.getLogger(AiAuditAdvisor.class);

    @Override
    public AdvisedResponse aroundCall(AdvisedRequest request, CallAroundAdvisorChain chain) {
        String requestId = UUID.randomUUID().toString();
        long start = System.currentTimeMillis();

        log.info("[AI-AUDIT] requestId={} model={} promptLength={}",
            requestId,
            request.chatOptions() != null ? request.chatOptions().getModel() : "default",
            request.userText().length());

        try {
            AdvisedResponse response = chain.nextAroundCall(request);
            long latency = System.currentTimeMillis() - start;

            ChatResponse chatResponse = response.response();
            if (chatResponse != null && chatResponse.getMetadata() != null) {
                Usage usage = chatResponse.getMetadata().getUsage();
                log.info("[AI-AUDIT] requestId={} latencyMs={} inputTokens={} outputTokens={}",
                    requestId, latency,
                    usage.getPromptTokens(), usage.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; }
}

Cost Estimation

@Service
public class AiCostEstimator {

    // Prices per million tokens — update when pricing changes
    private static final Map<String, double[]> PRICING = Map.of(
        "claude-sonnet-4-20250514", new double[]{3.0, 15.0},  // [input, output] per 1M tokens
        "claude-haiku-4-5-20251001", new double[]{0.8, 4.0},
        "gpt-4o", new double[]{5.0, 15.0},
        "gpt-4o-mini", new double[]{0.15, 0.6}
    );

    public double estimateCost(String model, int inputTokens, int outputTokens) {
        double[] prices = PRICING.getOrDefault(model, new double[]{5.0, 15.0});
        return (inputTokens * prices[0] + outputTokens * prices[1]) / 1_000_000;
    }
}

Structured AI Audit Log (DB)

@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 — Full Observability

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

Gotchas

  • Agent logs full prompts in production — disable with include-prompt: false for PII safety
  • Agent skips async on audit saves — always @Async to avoid latency impact
  • Agent hardcodes token pricing — extract to config, prices change
  • Agent misses failed calls in metrics — track errors separately with error tag

评价

还没有评价。欢迎分享你的使用感受。

评论

无需登录即可发表。欢迎分享使用感受、提问,或说明在哪些环境下无法运行。

还没有评论。来写第一条吧。

提交后会立即公开。请勿填写邮箱、电话等个人信息。URL 最多 2 个。

0 / 1000 字

此表单受 reCAPTCHA 保护,适用 Google 的隐私权政策和服务条款。

同一仓库的技能

android

免费

Provides expert knowledge for building modern native Android apps using Kotlin, Jetpack Compose, Material Design 3, MVI architecture, Hilt dependency injection, and Media3/ExoPlayer.

中文简介正在准备中。当前显示原文说明。

lgzarturo/springboot-course32026年10月17日 更新

Design, implement, review, and audit Android Jetpack Compose UI/UX for phone interfaces. Use when a request concerns concrete Compose layout, components, interaction, accessibility, or visual behavior. Do not activate for framework or dependency presence alone; non-UI Android work is excluded.

中文简介正在准备中。当前显示原文说明。

lgzarturo/springboot-course32026年10月17日 更新

Provides expert knowledge for designing, implementing, and managing REST API versioning strategies with deprecation workflows.

中文简介正在准备中。当前显示原文说明。

lgzarturo/springboot-course32026年10月17日 更新

astro

免费

Provides expert knowledge for building Astro 5+ sites with Islands Architecture, Content Collections, TypeScript, and performance-first rendering strategies.

中文简介正在准备中。当前显示原文说明。

lgzarturo/springboot-course32026年10月17日 更新

Audits token storage mechanisms to prevent XSS-based JWT theft.

中文简介正在准备中。当前显示原文说明。

lgzarturo/springboot-course32026年10月17日 更新

Provides patterns to configure AWS RDS (Aurora, MySQL, PostgreSQL) with Spring Boot applications. Configures HikariCP connection pools, implements read/write splitting, sets up IAM database authentication, enables SSL connections, and integrates with AWS Secrets Manager. Use when setting up RDS connections in Spring Boot, configuring connection pooling, or managing database credentials securely.

中文简介正在准备中。当前显示原文说明。

lgzarturo/springboot-course32026年10月17日 更新

查看 lgzarturo 的全部技能

报告此技能的问题