跳到正文
AWS Machine Learning Blog· Prabhakar Chandrasekaran·· 3 小时前AI 评分53

用 AWS CDK 在 Amazon Bedrock 托管知识库上构建企业级智能体检索

Build agentic retrieval on a Managed Amazon Bedrock Knowledge Base with CDK

AI 导读

AWS Machine Learning Blog 发布教程,用单个 AWS CDK 应用在 Managed Amazon Bedrock Knowledge Base 与 AgentCore 上构建企业级智能体检索。

正文 · AI 翻译

本文介绍如何使用 AWS Cloud Development Kit(AWS CDK)在托管型 Amazon Bedrock Knowledge Base 上构建企业级智能体检索。要理解为什么值得构建这样的方案,不妨先看看普通的检索增强生成(RAG)在哪些地方会失效。大多数 RAG 系统的流程都是一样的:对问题做向量化,从单一索引中取出最相近的文本块,交给模型,然后返回答案。一次检索,一次生成。对于一大类问题来说,这就是正确的设计,没有理由构建更复杂的东西。

它失效的方式是可以预见的。如果问题的答案跨越两个语料库,单次检索只会返回模型所需信息的一半,这比什么都检索不到更糟,因为返回的答案流畅却不完整。如果问题是正确的数据源取决于问题本身,单一索引没有任何机制来做出选择。如果问题需要在对第一个结果进行查看后再回过头查看语料库,这个系统中没有任何地方可以完成这第二次检索。

企业级智能体检索通过在流程中引入一个具备推理能力的智能体来解决这三个问题。该智能体决定是否检索、查询哪个知识库,以及在生成最终答案之前是否需要再次检索更多信息。这正是 Amazon Bedrock Knowledge Bases 演进的方向。随着托管型知识库(Managed Knowledge Base)的发布,检索不再是单次的向量化加取回调用。它已成为一种智能体能力:多轮规划、工具驱动的检索,以及有依据、带引用的答案。AgenticRetrieveStream API 将此作为一等服务功能提供,而无需你自己组装。这固然可行,但也带来了一个真正更棘手的运维问题。单次执行的流水线是一个通过阅读代码就能理解的函数。而一个在循环中推理和检索的智能体,对每个问题都会走出不同的路径。默认情况下,你既看不到它走过的路径,也无法判断它生成的答案是否靠谱。可观测性不再是锦上添花,而是验证系统正常工作的唯一途径。

2024 年 8 月,Build an end-to-end RAG solution using Knowledge Bases for Amazon Bedrock and the AWS CDK 将单次执行的设计部署为 CDK 应用:一个 Amazon Simple Storage Service(Amazon S3) 语料库、一个 Amazon OpenSearch Serverless 集合与索引、一个知识库和一个数据源,均以 CDK 堆栈的形式预置,其部署顺序需要手动编排。自那篇文章发布以来,有三件事发生了变化。

  1. Amazon Bedrock 现在提供托管型知识库(类型:MANAGED),它自己负责数据的摄取、存储、索引和检索。那篇文章中预置的 OpenSearch Serverless 集合和索引,不再需要你去创建、扩缩容,也不再需要用 OpenSearch Compute Units(OCU)付费。
  2. Amazon Bedrock AgentCore 提供了智能体那一半的能力:一个 Gateway,将每个知识库暴露为 Model Context Protocol(MCP)工具;一个 Runtime,托管推理智能体;以及 AgenticRetrieveStream,它在内部进行重排序并返回经过综合、带有引用的答案。智能体检索仅在托管型知识库上可用。
  3. 可观测性与评估现在成为你可以直接配置的东西,而不是事后拼凑的补丁。该解决方案通过同一次部署交付两个 Amazon CloudWatch 仪表板,覆盖七层遥测,并附带两种评估方式:按需评估和持续评估。

本文将构建这样一个系统:一个跨两个知识库进行路由、通过 Gateway 进行检索、并返回有依据、带引用答案的智能体,其每一层行为都有埋点监测,整体作为一个 Python 编写的 AWS CDK 应用程序交付。一条命令即可部署全部内容,部署顺序由 CDK 推导得出。

如果你读过 2024 年的文章,那么从 Amazon S3 存储桶到知识库的路径会显得很熟悉,而之后的一切都是全新的:向量存储不复存在,检索改为智能体化,而且应用程序所配置的内容中约有半数是为了告诉你智能体做了什么。如果你没读过,本文也自成一体。

图示了所涉及的服务以及一个问题经过这些服务的路径:智能体决定是否检索以及查询哪个知识库,Gateway 将每个知识库暴露为它可以调用的工具,托管知识库负责搜索并引用,这个循环会根据问题的需要重复多次。智能体在此过程中的所有操作都会记录到 Amazon CloudWatch 中。

Agent routing a question across two managed knowledge bases through the Gateway, with every step landing in Amazon CloudWatch

图 1:所涉及的服务以及单个问题经过这些服务的路径

背景

在深入演练之前,先厘清三件事会有所帮助:是什么让检索变得智能体化、为什么托管知识库是其正确的基础,以及为什么可观测性与评估应当融入设计而非事后补充。

从 RAG 到企业级智能体检索

