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

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

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

AI 导读

AWS 发布教程,介绍如何用单个 AWS CDK 应用在 Amazon Bedrock 托管知识库(Type: MANAGED)和 AgentCore 上构建企业级智能体检索系统,通过 AgenticRetrieveStream 实现多知识库语义路由和带引用的答案。

正文 · AI 翻译

本文使用 AWS Cloud Development Kit(AWS CDK)在 Managed 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 现在提供托管知识库(Type: MANAGED),它自行负责摄取、存储、索引和检索。那篇文章所预置的 OpenSearch Serverless 集合和索引不再需要你创建、扩展,也不再需要用 OpenSearch Compute Units(OCUs)付费。
  2. Amazon Bedrock AgentCore 提供了智能体部分:一个 Gateway 将每个知识库暴露为 Model Context Protocol(MCP)工具,一个 Runtime 托管推理智能体,以及 AgenticRetrieveStream,它在内部进行重排序并返回经过综合、带引用的答案。智能体检索仅在 Managed Knowledge Base 上可用。
  3. 可观测性和评估现在是可以直接预置的能力,而不是事后附加的东西。该解决方案附带两个 Amazon CloudWatch 仪表板,覆盖七层遥测数据,并从同一次部署中提供按需和持续两种形式的评估。

本文构建的正是这套系统:一个智能体,它跨两个知识库进行路由,通过 Gateway 执行检索,并返回有依据、带引用的答案,其行为的每一层都被插桩监测,整体作为一个用 Python 编写的单一 AWS CDK 应用。一条命令即可部署全部内容,CDK 会自动推导部署顺序。

如果你读过2024 年的文章,从 Amazon S3 存储桶到知识库的路径会看起来很熟悉,而之后的一切都是新的:向量存储没有了,检索变成了智能体式的,并且应用所预置的资源中大约有一半是为了告诉你智能体做了什么。如果你没读过,本文也可以独立阅读。

涉及的服务以及一个问题经过它们的路径:智能体决定是否检索以及查阅哪个知识库,Gateway 将每个知识库暴露为它可以调用的工具,Managed Knowledge Bases 负责搜索和引用,这个循环会根据问题的需要重复任意多次。智能体沿途所做的一切都会进入 Amazon CloudWatch。

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

图 1:涉及的服务以及单个问题经过它们的路径

背景

在开始逐步讲解之前,先确立三件事会有所帮助:是什么让检索变得智能体化,为什么 Managed Knowledge Base 是它的合适基础,以及为什么可观测性和评估应当属于设计的一部分,而不是事后附加。

从 RAG 到企业级智能体检索

经典的检索增强生成(RAG)执行一次检索和一次生成。企业级智能体检索将推理智能体放入循环中:它决定是否检索以及检索什么,可以多次检索以进行细化,选择哪个知识库是相关的(语义路由),然后才组合出有依据、带引用的答案。借助 Managed Knowledge Base,这是一项一等服务能力,AgenticRetrieveStream API 执行多轮规划、执行检索并返回带引用的答案,而不是你在原始向量搜索之上构建和维护的编排层。这在复杂问题上能产生更好的答案,但也产生了一个更复杂的待运维系统,这正是可观测性和评估从一开始就内置的原因。

Managed 与自助式 Knowledge Bases 的对比

Amazon Bedrock 现在提供 Managed Knowledge Base(Type: MANAGED):Amazon Bedrock 为你管理摄取、存储、索引和检索,默认使用服务托管模型进行嵌入和重排序,因此无需预置、扩展或修补向量数据库。这与客户管理(DIY)Knowledge Base 不同,后者由你自己选择和运维向量存储(例如 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。仪表板需要知识库 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 中的每个语料库都会被摄取到各自独立的 Managed Knowledge Base 中:Amazon Bedrock 会自动对其进行解析、分块、嵌入和索引。这一过程在堆栈部署时执行一次,不属于每次提问的处理路径,因此用虚线箭头表示并标记为步骤 0。之后每个问题都遵循步骤 1–6:

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

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

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 agent、提供的日志/跟踪、在线评估 约 8–10 分钟(镜像构建)
4 -dashboards agentic-observability + kb-observability 仪表板 在你产生流量之前为空

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

七个层。L1 知识库原生指标、L2 摄取、L3 agentic 检索质量、L4 Gateway/MCP 指标、L5 OTEL span 树、L6 token 使用量、L7 评估分数。第 1、4、5 层自动发出。第 3、6、7 层由 driver notebook 作为自定义指标发布。

所有可调项都在一个文件中,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 会得到整个解决方案的一个独立命名副本,因此两个部署可以在同一个账户中共存。驱动程序 notebook 读取同一个常量,因此它会自动跟随重命名,无需手动保持堆栈名称同步。

描述数据集

该解决方案附带两个小型语料库,捆绑在仓库的 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、对其进行分块、使用其托管模型进行嵌入,并自动为其建立索引。

我们使用两个独立的知识库,每个都通过 Gateway 作为自己的检索工具暴露,而不是用一个知识库同时容纳两个语料库,这一选择是有意为之。为智能体提供两个具名工具,使路由成为一次可被观察到的显式推理行为:span 树会显示它针对给定问题选择了哪个工具,而第 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 值得了解,正是因为替代方案太诱人:没有它,触发摄取的 custom resource 往往会增加一个上传步骤,于是现在一个资源承担了两项不相关的工作。在这里,它只需负责摄取。

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

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

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

部署解决方案

本节将介绍如何用一条命令部署整个解决方案。满足先决条件后,cdk deploy --all 会按 CDK 推导出的依赖顺序预置全部四个堆栈,并自动传递每个跨堆栈值。

先决条件

  • 一个具有 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 权限的 AWS 账户。
  • 账户中可用的智能体模型(默认为 us.anthropic.claude-haiku-4-5-20251001-v1:0)。
  • 已启用 CloudWatch Transaction Search,以便 OTEL span 落入 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],而且这个顺序是推导出来的,不是声明的:它源自堆栈之间的构造引用,因此添加一个消费现有输出的第五个堆栈会自动重新排列部署顺序。

