用 CDK 在托管式 Amazon Bedrock 知识库上构建智能体检索(Agentic Retrieval)

AWS ML Blog 2026-10-27T01:15:14+08:00

本文将介绍如何用 AWS 云开发工具包(AWS CDK)在托管的 Amazon Bedrock 知识库(Managed Amazon Bedrock Knowledge Base)上构建企业级的 Agentic 检索。要说清楚为什么值得做这件事,最好先看看普通的检索增强生成(RAG)在哪里会失效。大多数 RAG 系统的起点都一样:把问题向量化,从某个索引里取出最相近的文本块,交给模型,然后返回答案。一次检索,一次生成。对一大类问题来说,这确实是正确的设计,也没必要把系统搞得更复杂。但它的失效方式非常典型。如果你问的问题答案横跨两个语料库,单次检索只能返回模型所需的一半信息——这比什么都不返回还糟,因为答案会显得流畅,却并不完整。如果该用哪个数据源取决于问题本身,单一索引根本没有选择机制。如果需要在看到第一个结果之后再回语料库查一次,这个“第二次查看”也无处发生。企业级的 Agentic 检索把推理代理放进流程里,正好解决了这三个问题。代理会决定是否要检索、该查哪个知识库、以及在组织答案之前要不要再查一轮。这也正是 Amazon Bedrock 知识库的演进方向。随着托管知识库的发布,检索不再是一次“向量化加取回”的调用,而变成了一种 Agentic 能力:多轮规划、由工具驱动的检索,以及有依据、带引用的回答。AgenticRetrieveStream API 把这一切作为服务的一等功能提供出来,而不需要你自己去拼装。

这一方案确实可行,但也带来了一个真正更难的操作性问题。单次执行的流水线是一个函数,读一遍代码就能理解它的行为。而一个在循环中推理并检索的代理,面对每个问题都会走不同的路径。默认情况下,你既看不到它走了哪条路,也不知道它给出的答案好不好。可观测性因此不再是可有可无的加分项,而成了判断系统是否正常工作的唯一途径。

2024 年 8 月,文章《Build an end-to-end RAG solution using Knowledge Bases for Amazon Bedrock and the AWS CDK》以 CDK 应用的形式部署了单轮问答(single-shot)方案:一个 Amazon S3 语料桶、一个 Amazon OpenSearch Serverless 集合与索引、一个知识库、一个数据源,分别做成 CDK 栈,栈的顺序由你手动排定。文章发布至今,有三件事变了。

第一,Amazon Bedrock 现在提供托管知识库(Type: MANAGED),数据摄入、存储、索引和检索全部由它接管。那篇文章里需要另外创建的 OpenSearch Serverless 集合与索引,现在你既不用创建,也不用扩缩容,更不必按 OpenSearch 计算单元(OCU)付费。

第二,Amazon Bedrock AgentCore 补齐了智能体这一半:Gateway 把每个知识库暴露成一个模型上下文协议(MCP)工具,Runtime 负责运行推理智能体,AgenticRetrieveStream 则在内部做重排序(rerank),直接返回带引用的综合答案。智能体检索目前只在托管知识库上可用。

第三,可观测性和评估不再是事后自己往上贴的东西,而是可以直接部署出来。这套方案自带两个 Amazon CloudWatch 仪表盘,覆盖七层遥测数据;同一次部署还提供两种评估方式:按需评估和持续评估。

本文要搭的就是这套系统:一个在多个知识库之间路由、通过 Gateway 检索、最终返回有依据且带引用答案的智能体,它的每一层行为都做了埋点,整体是一个用 Python 写的 AWS CDK 应用。一条命令即可全部部署,资源顺序交给 CDK 推导。

如果你读过 2024 年那篇文章,从 Amazon S3 桶到知识库这一段会看着眼熟,之后的内容则全是新的:向量库不见了,检索变成智能体式,而应用部署的资源里,大约有一半存在的意义就是告诉你智能体做了什么。没读过也没关系,本文自成一体。

这条链路上涉及几个服务,一个问题会依次经过它们:智能体(Agent)先判断要不要检索、该查哪个知识库;Gateway 把每个知识库包装成一个可调用的工具;托管知识库负责检索并给出引用来源;这个循环会按问题需要反复执行多次。智能体在这一过程中做的每一步,都会记录到 Amazon CloudWatch。