经典的检索增强生成(RAG)只做一次检索和一次生成。企业级智能体检索则在流程中引入一个具备推理能力的智能体:它决定是否检索以及检索什么,可以多次检索以逐步优化,选择哪个知识库与问题相关(语义路由),然后才生成带引用、有依据的答案。使用托管知识库时,这是一项一等的服务能力——AgenticRetrieveStream API 执行多轮规划、执行检索并返回带引用的答案,而不是需要你基于原始向量搜索自行构建和维护的编排层。这能在复杂问题上产生更好的答案,但也带来了一个更复杂、需要运营的系统,这正是从一开始就内置可观测性与评估的原因。

托管知识库与自建知识库的对比

Amazon Bedrock 现在提供托管知识库(类型:MANAGED):由 Amazon Bedrock 为你管理摄取、存储、索引和检索,默认情况下包括使用服务托管模型进行嵌入和重排序,因此无需配置、扩展或修补任何向量数据库。这不同于客户自管(DIY)知识库,后者需要你自行选择并运维向量存储(例如 Amazon OpenSearch Serverless)。

对本解决方案而言,有两点差异最为重要:

能力 Amazon Bedrock 托管 客户自管(DIY)
智能体检索(AgenticRetrieveStream) ✅ 支持 ❌ 不支持
AgentCore Gateway 集成 ✅ 支持 ❌ 不支持
数据存储 自动扩展,由 Amazon Bedrock 全托管 由你配置、扩展和维护
嵌入 + 重排序 内置托管模型,无额外费用 由你自行配置
需要管理的基础设施 无 向量数据库等更多

由于智能体检索和 AgentCore Gateway 连接器仅在托管知识库上可用,它自然成为企业级智能体检索的基础。这也意味着 Amazon Bedrock 会替你管理存储和索引,因此在此设计中无需预配 OpenSearch Serverless 集合或 OCU 容量。当你想要选择并运营自己的向量存储时,例如复用现有的 OpenSearch Serverless 集合,或满足特定的数据驻留、调优或合规要求,客户自管选项仍是正确的选择。对于企业级智能体检索而言,正是托管知识库使这项能力成为可能。

为什么需要可观测性与评估

一个只返回答案的智能体系统不足以投入生产。你需要观察它的行为表现(延迟、调用量、token 用量),评估检索和答案的质量,并且需要持续获得这些信号。本方案内置两个 CloudWatch 仪表板,覆盖七层遥测数据,外加两种评估方式(按需和持续),全部由同一个 CDK 应用程序预配。接地、引用和评估层也支持负责任的使用。带引用的答案可以追溯到其来源段落,使响应保持可审计;忠实度和正确性评分让你能在答案偏离来源时及时发现,而不是在生产环境中才察觉。

为什么这个方案选用 CDK

任何基础设施即代码工具都能创建这些资源。CDK 在此有其独特价值,原因在于这一架构本身:四个堆栈之间存在密集的交叉引用。Gateway 需要两个知识库 id。智能体需要 Gateway 的 URL 和 ARN。仪表板需要 KB id、Gateway ARN 以及智能体运行时 id——七个跨越堆栈边界的值,每一个都是某个指标的维度或某个连接器的目标。

如果用原始模板编写,那就意味着一组堆栈导出和跨堆栈查找需要靠人工保持一致,还需要一个按正确顺序部署并逐层传递输出的脚本,而一旦有任何重命名,两者都会立即失配。在 CDK 中,你只需传递构造,导出和依赖边就由这一传递动作自动推导出来。2024 年的方案有三个堆栈和一条基本线性的依赖链。而现在是四个堆栈、七个跨堆栈传递的值,这种差异已不再是风格问题。

方案概览

下面是一个问题在系统中端到端流转的完整路径。值得仔细看一遍,因为本文其余部分将按从左到右的顺序逐一讲解。

下图展示了端到端的智能体检索工作流。用户的问题进入 AgentCore Runtime 上的智能体,经由 AgentCore Gateway 路由到两个 AgenticRetrieveStream 工具之一——每个托管知识库各对应一个工具,分别是金融和天气,两者均从 Amazon S3 摄取数据。所选工具返回一个有依据、带引用的答案,每一步都会流式传输到 Amazon CloudWatch 和 AWS X-Ray。图中的编号徽标对应下文的逐步讲解。

Flow diagram: user question to AgentCore Runtime, out through AgentCore Gateway to two AgenticRetrieveStream tools backed by financial and weather managed knowledge bases fed from Amazon S3, returning a cited answer, with every step streaming to CloudWatch and X-Ray.

图 2:单个问题的端到端智能体检索工作流

图中的编号步骤对应如下工作流。

步骤 0 — 一次性操作,在部署时完成。在提出任何问题之前,Amazon S3 中的每个语料库都会被摄取到各自的托管知识库中:Amazon Bedrock 会自动解析、分块、嵌入并建立索引。这一步在堆栈部署时只发生一次,不属于每个问题的处理路径,因此画为虚线箭头并标记为步骤 0。之后的每个问题都遵循步骤 1–6:

  1. 用户向智能体发送问题,该智能体托管在 Amazon Bedrock AgentCore Runtime 上。Runtime 使用 OpenTelemetry (OTEL) span 自动对整个推理与行动循环进行埋点,因此系统从第一次调用起就是可观测的。
  2. 智能体的推理模型规划并跨知识库路由。由于每个知识库都作为独立的检索工具暴露,智能体会选择主题与问题匹配的工具——金融或天气——而不是搜索一个混合索引。
  3. 所选的工具调用由 AgentCore Gateway 通过 MCP 代理,由智能体的 IAM 任务角色授权并以 SigV4 签名,进而调用该知识库在托管知识库上的 AgenticRetrieveStream API。
  4. AgenticRetrieveStream 完成知识库内部的工作:它将问题分解为子查询,从 Amazon Bedrock 管理的数据存储(已从 Amazon S3 中的语料库摄取)中迭代检索,并综合出一个有依据、带引用的答案,再通过 Gateway 流式传回。
  5. 智能体检查返回的上下文是否足够。如果不够,它会循环执行,再次路由和检索,然后才撰写最终答案。如果足够,它将带引用的答案返回给用户。
  6. 在整个过程中,Runtime 会向 Amazon CloudWatch 和 AWS X-Ray 发送 span、令牌计数和指标,填充七个可观测性层,并为按需评估和持续评估的评分提供数据。

