Skip to content
Embabel 文档版本 0.3.0-SNAPSHOT 源快照 c0051628 更新于 2026-09-29

36 · 集成(Integrations)

模型上下文协议(MCP)

发布(Publishing)

概览

Embabel Agent 可以把你的 Agent 暴露为 MCP 服务器, 使其对 Claude Desktop、VS Code 扩展、或其它兼容 MCP 的客户端可用。 框架会自动把 Agent 的 goal 发布为工具与提示词,无需手动配置。

服务器配置

在 application.yml 中配置 MCP 服务器功能。服务器类型决定执行模式:

yaml
spring:
  ai:
    mcp:
      server:
        type: SYNC  # 或 ASYNC

服务器类型

Embabel Agent 支持两种 MCP 服务器执行模式,由 spring.ai.mcp.server.type 属性控制:

SYNC 模式(默认)

  • 把阻塞式操作包装进 reactive stream;
  • 更易开发与调试;
  • 适合大多数场景;
  • 更好的错误处理与日志。
yaml
spring:
  ai:
    mcp:
      server:
        type: SYNC

ASYNC 模式

  • 真正的非阻塞响应式操作;
  • 并发请求下吞吐更高;
  • 错误处理更复杂;
  • 适合高性能场景。
yaml
spring:
  ai:
    mcp:
      server:
        type: ASYNC

传输协议

Embabel Agent 使用 SSE(Server-Sent Events) 传输,在 http://localhost:8080/sse 暴露你的 MCP 服务器。 这与 Claude Desktop、MCP Inspector、Cursor 以及大多数桌面 MCP 客户端兼容。

需要 Streamable HTTP 的客户端 某些客户端(例如 OpenWebUI)要求 Streamable HTTP 传输而非 SSE。 用 mcpo 代理来桥接你的 SSE 服务器:

bash
uvx mcpo --port 8000 --server-type sse -- http://localhost:8080/sse

然后把客户端连到 http://localhost:8000。

自动发布

工具(Tools)

当 goal 被标注 @Export(remote = true) 时,Agent 的 goal 会被自动发布为 MCP 工具。 PerGoalMcpToolExportCallbackPublisher 会自动发现并暴露这些 goal,无需任何额外配置。

提示词(Prompts)

每个 goal 的起始输入类型会自动生成提示词,由 PerGoalStartingInputTypesPromptPublisher 完成。 这基于你的 Agent 定义提供开箱可用的提示词模板。

把 Agent goal 暴露为工具

当标注 @Export 时,Agent 的 goal 会自动成为 MCP 工具:

java
@Agent(
        goal = "Provide weather information",
        backstory = "Weather service agent"
)
public class WeatherAgent {

    @Goal
    @Export(remote = true) // 自动成为 MCP 工具
    public String getWeather(
            @Param("location") String location,
            @Param("units") String units
    ) {
        return "Weather for " + location + " in " + units;
    }

    @Goal
    public String internalMethod() {
        // 不暴露给 MCP(没有 @Export 注解)
        return "Internal use only";
    }
}

把 Embabel 的 ToolObject 与 LlmReference 类型暴露为工具

一个常见需求是把已有的 Embabel 能力通过 MCP 暴露出去。 例如,一个 LlmReference 可能既被加进 PromptRunner,也希望通过 MCP 作为外部工具使用。

要做到这一点,用 McpToolExport 创建一个类型为 McpToolExportCallbackPublisher 的 bean。

例如,要把一个 ToolishRag LLM reference 暴露为 MCP 工具,定义如下 Spring 配置类:

java
@Configuration
public class RagMcpTools {

    @Bean
    McpToolExport ragTools( // ①
            SearchOperations searchOperations) {
        var toolishRag = new ToolishRag(
                "docs",
                "Embabel docs",
                searchOperations
        );
        return McpToolExport.fromLlmReference(toolishRag); // ②
    }
}

① 你的 bean 类型应为 McpToolExport ② 用 McpToolExport.fromLlmReference 返回该实例

命名策略(Naming Strategies)

导出工具时,你可以用命名策略控制工具名如何变换。 这在从多个来源导出、需要给工具加命名空间以避免冲突时很有用。

对 ToolObject 使用命名策略:

java
@Bean
public McpToolExport prefixedTools() {
    return McpToolExport.fromToolObject(
            new ToolObject(
                    List.of(myToolInstance),
                    name -> "myservice_" + name // ①
            )
    );
}

① 所有工具名都会加上 myservice_ 前缀

常见命名策略:

  • 加前缀:{ "namespace_$it" } —— 避免冲突;
  • 转大写:{ it.uppercase() };
  • 恒等:StringTransformer.IDENTITY —— 保持原名(默认)。

LlmReference 的命名:

使用 fromLlmReference 时,会自动应用该 reference 内建的命名策略: 以小写化、规范化后的 reference 名作为工具名前缀。 例如名为 "MyAPI" 的 LlmReference 会给所有工具加上 myapi_ 前缀。

java
// 名为 "WeatherService" 的 reference 会给工具加 "weatherservice_" 前缀
var reference = new MyWeatherReference(); // name = "WeatherService"
McpToolExport.fromLlmReference(reference);
// 工具 "getWeather" 变成 "weatherservice_getWeather"

以不同前缀导出多个来源:

java
@Bean
public McpToolExport multiSourceTools() {
    return McpToolExport.fromToolObjects(
            List.of(
                    new ToolObject(
                            List.of(weatherTools),
                            name -> "weather_" + name
                    ),
                    new ToolObject(
                            List.of(stockTools),
                            name -> "stocks_" + name
                    )
            )
    );
}

过滤工具(Filtering Tools)

你可以用 ToolObject 上的 filter 属性过滤哪些工具被导出:

java
@Bean
public McpToolExport filteredTools() {
    return McpToolExport.fromToolObject(
            new ToolObject(
                    List.of(myToolInstance),
                    StringTransformer.IDENTITY,
                    name -> name.startsWith("public_") // ①
            )
    );
}

① 只导出名字以 public_ 开头的工具

命名策略与过滤器可以组合使用:

java
@Bean
public McpToolExport combinedTools() {
    return McpToolExport.fromToolObject(
            new ToolObject(
                    List.of(myToolInstance),
                    name -> "api_" + name,
                    name -> !name.startsWith("internal") // ①
            )
    );
}

① 过滤器作用于原始工具名,在命名策略变换之前

以 Spring AI 风格在 Spring 组件上暴露工具

你也可以像常规 Spring AI 那样,在 Spring 组件上暴露工具。例如:

java
@Component
public class CalculatorTools {

    @McpTool(name = "add", description = "Add two numbers together")
    public int add(
            @McpToolParam(description = "First number", required = true) int a,
            @McpToolParam(description = "Second number", required = true) int b) {
        return a + b;
    }

    @McpTool(name = "multiply", description = "Multiply two numbers")
    public double multiply(
            @McpToolParam(description = "First number", required = true) double x,
            @McpToolParam(description = "Second number", required = true) double y) {
        return x * y;
    }
}

当然,如果愿意,你可以注入 Embabel 的 Ai 接口来帮助完成工具的工作,也可以在工具方法内部调用其它 Agent。

更多信息见 Spring AI MCP Annotations Reference。

服务器架构

MCP 服务器实现使用了若干设计模式:

  • 模板方法模式
    • AbstractMcpServerConfiguration 提供公共初始化逻辑;
    • 具体实现(McpSyncServerConfiguration、McpAsyncServerConfiguration)处理各模式的细节。
  • 策略模式
    • 服务器策略抽象了 sync 与 async 操作;
    • 各模式的实现分别处理工具、资源、提示词的管理。
  • 发布者模式
    • 工具、资源、提示词通过 publisher 接口被发现;
    • 自动注册与生命周期管理;
    • 事件驱动的初始化确保时序正确。

内建工具

每个 MCP 服务器都包含一个内建工具 helloBanner,用于展示服务器信息:

json
{
  "type": "banner",
  "mode": "SYNC",
  "lines": [
    "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~",
    "Embabel Agent MCP SYNC Server",
    "Version: 0.3.0-SNAPSHOT",
    "Java: 21.0.2+13-LTS-58",
    "Started: 2025-01-17T14:23:47.785Z",
    "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
  ]
}

健康检查(Spring Boot Actuator)

当 spring-boot-starter-actuator 在 classpath 上时, Embabel 会注册一个名为 mcpServer 的健康组件。 它会报告执行模式、已注册工具数、初始化状态,以及 MCP 服务器启动期间收集到的任何问题。