图 1:涉及的服务,以及单个问题在它们之间的流转路径。

背景

在开始逐步讲解之前,先说明三件事会很有帮助:是什么让检索具备智能体特征、为什么托管知识库是实现这一目标的合适基础,以及为什么可观测性和评估应该纳入设计阶段,而不是事后补上。

从 RAG 到企业级智能体检索

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

托管与自建知识库的对比

Amazon Bedrock 现在提供托管知识库(Type: MANAGED):Amazon Bedrock 为你管理摄取、存储、索引和检索,默认使用服务托管模型进行嵌入和重排序,因此无需预置、扩展或修补向量数据库。这与客户管理(DIY)知识库不同,后者由你自己选择和运维向量存储(例如 Amazon OpenSearch Serverless)。对本方案来说,最重要的差异有两点:

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

智能体检索和 AgentCore Gateway 连接器只在托管知识库上提供,因此它天然是企业级智能体检索的基础。这也意味着 Amazon Bedrock 会替你管理存储和索引,这套架构里不需要预置 OpenSearch Serverless 集合,也不用规划 OCU 容量。如果你需要特定的向量引擎、自定义嵌入模型,或者想完全掌控索引本身,客户自管理方案依然是合适的选择。

当你希望自行选择和运维向量存储时,比如复用已有的 OpenSearch Serverless 集合,或满足特定的数据驻留、调优、合规要求,就应该选客户自管理方案。

对企业级智能体检索来说,正是托管知识库让这项能力成为可能。

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

在生产环境中,一个智能体系统只返回答案是不够的。你需要看清它的运行表现(延迟、调用量、令牌用量),需要知道检索结果和答案的质量如何,而且这些信号要持续获取。本方案提供两个 CloudWatch 仪表盘,覆盖七个层次的遥测数据,还包含两种评估方式(按需评估和持续评估),全部由同一个 CDK 应用预置。事实依据、引用和评估这几层同时也有助于负责任地使用。带引用的答案可以追溯到原文段落,使回答保持可审计;忠实度和正确性评分则能帮你发现答案偏离来源的情况,而不是等到生产环境出问题才知道。

为什么这个方案要用 CDK

任何基础设施即代码(IaC)工具都能创建这些资源。CDK 在这里有它的用武之地,原因跟这套架构本身有关:四个栈之间有着密集的交叉引用。Gateway 需要两个知识库的 ID;agent 需要 Gateway 的 URL 和 ARN;仪表盘需要知识库 ID、Gateway ARN 和 agent 运行时 ID——总共七个值跨栈传递,每一个要么是指标上的一个维度,要么是连接器上的一个目标。如果改用原始模板来写,就得维护一组栈导出和跨栈查找并保证它们彼此一致,再写一个脚本按正确顺序部署、把输出一路传下去;只要有任何东西被重命名,这两处就会同时失准。而在 CDK 里,你只要把构造体(construct)传过去,导出和依赖边就由这一次传递自动产生。2024 年那版方案只有三个栈,依赖链基本是线性的。到了四个栈、七个值在栈之间穿行的时候,这种差别就不再是风格问题了。

方案概览

下面是一个问题在系统中从头到尾走过的完整路径。值得慢慢看一遍,因为后文基本上就是从左到右把它讲一遍。

下图描绘了端到端的 agentic retrieval(智能体检索)工作流。用户的问题进入运行在 AgentCore Runtime 上的 agent,agent 再经 AgentCore Gateway 把它路由到两个 AgenticRetrieveStream 工具之一——每个托管知识库对应一个,分别是财务和天气,数据都从 Amazon S3 摄取而来。被选中的工具返回一个有依据、带引用的回答,每一步都会流式写入 Amazon CloudWatch 和 AWS X-Ray。图中的编号标记对应下面的逐步讲解。

图 2:单个问题的端到端 agentic retrieval 工作流

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

第 0 步——一次性操作,发生在部署时。 在任何问题被提出之前,Amazon S3 中的每个语料都会先摄取进各自独立的托管知识库:Amazon Bedrock 会自动完成解析、分块、向量化和索引。这一步只在栈部署时执行一次,不属于每个问题的处理路径,所以图中用虚线箭头表示,并标为第 0 步。