在内部,这个工作流是一个 CDK 应用,app.py,声明了四个堆栈。你无需 选择顺序,CDK 会根据你传入的构造引用推导出顺序:

kb_stack = KnowledgeBasesStack(app, f"{project}-kb", env=env)

gateway_stack = AgenticGatewayStack(
    app, f"{project}-gateway", env=env,
    financial_kb=kb_stack.financial_kb,  # ← the reference creates the dependency
    weather_kb=kb_stack.weather_kb,
)

agent_stack = AgentRuntimeStack(
    app, f"{project}-agent", env=env,
    gateway=gateway_stack.gateway,
)

DashboardsStack(
    app, f"{project}-dashboards", env=env,
    financial_kb=kb_stack.financial_kb,
    weather_kb=kb_stack.weather_kb,
    gateway=gateway_stack.gateway,
    agent_runtime=agent_stack.agent_runtime,
)

这就是全部的连接方式。这四个构造函数调用取代了导出/导入的簿记工作和部署脚本。

# 堆栈 它创建的内容 说明
1 -kb S3 存储桶、语料库上传、两个 Type: MANAGED 知识库、数据源、IAM、摄取 知识库达到 ACTIVE 状态,语料库已摄取
2 -gateway Gateway(AWS_IAM / MCP)、每个知识库的 AgenticRetrieveStream 目标 无需 Cognito
3 -agent ECR、CodeBuild、Runtime 上带埋点的 Strands 智能体、托管的日志/追踪、在线评估 约 8–10 分钟(镜像构建)
4 -dashboards agentic-observability + kb-observability 仪表板 在有流量之前为空

智能体运行一个推理与行动循环:它思考(一次大语言模型 (LLM) 调用),决定调用哪个知识库工具,读取通过 Gateway 返回的内容,并且通常会在撰写最终的带引用答案之前再次检索。每一步都由 Runtime 自动埋点,因此七个可观测性层由真实流量填充。

七个层。L1 知识库原生指标、L2 摄取、L3 智能体检索质量、L4 Gateway/MCP 指标、L5 OTEL span 树、L6 令牌使用量、L7 评估评分。层 1、4 和 5 自动发送。层 3、6 和 7 由驱动笔记本作为自定义指标发布。

所有可调项都在一个文件中,config.py:

设置 含义 默认值
EnvSettings.PROJECT_NAME 所有堆栈和资源的名称前缀 bmkb-obs-cdk
EnvSettings.ACCOUNT_REGION 要部署到的 AWS 区域 us-west-2(或你的配置文件中的区域)
KbConfig.EMBEDDING_MODEL_TYPE MANAGED(内置)或 CUSTOM(使用你自己的 ARN) MANAGED
AgentConfig.MODEL_ID 智能体的推理模型,即第 6 层的开销 Claude Haiku 4.5
EvalConfig.SAMPLING_PERCENTAGE 在线评估采样率,请参阅免责声明 100

修改 PROJECT_NAME 会生成整个解决方案的一个独立命名副本,因此两次部署可以在同一账户中共存。驱动程序笔记本读取同一个常量,因此会自动跟随重命名,无需手动保持堆栈名称同步。

描述数据集

该解决方案附带两个小型语料库,打包在仓库的 data/ 目录下:

  • 金融 — 一份合成的 Octank Financial 10-K 文件(octank_financial_10K.pdf,约 198 KB)。
  • 天气 — 一份真实的、公开可获取的美国国会研究服务局关于龙卷风的报告(IF12695,tornadoes_report.pdf,约 560 KB)。

⚠️ 关于数据: 金融语料库是合成的。Octank 是一家虚构的公司,10-K 文件是模型生成的示例数据(采用 CC0 许可)。它不是真实的金融数据。天气语料库是一份真实的、属于公有领域的美国政府作品,是一份国会研究服务局报告,可在 sgp.fas.org/crs/misc/IF12695.pdf 公开获取(美国政府作品不受版权保护)。要使用你自己的数据,只需将文件放入 data/ 目录并调整 config.py 中的前缀,无需其他更改。

这两个语料库刻意设计得彼此不同,因此智能体必须将每个问题路由到正确的知识库——这就是语义路由的意义所在。由于这些是托管知识库,我们无需配置分块、嵌入或索引:在摄取时,Amazon Bedrock 会解析每个 PDF、对其进行分块、使用其托管模型进行嵌入并自动建立索引。

我们使用两个独立的知识库,每个知识库通过网关作为一个独立的检索工具暴露,而不是用一个知识库同时容纳两个语料库——这一选择是深思熟虑的结果。为智能体提供两个命名工具,使路由成为一种明确可观察的推理行为:跨度树(span tree)会显示它针对特定问题选择了哪个工具,并且第 3 层中的检索质量指标在每个知识库上保持独立,因此金融和天气的精确度永远不会混为一个数字。如果用单一工具覆盖两个语料库,这种区分就会消失——路由决策将移入单个索引的排序过程中,变得不可见,而每个质量信号都将是两个不相关领域的平均值。保持语料库及其工具的分离,正是让智能体行为清晰可读的关键,而这正是本练习的全部意义所在。

