将 Litefuse 与 Spring AI 集成
本指南介绍如何使用 OpenTelemetry 将 Litefuse 与 Spring AI 集成。
Spring AI:基于 Spring 的 AI 开发框架,内置对 AI 调用的 OTel 追踪。
Litefuse:开源 AI Agent 可观测性与评估平台,提供可观测性、评估和 prompt 管理能力。
查看 Litefuse 示例仓库,里面有一个已完整 instrumentation 的示例应用。
第 1 步:在 Spring AI 中启用 OpenTelemetry
添加 OpenTelemetry 和 Spring Observability 依赖(Maven):
确保你的项目包含 Spring Boot Actuator 和 Micrometer 的 tracing 依赖,以支持 OpenTelemetry。启用 Micrometer 的 observation 和 tracing 特性需要 Spring Boot Actuator。
你还需要 Micrometer -> OpenTelemetry 的桥接以及一个 OTLP exporter。Maven 用户请在 pom.xml 中添加如下依赖(Gradle 用户可以使用等价的 Gradle 配置):
<dependencyManagement>
<dependencies>
<dependency>
<groupId>io.opentelemetry.instrumentation</groupId>
<artifactId>opentelemetry-instrumentation-bom</artifactId>
<version>2.17.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-bom</artifactId>
<version>1.0.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-model-openai</artifactId>
</dependency>
<!-- Spring AI needs a reactive web server to run for some reason-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>io.opentelemetry.instrumentation</groupId>
<artifactId>opentelemetry-spring-boot-starter</artifactId>
</dependency>
<!-- Spring Boot Actuator for observability support -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- Micrometer Observation -> OpenTelemetry bridge -->
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-tracing-bridge-otel</artifactId>
</dependency>
<!-- OpenTelemetry OTLP exporter for traces -->
<dependency>
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-exporter-otlp</artifactId>
</dependency>
</dependencies>启用 Span 导出并配置 Spring AI 的 observation(application.yml):
加入上述依赖后,只要提供合适的配置,Spring Boot 会基于 OpenTelemetry 自动配置 tracing。我们需要指定 span 的发送目标(OTLP endpoint),并确保 Spring AI 把期望的数据写入这些 span。创建或更新你的 application.yml(或 application.properties),加入以下配置:
spring:
application:
name: spring-ai-llm-app # 用于 tracing 的服务名(在 Litefuse UI 中作为来源服务展示)
ai:
chat:
observations:
log-prompt: true # 在 tracing 中包含 prompt 内容(出于隐私默认关闭)
log-completion: true # 在 tracing 中包含补全内容(默认关闭)
management:
tracing:
sampling:
probability: 1.0 # 对所有请求 100% 采样以获得完整追踪(生产环境可按需调整)
observations:
annotations:
enabled: true # 启用 @Observed(如果你在代码中使用 observation 注解)添加自定义 Span Processor(ChatModelCompletionContentObservationFilter.java):
添加一个自定义 span processor,将 prompt 与 completion 属性写入 OpenTelemetry 产生的 span。
package com.langfuse.springai;
import io.micrometer.common.KeyValue;
import io.micrometer.observation.Observation;
import io.micrometer.observation.ObservationFilter;
import org.springframework.ai.chat.observation.ChatModelObservationContext;
import org.springframework.ai.content.Content;
import org.springframework.ai.observation.ObservabilityHelper;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import java.util.List;
@Component
public class ChatModelCompletionContentObservationFilter implements ObservationFilter {
@Override
public Observation.Context map(Observation.Context context) {
if (!(context instanceof ChatModelObservationContext chatModelObservationContext)) {
return context;
}
var prompts = processPrompts(chatModelObservationContext);
var completions = processCompletion(chatModelObservationContext);
chatModelObservationContext.addHighCardinalityKeyValue(new KeyValue() {
@Override
public String getKey() {
return "gen_ai.prompt";
}
@Override
public String getValue() {
return ObservabilityHelper.concatenateStrings(prompts);
}
});
chatModelObservationContext.addHighCardinalityKeyValue(new KeyValue() {
@Override
public String getKey() {
return "gen_ai.completion";
}
@Override
public String getValue() {
return ObservabilityHelper.concatenateStrings(completions);
}
});
return chatModelObservationContext;
}
private List<String> processPrompts(ChatModelObservationContext chatModelObservationContext) {
return CollectionUtils.isEmpty((chatModelObservationContext.getRequest()).getInstructions()) ? List.of() : (chatModelObservationContext.getRequest()).getInstructions().stream().map(Content::getText).toList();
}
private List<String> processCompletion(ChatModelObservationContext context) {
if (context.getResponse() != null && (context.getResponse()).getResults() != null && !CollectionUtils.isEmpty((context.getResponse()).getResults())) {
return !StringUtils.hasText((context.getResponse()).getResult().getOutput().getText()) ? List.of() : (context.getResponse()).getResults().stream().filter((generation) -> generation.getOutput() != null && StringUtils.hasText(generation.getOutput().getText())).map((generation) -> generation.getOutput().getText()).toList();
} else {
return List.of();
}
}
}配置好这些依赖与设置后,你的 Spring Boot 应用就可以产出 OpenTelemetry 追踪数据。Spring AI 的内部调用(例如调用 chat 模型或生成 embedding)会被记录为 span。
每个 span 都会带有诸如 gen_ai.operation.name、gen_ai.system(提供商,如 “openai”)、模型名、token 使用量等属性 —— 由于我们启用了相关设置,还会带上 prompt 与响应内容的事件。
第 2 步:配置 Litefuse
现在你的 Spring AI 应用已经在产出 OpenTelemetry trace 数据,下一步就是把这些数据导向 Litefuse。
在这一套配置中,Litefuse 充当 OpenTelemetry 的 “后端” —— 本质上是用 Litefuse 的 trace 摄入 API 替代了常见的 Jaeger/Zipkin/OTel Collector。
Litefuse 配置
- 注册 Litefuse Cloud 或选择自托管 Litefuse。
- 设置 OTLP endpoint(例如
https://litefuse.cloud/api/public/otel)和 API Key。
通过环境变量进行配置:
OTEL_EXPORTER_OTLP_ENDPOINT: 设置为 Litefuse 的 OTLP URL(例如 https://litefuse.cloud/api/public/otel)。
OTEL_EXPORTER_OTLP_HEADERS: 设置为 Authorization=Basic <base64 public:secret>。关于 Basic Auth 认证的更多信息,请参见这里。
第 3 步:运行一次测试 AI 操作
启动你的 Spring Boot 应用。触发一次由 Spring AI 处理的 AI 操作 —— 例如调用一个使用 ChatModel 生成补全的 service 或 controller,或者使用 EmbeddingModel 生成 embedding。
@Autowired
private ChatService chatService;
@EventListener(ApplicationReadyEvent.class)
public void testAiCall() {
String answer = chatService.chat("Hello, Spring AI!");
System.out.println("AI answered: " + answer);
}设置 userId 和 sessionId
本节说明如何将 Spring AI 的 span 与用户和会话关联起来。 创建一个自定义 span,并直接设置 Litefuse 特定的属性:
public String processUserRequest(String userInput, String userId) {
// 创建一个带有用户和会话属性的新 span
Span span = tracer.spanBuilder("user-interaction")
.setAttribute("langfuse.user.id", userId)
.setAttribute("langfuse.session.id", UUID.randomUUID().toString())
.startSpan();
// 确保把这个新 span 设为当前 active span。
try (Scope ignored = span.makeCurrent()) {
// 在这里放你的 AI 处理逻辑
// 在该 scope 内创建的任何 span 都会成为父 span 的子 span
String result = performAiOperation(userInput);
return result;
} finally {
span.end();
}
}
private String performAiOperation(String userInput) {
// 你的 Spring AI 代码放这里
// 例如:
List<Message> messages = new ArrayList<>();
messages.add(new SystemMessage("You are a helpful assistant."));
messages.add(new UserMessage(userInput));
Prompt prompt = new Prompt(messages);
ChatResponse response = chatClient.call(prompt);
return response.getResult().getOutput().getContent();
}注意,要在 Litefuse 中实现正确的用户跟踪,langfuse.user.id 和 langfuse.session.id 属性最好设置在根 span 上。如果无法直接在根 span 上添加这些属性,你可以使用 Litefuse Java SDK 在 trace 写入后再进行更新。
故障排查
没有 trace:
- 检查 endpoint 和凭证,确认 AI 调用确实被触发。
- 不要混用多个 tracer 实现。
Prompt/Completion 缺失:
- 确保
log-prompt和log-completion都为true。 - 采样概率要足够高。