此后每个问题都走第 1~6 步:

  1. 用户向托管在 Amazon Bedrock AgentCore Runtime 上的智能体提出问题。Runtime 自动为整个“推理-行动”循环注入 OpenTelemetry(OTEL)追踪片段,因此从第一次调用起,系统就是可观测的。

  2. 智能体的推理模型进行规划,并在多个知识库之间路由。由于每个知识库都作为独立的检索工具暴露,智能体会选择与问题主题匹配的那个工具,比如金融或天气,而不是在一个混杂的索引里搜索。

  3. 选中的工具调用由 AgentCore Gateway 通过 MCP 代理,经智能体的 IAM 任务角色授权,并用 SigV4 签名,随后调用该知识库在托管知识库上的 AgenticRetrieveStream API。

  4. AgenticRetrieveStream 负责知识库内部的工作:把问题拆解为子查询,从 Amazon Bedrock 管理的数据存储中迭代检索(数据由 Amazon S3 中的语料库导入),然后合成一个有依据、带引用的答案,通过 Gateway 流式返回。

  5. 智能体检查返回的上下文是否足够。如果不够,它就循环,再次路由和检索,然后再组织最终答案。如果足够,就把带引用的答案返回给用户。

  6. 整个过程里,Runtime 向 Amazon CloudWatch 和 AWS X-Ray 发出追踪片段、token 计数和指标,填充七个可观测层,并为按需评估和持续评估的分数提供数据。

在内部,这个工作流就是一个 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,  # ← 这个引用创建了依赖关系
    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 网关(AWS_IAM / MCP)、每个知识库的 AgenticRetrieveStream 目标 不需要 Cognito
3 -agent ECR、CodeBuild、Runtime 上带有埋点的 Strands agent、自动提供的日志/追踪、在线评估 约 8–10 分钟(镜像构建)
4 -dashboards agentic-observability 和 kb-observability 仪表盘 在有流量之前为空

agent 运行的是“推理—行动”循环:先思考(调用大语言模型(LLM)),决定调用哪个知识库工具,通过 Gateway 读取返回结果,往往还会再次检索,然后才撰写带引用的最终答案。每一步都由 Runtime 自动埋点,因此七个可观测性层会由真实流量填充。

参数 说明 值
AgentConfig.MODEL_ID agent 的推理模型,对应第 6 层的开销 Claude Haiku 4.5
EvalConfig.SAMPLING_PERCENTAGE 在线评估采样率,参见免责声明 100

修改 PROJECT_NAME 就能得到一份独立命名的整套方案副本,因此两个部署可以共存于同一个账户。驱动 notebook 读取同一个常量,会自动跟随重命名,无需手动同步栈名。

描述数据集

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

关于数据: 金融语料是合成的。Octank 是一家虚构公司,那份 10-K 文件是模型生成的样本数据(CC0 许可),不是真实的财务数据。天气语料则是真实的、属于公有领域的美国政府作品,是一份美国国会研究服务局的报告,可在 sgp.fas.org/crs/misc/IF12695.pdf 公开获取(美国政府的作品不受版权保护)。

如果想用自己的数据,把文件放进 data/ 目录,再调整 config.py 里的前缀即可,其他地方不用改。

两个语料库刻意保持彼此独立,这样 agent 就必须把每个问题路由到正确的知识库——这正是语义路由要讲的故事。由于用的是托管知识库(Managed Knowledge Base),我们不需要配置分块、嵌入或索引:数据摄入时,Amazon Bedrock 会自动解析每个 PDF、切分、用托管模型做嵌入,并建立索引。

我们用了两个独立的知识库,各自通过 Gateway 暴露为独立的检索工具,而不是用一个知识库同时装下两个语料——这个选择是有意为之。给 agent 两个有名字的工具,路由就变成一次可观察的显式推理动作:span 树会显示它对某个问题选了哪个工具,Layer 3 里的检索质量指标也能按知识库分别统计,金融和天气的精确率不会混成一个数字。反过来,如果只用一个工具横跨两个语料,这种区分就没了:路由决策会藏进单个索引的排序里,看不见,而所有质量信号都会变成两个不相关领域的平均值。把语料和工具分开,才能让 agent 的行为可读——这正是整套设计的用意所在。