yaml
management:
  endpoint:
    health:
      show-details: always
  probes:
    enabled: true
embabel:
  agent:
    mcpserver:
      health:
        enabled: true
        min-tools: 1

min-tools 让该组件在初始化后若注册的工具数低于阈值则标记为 DOWN。 把它用于"应当等到 Agent 工具被导出后才就绪"的 readiness probe (仅靠内建的 helloBanner 工具,对你的部署来说可能不够)。

Kubernetes 示例:

yaml
livenessProbe:
  httpGet:
    path: /actuator/health/liveness
    port: 8080
readinessProbe:
  httpGet:
    path: /actuator/health/readiness
    port: 8080

当 MCP 暴露流水线仍在运行时,mcpServer 组件报告 OUT_OF_SERVICE;初始化失败后报告 DOWN。

安全(Security)

Embabel MCP 服务器支持两个互补、协同工作的安全层。 可以把它们想成一栋楼里的前台与上锁的办公室门: HTTP 过滤器链是前台,把没有门禁卡的人挡在外面; @SecureAgentTool 则是每一间单独办公室的门锁,检查这张卡究竟允许什么。

第 1 层 —— HTTP 传输(过滤器链)

所有发往 MCP 端点(/sse/***、/mcp/****、/message/***)的请求必须携带有效的 JWT Bearer token, 否则在 GOAP 规划器被调用之前就被以 401 Unauthorized 拒绝。

在你的 Spring Security 配置中配置一个 SecurityFilterChain 与 JWT resource server:

kotlin
@Configuration
@EnableWebSecurity
class McpSecurityConfiguration {

    @Bean
    fun mcpFilterChain(http: HttpSecurity): SecurityFilterChain {
        http
            .securityMatcher("/sse/**", "/mcp/**", "/message/**")
            .authorizeHttpRequests { it.anyRequest().authenticated() }
            .sessionManagement {
                it.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
            }
            .oauth2ResourceServer { oauth2 ->
                oauth2.jwt { jwt ->
                    jwt.jwtAuthenticationConverter(jwtAuthenticationConverter())
                }
            }
            .csrf { it.disable() }
        return http.build()
    }

    @Bean
    fun jwtAuthenticationConverter(): JwtAuthenticationConverter {
        val authoritiesConverter = JwtGrantedAuthoritiesConverter().apply {
            setAuthoritiesClaimName("authorities")
            setAuthorityPrefix("") // ①
        }
        return JwtAuthenticationConverter().apply {
            setJwtGrantedAuthoritiesConverter(authoritiesConverter)
        }
    }
}

① 空前缀意味着 JWT claim 值如 news:read 会直接映射为 Spring Security authority, 于是 @SecureAgentTool 表达式里的 hasAuthority('news:read') 不需要任何 SCOPE_ 前缀即可工作。

在 application.yml 中配置 JWT 校验:

yaml
spring:
  security:
    oauth2:
      resourceserver:
        jwt:
          public-key-location: classpath:keys/public.pem  # 本地开发
          jws-algorithms: RS256
          # 生产环境请改用 issuer-uri 或 jwk-set-uri

第 2 层 —— 方法级(@SecureAgentTool)

在 HTTP 层校验完 token 之后,在 GOAP 执行流水线内部实施按动作的授权。 把 @SecureAgentTool 放在 @Agent 类上,即可保护该 Agent 中的每一个 @Action:

kotlin
@Agent(description = "Curated news digest agent")
@SecureAgentTool("hasAuthority('news:read')") // ①
class NewsDigestAgent {

    @Action
    fun extractTopic(userInput: UserInput, context: OperationContext): NewsTopic { ... } // ②

    @AchievesGoal(description = "Produce news digest",
        export = Export(remote = true, name = "newsDigest",
            startingInputTypes = [UserInput::class]))
    @Action
    fun produceDigest(topic: NewsTopic, context: OperationContext): NewsDigest { ... } // ②
}

① 类级注解作用于该 Agent 中的每一个 @Action。 ② extractTopic(中间步骤)与 produceDigest(目标动作)都要求 news:read —— 若没有类级安全,中间动作会在目标动作的检查触发之前自由执行, 可能在一个未授权的请求上白烧 LLM token。

完整的注解参考(包括支持的 SpEL 表达式与方法级覆写行为)见 @SecureAgentTool。

依赖

xml
<dependency>
    <groupId>com.embabel.agent</groupId>
    <artifactId>embabel-agent-starter-mcpserver-security</artifactId>
    <version>${embabel-agent.version}</version>
</dependency>

该 starter 会自动配置 SecureAgentToolAspect 并装配 Spring Security 的 MethodSecurityExpressionHandler。不需要额外的 @EnableMethodSecurity。

消费(Consuming)

Embabel Agent 可以把外部 MCP 服务器作为工具来源消费, 并自动把它们组织成 Agent 可用的工具组(Tool Groups)。

Docker 工具集成

配置方式

  • Docker MCP Gateway(推荐) 用 Docker Desktop 的 MCP Toolkit 扩展作为通往多个工具的统一网关:
yaml
spring:
  ai:
    mcp:
      client:
        type: SYNC
        stdio:
          connections:
            docker-mcp:
              command: docker
              args: [ mcp, gateway, run ]
  • 独立容器 把每个 MCP 服务器作为单独的 Docker 容器运行:
yaml
spring:
  ai:
    mcp:
      client:
        type: SYNC
        stdio:
          connections:
            brave-search-mcp:
              command: docker
              args: [ run, -i, --rm, -e, BRAVE_API_KEY, mcp/brave-search ]
              env:
                BRAVE_API_KEY: ${BRAVE_API_KEY}

可用的工具组

工具组会基于已配置的 MCP 连接,通过 @ConditionalOnMcpConnection 条件化创建:

工具组需要的连接能力
Web Toolsbrave-search-mcp、fetch-mcp、wikipedia-mcp 或 docker-mcp网页搜索、URL 抓取、Wikipedia 查询
Mapsgoogle-maps-mcp 或 docker-mcp地理编码、路线、地点搜索
Browser Automationpuppeteer-mcp 或 docker-mcp页面导航、截图、表单交互
GitHubgithub-mcp 或 docker-mcpIssue、Pull Request、评论

工作原理

@ConditionalOnMcpConnection 注解在启动时检查已配置的连接:

java
@Bean
@ConditionalOnMcpConnection({"github-mcp", "docker-mcp"}) // ①
public ToolGroup githubToolsGroup() {
    return new McpToolGroup(
            CoreToolGroups.GITHUB_DESCRIPTION,
            "docker-github",
            mcpSyncClients,
            tool -> tool.toolDefinition().name().contains("create_issue") // ②
    );
}

① 只要列出的连接中任意一个被配置,该 bean 就会被创建 ② 过滤器选择哪些 MCP 工具属于该组

自定义工具组

通过配置属性定义自定义工具组:

yaml
embabel:
  agent:
    platform:
      tools:
        includes:
          my-tools:
            description: "Custom tool collection"
            provider: "MyOrg"
            tools:
              - tool_name_suffix

A2A

(官方原文此节暂无内容。)

可观测性(Observability)

Embabel Agent 提供一个统一的可观测性模块,自动追踪 Agent 生命周期、动作、LLM 调用、工具调用等等 —— 零代码改动。 它可与任何兼容 OpenTelemetry 的后端集成(Zipkin、Langfuse、LangSmith、Jaeger、Prometheus 等)。

安装

在 pom.xml 中加入可观测性 starter:

xml
<dependency>
    <groupId>com.embabel.agent</groupId>
    <artifactId>embabel-agent-starter-observability</artifactId>
    <version>${embabel-agent.version}</version>
</dependency>

然后添加一个 exporter 依赖。例如 Zipkin:

xml
<dependency>
    <groupId>io.opentelemetry</groupId>
    <artifactId>opentelemetry-exporter-zipkin</artifactId>
</dependency>

或者用 Embabel OpenTelemetry exporter 做面向 LLM 的可观测性 —— 它用一个 exporter 就能把 span 发到 Langfuse 和/或 LangSmith:

xml
<dependency>
    <groupId>com.quantpulsar</groupId>
    <artifactId>opentelemetry-exporter-embabel</artifactId>
    <version>0.6.0</version>
</dependency>

你可以同时使用多个 exporter(例如 Langfuse 看 trace + Prometheus 看指标)。

配置

⚠️ 从更早版本迁移时的破坏性变更:

  • 配置前缀改名:embabel.observability.** → embabel.agent.platform.observability.**。 旧前缀会被静默忽略(不报启动错误),所以既有配置会失效,直到你改名为止。
  • trace-http-details 现在默认为 false(原来是 true)。 除非显式开启 trace-http-details: true,否则不再采集 HTTP 请求/响应体、header 与参数 —— 从而默认避免采集可能敏感的 HTTP 载荷。
  • 已移除的属性:tracer-name 与 tracer-version 不再存在。
  • Langfuse exporter 被替换:com.quantpulsar:opentelemetry-exporter-langfuse:0.4.0 → 统一的 com.quantpulsar:opentelemetry-exporter-embabel:0.6.0, 它同时导出到 Langfuse(management.langfuse.**)与 LangSmith(management.langsmith.**)。请更新依赖。

在 application.yml 中启用可观测性并配置 exporter:

yaml
embabel:
  agent:
    platform:
      observability:
        enabled: true
        service-name: my-agent-app

management:
  tracing:
    enabled: true
    sampling:
      probability: 1.0

  # Zipkin
  zipkin:
    tracing:
      endpoint: http://localhost:9411/api/v2/spans

Langfuse(management.langfuse.*):

yaml
management:
  langfuse:
    enabled: true
    endpoint: https://cloud.langfuse.com/api/public/otel  # 或自托管 URL
    public-key: pk-lf-...
    secret-key: sk-lf-...

LangSmith(management.langsmith.*)—— 可与 Langfuse 同时运行:

yaml
management:
  langsmith:
    enabled: true
    endpoint: https://eu.api.smith.langchain.com/otel  # 美国区: https://api.smith.langchain.com/otel —— 会自动追加 /v1/traces
    api-key: lsv2_...
    project: my-project
    # embabel-only: true   # 可选 —— 只导出 Embabel/GenAI span

LangSmith exporter 会从 span 的 Embabel 类型推导出其 langsmith.span.kind (embabel.llm → LLM,embabel.tool → TOOL,embeddings → RETRIEVER/EMBEDDING 等), 从而让 LangSmith 按正确类别渲染,而不是当作一个泛型 span。

会追踪什么

模块一旦在 classpath 上,所有追踪都是自动的。 操作被捕获为 OpenTelemetry span,并以父子层级组织。 每个 span 都有一个人类友好的上下文标签(见下),以及括号中一个稳定的低基数 meter 名(embabel.*,用于指标):

customer-service-agent (embabel.agent —— 一次 run() 轮次)
├── planning (embabel.planning) [goal=RequestProcessed, action_count=3]
├── analyze-request (embabel.action)
│   └── gpt-4o (embabel.llm —— 一次 LLM 交互)
│       └── tool-loop (embabel.tool_loop)
│           ├── gpt-4o (embabel.llm.invocation) [input/output tokens, cost]
│           ├── search-knowledge-base (embabel.tool) [status=success]
│           └── tool-loop-completed (embabel.tool_loop.completed) [iterations, replan flag]
├── generate-response (embabel.action)
│   └── gpt-4o (embabel.llm)
├── request-processed (embabel.goal)
└── completed (embabel.lifecycle)

embabel.agent、embabel.action 与 embabel.tool_loop 这三个 span 还会在 厂商中立的 OpenInference 键 input.value / output.value 下携带该步骤的输入与输出, 以便 LLM 可观测性后端(Langfuse 等)在它们的输入/输出面板中渲染:

  • agent —— 输入:绑定的 UserInput;输出:该次 run 的最后结果;
  • action —— 输入:该动作声明的、来自黑板的输入;输出:该动作的结果;
  • tool_loop —— 输入:提示词消息;输出:循环结果。

两者都会被截断到 embabel.agent.platform.observability.max-attribute-length。

Spring AI 的 ChatModel span 还会附带结构化的 OTel GenAI 消息属性 gen_ai.input.messages / gen_ai.output.messages ({role, parts:[{type, content}]} 形式的 JSON 数组,输出带上 finish_reason), 并与 OpenInference 的 input.value / output.value 桥接并存。

以上所有这些载荷内容 —— embabel.agent / embabel.action / embabel.tool_loop span 上的 input.value / output.value 与 *.result 主体、ChatModel span 上的 GenAI 消息属性, 以及各类 point span 的主体(工具调用参数与结果、RAG 查询、规划与目标的世界状态、重规划原因、 @Tracked 方法参数/返回值)—— 都受 embabel.agent.platform.observability.capture-message-content(默认 true) 控制。 设为 false 可保留模型/token/身份元数据而省略所有消息体 —— 因为 GenAI 规范建议内容采集应当显式选择加入(可能含 PII)。

追踪配置项

所有追踪选项默认开启,可以单独开关:

属性默认值说明
embabel.agent.platform.observability.enabledtrue整个模块的总开关(trace 与 metrics)
embabel.agent.platform.observability.tracing-enabledtrue追踪(span)的总闸。设为 false 会一次性关掉所有 trace-* 层;与 metrics-enabled 相互独立
embabel.agent.platform.observability.service-nameembabel-agenttrace 中的服务名
embabel.agent.platform.observability.trace-agent-eventstrue核心 scoped span 层的总闸(embabel.agent / embabel.action / embabel.tool_loop / embabel.llm)。设为 false 会同时抑制这四个;想细粒度控制就保持 true 并用下面的单项开关
embabel.agent.platform.observability.trace-agenttrueembabel.agent scoped span(一次 run 轮次)。在 trace-agent-events=true 时生效
embabel.agent.platform.observability.trace-actiontrueembabel.action scoped span。在 trace-agent-events=true 时生效
embabel.agent.platform.observability.trace-tool-callstrue工具调用(embabel.tool span:名称、组、状态、耗时、参数/结果、错误)
embabel.agent.platform.observability.trace-tool-looptrue工具循环执行(embabel.tool_loop scoped span + embabel.tool_loop.completed point span)
embabel.agent.platform.observability.trace-tool-loop-completedtrue是否发出 embabel.tool_loop.completed point span(循环结果:迭代数、重规划标志、耗时)。设为 false 保留 scoped 的 embabel.tool_loop span 但去掉额外的完成节点。trace-tool-loop=false 时此项无效
embabel.agent.platform.observability.trace-llm-callstrueLLM 调用:embabel.llm scoped span 与 embabel.llm.invocation point span(token 用量、成本),以及 Spring AI ChatModel 过滤器
embabel.agent.platform.observability.trace-embeddingtrue带 token 用量的 embedding 调用(embabel.embedding),包括 Agent 内与独立使用(Agent 进程之外的 RAG/pgvector)
embabel.agent.platform.observability.trace-planningtrue计划形成(embabel.planning)与重规划请求(embabel.replan)
embabel.agent.platform.observability.trace-state-transitionstrue工作流状态变化
embabel.agent.platform.observability.trace-lifecycle-statestrue生命周期状态 —— COMPLETED/FAILED/KILLED/TERMINATED/WAITING/PAUSED/STUCK(embabel.lifecycle)与目标达成(embabel.goal)
embabel.agent.platform.observability.trace-ragtrueRAG 响应(查询、top-k、相似度阈值、结果数、最高分、RAGAS 质量指标)
embabel.agent.platform.observability.trace-rankingtrue排序/选择事件(Agent 路由)
embabel.agent.platform.observability.trace-dynamic-agent-creationtrue动态 Agent 创建事件
embabel.agent.platform.observability.trace-http-detailsfalseHTTP 请求/响应细节(body、header)
embabel.agent.platform.observability.trace-tracked-operationstrue@Tracked 注解切面
embabel.agent.platform.observability.mdc-propagationtrue把 Agent 上下文传播进 SLF4J MDC
embabel.agent.platform.observability.metrics-enabledtrueMicrometer 业务指标(counter、gauge)
embabel.agent.platform.observability.max-attribute-length4000span 属性截断前的最大长度
embabel.agent.platform.observability.capture-message-contenttrue是否捕获所有 span 的消息/载荷内容:ChatModel 的 gen_ai.input.messages/gen_ai.output.messages,以及 agent/action/tool_loop span 上的 input.value/output.value/*.result 主体、工具调用参数/结果、RAG 查询、规划/目标世界状态、重规划原因、@Tracked 参数/返回值。OTel GenAI 规范建议此项选择加入,因为内容可能含 PII;设为 false 可保留模型/token/身份元数据而省略所有消息体
embabel.agent.platform.observability.disabled-traces[](空)要抑制的 observation 名称列表,按精确名称匹配。让你无需改代码即可丢弃不想导出的非 Embabel 基础设施 span(例如 tasks.scheduled.execution、http.server.requests、http.client.requests)。对任何携带真实名称的 span 都有效(包括 embabel.embedding 这类 Embabel point span),但不适用于四个核心 scoped span(embabel.agent/embabel.action/embabel.llm/embabel.tool_loop)—— 它们在 start() 之前是占位名,请改用对应的 trace-* 开关。被抑制的 observation 变成 no-op,其子节点会重新挂到下一个存活的祖先上

例如,要保留 Embabel 的 Agent trace,但停止导出 Spring 的定时任务与入站 HTTP span:

yaml
embabel:
  agent:
    platform:
      observability:
        disabled-traces:
          - tasks.scheduled.execution
          - http.server.requests

"重新挂到下一个存活祖先"这一行为在异步线程跳转之间同样成立,不限于单线程内。

用 @Tracked 追踪自定义操作

@Tracked 注解让你给自己的方法加上可观测性 span。输入、输出、耗时与错误都会被自动捕获。

java
@Tracked("enrichCustomer")
public Customer enrich(Customer input) {
    // 自动创建一个 span,携带方法参数与返回值
}

你还可以指定类型与描述,获得更丰富的 trace:

java
@Tracked(
        value = "callPaymentApi",
        type = TrackType.EXTERNAL_CALL,
        description = "Payment gateway call"
)
public PaymentResult processPayment(Order order) {
    // ...
}

可用的 track 类型:

类型说明
CUSTOM通用(默认)
PROCESSING数据处理操作
VALIDATION校验或验证步骤
TRANSFORMATION数据转换
EXTERNAL_CALL外部服务/API 调用
COMPUTATION计算或算式

在 Agent 执行过程中被调用时,@Tracked span 会自动嵌套在当前动作之下:

customer-service-agent (embabel.agent)
├── process-order (embabel.action)
│   ├── enrich-customer (@Tracked, PROCESSING)
│   ├── gpt-4o (embabel.llm)
│   └── call-payment-api (@Tracked, EXTERNAL_CALL)
└── completed (embabel.lifecycle)

⚠️ @Tracked 使用 Spring AOP 代理。同一个类内部的内部方法调用不会被拦截。 请把要追踪的方法抽取到单独的 @Component bean 中,注解才会生效。

MDC 日志关联

Agent 上下文会自动传播进 SLF4J MDC,从而支持按 Agent run 或按 action 过滤日志。

自动设置的 MDC 键:

MDC 键说明何时设置何时移除
embabel.agent.run_idAgent 进程 IDAgent 创建时Agent 完成/失败/被 kill 时
embabel.agent.nameAgent 名称Agent 创建时Agent 完成/失败/被 kill 时
embabel.action.name当前动作名动作开始时动作返回结果时

Logback pattern 示例:

xml
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} [runId=%X{embabel.agent.run_id} agent=%X{embabel.agent.name} action=%X{embabel.action.name}] - %msg%n</pattern>

产出的日志类似:

14:23:45.123 [main] INFO c.e.MyService [runId=abc-123 agent=CustomerServiceAgent action=AnalyzeRequest] - Processing request

这些键会跨 Agent 内部的线程跳转传播(规划循环、工具循环、异步 fan-out): 通过 Embabel 的 Asyncer 派发的工作会把 MDC 键带到 worker 线程, 因此在调用线程之外产生的日志行也保持同一套 runId/agent/action 关联。 无论追踪是否开启,这一点都成立。

要关闭 MDC 传播:

yaml
embabel:
  agent:
    platform:
      observability:
        mdc-propagation: false

支持的后端

后端类型模块
LangfuseTracesopentelemetry-exporter-embabel
LangSmithTracesopentelemetry-exporter-embabel
ZipkinTracesopentelemetry-exporter-zipkin
OTLP(Jaeger、Tempo)Tracesopentelemetry-exporter-otlp
PrometheusMetricsmicrometer-registry-prometheus

完整细节见 Observability Module Documentation。

BytesRouter 是社区翻译站点,所有译文仅供参考,一切以官方英文原文为准。