主题
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: SYNCASYNC 模式
- 真正的非阻塞响应式操作;
- 并发请求下吞吐更高;
- 错误处理更复杂;
- 适合高性能场景。
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 服务器:bashuvx 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: 1min-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 Tools | brave-search-mcp、fetch-mcp、wikipedia-mcp 或 docker-mcp | 网页搜索、URL 抓取、Wikipedia 查询 |
| Maps | google-maps-mcp 或 docker-mcp | 地理编码、路线、地点搜索 |
| Browser Automation | puppeteer-mcp 或 docker-mcp | 页面导航、截图、表单交互 |
| GitHub | github-mcp 或 docker-mcp | Issue、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_suffixA2A
(官方原文此节暂无内容。)
可观测性(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/spansLangfuse(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 spanLangSmith 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_loopspan 上的input.value/output.value与*.result主体、ChatModelspan 上的 GenAI 消息属性, 以及各类 point span 的主体(工具调用参数与结果、RAG 查询、规划与目标的世界状态、重规划原因、@Tracked方法参数/返回值)—— 都受embabel.agent.platform.observability.capture-message-content(默认true) 控制。 设为false可保留模型/token/身份元数据而省略所有消息体 —— 因为 GenAI 规范建议内容采集应当显式选择加入(可能含 PII)。
追踪配置项
所有追踪选项默认开启,可以单独开关:
| 属性 | 默认值 | 说明 |
|---|---|---|
embabel.agent.platform.observability.enabled | true | 整个模块的总开关(trace 与 metrics) |
embabel.agent.platform.observability.tracing-enabled | true | 追踪(span)的总闸。设为 false 会一次性关掉所有 trace-* 层;与 metrics-enabled 相互独立 |
embabel.agent.platform.observability.service-name | embabel-agent | trace 中的服务名 |
embabel.agent.platform.observability.trace-agent-events | true | 核心 scoped span 层的总闸(embabel.agent / embabel.action / embabel.tool_loop / embabel.llm)。设为 false 会同时抑制这四个;想细粒度控制就保持 true 并用下面的单项开关 |
embabel.agent.platform.observability.trace-agent | true | embabel.agent scoped span(一次 run 轮次)。在 trace-agent-events=true 时生效 |
embabel.agent.platform.observability.trace-action | true | embabel.action scoped span。在 trace-agent-events=true 时生效 |
embabel.agent.platform.observability.trace-tool-calls | true | 工具调用(embabel.tool span:名称、组、状态、耗时、参数/结果、错误) |
embabel.agent.platform.observability.trace-tool-loop | true | 工具循环执行(embabel.tool_loop scoped span + embabel.tool_loop.completed point span) |
embabel.agent.platform.observability.trace-tool-loop-completed | true | 是否发出 embabel.tool_loop.completed point span(循环结果:迭代数、重规划标志、耗时)。设为 false 保留 scoped 的 embabel.tool_loop span 但去掉额外的完成节点。trace-tool-loop=false 时此项无效 |
embabel.agent.platform.observability.trace-llm-calls | true | LLM 调用:embabel.llm scoped span 与 embabel.llm.invocation point span(token 用量、成本),以及 Spring AI ChatModel 过滤器 |
embabel.agent.platform.observability.trace-embedding | true | 带 token 用量的 embedding 调用(embabel.embedding),包括 Agent 内与独立使用(Agent 进程之外的 RAG/pgvector) |
embabel.agent.platform.observability.trace-planning | true | 计划形成(embabel.planning)与重规划请求(embabel.replan) |
embabel.agent.platform.observability.trace-state-transitions | true | 工作流状态变化 |
embabel.agent.platform.observability.trace-lifecycle-states | true | 生命周期状态 —— COMPLETED/FAILED/KILLED/TERMINATED/WAITING/PAUSED/STUCK(embabel.lifecycle)与目标达成(embabel.goal) |
embabel.agent.platform.observability.trace-rag | true | RAG 响应(查询、top-k、相似度阈值、结果数、最高分、RAGAS 质量指标) |
embabel.agent.platform.observability.trace-ranking | true | 排序/选择事件(Agent 路由) |
embabel.agent.platform.observability.trace-dynamic-agent-creation | true | 动态 Agent 创建事件 |
embabel.agent.platform.observability.trace-http-details | false | HTTP 请求/响应细节(body、header) |
embabel.agent.platform.observability.trace-tracked-operations | true | @Tracked 注解切面 |
embabel.agent.platform.observability.mdc-propagation | true | 把 Agent 上下文传播进 SLF4J MDC |
embabel.agent.platform.observability.metrics-enabled | true | Micrometer 业务指标(counter、gauge) |
embabel.agent.platform.observability.max-attribute-length | 4000 | span 属性截断前的最大长度 |
embabel.agent.platform.observability.capture-message-content | true | 是否捕获所有 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 代理。同一个类内部的内部方法调用不会被拦截。 请把要追踪的方法抽取到单独的@Componentbean 中,注解才会生效。
MDC 日志关联
Agent 上下文会自动传播进 SLF4J MDC,从而支持按 Agent run 或按 action 过滤日志。
自动设置的 MDC 键:
| MDC 键 | 说明 | 何时设置 | 何时移除 |
|---|---|---|---|
embabel.agent.run_id | Agent 进程 ID | Agent 创建时 | Agent 完成/失败/被 kill 时 |
embabel.agent.name | Agent 名称 | 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支持的后端
| 后端 | 类型 | 模块 |
|---|---|---|
| Langfuse | Traces | opentelemetry-exporter-embabel |
| LangSmith | Traces | opentelemetry-exporter-embabel |
| Zipkin | Traces | opentelemetry-exporter-zipkin |
| OTLP(Jaeger、Tempo) | Traces | opentelemetry-exporter-otlp |
| Prometheus | Metrics | micrometer-registry-prometheus |
完整细节见 Observability Module Documentation。