在 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 之所以值得一提,恰恰是因为替代方案太诱人:如果没有它,触发摄入(ingestion)的自定义资源往往会多出一个上传步骤,于是一个资源就干起了两件互不相关的事。用了它之后,这个资源只需负责摄入。托管式摄入会自动扫描、分块、嵌入并索引每份文档:每个知识库索引一份文档,零失败。

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

部署方案

本节介绍如何用一条命令部署整个方案。满足前置条件后,运行 cdk deploy --all,CDK 会按推导出的依赖顺序创建全部四个栈,并自动把各个跨栈的值串接好。

前置条件

一条命令

要部署方案,先克隆仓库、创建虚拟环境、安装依赖,然后运行 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 profile(CDK_DEFAULT_ACCOUNT / CDK_DEFAULT_REGION),所以不需要改任何配置就能直接部署。

区域可用性: 托管知识库、agentic retrieval(AgenticRetrieveStream)以及 Amazon Bedrock AgentCore(Runtime 和 Gateway)并非在每个 AWS 区域都提供。部署之前,请先确认目标区域是否同时支持这些服务,并在 config.py 中相应设置 ACCOUNT_REGION。默认值是 us-west-2。最新的可用情况,可以查阅 Amazon Bedrock 和 Amazon Bedrock AgentCore 的文档,以及 AWS 区域服务列表。

这个版本没有部署脚本,而这恰恰是重点:cdk deploy --all 会沿着从 app.py 推导出的依赖关系图,按顺序部署四个栈,并自动传递所有跨栈的取值。终端会在每个栈完成时给出提示——先是 bmkb-obs-cdk-kb [1/4](默认的 PROJECT_NAME),然后是 gateway [2/4]、agent [3/4]、dashboards [4/4]——这个顺序是推导出来的,不是写死的:它由栈之间的 CDK 构造(construct)引用自然决定,所以如果你再加一个消费已有输出的第五个栈,部署顺序会自动调整。agent 栈用 CodeBuild 构建 agent 容器,这一步大约需要 8–10 分钟,另外三个栈都很快。每个栈在完成时都会打印自己的输出,知识库 ID、Gateway URL、runtime ARN 以及两个 dashboard 的 URL 就在这里获取。

部署结束后,四个栈全部完成。下图展示的是 cdk deploy --all 执行成功后的 AWS CloudFormation 控制台。

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

控制台上,四个栈——kb、gateway、agent 和 dashboards——都显示 CREATE_COMPLETE,每个栈的描述里都标出了它在依赖顺序中的 [n/4] 位置。

长时间运行的资源说明

这个方案里有两个环节明显比其他步骤更耗时,都值得提前做好规划。

智能体镜像构建。 agent 栈把容器构建交给 CodeBuild,由它构建 ARM64 镜像、推送到 Amazon ECR,之后运行时自定义资源才会创建 AgentCore Runtime。这一步大约需要 8–10 分钟,其余三个栈相比之下都很快。由于构建在 CodeBuild 中运行,本地不需要装 Docker,但也意味着首次部署要承担完整的构建开销。后续部署时,如果源代码没变,会复用同一个镜像标签。

知识库数据摄入。 托管摄入是异步的。启动数据源同步的自定义资源会轮询,直到每个知识库都报告其文档已建立索引,因此 kb 栈会一直停留在 CREATE_IN_PROGRESS,等待 Bedrock 解析、切块、向量化并索引 PDF 文件。这里的语料很小(几百 KB),几分钟就能完成;但这个等待是服务本身固有的,不是本方案引入的——语料越大,等待时间会成比例增加。

由此带来两个实际影响:

这两个资源的重建成本都不低:重建知识库会重新摄入数据,重建运行时则会重新拉取镜像。如果要反复修改智能体代码,只部署改动的栈即可(cdk deploy <PROJECT_NAME>-agent),不要动知识库。

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)。这个笔记本会读取堆栈输出,再按主题向已部署的 agent 发问,每个知识库(KB)单独开一个会话,这样答案和语义路由就能一次看到:财务类问题由 Octank 10-K 回答,天气类问题由龙卷风报告回答,各自自动路由到对应的 AgenticRetrieveStream 工具。

