可观测性
Spring AI 基于 Micrometer 提供开箱即用的可观测性支持,自动记录 ChatModel、EmbeddingModel、ImageModel、VectorStore、Tool Calling 等组件的调用指标、耗时和内容快照,可对接 Zipkin、Grafana、Datadog 等主流监控后端。
1. 快速启用
引入对应 AI 模型或向量数据库的 Spring Boot Starter 即可自动启用可观测性,无需额外依赖。
首次使用时只需加入 Micrometer 的监控后端实现,例如 Zipkin:
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-tracing-bridge-brave</artifactId>
</dependency>
<dependency>
<groupId>io.zipkin.reporter2</groupId>
<artifactId>zipkin-reporter-brave</artifactId>
</dependency>
启动应用后,所有 AI 组件的调用自动出现在 Zipkin 的 trace 面板中。
2. 配置项
各组件的可观测性默认开启,可通过配置控制日志输出和指标采集:
| 配置项 | 默认值 | 说明 |
|---|---|---|
spring.ai.chat.observations.log-completion | false | 是否在日志中输出模型的完整回复内容 |
spring.ai.chat.observations.log-error | false | 是否在日志中输出模型的错误回复 |
spring.ai.chat.observations.include-input | false | 是否在指标中附带用户输入内容 |
spring.ai.chat.client.observations.include-input | false | ChatClient 观测是否附带用户输入 |
spring.ai.image.observations.log-response | false | 是否在日志中输出图片生成响应 |
spring.ai.image.observations.log-error | false | 是否在日志中输出图片生成的错误 |
spring.ai.image.observations.include-input | false | 是否在指标中附带输入的提示词 |
spring.ai.tool.observations.include-input | false | 工具调用是否附带输入参数 |
spring.ai.tool.observations.include-error | false | 工具调用是否附带错误信息 |
spring.ai.vectorstore.observations.log-query | false | 是否在日志中输出向量搜索查询 |
spring.ai.vectorstore.observations.log-results | false | 是否在日志中输出向量搜索结果 |
spring.ai.vectorstore.observations.log-error | false | 是否在日志中输出向量存储的错误 |
spring.ai.vectorstore.observations.include-input | false | 是否在指标中附带输入查询 |
spring.ai.tracing.include-request-response | false | 总体开关:是否在所有 trace span 中附带请求/响应内容 |
spring:
ai:
chat:
observations:
log-completion: true # 调试时开启,便于排查模型回复
log-error: true
vectorstore:
observations:
log-query: true
log-error: true
tracing:
include-request-response: true
3. 可观测的组件
3.1 ChatModel
每次调用 ChatModel 或 StreamingChatModel 自动创建一个 Observation,记录模型名称、操作类型、AI 提供商等维度信息。
低基数标签(维度):
| 标签 | 值示例 | 说明 |
|---|---|---|
gen_ai.operation.name | chat, streaming | 调用操作类型 |
gen_ai.system | spring_ai | AI 框架标识 |
gen_ai.request.model | qwen3:8b | 使用的模型名称 |
gen_ai.provider | ollama, openai | AI 服务提供商 |
gen_ai.content.usage.input_tokens | 150 | 输入 token 数 |
gen_ai.content.usage.output_tokens | 80 | 输出 token 数 |
高基数标签(详细诊断):
| 标签 | 说明 |
|---|---|
gen_ai.error.type | 错误类型(发生错误时) |
gen_ai.request.temperature | 模型温度参数 |
gen_ai.request.top_p | top_p 参数 |
gen_ai.request.top_k | top_k 参数 |
gen_ai.response.id | 响应的唯一标识 |
3.2 EmbeddingModel
每次向量化调用自动记录嵌入模型、输入 token 数等指标。
| 标签 | 值示例 | 说明 |
|---|---|---|
gen_ai.operation.name | embedding | 操作类型 |
gen_ai.system | spring_ai | AI 框架标识 |
gen_ai.request.model | mxbai-embed-large | 嵌入模型名称 |
gen_ai.provider | ollama | AI 服务提供商 |
3.3 ImageModel
图片生成调用自动记录模型名称、生成数量、尺寸等参数。
| 标签 | 值示例 | 说明 |
|---|---|---|
gen_ai.operation.name | image_generation | 操作类型 |
gen_ai.system | spring_ai | AI 框架标识 |
gen_ai.request.model | dall-e-3 | 图片模型名称 |
gen_ai.provider | openai | AI 服务提供商 |
3.4 VectorStore
向量存储的写入、删除和检索操作自动记录,包含数据库信息、查询相似度等指标。
| 标签 | 值示例 | 说明 |
|---|---|---|
db.system | postgresql, redis, qdrant | 数据库类型 |
db.operation | add, delete, similarity_search | 操作类型 |
db.vector.metrics | cosine, euclidean | 相似度度量方式 |
db.vector.top_k | 5 | 返回结果数 |
db.vector.similarity_threshold | 0.75 | 相似度阈值 |
如果 VectorStore 实现未自动集成观测,可使用 AbstractObservationVectorStore 包装:
VectorStore rawStore = new SimpleVectorStore(embeddingModel);
// 包装后可观测
VectorStore observableStore = new ObservationVectorStore(
rawStore, observationRegistry, "custom_convention");
3.5 Tool Calling
工具调用的执行自动记录,包括工具名称、执行耗时、错误信息。
| 标签 | 值示例 | 说明 |
|---|---|---|
spring.ai.tool.name | getWeather | 被调用的工具名称 |
spring.ai.tool.status | success, error | 执行结果状态 |
3.6 ChatClient
ChatClient 层面的整次调用(涵盖 Advisors 链 + ChatModel 调用 + 工具回调 + 响应生成)统一记录。
| 标签 | 值示例 | 说明 |
|---|---|---|
spring.ai.chat.client.name | myChatClient | ChatClient 名称(Builder 指定时) |
spring.ai.chat.client.prompt.type | text, multimodal | 提示词类型 |
3.7 Advisor
Advisor 链中每个 Advisor 的执行开始和结束自动记录:
| 标签 | 说明 |
|---|---|
spring.ai.advisor.name | Advisor 实现类简称 |
spring.ai.advisor.type | 执行阶段:before 或 after |
4. 自定义观测约定
可以通过继承默认约定类并重写标签方法,在指标中添加自定义维度。
@Component
public class TenantAwareChatModelObservationConvention
extends DefaultChatModelObservationConvention {
@Override
public KeyValues getLowCardinalityKeyValues(
ChatModelObservationContext context) {
return super.getLowCardinalityKeyValues().and(
KeyValue.of("tenant.id", TenantContext.getCurrentTenant())
);
}
@Override
public KeyValues getHighCardinalityKeyValues(
ChatModelObservationContext context) {
return super.getHighCardinalityKeyValues().and(
KeyValue.of("user.id", UserContext.getCurrentUserId())
);
}
}
约定类只需声明为 Spring Bean,Spring AI 的自动配置会优先使用自定义约定而非默认约定。
可重写的约定类:
| 默认约定类 | 观测范围 |
|---|---|
DefaultChatModelObservationConvention | ChatModel / StreamingChatModel |
DefaultEmbeddingModelObservationConvention | EmbeddingModel |
DefaultImageModelObservationConvention | ImageModel |
DefaultVectorStoreObservationConvention | VectorStore 读/写/搜索 |
5. 手动创建 Span
对于自定义组件或需要更细粒度的追踪,可以直接使用 Micrometer 的 ObservationRegistry 手动创建观测:
@Component
public class CustomPipeline {
private final ObservationRegistry registry;
public CustomPipeline(ObservationRegistry registry) {
this.registry = registry;
}
public String execute(String input) {
Observation observation = Observation.createNotStarted(
"custom.ai.pipeline", registry)
.lowCardinalityKeyValue("pipeline.name", "document-etl")
.start();
try (Observation.Scope scope = observation.openScope()) {
// 执行业务逻辑
String result = process(input);
observation.event(Observation.Event.of("pipeline.completed"));
return result;
} catch (Exception e) {
observation.error(e);
throw e;
} finally {
observation.stop();
}
}
private String process(String input) {
return "processed: " + input;
}
}
6. 完整示例
完整示例:ObservabilityExample.java
public class ObservabilityExample {
public static void main(String[] args) {
var ctx = SpringApplication.run(ObservabilityExample.class, args);
OllamaApi ollamaApi = OllamaApi.builder()
.baseUrl("http://localhost:11434")
.build();
// ChatModel 调用自动观测
OllamaChatModel chatModel = OllamaChatModel.builder()
.ollamaApi(ollamaApi)
.defaultOptions(OllamaOptions.builder().model("qwen3:8b").build())
.build();
ChatClient chatClient = ChatClient.builder(chatModel)
.defaultAdvisors(
new SimpleLoggerAdvisor(),
new MessageChatMemoryAdvisor(new InMemoryChatMemory())
)
.build();
// 每次调用都会在 Zipkin/Grafana 中产生 trace span
String response = chatClient.prompt()
.user("介绍一下 Spring AI 的可观测性")
.advisors(a -> a.param("chat_memory_conversation_id", "obs-001"))
.call()
.content();
System.out.println(response);
}
}