在 CDK 中,将语料库放入 S3 是通过 BucketDeployment 实现的:

s3deploy.BucketDeployment(
    self, "FinancialCorpus",
    sources=[s3deploy.Source.asset(str(ROOT / "data" / "financial"))],
    destination_bucket=self.source_bucket,
    destination_key_prefix=KbConfig.FINANCIAL_PREFIX,
)

BucketDeployment 值得了解,恰恰是因为替代方案太具诱惑力:没有它,触发摄取的自定义资源往往会逐渐增加一个上传步骤,从而让一个资源承担两个不相关的任务。而在这里,它只需要负责摄取。

托管式摄取会自动扫描、分块、嵌入每个文档并建立索引:每个知识库索引一个文档,零失败。

Managed ingestion result showing one document indexed per knowledge base with zero failures

图 3:托管式摄取结果,每个知识库索引一个文档,零失败

部署解决方案

本节将引导你使用一条命令部署整个解决方案。在满足前提条件后,cdk deploy --all 会按照 CDK 推导出的依赖顺序预置所有四个堆栈,并自动传递所有跨堆栈的值。

前提条件

  • 一个 AWS 账户,具备 Amazon Bedrock、AgentCore(Runtime + Gateway)、AWS Identity and Access Management (IAM)、CloudWatch、AWS X-Ray、Amazon Elastic Container Registry (Amazon ECR)、AWS CodeBuild、Amazon S3、AWS Lambda 和 AWS CloudFormation 的权限。
  • 该智能体所用模型在账户中可用(默认为 us.anthropic.claude-haiku-4-5-20251001-v1:0)。
  • 已启用 CloudWatch Transaction Search,以便 OTEL 跨度落入 aws/spans,供第 5–7 层使用。
  • Node.js 18+ 和 CDK CLI(npm install -g aws-cdk),以及 Python 3.13+。
  • 账户和区域已完成 CDK 引导(cdk bootstrap),每个账户/区域一次。
  • 无需本地 Docker,智能体镜像由 CodeBuild 构建(ARM64)。

一条命令

要部署该方案,请克隆仓库、创建虚拟环境、安装依赖并运行 cdk deploy --all。在终端中运行以下命令:

git clone https://github.com/aws-samples/amazon-bedrock-samples.git
cd rag/managed-knowledge-bases/07-IaaC/managed-kb-observability-cdk/
python3 -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
cdk bootstrap  # once per account/region
cdk deploy --all

前两条命令克隆示例并进入项目目录。接下来两条命令创建并激活 Python 虚拟环境,并安装 CDK 应用的依赖。cdk bootstrap 为账户和区域准备 CDK(每个账户/区域只需一次),cdk deploy --all 部署全部四个堆栈。

账户和区域来自你的 AWS 配置文件(CDK_DEFAULT_ACCOUNT / CDK_DEFAULT_REGION),因此无需修改即可部署。

区域可用性:托管式知识库、智能体检索(AgenticRetrieveStream)以及 Amazon Bedrock AgentCore(Runtime 和 Gateway)并非在所有 AWS 区域都可用。部署之前,请确认你的目标区域提供所有这些服务,并相应地在 config.py 中设置 ACCOUNT_REGION。默认值为 us-west-2。有关当前可用性,请参阅 Amazon Bedrock 和 Amazon Bedrock AgentCore 文档以及 AWS Regional Services List。

这个版本没有部署脚本,而这正是要点所在:cdk deploy --all 会遍历它从 app.py 推导出的依赖图,按顺序部署四个堆栈,并自动传递所有跨堆栈的值。终端会在每个堆栈完成时输出报告:bmkb-obs-cdk-kb [1/4](默认 PROJECT_NAME),然后是 gateway [2/4]、agent [3/4]、dashboards [4/4]——这个顺序是推导出来的,而非声明式的:它由堆栈之间的构造引用决定,因此添加一个消费现有输出的第五个堆栈时,部署顺序会自行重新排列。

Stack -agent 使用 CodeBuild 构建智能体容器,因此该阶段大约需要 8–10 分钟。其余三个堆栈很快。每个堆栈完成时都会打印其输出,你可以从中获取知识库 ID、Gateway URL、运行时 ARN 以及两个仪表板 URL。部署完成时,全部四个堆栈均为完成状态。下面的截图显示 cdk deploy --all 成功后的 AWS CloudFormation 控制台。

AWS CloudFormation console listing four stacks — bmkb-obs-cdk-kb, -gateway, -agent, and -dashboards — each with status CREATE_COMPLETE.

图 4:部署成功后的 AWS CloudFormation 控制台,四个堆栈均为 CREATE_COMPLETE

在控制台中,全部四个堆栈——kb、gateway、agent 和 dashboards——都显示 CREATE_COMPLETE,各自的描述中显示了其在推导出的依赖顺序中的 [n/4] 位置。

关于长时间运行资源的说明

本方案中有两件事远超 AWS Lambda 函数 15 分钟上限所允许的简单轮询时间:知识库摄取同步和 CodeBuild 镜像构建。AWS CDK Provider 框架通过 on_event / is_complete 拆分来处理这种情况:一个处理程序启动工作,另一个被轮询直到报告完成。