笔记本里的一切都从堆栈输出中获取,不需要手动粘贴任何内容:

# 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', [])}

笔记本和应用导入的是同一个 config.py,所以两边对堆栈名称的说法不会出现分歧。

驱动脚本通过已部署的 agent 按知识库发送提示词。财务问题走财务知识库,天气问题走天气知识库,每个答案都有依据、附引用。

图 5:按知识库分发的提示词被路由到对应的知识库,各自返回有依据、带引用的答案

Notebook 还会基于 agent 实际检索并引用的内容,算出第 3 层的检索质量信号——无需标准答案(ground truth),也不需要额外的评判模型(judge),把这些信号发布到 CloudWatch。

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

观察:七层看板

七个层次最终都体现在这两块看板上。整个 stack 会创建这两块看板,喂入流量后它们就被填满。下面截图展示了两块看板在 CloudWatch 控制台中的样子。

图 7:CloudWatch 控制台中的两块看板——端到端 agentic 可观测性和按知识库(per-KB)的可观测性

两块看板都会出现在 CloudWatch 控制台里:一块用于端到端 agentic 可观测性,一块用于按知识库(BMKB,Bedrock Managed Knowledge Base)的可观测性。本节剩余部分逐层讲解每一层展示了什么。

这个版本里,看板是用类型化对象构建的,而不是 JSON 字符串。一个指标是 Metric,一个 widget 是 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 还多。好处在于:属性拼错或 widget 组合非法,会在你本机跑 cdk synth 时大约一秒内报错。同样的错误写进 DashboardBody 字符串里,却能部署成功,然后在 AWS 管理控制台里渲染成一个坏掉的 widget,只能靠肉眼看出来。另外,因为 Row 可以接受任意 widget,文档就成了布局本身的一部分,而不再是会逐渐过期的 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 的控件和绘制它的控件就随同一次提交一起上线;哪怕是第一次打开看板的读者,也不用去猜「无参考(reference-free)指标」到底是什么。

看板 A —— 端到端的 agentic 可观测性

看板开头先解释每一层统计的东西各不相同(N 次查询大约对应 N 次 agent 调用、约 2N 次检索、约 3N 次 LLM 调用、约 5N 次 Gateway MCP 操作,让 agentic 循环变得肉眼可见),然后展示 KB 指标(L1)、Gateway 指标与延迟(L4)、token 用量(L6)。由于这个 agent 用的是 AgenticRetrieveStream,返回的是带引用的合成答案,而不是每个 chunk 的分数,所以还包含无参考的 Layer 3 质量信号——就地定义,按 KB 分别绘制。

在看板 A 上,Layer 3 的无参考质量信号按知识库分别绘制。先看金融 KB。下图绘制它的 retrieval_utilization、grounded_coverage 和 duplicate_rate。

图 8:金融 KB 的 Layer 3 质量,绘制检索利用率(retrieval utilization)、有据覆盖率(grounded coverage)和重复率(duplicate rate)

天气 KB 展示同样的三个信号,但单独跟踪,两个领域的数字永远不会混成一个。

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

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

看板 B —— 按 KB(BMKB)的可观测性

按 KB 展示那些同时决定开销的运营信号:索引大小(来自 S3 源文件字节数)、检索量、agentic 工具调用次数、会话 token 用量,以及按模型划分的生成 token 用量,每项都带就地说明。

以下截图展示的是 Dashboard B 的四个指标组件,它们在一次运行后填充数据。第一个组件报告索引大小(index size),由单个知识库对应的 S3 源字节数计算得出。

图 10:Dashboard B 中每个知识库的索引大小,由 Amazon S3 源字节数计算得出

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

图 11:Dashboard B 中每个知识库的 retrieve 调用次数

第 5 层:Span 树

第三个组件统计经由 Gateway 的 agentic 工具调用次数。这一项不做拆分,由两个知识库共用。

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

