> ## Documentation Index
> Fetch the complete documentation index at: https://docs.coze.cn/llms.txt
> Use this file to discover all available pages before exploring further.

本文介绍如何通过 Spring AI 上报 Trace 数据到扣子罗盘。
## 功能介绍 {#20d5b48e}
Spring AI 是一个强大的框架，通过与 OpenTelemetry 的集成，能够自动捕获应用程序中的关键操作和性能指标，并将这些数据作为 Trace 信息上报到扣子罗盘。Spring AI 的自动化功能可以大大简化集成过程。
## 配置 OpenTelemetry {#93390000}
### Maven 配置 {#9348bcb4}
在通过 Spring AI 上报 Trace 前，需要先添加 Maven 依赖，包括 Spring AI 的模型调用库、OpenTelemetry 检测库、Micrometer 到 OpenTelemetry 的连接器以及 OpenTelemetry 导出器。
本示例使用的是 Spring AI 应用程序调用 OpenAI 模型的 pom.xml 文件，如需使用 Gradle 工具，配置类似。
:::tip 说明
* 如果你需要调用其他不支持 OpenAI 协议的模型，可以将下面示例中的 `spring-ai-openai-spring-boot-starter` 替换为对应模型的依赖。
* 本示例以 Spring AI 1.0.0-M8 版本为例。具体说明，请参考[Spring AI 版本须知](/cozeloop/opentelemetry_spring_ai_trace_report#cb22d321)。
:::
```XML
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-parent</artifactId>
       <version>3.4.5</version>
       <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>ai-openai-helloworld</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>ai-openai-helloworld</name>
    <description>Simple AI Application using OpenAPI Service</description>
    <properties>
       <java.version>17</java.version>
    </properties>

    <dependencyManagement>
       <dependencies>
          <dependency>
             <groupId>org.springframework.ai</groupId>
             <artifactId>spring-ai-bom</artifactId>
             <version>1.0.0-M8</version>
             <type>pom</type>
             <scope>import</scope>
          </dependency>
          <dependency>
             <groupId>io.opentelemetry.instrumentation</groupId>
             <artifactId>opentelemetry-instrumentation-bom</artifactId>
             <version>2.13.2</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>

       <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-web</artifactId>
       </dependency>

       <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-actuator</artifactId>
       </dependency>

       <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-test</artifactId>
          <scope>test</scope>
       </dependency>

       <dependency>
          <groupId>io.opentelemetry.instrumentation</groupId>
          <artifactId>opentelemetry-spring-boot-starter</artifactId>
       </dependency>

       <dependency>
          <groupId>io.micrometer</groupId>
          <artifactId>micrometer-tracing-bridge-otel</artifactId>
       </dependency>

       <dependency>
          <groupId>io.opentelemetry</groupId>
          <artifactId>opentelemetry-exporter-otlp</artifactId>
          <version>1.47.0</version>
       </dependency>
    </dependencies>

    <build>
       <plugins>
          <plugin>
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-maven-plugin</artifactId>
          </plugin>
       </plugins>
    </build>

</project>
```

### 导出配置 {#1f369368}
下述代码是一个调用方舟模型的 `application.yaml` 配置示例，用于设置 Spring AI 针对 Span 导出的相关开关。
```YAML
spring:
  application:
    name: spring-ai-demo # 服务名称，自定义。
  ai:
    chat:
      observations:
        include-prompt: true  # 是否上报模型输入，默认关闭。
        include-completion: true # 是否上报模型输出，默认关闭。
    openai: # 使用 OpenAI 协议的模型接口，在此配置。
      api-key: "*********" # 填写你的大模型 API_KEY。
      chat:
        base-url: "https://ark.cn-beijing.volces.com"
        completions-path: "/api/v3/chat/completions"
management:
  tracing:
    sampling:
      probability: 1.0  # 采样率，1.0是100%采样，全量Span上报。
```

以上配置完成了 Spring AI 服务的 Opentelemetry 自动检测配置，服务内部的模型执行过程将被记录到 Span。
### 环境变量配置 {#825467e0}
在上报 Trace 数据前，你需要正确配置 OpenTelemetry 的环境变量 `OTEL_EXPORTER_OTLP_HEADERS` 和 `OTEL_EXPORTER_OTLP_TRACES_ENDPOINT`，用于指定上报数据时所需的扣子罗盘空间 ID，访问令牌和上报地址，以确保数据能够正确发送到指定的扣子罗盘空间中。环境变量配置格式及说明如下：
```Go
OTEL_EXPORTER_OTLP_HEADERS: cozeloop-workspace-id={CozeLoop空间ID},Authorization=Bearer {个人访问令牌或服务访问令牌}
OTEL_EXPORTER_OTLP_TRACES_ENDPOINT: https://api.coze.cn/v1/loop/opentelemetry/v1/traces
```

<!-- @cols-width: 322,595 -->
| | | \
|**环境变量** |**说明** |
|---|---|
| | | \
|OTEL_EXPORTER_OTLP_HEADERS |设置上报数据时所需的认证信息和工作空间标识。包括： |\
| | |\
| |* `cozeloop-workspace-id`：配置为扣子罗盘工作空间 ID。获取步骤，请参考[获取扣子罗盘工作空间 ID](/cozeloop/get_workspace_id_and_token#01dede13)。 |\
| |* `Authorization`：配置为扣子罗盘的个人访问令牌或服务访问令牌。获取步骤，请参考[配置个人访问令牌](/cozeloop/authentication-for-sdk#05d27a86)、[配置服务访问令牌](/cozeloop/authentication-for-sdk#83f924a1)。 |
| | | \
|OTEL_EXPORTER_OTLP_TRACES_ENDPOINT |用于指定 OpenTelemetry 数据的上报地址，固定为 `https://api.coze.cn/v1/loop/opentelemetry/v1/traces`。 |

## 上报 Trace 及验证结果 {#2c73412e}
### **示例 1：​**使用 Toolcall 增强模型 {#b4dc4d36}
本示例展示了一个基于 Spring AI 框架搭建的应用程序，如何集成豆包大模型实现智能交互，并上报 Trace 数据到扣子罗盘。通过定义 `DateTimeTools` 类提供获取当前日期时间和天气信息的工具方法，供大模型在需要时调用。使用 `ChatClient` 构建器配置豆包模型，支持非流式和流式两种响应模式。
```TypeScript
package spring.ai.example.spring_ai_demo;

import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.model.tool.DefaultToolCallingChatOptions;
import org.springframework.ai.tool.annotation.Tool;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.context.i18n.LocaleContextHolder;
import reactor.core.publisher.Flux;
import java.time.LocalDateTime;



@SpringBootApplication
public class Application_tool {
    public static void main(String[] args) {
        SpringApplication app = new SpringApplication(Application_tool.class);
        app.run(args);
    }

    static class DateTimeTools {
        @Tool(description = "Get the current date and time in the user's timezone")
        String getCurrentDateTime() {
            return LocalDateTime.now().atZone(LocaleContextHolder.getTimeZone().toZoneId()).toString();
        }

        @Tool(description = "Get the current weather in the given location")
        String getCurrentWeather() {
            return "今天上海多云转晴，温度25摄氏度";
        }
    }

    @Bean
    CommandLineRunner cli_tool(ChatClient.Builder builder) {
        return args -> {
            invokeAI(builder);
        };
    }

    private void invokeAI(ChatClient.Builder builder) {
        var chat = builder.build();
        var prompt = new Prompt("你是一个专业的沟通师，可以根据用户的问题给出准确的回答",
                DefaultToolCallingChatOptions.builder().model("doubao-1-5-pro-256k-250115").build());
// 非流式
        System.out.println(chat.prompt(prompt).user("今天的日期是什么?")
                .tools(new DateTimeTools())
                .call()
                .content());
// 流式
//        Flux<String> stream = chat.prompt(prompt).user("今天的日期是什么?")
//                .tools(new DateTimeTools())
//                .stream()
//                .content();
//        stream.subscribe(
//                System.out::print, // 处理每个响应部分
//                error -> System.err.println("Error: " + error.getMessage()), // 处理错误
//                System.out::println // 处理完成事件
//        );

    }
}
```

上报 Trace 数据后，你可以在[扣子罗盘](https://loop.coze.cn/)的 **Trace** 页面，找到并单击目标 Span，查看上报的 Trace 数据。
![Image=662x365](https://p9-arcosite.byteimg.com/tos-cn-i-goo7wpa0wc/3137ff66314b4979b32af180b67187ff~tplv-goo7wpa0wc-image.image)
### 示例 2：关联用户ID、会话ID {#d8829c28}
本示例展示了如何在 Spring AI 中借助 OpenTelemetry 实现 Span 与用户 ID、会话 ID 的关联，将用户和会话信息添加到 Trace 数据中。示例首先展示了 OpenTelemetry 的初始化配置、工具类定义、追踪上下文管理及 AI 交互逻辑。然后在此基础上，创建 Tracer 和根 Span，通过设置 user.id 与 session.id 绑定用户与会话标识。
```Java
package spring.ai.example.spring_ai_demo;

import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.model.tool.DefaultToolCallingChatOptions;
import org.springframework.ai.tool.annotation.Tool;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.context.i18n.LocaleContextHolder;

import io.opentelemetry.exporter.otlp.http.trace.OtlpHttpSpanExporter;
import io.opentelemetry.sdk.trace.export.BatchSpanProcessor;
import io.opentelemetry.api.GlobalOpenTelemetry;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.api.trace.Tracer;
import io.opentelemetry.context.Scope;
import io.opentelemetry.sdk.OpenTelemetrySdk;
import io.opentelemetry.sdk.trace.SdkTracerProvider;
import reactor.core.publisher.Flux;

import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Supplier;



@SpringBootApplication
public class Application {
    public static void initOpenTelemetry() {
        // 创建一个 OTLP HTTP 导出器
        Supplier<Map<String, String>> headersSupplier = () -> {
            Map<String, String> headers = new HashMap<>();
            headers.put("cozeloop-workspace-id", "750894316061025****");
            headers.put("Authorization", "Bearer pat_AyJcG40P6TeIQLJENe6QpVuGs3SiJ3t****");
            return headers;
        };
        OtlpHttpSpanExporter otlpExporter = OtlpHttpSpanExporter.builder()
                .setEndpoint("https://api.coze.cn/v1/loop/opentelemetry/v1/traces") // 设置你的 OTLP 接收器的 URL
                .setHeaders(headersSupplier)
                .build();

        // 创建 TracerProvider 并添加批量处理器
        SdkTracerProvider tracerProvider = SdkTracerProvider.builder()
                .addSpanProcessor(BatchSpanProcessor.builder(otlpExporter).build())
                .build();

        // 创建 OpenTelemetrySdk 实例
        OpenTelemetrySdk openTelemetrySdk = OpenTelemetrySdk.builder()
                .setTracerProvider(tracerProvider)
                .buildAndRegisterGlobal();
    }

    public static void main(String[] args) {
        SpringApplication app = new SpringApplication(Application.class);
        app.run(args);
    }

    static class DateTimeTools {

        @Tool(description = "Get the current date and time in the user's timezone")
        String getCurrentDateTime() {
            return LocalDateTime.now().atZone(LocaleContextHolder.getTimeZone().toZoneId()).toString();
        }

        @Tool(description = "Get the current weather in the given location")
        String getCurrentWeather() {
            return "今天上海多云转晴，温度25摄氏度";
        }

    }

    @Bean
    CommandLineRunner cli(ChatClient.Builder builder) {
        return args -> {
            // 初始化 OpenTelemetry, 供自定义节点使用
            initOpenTelemetry();

            // 创建一个 Tracer
            Tracer tracer = GlobalOpenTelemetry.getTracer("exampleTracer");
            // 创建根节点，存储userID和sessionID
            Span span = tracer.spanBuilder("root-span")
                    .setAttribute("user.id", "user-123456")
                    .setAttribute("session.id", "session-123456")
                    .startSpan();

            try (Scope ignored = span.makeCurrent()) {
                invokeAI(builder);
            } finally {
                span.end();
            }
        };
    }

    private void invokeAI(ChatClient.Builder builder) {
        var chat = builder.build();
        var prompt = new Prompt("你是一个专业的沟通师，可以根据用户的问题给出准确的回答",
                DefaultToolCallingChatOptions.builder().model("doubao-1-5-pro-256k-250115").build());

// 非流式
        System.out.println(chat.prompt(prompt).user("今天是周几?")
                .tools(new DateTimeTools())
                .call()
                .content());

// 流式
//        Flux<String> stream = chat.prompt(prompt).user("What day is today?")
//                .tools(new DateTimeTools())
//                .stream()
//                .content();
//        stream.subscribe(
//                System.out::print, // 处理每个响应部分
//                error -> System.err.println("Error: " + error.getMessage()), // 处理错误
//                System.out::println // 处理完成事件
//        );

    }
}
```

上报 Trace 数据后，你可以在[扣子罗盘](https://loop.coze.cn/)的 **Trace** 页面，找到并单击目标 Span，查看上报的 Trace 数据。
![Image=681x312](https://p9-arcosite.byteimg.com/tos-cn-i-goo7wpa0wc/16b8f77e05c84914a14e8318ebf40ccb~tplv-goo7wpa0wc-image.image)
## 参考信息 {#92ccfd8a}
### Spring AI 版本须知 {#cb22d321}
**1.0.0-M版本（如1.0.0-M8）**

* 模型节点：支持上报模型的输入、输出到 Trace，对应 `application.yaml` 文件中的配置字段分别为 `spring.ai.chat.observations.include-prompt` 和 `spring.ai.chat.observations.include-completion`，值为 true，表示打开状态。
* Tool 节点：不支持上报。

**1.0.0-RC1、1.0.0-SNAPSHOT、1.1.0-SNAPSHOT**

* 模型节点：不支持上报模型的输入、输出到 Trace，只支持通过本地日志打印模型的输入、输出，对应 `application.yaml` 文件中的配置字段分别为 `spring.ai.chat.observations.log-prompt` 和`spring.ai.chat.observations.log-completion`，值为 true，表示打开状态。
* Tool 节点：支持上报，也支持控制 Tool 的输入、输出，对应 `application.yaml` 文件中的配置字段为`spring.ai.tools.observations.include-content`，值为 true，表示打开状态，具体数据将展示在 Metadata 里展示。

###  {#54999d87}