cr.Provider(
    self, "IngestSyncProvider",
    on_event_handler=on_event,        # StartIngestionJob, return the job id
    is_complete_handler=is_complete,  # GetIngestionJob, report IsComplete
    query_interval=Duration.seconds(30),
    total_timeout=Duration.minutes(30),
)

没有任何阻塞,返回给 CloudFormation 的响应协议由框架负责,而不是手写进每个处理器,30 分钟的时限也是一个属性,而不是需要想办法绕过的工程难题。

启动并测试

打开 notebooks/01-drive-and-observe.ipynb 并从头到尾运行它(先完成 pip install -r requirements-notebook.txt)。它会读取堆栈输出,然后向已部署的智能体发送针对特定主题的提示词,每个知识库一个会话,这样你可以在同一个地方看到答案和语义路由:财务问题由 Octank 10-K 回答,天气问题由龙卷风报告回答,每个都自动路由到匹配的 AgenticRetrieveStream 工具。

Notebook 会从堆栈输出中发现一切,因此无需手动粘贴任何内容:

# Stack names are derived from the same PROJECT_NAME the CDK app uses, so renaming the
# project in config.py cannot leave this notebook pointing at stale stacks.
from config import EnvSettings

PROJECT = EnvSettings.PROJECT_NAME
STACKS = {suffix: f'{PROJECT}-{suffix}'
          for suffix in ('kb', 'gateway', 'agent', 'dashboards')}

def outputs(stack_name):
    s = cfn.describe_stacks(StackName=stack_name)['Stacks'][0]
    return {o['OutputKey']: o['OutputValue'] for o in s.get('Outputs', [])}

该 Notebook 导入与应用程序所使用的相同的 config.py,因此两者对堆栈的命名永远不会出现分歧。

驱动程序通过已部署的智能体发送针对每个知识库的提示词。财务问题路由到财务知识库,天气问题路由到天气知识库,每个都返回有据可依、带引用的答案。

Driver notebook output showing financial and weather questions each routed to the matching knowledge base with a cited answer

图 5:针对每个知识库的提示词路由到匹配的知识库,每个都返回有据可依、带引用的答案

该 Notebook 还根据智能体实际检索和引用的内容计算无参考的第 3 层检索质量信号——无需 ground truth,无需额外的评判器——并将它们发布到 CloudWatch。

Per-KB Layer 3 retrieval-quality signals: retrieval utilization (precision proxy), grounded coverage (faithfulness proxy), duplicate rate, and retrieval-set counts

图 6:每个知识库的无参考第 3 层检索质量信号

观测:七层仪表板

这两个仪表板是七层可见性的所在。堆栈会创建这两个仪表板,而驱动流量即可填充它们。以下截图展示了这两个仪表板在 CloudWatch 控制台中的样子。

CloudWatch dashboards list showing two dashboards: the end-to-end agentic-observability dashboard and the per-KB (BMKB) observability dashboard.

图 7:CloudWatch 控制台中的两个仪表板——端到端智能体可观测性和每知识库可观测性

这两个仪表板都出现在 CloudWatch 控制台中:一个用于端到端智能体可观测性,一个用于每知识库可观测性(BMKB,Bedrock Managed Knowledge Base)。本节的其余部分将逐一介绍每一层显示的内容。

在这个版本中,仪表板由类型化对象构建,而不是 JSON 字符串。指标是一个 Metric,小部件是一个 GraphWidget,布局是一个 Rows 列表:

cw.GraphWidget(
    title="L4 — Gateway metrics (MCP)",
    width=12, height=6, period=PERIOD, statistic="Sum",
    left=[
        self._gateway_metric("Invocations", gateway_arn, "tools/call Invocations"),
        self._gateway_metric("SystemErrors", gateway_arn, "SystemErrors"),
        self._gateway_metric("UserErrors", gateway_arn, "UserErrors"),
        self._gateway_metric("Throttles", gateway_arn, "Throttles"),
    ],
)

实际的好处并不在于代码行数更少——dashboards_stack.py 运行到 400 行,比等效的 DashboardBody JSON 还要多。而在于拼写错误的属性或无效的小部件组合会在 cdk synth 时失败——在你的笔记本电脑上,大约一秒钟内。同样的错误如果发生在 DashboardBody 字符串中,则会成功部署,并在 AWS Management Console 上渲染为一个损坏的小部件,你只能通过肉眼查看才能发现问题。

由于 Row 接受任何小部件,文档就成为布局的一部分,而不是会逐渐过时的 wiki 页面。每个仪表板都将 TextWidget 与图表交错排列:

[cw.TextWidget(width=24, height=7, markdown=self._l3_explainer())],
[
    self._quality_widget("L3 · Financial KB — agentic quality (reference-free, 0–1)", fin_id),
    self._quality_widget("L3 · Weather KB — agentic quality (reference-free, 0–1)", wx_id),
],

_l3_explainer() 返回一个 Markdown 表格,定义每个比率及其解读方式,因此解释 grounded_coverage 的小部件与绘制它的小部件在同一个提交中交付,首次打开仪表板的读者无需猜测无参考指标是什么。

仪表盘 A —— 智能体可观测性(端到端)。 面板开头解释了每一层统计的是不同的东西(对于 N 个查询,你会看到约 N 次智能体调用、约 2N 次检索、约 3N 次 LLM 调用,以及约 5N 次 Gateway MCP 操作,让智能体循环变得可见),然后展示 KB 指标(L1)、Gateway 指标与延迟(L4)、token 用量(L6),并且由于该智能体使用 AgenticRetrieveStream(返回合成的、带引用的答案,而不是逐 chunk 的分数),所以展示的是无参考的 Layer 3 质量信号,内嵌定义并按 KB 逐个绘制。

