集成框架Spring AI

将 Litefuse 与 Spring AI 集成

本指南介绍如何使用 OpenTelemetry 将 LitefuseSpring 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 的 observationapplication.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 ProcessorChatModelCompletionContentObservationFilter.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.namegen_ai.system(提供商,如 “openai”)、模型名、token 使用量等属性 —— 由于我们启用了相关设置,还会带上 prompt 与响应内容的事件。

第 2 步:配置 Litefuse

现在你的 Spring AI 应用已经在产出 OpenTelemetry trace 数据,下一步就是把这些数据导向 Litefuse。

在这一套配置中,Litefuse 充当 OpenTelemetry 的 “后端” —— 本质上是用 Litefuse 的 trace 摄入 API 替代了常见的 Jaeger/Zipkin/OTel Collector。

Litefuse 配置

通过环境变量进行配置:

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.idlangfuse.session.id 属性最好设置在根 span 上。如果无法直接在根 span 上添加这些属性,你可以使用 Litefuse Java SDK 在 trace 写入后再进行更新。

故障排查

没有 trace:

  • 检查 endpoint 和凭证,确认 AI 调用确实被触发。
  • 不要混用多个 tracer 实现。

Prompt/Completion 缺失:

  • 确保 log-promptlog-completion 都为 true
  • 采样概率要足够高。
这个页面对你有帮助吗?