这四个组件分别展示索引大小(来自 S3 源字节数)、retrieve 调用量、经由 Gateway 的 agentic 工具调用,以及会话 token 用量。除 Gateway 工具调用次数由两个知识库共用外,其余都按知识库分别拆开统计。

有一个维度上的细节值得单独提一下,因为类型化的 API 不会帮你发现它:AWS/Bedrock/KnowledgeBases 命名空间要求 id 带前缀,即 knowledge-base/<id>,而本方案自己的 BMKB/* 命名空间用的是不带前缀的 id。对 CDK 来说,两者都只是字符串。

# L1 —— AWS/Bedrock/KnowledgeBases 需要带前缀的形式。
dimensions_map={"Operation": "Retrieve", "KnowledgeBaseId": f"knowledge-base/{kb_id}"}

# BMKB/* —— 我们自己的命名空间使用不带前缀的 id。
dimensions_map={"KnowledgeBaseId": kb_id}

第 5 层是 aws/spans 中的原始 OTEL span 树,也是能直接看到 agentic 循环的一层。这一层需要仔细读,因为它的嵌套关系并不直观:一次 LLM 调用会产生三个 span。Strands 的 invoke_agent span 包住整个回合,内部的 chat span 包住模型调用,而一个类型为 CLIENT 的 chat <model> span 才是真正的调用。这三个 span 都重复记录同一组 gen_ai.usage.* token 属性。

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

看最上面三行:一个 invoke_agent 的 Strands Agents 聚合 span,下面是一个内部 chat span,再往下是一个 CLIENT 类型的 chat 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 调用只计一次。

加了过滤后,这次运行显示 14 次 LLM 调用,共 60,472 输入 token、2,709 输出 token。不加过滤的话,同一查询会报出三倍的调用次数和三倍的 token。

评估:按需评估与持续评估

质量用两种方式衡量,两者都由应用预置。

按需评估:驱动 notebook 针对每个会话的 span 调用 AgentCore Evaluate(用 LLM 做裁判),跑内置评估器——正确性、忠实度、工具选择准确率——并把分数发布到 CloudWatch,作为 Dashboard A 的第 7 层展示。这个版本没有部署脚本,而这正是关键所在。

下面的截图展示 Dashboard A 的第 7 层:各知识库的按需评估分数,同一面板上还有第 5 层的 OTEL span 表。

Layer 7 展示各知识库的按需评估分数,旁边是 Layer 5 的 OTEL 跨度表,这样分数和产生这些分数的跨度就能在同一块面板上一起查看。

持续(在线): 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。

该配置列出了四个内置评估器——Tool-Selection Accuracy、Faithfulness、Correctness 和 Response Relevance——采样率设为 100%,状态显示为 ACTIVE 且已启用。由于这些分数就是普通的 CloudWatch 指标,它们可以像其他指标一样绘制图表,无需解析日志,同一个小组件也可以承载告警。

下图展示了这四个评估器分数作为普通 CloudWatch 指标绘制的图表。

四个评估器分数会作为指标出现在 Bedrock-AgentCore/Evaluations 命名空间中,每个指标都以评估配置 ID 和 Agent 的服务名作为键,因此你可以直接绘制图表或设置告警,无需解析日志。

在线评估的结果只有在会话关闭后才会出现。评分是对已完成的会话运行的,所以刚打完流量后,结果日志组会空着,大约要等一个会话闲置超时时间(约 30 分钟)才会有数据。跑完一次发现日志组是空的,这是正常现象,不是配置错了。每条结果事件都带 session.id,可以和其他各层的数据关联起来。notebook 里的按需路径没有这个等待,这也是两种方式都放在这里的原因。

这次运行中,四个内置评估器上,持续路径在正确性(Correctness)、忠实性(Faithfulness)、响应相关性(Response Relevance)和工具选择准确率(Tool-Selection Accuracy)上都得了 1.00 分,每个评估器五条已评分的追踪记录,每条结果都附有文字说明。

清理

……以及仍在运行的在线评估,都会在栈闲置时产生费用。一条命令就能全部拆除:

cdk destroy --all

CDK 会自己按反向依赖顺序删除栈:先是 dashboard,然后 agent,再是 gateway,最后是 kb——沿用的就是部署时推导出的同一张依赖图,只不过倒着走了一遍。不需要再维护一份与部署脚本保持一致的拆除脚本,这是只建模一次依赖关系带来的一个不太起眼但很有价值的特性。

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

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 bootstrap 栈(CDKToolkit)及其暂存桶不会被删除。它们是账户级共享基础设施,其他 CDK 应用可能依赖它们。

相比单次问答方案,有哪些变化

本文这套方案和 2024 年那套方案都是 Python 写的 CDK 应用,用 cdk deploy 部署。不变的是上面那层编排工具,变的是它底下的架构;而且大部分变化是「做减法」。

关注点 2024:单次 RAG 本方案
向量存储 自己开通、扩缩容并按 OCU 付费的 OpenSearch Serverless 集合与索引 不需要。类型设为 MANAGED,存储和索引都由 Amazon Bedrock 负责
索引生命周期 用自定义资源创建索引,再等待它变成可搜索状态 没有东西需要创建
嵌入模型 自己选模型,并申请模型访问权限 默认由服务托管
栈 3 个:KB 角色 → OpenSearch 基础设施 → KB + 数据源 4 个:kb → gateway → agent → dashboards
检索 一次 Retrieve,按向量相似度取 top-k 片段 AgenticRetrieveStream,内部重排序,直接返回带引用的合成答案
选知识库 不适用,只有一个索引 由 agent 按语义在两个知识库之间路由
二次查看语料 单次问答做不到 agent 在回答前会再检索一轮
遥测 没有预置 七个层次、两个仪表盘
评估 手动:同步、选模型、提问 按需评估和持续评估,均已预置
空闲时的成本底线 查不查都照样产生 OCU 费用 只有存储费用

这个取舍是双向的、实打实的。单个索引加单次问答,每次查询更便宜、逻辑更好理解,当一个语料库就能装下答案时完全够用。如果装不下,再考虑这套设计。

真正干活的几个 CDK 构造体

这个应用里,大部分工作由四个构造体(construct)承担。之所以值得逐个点名,是因为它们各自都替代了原本需要手写的东西:

有两件事 CDK 改变不了,动手之前都值得了解:

这两点都不是选择某个工具的理由。它们划定了“类型化”能帮你到哪一步的边界,而知道边界在哪,正是让 CDK 好用而不至于让人意外的关键。

结论

我们在 Managed Amazon Bedrock Knowledge Base 和 AgentCore 之上搭建了一套完整的企业级智能体检索方案:多知识库语义路由、推理智能体、七层可观测性,以及按需和持续两种评估方式——全部作为一个 AWS CDK 应用完成部署。

有两点值得带走。

托管知识库做的是减法,不是加法:不用开向量库,不用建索引再等它建完,系统闲置时也不会有一行 OCU 费用在悄悄累积。它也是目前支持 agentic retrieval(智能体式检索)和 AgentCore Gateway 连接器的知识库类型,具体可查阅 Amazon Bedrock 文档。

而一旦检索变成智能体式,可观测性就不再是可选项。这个应用所配置的资源里,大约一半都是为了回答两个问题:智能体到底做了什么,以及它给出的答案到底好不好。这不算智能体系统上的额外负担,恰恰是让它可运维的那部分。

CDK 的编写模型赢在“接缝”上。这套架构里有七个值要跨栈传递,每一个都是 construct 引用,而不是导出/导入配对——这也是为什么没有部署脚本、没有拆除脚本,也没有需要记住的固定顺序。

接下来,你可以把数据源指向自己的语料库,把智能体放进虚拟私有云(VPC),按预算和策略调整在线评估的采样率,或者给路由器再加几个知识库。想了解更多,可以查看 Amazon Bedrock 详情页和 Amazon Bedrock 文档。

应用本身、驱动 notebook 和自包含的工具脚本都在配套的 GitHub 仓库里,克隆下来一条命令就能部署你自己的副本。


用 AWS CDK 在托管 Amazon Bedrock 知识库上构建企业级智能体式检索:一个智能体,跨两个知识库路由,通过 AgentCore Gateway 完成检索,返回有据可查、带引用的答案,配备七层可观测性,同时支持按需评估和持续评估——全部作为一个 CDK 应用一键部署。

查看原文