AWS博客展示用Bedrock+S3向量+Step Functions从历史工单自动生成知识库文章的全流程。
KnowledgeForge 致力于从 IT 服务管理(ITSM)工单坟场中挖掘黄金:那些已解决的incident工单,其知识从未转化为知识库文章。企业 IT 支持团队每月处理数千张工单,每张工单都蕴含着有价值的信息:症状、根因,以及工程师应用的修复方案。这些知识被锁在工单历史中,而下一个遇到同样问题的工程师根本找不到它们。
知识库本身则存在相反的问题。它在增长,但增长得很混乱:重复文章堆积、内容过时、质量参差不齐——取决于每篇文章的作者和撰写时间。支持工程师搜索答案时,往往要在大量几乎一模一样的草稿中艰难跋涉,其中有些是准确的,有些已经落后了三个产品版本。
我们构建 KnowledgeForge 来解决这两端的鸿沟。它从已解决的 incident 工单中挖掘新文章。同时,它通过对现有知识库进行策管来改善库质量:按类型对文章分类、去除重复文章、对文章质量打分、并重写薄弱内容。知识管理员审核并批准最终结果,所以最终上线的内容仍由人负责。
本文深入探讨 KnowledgeForge 背后的 AWS 构建模块:Amazon Bedrock 用于生成和内容改进、Amazon S3 Vectors(Amazon Simple Storage Service 的一项能力)用于重复检测、以及 AWS Step Functions 用于编排。对于每个组件,我们解释选择它的原因并提供文档链接。如果你正在基于生成式 AI 构建大规模文档处理管道,可以复用这些模式。
要部署并跟随这个解决方案,你需要:
一个拥有 Amazon Bedrock 访问权限的 AWS 账户,且已启用对 Anthropic Claude Sonnet 4.5 和 Amazon Titan Text Embeddings V2 模型的访问权限。
创建该解决方案所用资源的权限:Amazon S3 存储桶和 Amazon S3 Vectors 索引、AWS Step Functions 状态机、AWS Lambda 函数、Amazon Elastic Container Service(Amazon ECS)上的 AWS Fargate 服务、Amazon DynamoDB 表、Amazon Simple Queue Service(Amazon SQS)队列、Amazon Bedrock 护栏、以及 AWS Key Management Service(AWS KMS)密钥。
安装了 AWS Cloud Development Kit(AWS CDK),且熟悉 Amazon Bedrock、AWS Step Functions、Amazon ECS 和向量嵌入。
aws-samples/sample-knowledgeforge 仓库中的代码。
闭环知识库生命周期
KnowledgeForge 是两个相互供能的子系统。生成模块将聚类的 incident 工单转化为新的文章草稿。当一组相关工单描述了同一问题时,系统根据该聚类撰写一篇知识库文章和一份根因分析文档。策管模块随后对每篇文章——无论新生成的还是现有的——经过四个步骤:按类型分类、检查重复、给质量打分、以及改进薄弱内容。完成后的文章送往 ServiceNow 由知识管理员审核。
闭环的闭环在于:策管模块为每篇文章存储一个向量,而生成模块在下一次写任何内容之前都会读取这些向量。下图展示这两个子系统如何连接。
图 1:闭环知识库生命周期。摄取为生成和策管提供数据输入,知识管理员审核输出,策管将每篇文章嵌入向量,以便生成模块在下一轮运行时复用这些向量作为 grounding。
从高层次看,一篇文章经历五个阶段:
摄取 – 已解决的工单和现有文章落入 Amazon S3,由数据目录追踪。
生成 – 聚类的工单在 Amazon ECS 上通过 AWS Fargate 成为新的文章草稿。
策管 – 每篇文章通过 AWS Lambda 上的 AWS Step Functions 工作流进行分类、去重、质量评分和改进。
人工审核 – enriched 文章送往 ServiceNow 由知识管理员批准,决策写回 Amazon DynamoDB。
闭环 – 策管模块将每篇文章嵌入 Amazon S3 Vectors 索引,生成模块在下一轮运行时复用这些向量。
两个子系统运行在不同的计算环境上,原因将在后续章节跟随一篇文章穿过系统时加以解释。
从 incident 聚类中生成文章
生成始于一组共享主题的工单聚类。上游流程按所描述的问题对已解决的 incidents 进行分组,并将结果作为 JSON 文件Drop到 Amazon S3 存储桶中,作用域限定为单一客户。每个主题携带关键词、文章范围、以及工单描述和工作笔记的样本。
桶中的新文件向 Amazon SQS 队列发送一个事件。Amazon ECS 上通过 AWS Fargate 运行的容器轮询队列、读取文件、每次最多同时处理五个主题(针对一个客户)。
在写入之前,系统先以现有内容为 grounding。对于每个主题,它从该客户的 Amazon S3 Vectors 索引中检索五个最相似的现有文章,并将其作为参考上下文传递给模型。这种检索增强生成(RAG)保持了术语的一致性并减少了虚假流程的产生。当尚无参考文章时,系统仅根据工单数据进行生成,并将流程标记为待审核。
生成模块在 Amazon Bedrock 上运行 Anthropic Claude Sonnet 4.5。对于每个主题,模型生成两份具有固定结构的文档:
知识库文章:标题和摘要、症状、根因、解决步骤、预防措施和相关主题。
根因分析文档:执行摘要、问题描述、客户影响、五问法分析、临时解决方案和修复方案、纠正和预防措施、关键事件时间线、以及原因代码。
我们使用 Amazon Bedrock 的响应流式输出,因此容器在令牌到达时就开始组装每份文档,而无需等待完整响应。
为什么用容器而非函数
我们在 Amazon ECS 上通过 AWS Fargate 运行生成模块,这是由工作负载的形态决定的。为一个主题生成两份完整文档可能需要几分钟,而一个繁忙的文件包含许多主题,所以单个工作单元可能运行很长时间。工作负载还以突发方式到达——一段时间很安静,然后突然涌入大批量任务。一种能够根据队列深度扩展任务数量、在队列排空时缩减的长运行容器服务,非常契合这种模式。
AWS Fargate 恰好匹配这个场景。它以无服务器方式运行我们的容器,根据主题到达的情况按需扩展,让团队专注于生成逻辑而非管理计算容量。每份文档以 JSON 形式落入 Amazon S3,准备好进入策管流程。
使用 Amazon S3 Vectors 查找重复文章
第一个策管挑战是检测文章是否已存在。重复有几种形式。两篇文章用不同词语描述同一修复方案、一篇新文章取代了旧文章、或者工程师复制一篇文章、改了两行、保存为新文章。
我们选择 Amazon S3 Vectors 来解决这个问题。它直接在 Amazon S3 中存储嵌入向量,无需独立的向量数据库。它将元数据与每个向量一起保存以便按客户过滤,并按查询和每 GB 计费而非按运行节点计费。这使得为库中每篇文章保留一个向量变得经济实惠。如果你已经在 Amazon S3 中存储内容,可以在此基础上添加向量搜索而无需新基础设施。详细信息请参阅 Amazon S3 用户指南。
关键词匹配无法捕获这些重复形式,所以管道通过语义进行匹配。每篇文章通过 Amazon Titan Text Embeddings V2 生成 1,024 维嵌入向量,存储在按客户划分的 Amazon S3 Vectors 索引中。向量存储通常为检索提供动力。而这里同一个索引兼作重复检测器。一篇新文章被嵌入后,系统查询索引中最近的现有向量,任何在紧密余弦距离阈值内的内容都被视为重复。我们初始设定余弦距离为 0.05(即相似度 0.95 或更高),top-K 为 5。我们通过采样标记的配对来调优距离——收紧阈值直到近重复的文章被匹配上、而只是相关的文章不会被捕获。阈值过松产生假重复,阈值过紧则漏掉改写后的副本。查询为单次调用,过滤到当前客户和活跃文章:
response = s3vectors.query_vectors(
vectorBucketName=VECTOR_BUCKET,
indexName=VECTOR_INDEX,
queryVector={"float32": embedding},
topK=TOP_K + 1, # +1 because the article can match itself
returnMetadata=True,
returnDistance=True,
filter={"$and": [
{"tenant_id": {"$eq": TENANT_ID}}, # per-customer isolation
{"status": {"$in": ["UNIQUE", "PENDING"]}}, # ignore retired articles
]},
)
matches = [v for v in response.get("vectors", []) if v["key"] != article_id]
当系统发现一对重复项时,它会保留较新的文章并淘汰过时的那篇,而不是默认丢弃新来的那篇。最新的准确版本获胜,这是支持工程师想要的行为。
以这种方式复用索引也有助于重试。嵌入向量需要一次模型调用来生成。重跑时直接从存储中读取向量而无需重新计算,既节省时间又节省 Amazon Bedrock 的费用。
策展流程在需要编排的成批文章上运行,以将工作分配到多个 worker 并从故障中恢复。AWS Step Functions 提供了两阶段分布式 map 来实现这一点。它管理工作流状态、应用定义中声明的重试和错误处理,并将工作分发到各个 worker,无需自定义协调代码。关于分布式 map 的工作原理,请参阅 AWS Step Functions 开发者指南。
有两个配置选择值得特别说明。首先,我们将 item processor 设置为 STANDARD 而不是 EXPRESS。每篇文章都会发出超长的 Amazon Bedrock 调用,超过 5 分钟的 EXPRESS 限制,而 STANDARD 可以保留完整的执行历史用于调试。其次,我们将 ItemReader 指向 Amazon S3 中的清单文件,而不是内联传递内容,这样可以使工作流状态保持小巧。
Amazon EventBridge 上的每日计划触发运行。一个 AWS Lambda 函数找到有新文章或文章有变更的客户,将变更分组到批次中,然后将每个批次放入 Amazon SQS 先进先出(FIFO)队列。FIFO 队列按客户 ID 作为消息组键,对跨运行周期的批次进行排序,使不同客户仍然并行运行。跨运行周期的排序只是一部分。在单个执行中,第二阶段最多可以并发处理 40 个批次,因此仅靠 FIFO 并不能阻止不同批次中的两个重复项竞争。真正保护去重的机制是:它在 worker 内部按批次顺序运行,而质量评分和改进则并行运行。每个批次内顺序去重,质量评分和改进并行,跨运行周期 FIFO 排序——三者共同保证了重复检测的一致性。
调度函数每次拉取一个批次并启动一个 Step Functions 执行。状态机分两个阶段运行,每个阶段都是一个分布式 map,使批次中的文章可以并发处理:
分类并嵌入 – 每篇文章按类型分类并赋予向量嵌入。
去重、评分并改进 – 流水线找出重复项、评估质量,并对低于阈值的文章进行内容改进。
下图展示了触发链、两个阶段以及保护运行容错机制。
图 2:策展以两阶段分布式 map 方式运行。批次通过 Amazon SQS FIFO 队列按客户排序,文章内容作为 Amazon S3 指针传递而非内联状态,断路器和死信队列防止坏批次阻塞整个运行。
Step Functions 执行在状态之间传递状态,大小上限为 256 KB。知识库文章包含完整的 HTML 正文,很快就会超出该限制。我们没有将文章内容贯穿整个状态机,而是将批次写入 Amazon S3,只在 workflow 中传递位置信息。每个 map worker 直接从 Amazon S3 读取所需内容,因此无论文章多大,状态载荷都能保持小巧。
分布式 map 从 Amazon S3 中的清单文件读取工作项,而不是从内联状态读取。容许失败百分比使运行可以吸收少量坏文章而不中止整个批次:
"Phase1_ClassifyEmbed": {
"Type": "Map",
"ItemProcessor": {
"ProcessorConfig": { "Mode": "DISTRIBUTED", "ExecutionType": "STANDARD" }
},
"ItemReader": {
"Resource": "arn:aws:states:::s3:getObject",
"ReaderConfig": { "InputType": "JSON" },
"Parameters": { "Bucket.$": "$.bucket", "Key.$": "$.manifest_key" }
},
"MaxConcurrency": 40,
"ToleratedFailurePercentage": 10
}
两种机制防止坏批次导致整个运行中断:
死信队列 – 反复失败的批次会被搁置待查,而不是阻塞其后面的队列。
断路器(尽最大努力)– 连续三次失败后,调度器停止启动新批次,并将受影响的文章重置回起始状态。失败计数器位于 warm Lambda 函数的内存中,因此在并发调度器容器之间不共享,并在新作业启动时重置。在交错的多租户负载下,断路器可能不会跳闸,因此我们将其视为尽最大努力的保障,而非保证。无论如何都不会丢失任何内容:下一个预定运行会拾取重置的文章,跳闸的断路器则表明服务依赖需要先引起关注。
工程努力集中在向共享服务配额发出数以万计的并发模型调用。流水线调用 Anthropic Claude Sonnet 进行生成和改进,调用 Amazon Titan Text Embeddings V2 生成去重向量,全部通过 Amazon Bedrock 模型推理完成。关于各 AWS 区域的模型可用性,请参阅按 AWS 区域划分的支持模型。
三个控制措施在负载下保持生成的可预测性:
每个执行都有硬性挂钟超时,这样慢响应就不会无限期地占用 worker。
自动重试并退避 – Amazon Bedrock 客户端以自适应模式运行,遇到限流时自动重试,在较长的内容改进调用上使用指数退避。
护栏回退 – 我们在模型输出上运行 Amazon Bedrock 护栏以过滤意外内容。护栏是一种生产控制措施,而以下回退是一种弹性措施,在正常条件下保持护栏生效,而非表明它是可选的信号。
护栏 API 有自己的速率限制。如果它被限流,流水线会在没有护栏的情况下处理文章并记录警告而不是失败,这在高峰期间保持流程推进。在代码中,回退是模型调用包装器中的一小段:
try:
return converse_with_timeout(kwargs, BEDROCK_CALL_TIMEOUT)
except ClientError as e:
if "ThrottlingException" in str(e) and guardrail_config:
log.warn("Guardrail throttled, retrying without guardrail")
del kwargs["guardrailConfig"]
return converse_with_timeout(kwargs, BEDROCK_CALL_TIMEOUT)
raise
第四个控制措施处理截断的输出。内容改进响应有 token 预算,一篇长文章可能会触及上限,导致 JSON 响应被切断。系统检测到"在 token 限制处停止"信号后,使用双倍预算重试,最高不超过模型的最大限额。短文章永远不会触发此路径,因此常见情况无需为这个安全网付出代价。
并非每篇文章都需要改进,而且改进也不总是有帮助。质量评分来决定。每篇文章按十个加权维度进行评分,从最高权重到最低权重:
加权总分产生一个分数,阈值决定文章是按原样达标还是进入改进流程。阈值是可配置的,因此客户可以对生成的内容要求比导入的内容更高的标准。
低于阈值的文章会经过内容改进,也在 Amazon Bedrock 上进行。这一步有一个实际挑战:要求模型改进文章时,它往往会同时更改图像标签和超链接,并将其破坏。占位符解决了这个问题。在改进之前,系统将每个图像和链接替换为编号令牌,改进这些令牌周围的正文,然后再将真实的媒体内容换回去。模型永远不会看到原始标记,因此标题和结构得以保留。
在代码中,这是一对函数,包裹着改进调用。第一个函数将每个媒体标签和链接替换为编号令牌并记录移除的内容。第二个函数在模型返回后将原始内容恢复回去:
# Before improvement: swap media/links for [IMG_N] / [LINK_N] tokens
html_with_placeholders, placeholder_map = extract_placeholders(article_html)
enriched = improve_with_bedrock(html_with_placeholders) # model never sees raw markup
# After improvement: restore the exact original tags in place
restored_html, missing = restore_placeholders(enriched, placeholder_map)
因为模型只能看到 [IMG_1] 或 [LINK_3],它无法破坏 URL 或图像引用。恢复步骤使已发布文章的媒体内容与开始时完全一致。
改进之后,文章会再次打分。只有当新版本的分数超过原文时,新版本才会被保留。否则保留原文。这种改进前后的对比检查让流水线能够自动提升内容质量,而不会在悄无声息中让知识库变差。
安全地为众多客户服务
系统通过共享基础设施运行众多客户,而知识库正是客户期望保持隔离的数据。隔离从一开始就被内置其中:每个客户都会获得一份触及自身内容的完整副本:
一个配置 profile
一个 Amazon S3 Vectors 索引
一组模型提示词
一个 Amazon Bedrock 护栏
一个 AWS KMS 加密密钥
由于这些资源是按客户划分的,每个客户的数据都保持在各自的边界内。重复检查仅针对该客户的索引运行,内容改进使用该客户的提示词,数据在该客户的密钥下加密。不存在任何共享池,在那里一个客户的文章有可能出现在另一个客户的结果中。
为客户开通服务并不意味着编辑一个中央列表。系统从运行时数据目录中发现客户,因此添加一个客户只需为其配置资源,无需更改流水线代码。
此处的行为来自我们的内部测试,而非保证的服务级别。由于每个客户通过其自身的 Amazon SQS FIFO 消息组流动,添加一个客户只会增加一条并行工作流,而不会拖慢其他客户。您自己的吞吐量将取决于文章大小、模型延迟以及您的 Amazon Bedrock 配额。
一次代表性运行展示了您可以预期的模式。大多数文章在第一次通过时即完成处理,一小部分被搁置等待改进,更小一部分作为重复内容被移除。该运行完成时没有死信队列消息,也没有超时。少量文章达到了内容改进的 token 限额,被自动重置,并在下一次运行中以更高的预算重试,而非直接失败。最后这种情况很重要,因为它表明弹性控制机制将原本会出现的硬故障转化为自动重试。
有四条经验教训尤为突出。将向量索引复用为重复检测器,将一个难题转化为最近邻查询。通过 Step Functions 传递 Amazon S3 指针而非文章主体,移除了一类状态大小故障。在改进前后两端都对质量进行打分,使得自动运行变得安全。将护栏限流视为警告,保持了流水线在负载峰值期间的健康。更宏观的要点是这个闭环本身:生成的内容喂养着策展,策展又为下一轮生成奠定基础,因此知识库随着系统运行而不断提升。
此解决方案中的资源是计费的,因此在完成后将其删除以避免持续产生费用。删除两个 AWS CDK 堆栈——一个用于知识库生成,一个用于知识库策展——即可移除大部分资源。这包括 AWS Lambda 函数、AWS Step Functions 状态机、Amazon ECS on AWS Fargate 服务、Amazon SQS 队列以及 Amazon DynamoDB 表。
少数资源需要手动处理,因为它们持有数据或按客户创建:
Amazon S3 Vectors 索引和向量桶 – 删除每个按客户的索引,然后删除向量桶。
Amazon S3 存储桶 – 清空并删除源存储桶、输出存储桶和流水线存储桶。
Amazon Bedrock 护栏 – 如果堆栈保留了按客户的护栏,则将其删除。
AWS KMS 密钥 – 计划删除按客户的密钥。
Amazon DynamoDB 表 – 如果您禁用了时间点恢复保留,请确认指标表和文章状态表已被移除。
具体删除命令请参阅仓库 README。
ITSM 工单墓地是一个看起来像数据问题的知识问题。解决方案早已存在,只是被困在无人能复用它们的地方,旁边是一个衰减速度超过任何团队手动策展能力的知识库。KnowledgeForge 用 AWS 上的生成式 AI 封闭了这个缺口。它用 Amazon Bedrock 将工单挖掘成文章,用 Amazon S3 Vectors 捕捉重复内容,并用 AWS Step Functions 编排流程。知识管理员始终掌控着哪些内容可以上线。
要开始构建类似的流水线:
使用 Amazon S3 Vectors 文档创建一个向量索引。
嵌入您自己的文章样本并加载向量。
运行前面展示的最近邻查询来标记重复内容。
添加 AWS Step Functions 分布式映射以批量处理文章。
叠加评分-改进-重评的质量门控。
完整代码可在 GitHub 上的 aws-samples/sample-knowledgeforge 仓库中获取。贯穿全文的代码示例展示了每个模式的承重部分,可供您根据自身的知识库进行适配。
了解更多,请参阅 Amazon Bedrock 用户指南、AWS Step Functions 开发者指南和 Amazon S3 用户指南。