Bedrock AgentCore Evaluations 只要 Agent 发出 OpenTelemetry 数据即可评分,支持 LangGraph、LlamaIndex、OpenAI Agent SDK 等主流框架。
构建生产级 AI 智能体的团队面临一个令人沮丧的不对称问题:智能体框架的多样性持续增长,但评估工具却未能跟上。大多数评估系统默认你以特定方式构建智能体:特定的 SDK、特定的 LLM 客户端、特定的追踪模式。一旦你超出那个狭窄的兼容范围,评估流水线就会崩溃。
团队基于 LangGraph 的工作流编排模型构建智能体,基于 LlamaIndex 与检索流水线的深度集成构建智能体,在组织标准化使用 GPT 模型时使用 OpenAI Agents SDK。他们使用 Google ADK 进行多智能体协作,或使用 Claude Agent SDK 获得原生的 Anthropic 能力。他们选择 Strands Agents,因为其模型驱动的循环可以在几分钟内(而非几天)让智能体在 Amazon Bedrock AgentCore 上运行。越来越多的团队将这些框架都部署在 Amazon Bedrock AgentCore 运行时上,这是 Amazon Bedrock AgentCore 的一项能力。它处理托管、扩展、内存和可观测性基础设施,否则他们得为每个项目重新构建这些。
Amazon Bedrock AgentCore Evaluations 通过将评估与框架选择解耦来解决这种碎片化问题。每个主要框架都支持 OpenTelemetry,要么原生支持,要么通过社区插桩库支持。只要智能体的遥测数据通过 OpenTelemetry 传输,评估服务就可以对其进行评分,无论底层使用什么 SDK。本文将解释它的工作原理:服务读取哪些遥测数据、如何决定如何读取你的跨度(span)、哪些属性携带评估数据,以及覆盖率如何扩展到上述框架之外。
OpenTelemetry 是一个供应商中立的插桩框架,标准化了分布式系统如何发送 traces、指标和日志。trace 是一棵跨度树,每个跨度代表请求中的一个单独步骤:一个工作单元,包含其名称、时间戳、一组类型化属性和可选的跨度事件。跨度通过 OpenTelemetry Protocol (OTLP) 导出,由遥测后端收集。在 AgentCore 运行时上,该后端是 AWS Distro for OpenTelemetry (ADOT),它将跨度和时间记录路由到 Amazon CloudWatch。
智能体的执行会产生多种跨度,因为智能体执行多种工作。单个用户轮次可以生成模型调用、工具调用、从向量存储或数据库检索文档、对检索结果重排、嵌入生成、护栏检查、提示模板渲染、内存读写以及将它们绑定在一起的编排步骤等跨度。这两个约定明确命名了这些:OpenTelemetry GenAI 约定定义了 chat、embeddings、retrieval、execute_tool、invoke_agent、create_agent、plan 以及一系列内存操作。OpenInference 定义了 LLM、TOOL、RETRIEVER、RERANKER、EMBEDDING、AGENT、CHAIN、GUARDRAIL、EVALUATOR 和 PROMPT 等跨度种类。生产级 trace 通常混合其中几种。
在整个集合中,评估服务需要三种跨度角色来重建会话中发生的情况并对其进行评分:
一个 invoke agent 跨度代表顶层请求-响应循环,即对话中的一个用户轮次。它携带用户提示和最终智能体响应。
推理跨度代表单独的模型调用,每个都携带传递给模型的消息历史和模型的回复。
执行工具跨度代表智能体调用的每个工具,携带工具名称、输入参数和结果。
这三种是评估器操作的依据。服务对收到的每个跨度进行分类,从这三个角色中读取所需的值,其余的则忽略。包含检索、重排、护栏或内存跨度的更丰富 trace 可以无需任何特殊配置即可处理。那些跨度只是添加了评估器不需要的上下文。随着框架和约定在未来添加新的跨度种类,这保持了前向兼容:不熟悉的跨度种类是服务跳过的上下文,而不是错误。
Session (one runtimeSessionId)
└── Trace (one user turn, one trace_id)
├── invoke agent span ← read: user prompt + final agent response
├── inference span ← read: messages to model + model reply
├── execute tool span ← read: tool name + parameters + result
├── retriever span (context; not required by evaluators)
├── inference span ← read: next model call with tool result in history
└── ... (guardrail, memory, reranker, orchestration spans, and more)
服务读取这三个标记的跨度角色,将其他跨度视为额外上下文。
不同的框架和插桩库使用不同的属性名称、嵌套结构和跨度命名约定来记录这三个角色。OpenTelemetry GenAI 语义约定和 OpenInference 规范都为记录这三个跨度角色定义了模式,但它们使用不同的属性键和不同的跨度种类词汇表。AgentCore Evaluations 桥接两种模式到相同的结果。
当你运行评估时,按需或通过在线评估配置,服务从 CloudWatch 获取智能体的跨度和时间记录并重建会话。会话按 session.id 分组。在其中,每个 trace(一个 trace_id)是一个用户轮次。每个轮次由前面描述的三种跨度类型组成。
服务对每个跨度进行分类,提取所需的值,然后将重建的会话交给评估器。从那时起,评估完全与框架无关:相同的评估器——GoalSuccessRate、Correctness、Helpfulness 和自定义 LLM-as-a-judge——以完全相同的方式对每个框架评分。如下图所示。
图 1:从 AgentCore 运行时通过 Amazon CloudWatch 到 AgentCore Evaluations 的数据流
你无需配置任何这些。每个 OpenTelemetry 插桩库在其生成的跨度和时间记录上标记 scope.name,评估服务使用该值来决定如何读取它们。正确的处理从你安装的插桩包自动激活,无需更改智能体代码。每个支持的框架及其 scope name 都在支持的智能体框架文档中列出,目前涵盖 Strands Agents、LangGraph、OpenAI Agents SDK、LlamaIndex、Google ADK 和 Claude Agent SDK,大多数同时支持 OpenTelemetry 和 OpenInference 插桩。
覆盖率覆盖范围超出列出的框架。任何 scope name 属于 opentelemetry.instrumentation.(遵循 OpenTelemetry GenAI 语义约定)或 openinference.instrumentation.(遵循 OpenInference 规范)的库都通过通用路径读取。实际上,支持新框架通常只是安装合规的插桩包的问题:scope name 前缀是库选择加入的方式。名为 mycompany.agent.tracing 的 scope 不会被选中,即使其跨度完全遵循约定。前缀是信号,表明插桩作者有意遵守了文档化的模式。
端到端获取会话评估归结为两个要求。第一个是分组:你的智能体的跨度必须携带与调用智能体时使用的 runtimeSessionId 匹配的 session.id 属性。该属性是让服务将跨度组装成 traces、将 traces 组装成会话的依据。在 AgentCore 运行时上,ADOT 自动注入此属性,因此不需要更改智能体代码。
第二个要求是你的数据源包含消息内容,而不仅仅是跨度。对于具有统一可观测性的智能体(新创建的智能体的默认设置),这是自动的:消息内容与跨度存在于同一个每智能体日志组中,因此单个日志组就足够了。然而,对于仍运行统一前配置的在用智能体,跨度落在共享的 aws/spans 日志组中。消息内容作为相关事件记录单独存储在智能体的日志组中。在该配置中,如果你的数据源仅覆盖 aws/spans,跨度分类仍然成功,但消息内容返回为空,任何评估响应质量的评估器都会返回错误。
OpenTelemetry GenAI 语义约定为 LLM 框架生成的跨度定义了一个模式。AgentCore Evaluations 读取这些属性的特定子集来对跨度进行分类并提取评估数据。了解哪些属性携带这些数据在你调试评估或编写自定义插桩时会很有用。
跨度分类从 gen_ai.operation.name 开始:
invoke_agent 标记 invoke agent 跨度,即每轮次的顶层跨度,携带用户提示和最终响应。
chat 标记一个推理跨度(inference span)。服务读取消息历史和模型的回复。
execute_tool 标记一个执行工具跨度(execute tool span)。服务读取工具名称、输入参数和结果。
当 gen_ai.operation.name 不存在时(常见于某些 LlamaIndex 追踪),服务会回退到 traceloop.span.kind,其中 workflow 对应调用 AI 智能体跨度(invoke agent span),tool 对应执行工具跨度(execute tool span),llm 对应推理跨度(inference span)。
工具标识来自 gen_ai.tool.name,gen_ai.tool.call.id 是关联 ID,用于将推理跨度上请求的工具调用与其对应执行工具跨度上的结果关联起来。推理跨度上的 gen_ai.tool.definitions 属性携带一个 JSON 编码的工具模式数组,GoalSuccessRate 评估器使用它来验证 AI 智能体调用的工具是否在其声明可用的工具集中。
消息内容位于两个位置之一,具体取决于遥测数据的收集方式。当遥测数据被分离时,内容位于关联的事件记录体中(body.input.messages 和 body.output.messages)。当未分离时,内容保留在跨度上,作为 gen_ai.input.messages 或 gen_ai.output.messages 属性,或作为内联跨度事件。服务从包含内容的位置读取,因此你不需要知道给定部署走了哪条路径。关于内容所在位置的完整说明,请参阅 Spans、event records 和 telemetry signals。
OpenInference 约定:一个替代的语义层
OpenInference 是由 Arize AI 维护的开放规范,在 LlamaIndex、Haystack 和 Phoenix 生态系统中被广泛采用。它使用与 OpenTelemetry GenAI 约定不同的属性名来表示 LLM 操作。包括 OpenAI Agents SDK(配合 openinference-instrumentation-openai-agents)、Google ADK 和 Claude Agent SDK 在内的多个框架都会生成 OpenInference 跨度。
OpenInference 中的跨度分类使用 openinference.span.kind。值 LLM、TOOL、AGENT 和 CHAIN 分别对应推理跨度、执行工具跨度、调用 AI 智能体跨度和结构容器跨度。
推理跨度的消息内容使用扁平的、索引的属性约定。输入消息遵循模式 llm.input_messages.{i}.message.role 和 llm.input_messages.{i}.message.content,其中 i 从零开始,对应历史中每条消息递增。工具结果消息额外携带 llm.input_messages.{i}.message.tool_call_id 以便链接到相应的工具调用。输出消息遵循相同的索引模式,工具调用进一步嵌套:
llm.output_messages.0.message.role = "assistant"
llm.output_messages.0.message.tool_calls.0.tool_call.function.name = "get_pto_balance"
llm.output_messages.0.message.tool_calls.0.tool_call.function.arguments = "{\"employee_id\":\"EMP-001\"}"
llm.output_messages.0.message.tool_calls.0.tool_call.id = "call_abc123"
推理跨度上的工具模式使用 llm.tools.{i}.tool.json_schema,其中每个值是一个 JSON 字符串,编码一个普通函数模式或 OpenAI 风格的 {"type": "function", "function": {...}} 包装器。服务处理这两种格式并读取 name、description 和 parameters 字段。
执行工具跨度使用三个属性:tool.name 作为标识符,input.value 作为参数(多参数工具为 JSON 编码对象,单参数工具为纯字符串),output.value 作为结果。output.value 格式在 OpenInference instrumentation 库的不同版本中有所演变,服务同时读取旧和新两种格式,因此你不受限于特定版本。
评估任何兼容的框架
命名的框架有直接覆盖,但设计特意开放。对于该列表之外的任何框架,只要 instrumentation 遵循文档化约定,前面介绍的两条通用路径提供了广泛覆盖:
OpenInference 路径处理 openinference.instrumentation.* 下的任何范围。它使用 openinference.span.kind 对跨度进行分类,从索引的 llm.input_messages 和 llm.output_messages 属性读取推理内容,并为工具跨度读取 tool.name、input.value 和 output.value。这覆盖了大多数采用 OpenInference 规范的框架,包括在某个框架被明确命名之后发布的框架。
OpenTelemetry 路径处理 opentelemetry.instrumentation.* 下的任何范围。它使用 gen_ai.operation.name 对跨度进行分类,从事件记录体或跨度读取消息内容,并使用 gen_ai.tool.name 作为工具标识。
范围名称前缀是决定将库路由到哪条路径的关键。对于编写自定义 instrumentation 并希望被通用评估的团队,将范围命名为 opentelemetry.instrumentation.* 或 openinference.instrumentation.* 是接入的方式。
从 AI 智能体代码到评估分数:完整演示
为了使这具体化,AgentCore 示例仓库提供了完整的工作示例,例如使用 OpenAI Agents SDK、Google ADK、LlamaIndex 和 Claude Agent SDK 实现的 HR 助手。每个都部署到 AgentCore 运行时并使用相同的内置和自定义评估器进行评估。
Instrumentation 设置在两个 AI 智能体中都不需要显式代码。在 AgentCore 运行时上,AWS Distro for OpenTelemetry(ADOT)在启动时发现已安装的 instrumentation 包并自动激活它。对于 OpenAI Agents SDK 示例,只需在 requirements.txt 中添加 opentelemetry-instrumentation-openai-agents 即可。对于 LlamaIndex 示例,opentelemetry-instrumentation-llamaindex 起到同样的作用。OpenAI Agents SDK AI 智能体必须避免的一件事是调用 set_tracing_disabled(True):instrumentation 挂接到 SDK 自身的追踪管道中,因此禁用 SDK 追踪会使评估跨度静默。
对于 LlamaIndex,具体来说 AI 智能体结构很重要。AI 智能体必须构建为 FunctionAgent 或 ReActAgent(workflow 智能体),而不是普通的 AgentExecutor。Workflow 智能体发出顶级调用 AI 智能体跨度(invoke agent span),作为追踪的锚点。没有它,就没有顶级跨度来重建对话,session 无法仅从推理跨度组装。
为了可靠地将遥测数据传送到 CloudWatch,在返回响应之前在调用处理程序的末尾刷新。AgentCore 运行时在处理程序返回后挂起执行环境,而 OTel SDK 在客户端批量处理器中缓冲遥测数据,这些处理器按定期计时器导出。因此,在返回时未导出的数据可能仍停留在缓冲区中,无法保证在挂起前完成导出。在实践中,遗漏刷新导致的遥测数据丢失是评估失败最常见的来源。OpenAI Agents SDK 和 LlamaIndex 示例使用以下模式实现此功能:
def _flush_telemetry():
from opentelemetry import trace as _trace
from opentelemetry._logs import get_logger_provider as _get_lp
for provider in (_trace.get_tracer_provider(), _get_lp()):
flush = getattr(provider, "force_flush", None)
if flush:
flush()
@app.entrypoint
async def invoke(payload, context):
prompt = payload.get("prompt", "")
try:
result = await run_agent(prompt)
finally:
_flush_telemetry()
return str(result)
刷新必须同时覆盖追踪器提供者(跨度)和日志提供者(事件记录):跨度和事件记录通过独立的客户端批量处理器传输,因此只刷新其中一个会使另一个的缓冲区保持不变。
AgentCore Evaluations 在 CloudWatch 摄取等待 90–150 秒后,对两个框架使用完全相同的 EvaluationClient:
from bedrock_agentcore.evaluation import EvaluationClient
from bedrock_agentcore.evaluation.client import ReferenceInputs
from datetime import timedelta
ec = EvaluationClient(region_name=REGION)
# 客户端自动解析每个评估器的级别(SESSION、TRACE 或 TOOL_CALL),
# 因此这里不需要级别配置。
results = ec.run(
evaluator_ids=[
"Builtin.GoalSuccessRate",
"Builtin.Correctness",
"Builtin.Helpfulness",
CUSTOM_RESPONSE_QUALITY_ID,
CUSTOM_SESSION_COMPLETENESS_ID,
],
agent_id=AGENT_ID,
session_id=SESSION_ID,
look_back_time=timedelta(hours=1),
reference_inputs=ReferenceInputs(
assertions=ASSERTIONS,
expected_trajectory=EXPECTED_TRAJECTORY,
expected_response=EXPECTED_RESPONSES[-1],
),
)
GoalSuccessRate 评估器在会话级别运行,检查智能体的工具调用历史在所有轮次中是否与预期轨迹匹配。Correctness 和 Helpfulness 是追踪级别的评估器,对每个单独轮次打分。内置评估器在会话、追踪、工具级别均有配置,可作为模板使用。对于自定义评估器,你需要定义它在会话级别、追踪级别还是工具级别工作。也可以将不同级别的评估器组合成一次评估调用。不同框架示例的评估代码结构完全相同,唯一的区别是智能体名称和资源 ID。
按需评估和在线评估模式
示例仓库展示了两种评估模式,它们在生产环境 AI 智能体工作流中服务于不同目的。
按需评估非常适合持续集成和持续交付(CI/CD)流水线以及回归测试。你调用智能体生成一个会话,然后在同一脚本中调用 EvaluationClient.run() 进行评分。由于你控制着调用过程,可以通过 ReferenceInputs 提供真实标签:预期响应、预期工具轨迹和行为断言。每个拉取请求都运行的流水线可以调用一组固定的测试提示词,根据真实标签对其进行评分,并在任何评估器的分数低于阈值时使构建失败。
在线评估持续监控实时智能体流量。你只需创建一次 OnlineEvaluationConfig,指向智能体的 CloudWatch 日志组并指定采样率。服务会自动拾取新到来的会话:
_cp.create_online_evaluation_config(
onlineEvaluationConfigName="hr_agent_online_eval",
rule={"samplingConfig": {"samplingPercentage": 25.0}},
dataSourceConfig={
"cloudWatchLogs": {
"logGroupNames": [CW_LOG_GROUP],
"serviceNames": [OTEL_SERVICE_NAME],
}
},
evaluators=[
{"evaluatorId": "Builtin.GoalSuccessRate"},
{"evaluatorId": "Builtin.Correctness"},
{"evaluatorId": "Builtin.Helpfulness"},
],
evaluationExecutionRoleArn=ONLINE_EVAL_ROLE_ARN,
enableOnCreate=True,
)
在线评估只能使用不需要真实标签的评估器,因为实时流量没有关联的预期响应。内置评估器在两种模式下都可以使用。引用指令中包含 {expected_response} 或 {assertions} 占位符的自定义 LLM-as-a-judge 评估器仅适用于按需模式。
在线评估结果会发送到 CloudWatch 日志组 /aws/bedrock-agentcore/evaluations/results/{config_id}。基于这些数据构建 CloudWatch 告警或仪表板,可以让你通过单一一致的接口持续洞察生产环境中所有框架的智能体质量。由于相同的评估流水线以相同方式处理按需流量和在线流量,当智能体行为稳定时,CI 测试运行的分数与生产流量的分数可以直接比较。
AgentCore Evaluations 与框架无关。无论你使用 LangGraph、OpenAI Agents SDK、LlamaIndex、Google ADK、Claude Agent SDK 还是 Strands Agents 构建,相同的评估器在按需和在线模式下均匀适用,因为集成契约基于 OpenTelemetry 标准。如果你的智能体在公认的范围内发出 span、在推理 span 上包含工具模式,并通过 span 属性或事件记录暴露消息内容,评估服务就会在无需任何框架特定代码的情况下生成评分。对于上述列表之外的框架,只需遵循 OpenTelemetry GenAI 约定或 OpenInference 规范并使用正确的作用域前缀即可。通过一套评估套件,对于每个在公认作用域下发出遥测数据的智能体,你现在都可以获得一致的质量评估。
准备好亲自尝试了吗?可以从 AgentCore Evaluations 功能示例开始,并参阅支持的 AI 智能体框架文档,了解如何连接你选择的框架的完整详情。
感谢由 Shoaib Javed 和 Qiaoxuan Xue 领导的 AgentCore Evaluations 工程团队,以及为这项工作做出贡献的工程师们:Swarnim Singhal、Lefan Zhang、Athul Iddya、Irene Wang、Ritvika Pillai、Aditya Arepalli。