-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),
)

Nothing 会阻塞,返回 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 层检索质量信号,无需真实基准,无需额外评判器,并将它们发布到 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,布局是 Row 的列表:

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 管理控制台中呈现为损坏的小部件,你只能通过查看它来发现。

因为 Row 接受任何小部件,文档是布局的一部分,而不是一个会漂移的 wiki 页面。每个仪表板将 TextWidgets 与图表交错排列:

[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 的小部件与绘制它的小部件在同一个提交中发布,冷启动打开仪表板的读者不需要猜测什么是无参考指标。

Dashboard A — 智能体可观测性(端到端)。 该看板开篇解释了每一层统计的是不同内容(对于 N 次查询,你会看到约 N 次智能体调用、约 2N 次检索、约 3N 次 LLM 调用,以及约 5N 次 Gateway MCP 操作,让智能体循环变得可见),随后展示 KB 指标(L1)、Gateway 指标与延迟(L4)、token 用量(L6),并且由于智能体使用 AgenticRetrieveStream——它返回的是经过综合、带引用的答案,而非逐块分数——还展示了无参考的 Layer 3 质量信号,这些信号就地定义并按 KB 绘制。

在 Dashboard 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,因此每个知识库的检索质量保持可区分,而不会被平均成一个数字。

Dashboard B — 按 KB(BMKB)的可观测性。 这些同样决定开销的运营信号按 KB 展示:索引大小(来自 S3 源字节)、检索量、智能体工具调用、会话 token 用量,以及按模型划分的生成 token 用量,每项都配有就地说明。

以下截图展示了 Dashboard B 的四个指标小组件,它们在一次运行后填充数据。第一个小组件报告按知识库划分的索引大小,该大小由 S3 源字节推导而来。

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

图 10:Dashboard B 按知识库划分的索引大小,由 Amazon S3 源字节推导而来

第二个小组件统计按知识库划分的检索调用次数。

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

图 11:Dashboard B 按知识库划分的检索调用次数

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

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

图 12:Dashboard B 通过 AgentCore Gateway 的智能体工具调用次数,由两个知识库共享

第四个小组件报告按知识库划分的会话 token 用量。

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

图 13:Dashboard 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 包裹模型调用,而一个类型为 CLIENT 的 chat <model> span 才是实际调用,并且这三个 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:aws/spans 中的 Layer 5 span 树,展示单次 LLM 调用的三层 span 嵌套

读取前三行:一个 invoke_agent Strands Agents 聚合、一个内部 chat span,然后是一个 CLIENT chat span,它们都报告相同的 7,299 个输入 token 和 268 个输出 token。这就是同一个 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 数。

评估:按需与持续

质量通过两种方式衡量,两者都由应用提供。

按需:驱动程序 notebook 调用 AgentCore(LLM-as-judge),对每个会话的 span 运行内置评估器——Correctness、Faithfulness、Tool-Selection Accuracy——并将分数发布到 CloudWatch,在 Dashboard A 上显示为第 7 层。

下面的截图显示了 Dashboard A 上的第 7 层:按知识库划分的按需评估分数,同一仪表板上还有第 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:第 7 层按知识库划分的按需评估分数,旁边是第 5 层 span 表

第 7 层显示按知识库划分的按需评估分数,旁边是第 5 层 OTEL span 表,因此分数和产生这些分数的 span 可以在同一块板上一起查看。

持续(在线): agent 栈还提供了一个 AWS::BedrockAgentCore::OnlineEvaluationConfig,它会采样实时会话并自动评分,无需运行 notebook。配置本身可以在 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 和 agent 的服务名称为键,因此你可以绘制它们或对其设置告警,而无需解析日志。

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

在本次运行的四个内置评估器上,连续路径在正确性、忠实性、响应相关性和工具选择准确性上均获得 1.00 分,每个评估器评分五条追踪记录,每条结果都附带书面说明。

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

清理

完成后,请移除解决方案以停止产生费用,因为存储的知识库数据、ECR 中的代理镜像以及仍在运行的在线评估在堆栈闲置时都会产生费用。一条命令即可拆除所有内容:

cdk destroy --all

CDK 会自行按反向依赖顺序删除堆栈:先是仪表板,然后是代理,然后是网关,最后是知识库:与部署时推导出的相同依赖图,反向遍历。没有需要与部署脚本保持一致的拆除脚本,这是对依赖关系建模一次所带来的较为隐蔽的宝贵特性之一。

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

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:知识库角色 → OpenSearch 基础设施 → 知识库 + 数据源 4:知识库 → 网关 → 代理 → 仪表板
检索 一次 Retrieve,按向量相似度返回 top-k 块 AgenticRetrieveStream,内部重新排序,返回合成的带引用答案
选择知识库 不适用,只有一个索引 代理在两个知识库之间进行语义路由
二次查看语料库 单次无法实现 代理在回答前再次检索
遥测 未预置 七层,两个仪表板
评估 手动:同步、选择模型、提问 按需和连续,均已预置
闲置时的成本下限 无论是否查询,OCU 费用都会累积 仅存储

这种取舍在双向都是真实的。单个索引回答单次问题,每次查询更便宜、推理更简单,并且当一个语料库就包含答案时完全够用。当情况并非如此时,就该采用这种设计。

承担主要工作的 CDK 构造

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

  • Provider – 用于超出 AWS Lambda 函数 15 分钟上限的自定义资源。on_event / is_complete 的拆分意味着没有任何东西在轮询循环中休眠,而返回 CloudFormation 的响应协议是框架的职责,而不是在每个处理程序中内嵌一个 urllib 回调。
  • BucketDeployment – 上传语料库,因此摄取自定义资源只负责摄取。
  • auto_delete_objects=True 和 empty_on_delete=True,替代了手写的 list_object_versions 和镜像删除循环,否则堆栈根本无法被销毁。
  • repository.grant_pull_push(project) – 一行代码搞定 CodeBuild 所需的八个 ecr:* 操作,并且从构造上就是拼写正确的。

以及来自 Observe 部分的类型化仪表板:使用 Metric / GraphWidget / Row 对象而不是 DashboardBody JSON 字符串,这会将无效的组件组合从“在控制台上渲染出错”变为“在 cdk synth 时失败”。

有两件事 CDK 不会改变,而且都值得在开始前了解:

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

这两点都不是偏好某个工具而非另一个的理由。它们就是“类型化”能为你带来的边界,而知道这条边界在哪里,正是让 CDK 令人愉快而非令人惊讶的关键。

结论

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

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

CDK 的编写模型在衔接处赢得了自己的位置。在这个架构中有七个值跨越堆栈边界,每一个都是构造引用而不是导出/导入对,这也是为什么没有部署脚本、没有拆除脚本,也没有需要记住的固定顺序。

在这里,你可以将数据源指向自己的语料库,将代理置于虚拟私有云(VPC)中,根据预算和策略调整在线评估采样率,或向路由器添加更多知识库。要了解更多信息,请参阅 Amazon Bedrock 详情页面和 Amazon Bedrock 文档。该应用、驱动程序笔记本以及自包含实用工具均位于随附的 GitHub 仓库中,因此你可以克隆它并通过一条命令部署自己的副本。


关于作者

Prabhakar Chandrasekaran

Prabhakar Chandrasekaran

Prabhakar 是 AWS Enterprise Support 的高级技术客户经理。Prabhakar 乐于帮助客户在云上构建 AI 和 ML 解决方案。他还与企业客户合作,提供主动指导和运营协助,帮助他们在使用 AWS 时提升解决方案的价值。Prabhakar 持有八项 AWS 认证和七项其他专业认证。凭借超过 23 年的专业经验,Prabhakar 在加入 AWS 之前曾在金融服务领域担任数据工程师和项目负责人。

Siddhant Sahu

Siddhant Sahu

Siddhant 是 AWS 的高级产品经理,负责领导 Amazon Bedrock Managed Knowledge Base 的产品开发。他专注于构建和推出基础设施软件服务,通过完全托管的摄取、解析和代理式搜索管道,以经济高效、准确且安全的方式处理多模态、多语言企业数据,并利用混合搜索、多轮检索、工具使用和嵌入技术等最先进的能力。他的工作使数千家企业客户和 ISV 能够大规模构建代理。

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