在仪表盘 A 上,Layer 3 无参考质量信号按知识库逐个绘制。首先是金融 KB。下图绘制了它的 retrieval_utilization、grounded_coverage 和 duplicate_rate。

CloudWatch line chart of the financial KB’s Layer 3 quality, plotting retrieval_utilization, grounded_coverage, and duplicate_rate on a 0 to 1 scale.

图 8:金融 KB 的 Layer 3 质量,绘制检索利用率、接地覆盖率和重复率

天气 KB 展示同样的三个信号,分开跟踪,因此这两个领域绝不会混成一个数字。

CloudWatch line chart of the weather KB’s Layer 3 quality, plotting retrieval_utilization, grounded_coverage, and duplicate_rate on a 0 to 1 scale.

图 9:天气 KB 的 Layer 3 质量,与金融 KB 分开跟踪

这两张图分别为金融 KB(左)和天气 KB(右)绘制 retrieval_utilization、grounded_coverage 和 duplicate_rate,因此每个知识库的检索质量保持可分离,而不是被平均成一个数字。

仪表盘 B —— 按 KB(BMKB)可观测性。 同样决定支出的运营信号,按 KB 展示:索引大小(来自 S3 源字节数)、检索量、智能体工具调用、会话 token 用量,以及按模型细分的生成 token 用量,每项都附带内嵌说明。

以下截图展示仪表盘 B 的四个指标小组件,数据来自一次运行之后。第一个组件报告索引大小,由 S3 源字节数推导得出,按知识库细分。

CloudWatch widget showing index size in MB from S3 source bytes, broken out per knowledge base.

图 10:仪表盘 B 的每个知识库索引大小,由 Amazon S3 源字节数推导得出

第二个组件统计每个知识库的检索调用次数。

CloudWatch widget showing Retrieve call invocations count, per knowledge base.

图 11:仪表盘 B 的每个知识库检索调用次数

第三个组件统计经由 Gateway 的智能体工具调用次数。这一项由两个 KB 共享,而不是分开统计。

CloudWatch widget showing agentic tool-call invocations through the AgentCore Gateway, as a single shared count.

图 12:仪表盘 B 经由 AgentCore Gateway 的智能体工具调用次数,由两个知识库共享

第四个组件报告每个知识库的会话 token 用量。

CloudWatch widget showing session token usage (SessionQuotaTokens), per knowledge base.

图 13:仪表盘 B 的每个知识库会话 token 用量

这四个组件展示索引大小(来自 S3 源字节数)、检索调用量、经由 Gateway 的智能体工具调用次数,以及会话 token 用量。除由两者共享的 Gateway 工具调用次数外,每项都按知识库细分。

