【第26篇】阿里云 ARMS 可观测性实战
从零开始,手把手教你搭建具备企业级可观测能力的 AI 应用。本文覆盖原理讲解、代码实战、部署运维、问题排查全链路,让你真正理解"可观测性"在 AI 场景下的独特价值。
一、AI 应用可观测性
1.1 传统监控 vs AI 应用监控
在传统的微服务架构中,可观测性主要回答三个问题:系统是否健康?请求有多快?出错在哪里? 这三个问题通过 Metrics、Tracing、Logging 三大支柱就能较好地回答。
但当你引入大模型(LLM)后,事情变得复杂了:
| 维度 | 传统 Web 应用 | AI 应用(新增) |
|---|---|---|
| 性能指标 | QPS、P99 延迟 | Token 消耗速率、模型响应时间 |
| 错误类型 | HTTP 500、超时 | 模型幻觉、Prompt 注入、内容审核拒绝 |
| 成本维度 | 服务器资源 | API 调用费用(按 Token 计费) |
| 调试难度 | 看日志即可 | 需要看 Prompt + Response 才能定位问题 |
| 质量评估 | 接口返回正确即可 | 需要评估回答相关性、准确性 |
最核心的一点:AI 应用的"黑盒"特性使得调试极其困难。当模型返回了一个奇怪的回答,你必须知道:我发送了什么 Prompt?模型用了什么参数?消耗了多少 Token?响应耗时多久? 这些信息缺一不可。
1.2 一个真实的场景
假设你的 AI 客服系统突然收到用户投诉:“机器人答非所问”。在传统系统中,你只需要查看应用日志。但在 AI 系统中,你需要:
- 找到那条具体的请求链路(TraceId)
- 查看当时发送给模型的完整 Prompt(可能包含上下文历史)
- 查看模型返回的原始 Response
- 确认 Token 消耗是否正常(排除截断导致回答不完整)
- 检查模型参数(temperature 是否过高导致随机性太大)
如果没有可观测性体系,这几乎是一个不可能完成的任务。
二、技术架构全景
2.1 整体架构
2.2 数据流向详解
三、核心组件解析
3.1 依赖项详解(pom.xml)
<?xml version="1.0" encoding="UTF-8"?>
<project>
<!-- Spring Boot Web:提供 RESTful API 能力 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Spring Boot Actuator:健康检查、度量端点暴露 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- Micrometer OpenTelemetry 桥接:将 Micrometer 指标转换为 OTel 格式 -->
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-tracing-bridge-otel</artifactId>
</dependency>
<!-- 【核心】Spring AI Alibaba ARMS 可观测性 Starter -->
<dependency>
<groupId>com.alibaba.cloud.ai</groupId>
<artifactId>spring-ai-alibaba-starter-arms-observation</artifactId>
</dependency>
<!-- 【核心】通义千问大模型集成 -->
<dependency>
<groupId>com.alibaba.cloud.ai</groupId>
<artifactId>spring-ai-alibaba-starter-dashscope</artifactId>
</dependency>
</project>
各组件协作关系:
| 组件 | 角色定位 | 为什么需要它 |
|---|---|---|
spring-boot-starter-web |
HTTP 入口 | 接收用户请求,触发整个链路 |
spring-boot-starter-actuator |
健康探针 | 暴露 /actuator/health 等端点,K8s 存活检测依赖 |
micrometer-tracing-bridge-otel |
格式转换器 | Spring 生态用 Micrometer,ARMS 用 OpenTelemetry,需要"翻译" |
spring-ai-alibaba-starter-arms-observation |
自动埋点引擎 | 核心!自动拦截 AI 调用,生成追踪数据 |
spring-ai-alibaba-starter-dashscope |
模型连接器 | 封装 HTTP 调用,提供 ChatClient API |
3.2 配置层级设计
四、配置文件深度解读
4.1 application.yml 完整配置
spring:
application:
name: observability-arms-dashscope # 应用在 ARMS 控制台显示的名称
ai:
dashscope:
api-key: ${AI_DASHSCOPE_API_KEY} # 通义千问 API Key,从环境变量读取
# ==========================================
# 观测配置:控制记录哪些数据
# ==========================================
spring:
ai:
chat:
# 全局聊天观测配置
observations:
log-prompt: true # 记录发送给 AI 的 Prompt(调试用)
log-completion: true # 记录 AI 返回的 Completion(调试用)
include-error-logging: true # 错误时也记录
client:
observations:
log-prompt: true # ChatClient 级别的 Prompt 记录
log-completion: true # ChatClient 级别的 Completion 记录
# 向量存储观测
vectorstore:
observations:
log-query-response: true # 记录向量检索结果
# 工具调用观测
tool:
observations:
include-content: true # 包含工具调用的实际内容
# ==========================================
# 基础设施配置
# ==========================================
management:
endpoints:
web:
exposure:
include: "*" # 暴露所有 Actuator 端点(生产环境建议限制)
endpoint:
health:
show-details: always # 健康检查显示详细信息
tracing:
sampling:
probability: 1.0 # 采样率:1.0 = 100%(开发环境)
# HTTP 客户端超时
http:
client:
read-timeout: 60s
4.2 关键配置问答
Q1: log-prompt 和 log-completion 有什么区别?
想象你和 AI 的对话是一次书信往来:
log-prompt= 记录你寄出去的信(你问 AI 什么)log-completion= 记录 AI回给你的信(AI 回答什么)
两者都开启后,你可以在 ARMS 控制台完整还原一次对话的上下文,这对调试"AI 为什么这样回答"至关重要。
Q2: 为什么采样率要设成 1.0?
五、核心代码逐行解析
5.1 启动类:为什么要手动提供 OpenTelemetry?
@SpringBootApplication
public class ObservabilityApplication {
public static void main(String[] args) {
SpringApplication.run(ObservabilityApplication.class, args);
}
/**
* 关键配置:使用 ARMS Agent 提供的全局 OpenTelemetry 实例
*
* 原理说明:
* ARMS Java Agent 在 JVM 启动时(premain阶段)就已经初始化了一套完整的
* OpenTelemetry SDK,包括 TracerProvider、SpanProcessor、Exporter 等。
*
* 如果不配置这个 Bean,Spring AI 会尝试自己创建一套 OTel 实例,
* 导致两套体系并存,追踪数据无法关联,TraceId 传递中断。
*
* 通过 GlobalOpenTelemetry.get(),我们显式声明:
* "使用 Agent 已经初始化好的全局实例,不要重复创建"
*/
@Bean
public OpenTelemetry openTelemetry() {
return GlobalOpenTelemetry.get();
}
}
5.2 控制器:极简设计背后的深意
@RestController
public class ChatController {
private final ChatClient chatClient;
public ChatController(ChatClient.Builder builder) {
this.chatClient = builder.build();
}
@GetMapping("/joke")
public Map<String, String> joke() {
// 1. 调用 AI 模型(这一行会被 ARMS 自动拦截并生成 Span)
var reply = chatClient
.prompt()
.user("Tell me a joke. be concise.")
.call()
.content();
// 2. 获取当前追踪上下文中的 TraceId
// 这个 TraceId 贯穿了整个请求链路:HTTP -> ChatClient -> DashScope API
Span currentSpan = Span.current();
String traceId = currentSpan.getSpanContext().getTraceId();
// 3. 将 TraceId 返回给客户端
// 价值:当用户反馈"某个回答有问题"时,你只需要问用户要这个 traceId,
// 就能在 ARMS 控制台精准定位到那条调用链路
return Map.of(
"joke", reply,
"traceId", traceId
);
}
}
5.3 调用链路时序图
5.4 扩展示例:带参数的笑话生成
@GetMapping("/joke")
public Map<String, String> joke(@RequestParam(defaultValue = "funny") String type) {
// 根据类型构造不同提示词
String prompt = switch (type) {
case "dad" -> "Tell me a dad joke. be concise.";
case "tech" -> "Tell me a programming joke. be concise.";
case "dark" -> "Tell me a dark humor joke. be concise.";
default -> "Tell me a joke. be concise.";
};
var reply = chatClient
.prompt()
.user(prompt)
.call()
.content();
Span currentSpan = Span.current();
// 将类型参数也加入返回,便于在 ARMS 中按类型筛选分析
return Map.of(
"joke", reply,
"traceId", currentSpan.getSpanContext().getTraceId(),
"type", type,
"timestamp", Instant.now().toString()
);
}
六、Java Agent 原理:为什么能做到零代码侵入?
6.1 Java Agent 技术本质
6.2 字节码增强具体做了什么?
以 ChatClient.call() 方法为例,Agent 会在运行时将其字节码从:
// 原始方法(你写的)
public String call() {
// 发送 HTTP 请求到 DashScope
// 返回 AI 响应
}
增强为:
// 增强后的方法(运行时自动生成,你看不到)
public String call() {
Span span = tracer.spanBuilder("ai.chat").startSpan(); // Agent 插入
try {
// 发送 HTTP 请求到 DashScope
// 返回 AI 响应
span.setStatus(StatusCode.OK); // Agent 插入
} catch (Exception e) {
span.recordException(e); // Agent 插入
throw e;
} finally {
span.end(); // Agent 插入
}
}
6.3 ARMS Agent 的覆盖范围
| 拦截目标 | 采集内容 | 价值 |
|---|---|---|
| HTTP 请求 | 请求方法、URL、状态码、耗时 | 了解接口整体健康度 |
| AI 模型调用 | Prompt、Completion、Token 数、模型名 | 核心!调试 AI 行为 |
| 数据库访问 | SQL 语句、执行耗时、返回行数 | 定位慢查询 |
| Redis 操作 | 命令类型、Key 模式、耗时 | 发现缓存问题 |
| 工具调用 | 工具名、参数、返回结果 | 调试 Function Calling |
| 异常抛出 | 异常类型、堆栈、上下文 | 快速定位根因 |
七、部署实操指南
7.1 环境准备检查清单
# 快速检查环境
java -version && mvn -version
# 预期输出:
# openjdk version "17.0.x" 202x-xx-xx
# Apache Maven 3.8.x
7.2 阿里云账号准备
-
开通 ARMS 服务
- 访问 ARMS 控制台
- 首次进入按提示开通(有免费额度)
- 进入「应用监控」→「接入中心」获取 LicenseKey
-
开通 DashScope
- 访问 DashScope 控制台
- 开通「通义千问 API」服务
- 创建 API Key
7.3 本地启动完整流程
# 1. 进入项目目录
cd /home/tht/examples-main/spring-ai-alibaba-observability-example/observability-arms-example
# 2. 配置环境变量(替换成你的真实 Key)
export AI_DASHSCOPE_API_KEY="sk-xxxxxxxxxxxxxxxx"
export ARMS_LICENSE_KEY="xxxx-xxxx-xxxx-xxxx"
export ARMS_APP_NAME="my-ai-demo"
export ARMS_REGION_ID="cn-hangzhou"
# 3. 构建项目
mvn clean package -Dmvn.test.skip=true
# 4. 启动应用(关键:必须挂载 Java Agent)
java -javaagent:./src/javaagent/AliyunJavaAgent/aliyun-java-agent.jar -Darms.licenseKey=${ARMS_LICENSE_KEY} -Darms.appName=${ARMS_APP_NAME} -Daliyun.javaagent.regionId=${ARMS_REGION_ID} -jar ./target/observability-arms-example-1.0.0.jar
7.4 验证服务
# 测试接口
curl http://localhost:8080/joke
# 预期返回:
# {
# "joke": "Why don't scientists trust atoms? Because they make up everything!",
# "traceId": "abc123def4567890abc123def4567890"
# }
# 健康检查
curl http://localhost:8080/actuator/health
八、Docker 部署(生产推荐)
8.1 多阶段构建 Dockerfile
# 阶段一:构建
FROM eclipse-temurin:17-jdk-alpine AS builder
WORKDIR /app
COPY pom.xml .
COPY src ./src
RUN apk add --no-cache maven && mvn clean package -Dmvn.test.skip=true
# 阶段二:运行
FROM eclipse-temurin:17-jre-alpine
WORKDIR /app
# 复制构建产物
COPY --from=builder /app/target/observability-arms-example-1.0.0.jar app.jar
# 复制 ARMS Agent(关键!)
COPY --from=builder /app/src/javaagent ./javaagent
# 环境变量(生产环境建议用 K8s Secret 注入)
ENV AI_DASHSCOPE_API_KEY=""
ENV ARMS_LICENSE_KEY=""
ENV ARMS_APP_NAME="docker-ai-demo"
ENV ARMS_REGION_ID="cn-hangzhou"
EXPOSE 8080
# 启动命令
ENTRYPOINT ["sh", "-c", "java -javaagent:./javaagent/AliyunJavaAgent/aliyun-java-agent.jar -Darms.licenseKey=${ARMS_LICENSE_KEY} -Darms.appName=${ARMS_APP_NAME} -Daliyun.javaagent.regionId=${ARMS_REGION_ID} -jar app.jar"]
8.2 构建与运行
# 构建镜像
docker build -t my-ai-observability:latest .
# 运行容器
docker run -d -p 8080:8080 -e AI_DASHSCOPE_API_KEY=sk-xxxxxxxx -e ARMS_LICENSE_KEY=xxxx-xxxx -e ARMS_APP_NAME=my-docker-demo -e ARMS_REGION_ID=cn-hangzhou --name my-ai-app my-ai-observability:latest
九、Kubernetes 部署(企业级)
9.1 完整 Deployment 配置
apiVersion: apps/v1
kind: Deployment
metadata:
name: arms-observability-demo
labels:
app: arms-observability-demo
spec:
replicas: 2
selector:
matchLabels:
app: arms-observability-demo
template:
metadata:
labels:
app: arms-observability-demo
spec:
containers:
- name: app
image: my-ai-observability:latest
ports:
- containerPort: 8080
env:
# API Key 通过 Secret 注入(安全最佳实践)
- name: AI_DASHSCOPE_API_KEY
valueFrom:
secretKeyRef:
name: ai-secrets
key: dashscope-api-key
# ARMS 配置
- name: ARMS_LICENSE_KEY
valueFrom:
secretKeyRef:
name: arms-secrets
key: license-key
- name: ARMS_APP_NAME
value: "k8s-ai-demo"
- name: ARMS_REGION_ID
value: "cn-hangzhou"
# 资源限制
resources:
requests:
memory: "512Mi"
cpu: "500m"
limits:
memory: "1Gi"
cpu: "1000m"
# 存活探针
livenessProbe:
httpGet:
path: /actuator/health/liveness
port: 8080
initialDelaySeconds: 30
periodSeconds: 10
# 就绪探针
readinessProbe:
httpGet:
path: /actuator/health/readiness
port: 8080
initialDelaySeconds: 10
periodSeconds: 5
---
apiVersion: v1
kind: Service
metadata:
name: ai-observability-service
spec:
selector:
app: arms-observability-demo
ports:
- port: 80
targetPort: 8080
type: ClusterIP
9.2 创建 Secret
# 创建 API Key Secret
kubectl create secret generic ai-secrets --from-literal=dashscope-api-key=sk-xxxxxxxxxxxxxxxx
# 创建 ARMS License Secret
kubectl create secret generic arms-secrets --from-literal=license-key=xxxx-xxxx-xxxx-xxxx
# 部署
kubectl apply -f deployment.yaml
# 查看状态
kubectl get pods -w
十、问题排查完全手册
10.1 问题排查决策树
10.2 常见问题速查表
| 现象 | 根因 | 解决方案 |
|---|---|---|
启动报 Agent jar not found |
-javaagent 路径错误 | 使用绝对路径或确认相对路径正确 |
| 控制台无应用数据 | LicenseKey 错误或网络不通 | telnet arms-apm-cn-hangzhou.oss-cn-hangzhou.aliyuncs.com 443 |
| 有应用但无链路 | 采样率为 0 | 检查 tracing.sampling.probability |
| TraceId 为空 | Agent 未挂载 | `ps aux |
| AI 返回 401 | API Key 无效 | 在 DashScope 控制台重新生成 |
| 启动端口冲突 | 8080 被占用 | server.port=8081 或杀掉占用进程 |
| 构建失败 | Maven 版本过低 | 升级至 3.8+ |
10.3 日志排查命令
# 1. 确认 Agent 是否加载
grep -i "arms\|agent" logs/application.log
# 2. 查看 JVM 参数确认 Agent 挂载
jps -lvm | grep javaagent
# 3. 测试网络连通性
curl -v https://arms-apm-cn-hangzhou.oss-cn-hangzhou.aliyuncs.com
# 4. 查看应用实时日志
tail -f logs/application.log | grep -E "(ARMS|traceId|ERROR)"
十一、Token 成本监控与优化
11.1 为什么 Token 监控是 AI 应用特有的?
11.2 Prometheus 告警规则示例
groups:
- name: ai-application-alerts
rules:
# 告警一:Token 消耗突增(可能是异常循环或攻击)
- alert: AiTokenUsageSpike
expr: |
rate(gen_ai_client_token_usage_tokens_total[5m]) > 10000
for: 2m
labels:
severity: warning
annotations:
summary: "AI Token 消耗速率异常"
description: "过去5分钟 Token 消耗速率超过10000/min"
# 告警二:AI 服务响应超时(P99 > 30秒)
- alert: AiResponseLatencyHigh
expr: |
histogram_quantile(0.99,
rate(gen_ai_client_operation_duration_seconds_bucket[5m])) > 30
for: 5m
labels:
severity: critical
annotations:
summary: "AI 服务响应延迟过高"
# 告警三:AI 服务错误率超阈值
- alert: AiErrorRateHigh
expr: |
rate(gen_ai_client_operation_error_total[5m]) /
rate(gen_ai_client_operation_duration_seconds_count[5m]) > 0.05
for: 3m
labels:
severity: critical
annotations:
summary: "AI 服务错误率超过 5%"
十二、扩展:开源方案 vs 阿里云 ARMS
12.1 方案对比
12.2 开源替代方案依赖
如果你选择开源路线,需要引入以下依赖:
<!-- Prometheus 指标暴露 -->
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
</dependency>
<!-- OTLP 协议导出 -->
<dependency>
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-exporter-otlp</artifactId>
</dependency>
然后自己搭建:
- Prometheus:指标存储与查询
- Grafana:可视化大盘
- Jaeger/Zipkin:链路追踪存储与展示
十三、一键启动脚本
#!/bin/bash
set -e
# ==========================================
# Spring AI Alibaba + ARMS 一键启动脚本
# ==========================================
PROJECT_DIR="$(cd "$(dirname "$0")" && pwd)"
# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
# 请修改以下配置!
export AI_DASHSCOPE_API_KEY="${AI_DASHSCOPE_API_KEY:-sk-你的通义千问Key}"
export ARMS_LICENSE_KEY="${ARMS_LICENSE_KEY:-你的ARMS许可证Key}"
export ARMS_APP_NAME="${ARMS_APP_NAME:-my-arms-demo}"
export ARMS_REGION_ID="${ARMS_REGION_ID:-cn-hangzhou}"
echo -e "${GREEN}🚀 Spring AI Alibaba + ARMS 可观测性演示${NC}"
echo "================================================"
# 检查环境
echo -e "${YELLOW}📋 检查环境...${NC}"
if ! command -v java &> /dev/null; then
echo -e "${RED}❌ Java 未安装${NC}"; exit 1
fi
if ! command -v mvn &> /dev/null; then
echo -e "${RED}❌ Maven 未安装${NC}"; exit 1
fi
java -version 2>&1 | head -1
mvn -version 2>&1 | head -1
# 检查环境变量
if [ -z "$AI_DASHSCOPE_API_KEY" ] || [ "$AI_DASHSCOPE_API_KEY" = "sk-你的通义千问Key" ]; then
echo -e "${RED}❌ 请设置 AI_DASHSCOPE_API_KEY 环境变量${NC}"
exit 1
fi
if [ -z "$ARMS_LICENSE_KEY" ] || [ "$ARMS_LICENSE_KEY" = "你的ARMS许可证Key" ]; then
echo -e "${RED}❌ 请设置 ARMS_LICENSE_KEY 环境变量${NC}"
exit 1
fi
echo -e "${GREEN}✅ 环境检查通过${NC}"
# 构建项目
echo -e "${YELLOW}🔨 构建项目...${NC}"
cd "$PROJECT_DIR"
mvn clean package -Dmvn.test.skip=true -q
if [ ! -f "./target/observability-arms-example-1.0.0.jar" ]; then
echo -e "${RED}❌ 构建失败,JAR 文件未生成${NC}"
exit 1
fi
echo -e "${GREEN}✅ 构建成功${NC}"
# 检查 Agent
AGENT_JAR="./src/javaagent/AliyunJavaAgent/aliyun-java-agent.jar"
if [ ! -f "$AGENT_JAR" ]; then
echo -e "${RED}❌ ARMS Agent 未找到: $AGENT_JAR${NC}"
exit 1
fi
echo -e "${GREEN}✅ Agent 文件存在${NC}"
# 启动应用
echo -e "${YELLOW}🚀 启动应用...${NC}"
echo -e "${YELLOW} 应用名称: $ARMS_APP_NAME${NC}"
echo -e "${YELLOW} 地域: $ARMS_REGION_ID${NC}"
echo "================================================"
java -javaagent:"$AGENT_JAR" -Darms.licenseKey="$ARMS_LICENSE_KEY" -Darms.appName="$ARMS_APP_NAME" -Daliyun.javaagent.regionId="$ARMS_REGION_ID" -jar ./target/observability-arms-example-1.0.0.jar
保存为 start.sh,执行:
chmod +x start.sh
./start.sh
十四、总结与进阶路径
14.1 本项目核心价值
14.2 你已经掌握了什么?
✅ 零代码侵入的可观测性:通过 Java Agent 实现,业务代码完全无感知
✅ AI 调用全链路追踪:从 HTTP 请求到模型响应,每个环节清晰可见
✅ Prompt/Completion 审计:完整记录 AI 对话内容,满足合规要求
✅ Token 成本监控:精细化追踪每个请求的 Token 消耗,控制成本
✅ 多环境部署:本地、Docker、K8s 全覆盖
14.3 下一步进阶方向
- 接入 Spring AI Alibaba Graph:构建多 Agent 工作流,观测更复杂的编排场景
- 集成 Nacos MCP Registry:实现 MCP 工具的分布式注册与可观测
- 自定义业务埋点:在关键业务节点手动添加 Span,细化追踪粒度
- 配置 ARMS 告警:设置 Token 消耗、延迟、错误率的自动告警规则
- 接入 Higress AI 网关:在网关层实现统一的 AI 流量治理与观测
📚 参考资源
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐


所有评论(0)