文章目录
- [1. 项目概述](#1. 项目概述)
- [2. 架构原理](#2. 架构原理)
-
- [2.1 观测数据流](#2.1 观测数据流)
- [2.2 三个观测层级](#2.2 三个观测层级)
- [3. 环境准备](#3. 环境准备)
-
- [3.1 前置条件](#3.1 前置条件)
- [3.2 依赖说明](#3.2 依赖说明)
- [4. 自动配置](#4. 自动配置)
-
- [4.1 自动配置类](#4.1 自动配置类)
- [4.2 自动配置源码逻辑](#4.2 自动配置源码逻辑)
- [4.3 如何使用自动配置的 Bean](#4.3 如何使用自动配置的 Bean)
- [4.4 配置属性](#4.4 配置属性)
- [5. 应用配置](#5. 应用配置)
-
- [5.1 application.yml 关键配置](#5.1 application.yml 关键配置)
- [5.2 logback-spring.xml](#5.2 logback-spring.xml)
- [6. 核心代码实现](#6. 核心代码实现)
-
- [6.1 启动类](#6.1 启动类)
- [6.2 StateGraph + 观测](#6.2 StateGraph + 观测)
- [6.3 ReactAgent + 观测](#6.3 ReactAgent + 观测)
- [6.4 OtlpLoggingConfig](#6.4 OtlpLoggingConfig)
- [6.5 ObservationFilterConfig](#6.5 ObservationFilterConfig)
- [7. 观测指标(Metrics)](#7. 观测指标(Metrics))
-
- [7.1 自动采集的指标](#7.1 自动采集的指标)
- [7.2 查看指标](#7.2 查看指标)
- [8. 观测拦截器示例](#8. 观测拦截器示例)
-
- [8.1 Bean 注册 Hook](#8.1 Bean 注册 Hook)
- [8.2 树型 Span 结构](#8.2 树型 Span 结构)
- [9. API 端点](#9. API 端点)
1. 项目概述
本案例演示如何为 Spring AI Alibaba 的 ReactAgent 和 StateGraph 接入完整的可观测性方案,覆盖:
- 图执行观测 :
Graph/Node/Edge三级生命周期的Trace和Metrics - 模型调用观测 :
ChatClient/ChatModel的调用耗时、Token用量、Prompt/Response日志 - OTLP 导出 :
Trace+Metrics+Logs通过OTLP/gRPC统一导出到SkyWalking OAP - Prometheus + Grafana :
Metrics通过/actuator/prometheus暴露
2. 架构原理
2.1 观测数据流
┌──────────────────────────────────────────────────────────────┐
│ 应用层 │
│ ReactAgent / StateGraph ↔ ChatClient ↔ ChatModel │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ GraphObservation ChatClient ChatModel │
│ LifecycleListener Observation Observation │
│ │ │ │ │
│ └──────────────────────┼───────────────┘ │
│ ▼ │
│ Micrometer Observation API │
│ (ObservationRegistry) │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ Metrics Tracing Logging │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ OTLP/gRPC OTLP/gRPC OTLP/gRPC │
└────────────────────┬─────────┬─────────┬────────────────────┘
│ │ │
▼ ▼ ▼
┌──────────────────────────────────┐
│ SkyWalking OAP │
│ (192.168.1.111:4319 / 12800) │
└──────────────┬───────────────────┘
│
┌──────────────┴───────────────────┐
│ Prometheus + Grafana │
└──────────────────────────────────┘
2.2 三个观测层级
| 层级 | 范围 | 数据 | 来源 |
|---|---|---|---|
| Graph 级 | StateGraph 整体执行 | 执行次数、耗时、成功率、每个节点生命周期 | GraphObservationLifecycleListener |
| Chat 级 | 每次 LLM 调用 | 调用耗时、Token 用量(input/output/total) | Spring AI ChatModelObservation |
| HTTP 级 | REST API 请求 | 请求量、延迟、错误率 | Spring Boot Actuator |
3. 环境准备
3.1 前置条件
JDK 17+Maven 3.6+- 阿里云
DashScope API Key SkyWalking OAP(已部署在192.168.1.111:4319)
3.2 依赖说明
xml
<!-- 核心:Agent Framework -->
<dependency>
<groupId>com.alibaba.cloud.ai</groupId>
<artifactId>spring-ai-alibaba-agent-framework</artifactId>
<version>1.1.2.2</version>
</dependency>
<!-- 自动配置:Graph Observation(一键启用图执行观测) -->
<dependency>
<groupId>com.alibaba.cloud.ai</groupId>
<artifactId>spring-ai-alibaba-starter-graph-observation</artifactId>
<version>1.1.2.2</version>
</dependency>
<!-- 观测基础设施 -->
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-tracing</artifactId> <!-- Trace 抽象 -->
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-tracing-bridge-otel</artifactId> <!-- Micrometer → OTel 桥接 -->
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-otlp</artifactId> <!-- Metrics OTLP 导出 -->
</dependency>
<dependency>
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-exporter-otlp</artifactId> <!-- Trace OTLP 导出 -->
</dependency>
<dependency>
<groupId>io.opentelemetry.instrumentation</groupId>
<artifactId>opentelemetry-logback-appender-1.0</artifactId> <!-- Logs OTLP 导出 -->
<version>2.14.0-alpha</version>
</dependency>
4. 自动配置
spring-ai-alibaba-starter-graph-observation 通过 GraphObservationAutoConfiguration 提供一键自动装配。
4.1 自动配置类
类名 :com.alibaba.cloud.ai.autoconfigure.graph.GraphObservationAutoConfiguration
触发条件:
@ConditionalOnClass检测到ObservationRegistry和GraphObservationLifecycleListenerspring.ai.alibaba.graph.observation.enabled=true(默认就是true)
自动注册的 Bean:
| Bean 名称 | 类型 | 作用 |
|---|---|---|
observationGraphCompileConfig |
CompileConfig |
预配置了 ObservationRegistry + GraphObservationLifecycleListener,Graph 编译时直接传入 |
graphObservationLifecycleListener |
GraphObservationLifecycleListener |
监听 Graph 执行全生命周期,在 Node/Edge 级别创建 Span |
graphObservationHandler |
GraphObservationHandler |
Graph 级别的 Span 创建和属性填充 |
graphNodeObservationHandler |
GraphNodeObservationHandler |
Node 级别的 Span 创建 |
graphEdgeObservationHandler |
GraphEdgeObservationHandler |
Edge 路由级别的 Span 创建 |
4.2 自动配置源码逻辑
GraphObservationAutoConfiguration 的核心装配逻辑等价于:
java
@Configuration
@ConditionalOnClass({ObservationRegistry.class, GraphObservationLifecycleListener.class})
@EnableConfigurationProperties(GraphObservationProperties.class)
public class GraphObservationAutoConfiguration {
@Bean
public CompileConfig observationGraphCompileConfig(
ObjectProvider<ObservationRegistry> observationRegistry,
ObjectProvider<GraphObservationLifecycleListener> listeners) {
CompileConfig.Builder builder = CompileConfig.builder();
observationRegistry.ifUnique(builder::observationRegistry);
listeners.ifUnique(builder::withLifecycleListener);
return builder.build();
}
@Bean
public GraphObservationLifecycleListener graphObservationLifecycleListener(
ObservationRegistry observationRegistry,
ObjectProvider<GraphObservationConvention> convention,
ObjectProvider<GraphNodeObservationConvention> nodeConvention,
ObjectProvider<GraphEdgeObservationConvention> edgeConvention) {
return new GraphObservationLifecycleListener(
observationRegistry,
convention.getIfUnique(),
nodeConvention.getIfUnique(),
edgeConvention.getIfUnique()
);
}
// GraphObservationHandler, GraphNodeObservationHandler, GraphEdgeObservationHandler ...
}
4.3 如何使用自动配置的 Bean
在自己的 Config 中直接注入 observationGraphCompileConfig:
java
@Bean
public CompiledGraph myGraph(
@Qualifier("observationGraphCompileConfig") CompileConfig observationConfig) {
StateGraph graph = new StateGraph();
// ... 添加节点和边
return graph.compile(observationConfig); // ← 传入即可启用观测
}
@Bean
public ReactAgent myAgent(
ObservationRegistry observationRegistry,
@Qualifier("observationGraphCompileConfig") CompileConfig observationConfig) {
return ReactAgent.builder()
.model(chatModel)
.observationRegistry(observationRegistry) // ← ChatModel 层观测
.compileConfig(observationConfig) // ← Graph 层观测
.build();
}
4.4 配置属性
spring-ai-alibaba-starter-graph-observation 提供的 GraphObservationProperties:
yaml
spring.ai.alibaba.graph.observation:
enabled: true # 是否启用 Graph 观测(默认 true)
Spring AI 自带的 ChatClient/ChatModel 观测配置:
yaml
spring.ai.chat.client.observations:
log-prompt: true # 日志中记录 Prompt
log-completion: true # 日志中记录 Response
spring.ai.chat.observations:
log-prompt: true
log-completion: true
include-error-logging: true # 记录异常日志
spring.ai.tools.observations:
include-content: true # Span 中包含工具调用入参和返回值
5. 应用配置
5.1 application.yml 关键配置
yaml
server.port: 8080
spring.application.name: spring-ai-alibaba-observation
# DashScope 模型
spring.ai.dashscope:
api-key: ${DASHSCOPE_API_KEY}
chat.options:
model: qwen-plus
# Graph 观测开关
spring.ai.alibaba.graph.observation.enabled: true
# Chat 观测配置
spring.ai:
tools.observations.include-content: true
chat.client.observations:
log-prompt: true
log-completion: true
chat.observations:
log-prompt: true
log-completion: true
include-error-logging: true
# OTLP 导出到 SkyWalking
management:
tracing:
enabled: true
sampling.probability: 1.0
otlp:
tracing:
endpoint: http://192.168.1.111:4319
transport: grpc
export.enabled: true
logging:
endpoint: http://192.168.1.111:4319
transport: grpc
export.enabled: true
endpoints.web.exposure.include: health,metrics,info
# 日志格式(带上 traceId / spanId)
logging:
pattern:
level: "%5p [${spring.application.name:},%X{traceId:-},%X{spanId:-}]"
5.2 logback-spring.xml
xml
<configuration>
<property name="CONSOLE_LOG_PATTERN"
value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level [%mdc{traceId:-N/A}] [%mdc{spanId:-N/A}] %logger{36} - %msg%n"/>
<include resource="org/springframework/boot/logging/logback/defaults.xml"/>
<include resource="org/springframework/boot/logging/logback/console-appender.xml"/>
<!-- OTLP 日志 Appender:日志也通过 OTLP 上报到 SkyWalking -->
<appender name="OTLP"
class="io.opentelemetry.instrumentation.logback.appender.v1_0.OpenTelemetryAppender">
<captureExperimentalAttributes>true</captureExperimentalAttributes>
<captureCodeAttributes>true</captureCodeAttributes>
<captureMdcAttributes>traceId,spanId</captureMdcAttributes>
</appender>
<root level="INFO">
<appender-ref ref="CONSOLE"/>
<appender-ref ref="OTLP"/>
</root>
</configuration>
6. 核心代码实现
6.1 启动类
java
@SpringBootApplication
public class ObservationDemoApplication {
public static void main(String[] args) {
SpringApplication.run(ObservationDemoApplication.class, args);
}
}
只需 @SpringBootApplication------starter-graph-observation 的自动配置会自动生效。
6.2 StateGraph + 观测
通过注入自动配置的 observationGraphCompileConfig,一行代码启用:
java
@Bean("observedTextProcessGraph")
public CompiledGraph observedTextProcessGraph(
ChatModel chatModel,
@Qualifier("observationGraphCompileConfig") CompileConfig observationConfig) {
StateGraph graph = new StateGraph();
graph.addNode("parse_input", node_async(state -> { ... }))
.addNode("analyze_text", node_async(state -> {
String result = ChatClient.builder(chatModel).build()
.prompt().user("Analyze: " + text).call().content();
return Map.of("analysis", result);
}))
.addNode("format_output", node_async(state -> { ... }))
.addEdge(START, "parse_input")
.addEdge("parse_input", "analyze_text")
.addEdge("analyze_text", "format_output")
.addEdge("format_output", END);
return graph.compile(observationConfig); // ← 关键:传入观测 Config
}
效果:每个 Node 执行自动创建 Span,Graph 级别的 total duration 自动记录为 Metric。
6.3 ReactAgent + 观测
双层次观测:.observationRegistry() + .compileConfig():
java
@Bean("observedWeatherAgent")
public ReactAgent observedWeatherAgent(
ChatModel chatModel,
ObservationRegistry observationRegistry,
@Qualifier("observationGraphCompileConfig") CompileConfig observationConfig) {
return ReactAgent.builder()
.name("weather-assistant")
.model(chatModel)
.instruction("You are a helpful weather assistant.")
.methodTools(new WeatherTool())
.observationRegistry(observationRegistry) // ChatModel 层观测
.compileConfig(observationConfig) // Graph 层观测
.enableLogging(true)
.build();
}
6.4 OtlpLoggingConfig
Spring Boot 默认不自动装配 LoggerProvider,需手动安装 OpenTelemetryAppender:
java
@Component
public class OtlpLoggingConfig implements InitializingBean {
private final OpenTelemetry openTelemetry;
@Override
public void afterPropertiesSet() {
OpenTelemetryAppender.install(openTelemetry);
}
}
6.5 ObservationFilterConfig
Spring Boot 3.4+ 移除了 management.observations.http.server.requests.ignored-uris,改用编程方式过滤:
java
@Bean
ObservationPredicate onlyBusinessApiObservations() {
return (observationName, context) -> {
if (context instanceof ServerRequestObservationContext server) {
String uri = server.getCarrier().getRequestURI();
return uri.startsWith("/api/") && !uri.startsWith("/api/proxy/");
}
return true;
};
}
7. 观测指标(Metrics)
7.1 自动采集的指标
| 指标名 | 类型 | 说明 |
|---|---|---|
spring.ai.alibaba.graph.execution |
Timer | Graph 执行耗时 |
spring.ai.alibaba.graph.node.execution |
Timer | 单个 Node 执行耗时 |
spring.ai.alibaba.graph.edge.execution |
Timer | Edge 路由耗时 |
gen_ai.client.operation.duration |
Histogram | LLM 操作耗时(GenAI 语义约定) |
gen_ai.client.token.usage |
Histogram | Token 用量(input/output/total) |
7.2 查看指标
bash
# 查看所有 Graph 相关指标
curl http://localhost:8080/actuator/metrics | jq '.names[]' | grep graph
# 查看特定指标详情
curl http://localhost:8080/actuator/metrics/spring.ai.alibaba.graph.execution
# 查看观测状态
curl http://localhost:8080/api/observation/status
8. 观测拦截器示例
8.1 Bean 注册 Hook
GraphObservationLifecycleListener 作为 LifecycleHook 挂载在 Bean 生命周期的 afterPropertiesSet 时自动注册到 HookManager,全局生效,无需每个 Graph 手动注册:
java
// 源码:GraphObservationLifecycleListener
@Override
public void afterPropertiesSet() {
HookManager.register(this); // ← 全局注册,所有 Graph 编译时自动包含
}
8.2 树型 Span 结构
Graph Execution Span
├── Node: parse_input Span
│ └── (ChatClient Span)
│ └── (ChatModel Span)
├── Node: analyze_text Span
│ └── (ChatClient Span)
│ └── (ChatModel Span)
│ ├── gen_ai.token.usage (input=156, output=89, total=245)
│ └── gen_ai.operation.duration (1.2s)
├── Edge: analyze_text -> transform_text Span
├── Node: transform_text Span
├── Edge: transform_text -> format_output Span
├── Node: format_output Span
└── Edge: format_output -> END Span
9. API 端点
bash
# StateGraph 同步
GET http://localhost:8080/api/graph/text-process?text=hello
# StateGraph 流式(SSE)
GET http://localhost:8080/api/graph/text-process/stream?text=hello
# ReactAgent 同步
GET http://localhost:8080/api/agent/weather?query=Beijing
# ReactAgent 流式(SSE)
GET http://localhost:8080/api/agent/weather/stream?query=Beijing
# 观测状态查询
GET http://localhost:8080/api/observation/status
前端页面:
| 路径 | 功能 |
|---|---|
/index.html |
仪表盘首页 |
/ai-chat.html |
AI 对话界面 |
/trace-viewer.html |
Trace 链路查看器 |
/ai-metrics.html |
AI 指标监控面板 |
/log-viewer.html |
日志查看器 |