有一个维度细节值得指出,因为强类型 API 不会帮你发现它:AWS/Bedrock/KnowledgeBases 命名空间要求带前缀的 id,即 knowledge-base/<id>,而解决方案自己的 BMKB/* 命名空间使用不带前缀的裸 id。就 CDK 而言,两者都只是字符串。

# L1 — AWS/Bedrock/KnowledgeBases wants the prefixed form.
dimensions_map={"Operation": "Retrieve", "KnowledgeBaseId": f"knowledge-base/{kb_id}"}

# BMKB/* — our own namespaces use the bare id.
dimensions_map={"KnowledgeBaseId": kb_id}

Layer 5:span 树

Layer 5 是 aws/spans 中的原始 OTEL span 树,正是它直接向你展示智能体循环。值得仔细阅读,因为嵌套结构并不直观:一次 LLM 调用会产生三个 span。Strands 的 invoke_agent span 包裹这一轮对话,一个内部的 chat span 包裹模型调用,而一个 kind 为 CLIENT 的 chat <model> span 才是实际调用,且三者都重复同样的 gen_ai.usage.* token 属性。

以下截图展示 aws/spans 中的 span 树,最上面的几行可以看到三重 span 嵌套。

CloudWatch span tree in aws/spans. The top three rows show an invoke_agent Strands Agents aggregate, an internal chat span, and a CLIENT chat span, all reporting an identical 7,299 input and 268 output tokens.

图 14:Layer 5 在 aws/spans 中的 span 树,展示单次 LLM 调用的三重 span 嵌套

阅读最上面三行:一个 invoke_agent Strands Agents 聚合,然后是一个内部聊天 span,再是一个 CLIENT 聊天 span,三者报告的输入 token 均为 7,299、输出 token 均为 268,完全相同。也就是说同一次 LLM 调用出现在树的三个层级上,这正是聚合需要谨慎处理的原因。

一旦进行聚合,这种结构就变得重要。要只统计每次 LLM 调用一次,请过滤到 CLIENT 叶子节点:

fields attributes.gen_ai.request.model as model,
       attributes.gen_ai.usage.input_tokens as in_tok,
       attributes.gen_ai.usage.output_tokens as out_tok
| filter ispresent(in_tok) and kind = 'CLIENT' and name like /^chat /
| stats sum(in_tok) as input_tokens, sum(out_tok) as output_tokens,
        count(*) as llm_calls by model

如果没有这个过滤条件,同一查询将报告三倍的 token 和三倍的调用次数。

应用该 CLIENT 叶子过滤条件后,下面的截图展示了按模型划分的生成 token 用量,每次 LLM 调用只计一次。

CloudWatch results table of generation token usage by model, counting each LLM call once: 14 calls, 60,472 input tokens and 2,709 output tokens.

图 15:按模型划分的生成 token 用量,每次 LLM 调用只计一次

应用过滤条件后,这次运行显示 14 次 LLM 调用,总计 60,472 个输入 token 和 2,709 个输出 token。如果没有过滤条件,同一查询将报告三倍的调用次数和三倍的 token。

评估:按需与持续

质量通过两种方式衡量,两种方式均由该应用预置。

按需评估:驱动笔记本调用 AgentCore 本版本中没有部署脚本,而这正是关键所在: (LLM 作为评审)对每个会话的 span 运行内置评估器——Correctness(正确性)、Faithfulness(忠实度)、Tool-Selection Accuracy(工具选择准确率),并将分数发布到 CloudWatch,在 Dashboard A 上作为 Layer 7 呈现。

下面的截图展示了 Dashboard A 上的 Layer 7:每个知识库的按需评估分数,同一仪表板上还有 Layer 5 的 OTEL span 表格。

CloudWatch dashboard showing Layer 7 on-demand evaluation scores per knowledge base, with the Layer 5 OTEL span table beside them.

图 16:Layer 7 每个知识库的按需评估分数,旁边是 Layer 5 的 span 表格

Layer 7 显示每个知识库的按需评估分数,旁边是 Layer 5 的 OTEL span 表格,这样分数与产生这些分数的 span 可以在同一块面板上一起查看。

持续(在线)评估: 智能体栈还会预置一个 AWS::BedrockAgentCore::OnlineEvaluationConfig,对实时会话进行采样并自动评分,无需运行笔记本。该配置本身可在 Bedrock AgentCore 控制台的 Assess → Evaluations → Evaluation configurations 中查看。结果落在日志组 /aws/bedrock-agentcore/evaluations/results/<config-id> 中,每个分数还会作为 CloudWatch 指标发布在 Bedrock-AgentCore/Evaluations 命名空间中,以评估器名称和配置 id 作为键,因此无需解析日志即可绘制分数图表或对其设置告警。

下面的截图展示了 AgentCore 控制台中的在线评估配置,路径为 Assess、Evaluations、Evaluation configurations。

AgentCore console showing the online evaluation configuration with four built-in evaluators (Tool-Selection Accuracy, Faithfulness, Correctness, Response Relevance), 100 percent sampling, status ACTIVE and enabled.

图 17:AgentCore 控制台中的在线评估配置,四个内置评估器均为 100% 采样

该配置列出了四个内置评估器:Tool-Selection Accuracy、Faithfulness、Correctness 和 Response Relevance,采样率均设为 100%,状态显示为 ACTIVE 且已启用。

由于这些分数是普通的 CloudWatch 指标,它们可以像其他任何指标一样绘制图表,无需解析日志,而且同一个组件上还可以附加告警。下面的截图展示了这四个评估器分数作为普通 CloudWatch 指标绘制的图表。

CloudWatch graph of the four evaluator scores in the Bedrock-AgentCore/Evaluations namespace

图 18:四个持续评估器分数作为普通 CloudWatch 指标绘制的图表

这四个评估器分数以指标形式出现在 Bedrock-AgentCore/Evaluations 命名空间中,每个均以评估配置 id 和智能体的服务名称作为键,因此无需解析日志即可绘制图表或对其设置告警。

在线结果只有在会话结束后才会产生。评分基于已完成的会话运行,因此在您产生流量后,结果日志组在大约等于会话空闲超时时间(约 30 分钟)内会保持为空。运行后立即出现空组是正常现象,并非配置错误。每个结果事件都携带 session.id,因此可以与所有其他层关联。笔记本中的按需路径没有这种等待,这正是两者都包含在此处的原因。

在本次运行的四个内置评估器中,持续路径在正确性、忠实度、回答相关性和工具选择准确率上均得 1.00 分,每个评估器有五个已评分的追踪记录,每个结果都附有书面解释。

⚠️ 采样与成本。本应用将 SAMPLING_PERCENTAGE = 100 设置纯粹是为了博客实验,因此每个会话都会被评分,结果立即可见。这并非生产环境建议。在线评估会对每个被采样的会话调用一次 LLM 作为评判者,因此成本随采样率和流量规模而增加。对于真实部署,请选择符合您的质量监控需求和预算的采样百分比,并在启用之前使配置与您所在组织自身的策略和成本治理要求保持一致。该比率是一个单一常量,即 EvalConfig.SAMPLING_PERCENTAGE,位于 config.py 中。

清理

完成后,请移除该解决方案以停止产生费用,因为存储的 KB 数据、ECR 中的智能体镜像以及仍在运行的在线评估在堆栈闲置期间都会产生费用。一条命令即可全部拆除:

cdk destroy --all

CDK 会自行按逆依赖顺序删除堆栈:先是仪表板,然后是智能体,接着是网关,最后是 kb——即部署时推导出的同一个依赖图,反向遍历。无需维护一个与部署脚本保持一致的拆除脚本,这是将依赖关系建模一次的 quietly 有价值的好处之一。

有两种删除行为值得了解,因为它们都替代了手写的清理代码:

self.source_bucket = s3.Bucket(
    self, "SourceBucket",
    removal_policy=RemovalPolicy.DESTROY,
    auto_delete_objects=True,  # replaces a list_object_versions delete loop
)

self.repository = ecr.Repository(
    self, "AgentImageRepo",
    removal_policy=RemovalPolicy.DESTROY,
    empty_on_delete=True,  # replaces an image-delete loop
)

两者都是为了示例整洁而设置的,对于您关心的任何内容,请将存储桶切换为 RemovalPolicy.RETAIN。

CDK 引导堆栈(CDKToolkit)及其暂存存储桶不会被移除。它们是账户级共享基础设施,其他 CDK 应用可能依赖于此。

与单次执行方案相比的变化

本文与 2024 年的方案都是使用 cdk deploy 部署的 Python CDK 应用。编写工具在这里是常量,变化的是其底层架构,而大部分变化是做减法:

关注点 2024 年:单次执行 RAG 本方案
向量存储 由您预置、扩缩容并支付 OCU 费用的 OpenSearch Serverless 集合和索引 无。类型:MANAGED,由 Amazon Bedrock 负责存储和索引
索引生命周期 自定义资源创建索引并等待其变为可搜索 无需创建任何内容
嵌入模型 由您选择并授予模型访问权限 默认由服务托管
堆栈 3 个:KB 角色 → OpenSearch 基础设施 → KB + 数据源 4 个:kb → 网关 → 智能体 → 仪表板
检索 一次 Retrieve,按向量相似度返回 top-k 块 AgenticRetrieveStream,内部进行重排序,返回合成的带引用答案
选择知识库 不适用,只有一个索引 智能体在两个知识库之间进行语义路由
重新审视语料库 单次执行无法实现 智能体在回答前再次检索
遥测 未预置 七层,两个仪表板
评估 手动:同步、选择模型、提问 按需和持续评估,均已预置
闲置时的成本底线 OCU 费用无论你是否查询都会产生 仅存储费用

这种权衡在两个方向上都真实存在。回答单轮问题的单个索引,每次查询成本更低、推理起来更简单,并且当答案就在一个语料库中时完全够用。当不是这种情况时,再选择这种设计。

完成实际工作的 CDK 构造

这个应用中有四个构造承担了大部分工作,值得点名,因为每一个都取代了人们原本需要手写的东西:

  • Provider – 用于生命周期超过 AWS Lambda 函数 15 分钟上限的自定义资源。on_event / is_complete 的划分意味着没有任何东西会睡在轮询循环里,而返回 CloudFormation 的响应协议是框架的职责,而不是每个处理器里内置的某个 vendored urllib 回调。
  • BucketDeployment – 负责上传语料库,因此摄取自定义资源只需负责摄取。
  • auto_delete_objects=True 和 empty_on_delete=True,取代了手写的 list_object_versions 和镜像删除循环——否则一个栈连能否被销毁都成问题。
  • repository.grant_pull_push(project) – 一行代码搞定 CodeBuild 需要的八个 ecr:* 操作,而且从构造上就拼写正确。

还有来自可观测部分的类型化仪表板:用 Metric / GraphWidget / Row 对象代替 DashboardBody JSON 字符串,这将无效的组件组合从“在控制台上渲染异常”变成了“在 cdk synth 时失败”。

有两件事 CDK 并没有改变,在开始之前这两点都值得了解:

  • 自由格式的 JSON 仍然保持 camelCase。connector_parameters 和 parameter_values 被原样透传,因此 AWS CDK 从 snake_case 到 PascalCase 的转换不适用于其内部。像 bucketName 和 inclusionPrefixes 这样的键必须完全按照 API 期望的方式拼写,否则在资源失败之前没有任何提示。
  • 字符串类型的值仍然是字符串。可观测部分提到的 knowledge-base/<id> 与不带引号的 <id> 之间的维度差异对类型系统是不可见的。类型化构造检查的是结构,而不是语义。

这两点都不是偏好某个工具的理由。它们只是“类型化”所能带来收益的边界,而知道这个边界在哪里,正是让 CDK 用起来令人愉悦而不是充满意外的关键所在。

结论

我们在托管的 Amazon Bedrock Knowledge Base 和 AgentCore 上构建了一套完整的企业级智能体检索解决方案:多知识库语义路由、一个推理智能体、七层可观测性,以及按需和持续两种评估方式,并将所有内容部署为单个 AWS CDK 应用。

有两点值得记住。托管知识库是移除了基础设施而不是增加基础设施:无需预置向量存储,无需创建并等待索引,系统空闲时也不会累积 OCU 费用项。它也是支持智能体检索和 AgentCore Gateway 连接器的知识库类型,如 Amazon Bedrock 文档所述。而且一旦检索变得 agentic,可观测性就不再是可选项。这个应用所预置的内容中,大约有一半是为了弄清智能体实际做了什么以及答案是否够好,这并不是智能体系统的额外负担,而是让系统可运营的关键部分。

CDK 的编写模型在各个接缝处证明了自身的价值。在这个架构中,七个值跨越栈边界传递,而每一个都是构造引用而非导出/导入对,这也是为什么不需要部署脚本、不需要拆除脚本,也不需要记住固定的部署顺序。

您可以在此处将数据源指向您自己的语料库,把智能体部署在虚拟私有云(VPC)中,根据您的预算和策略调整在线评估采样率,或向路由器添加更多知识库。要了解更多信息,请参阅 Amazon Bedrock 详情页面和 Amazon Bedrock 文档。该应用程序、驱动程序笔记本和独立实用工具都在 随附的 GitHub 仓库中,您可以克隆它并使用一条命令部署自己的副本。


关于作者

来源: AWS Machine Learning Blog · aws.amazon.com