首页 / 文章

Bedrock智能体:记忆摘要功能、分级管控规则与知识库。

由Terraform驱动的Bedrock智能体控制功能:支持自定义摘要提示的会话记忆、采用检测后阻断策略的分级管控规则,以及基于S3的RAG技术。

发布者:REACTAPP.TOP阅读约 15 分钟3420 词

来源: 6A. Building Chat Agent with AWS Bedrock and Terraform: Summary, Knowledge Base, and Guardrails

无声无息的故障

AI智能体系统中可能出现诸多问题,最严重的故障甚至不会引发错误提示或警报,导致系统质量逐渐下降。会话内存中充斥着噪声而非有效信号,安全规则处于版本控制之外,而智能体却仍会调用本可由知识库直接提供的工具来获取内容。

Amazon Bedrock提供了三种解决方案:一种是通过MEMORY_SUMMARIZATION功能在多个会话之间保留有意义的用户信号;另一种是用Terraform实现的版本化Bedrock Guardrail安全机制;还有一种是由S3支持的可选知识库。

仅靠提示工程无法解决所有问题,真正的系统需要在三个层面设置控制措施:

  1. 会话内存质量。摘要功能必须保留关键的用户信号。
  2. 安全管控。安全规则应当明确且具备版本控制功能。
  • 检索边界。智能体需要明确的规则,以决定何时使用工具、知识库或两者兼用。
  • 如果没有这些规则,系统在演示时看似稳定,但一旦面临真实的、多样化的流量就会性能下降。

    会话内存的工作原理

    通过记忆摘要功能,用户的操作历史可以在不同会话之间保留,这样就不必每次都重新提取和实时处理旧消息。在Classic Agents中,Bedrock通过aws_bedrockagent_agent上的enabled_memory_types = ["SESSION_SUMMARY"]提供基础功能,而Agent Runtime中的GetAgentMemory则用于读取已存储的内容。

    启用记忆功能后,会话关闭后会异步处理相关操作。Bedrock会总结已完成的对话内容,将摘要存储起来,并注入到具有相同memoryId的后续会话中,整个过程不会延迟用户的回复。

    Bedrock提供了默认提示词,用于规定摘要生成的规则:

    {
      "messages": [
        {
          "role" : "user",
          "content" : "We will be given a conversation between a user and an AI
            assistant.
            When available, in order to have more context, we will also be given
            summaries we previously generated.
            Our goal is to summarize the input conversation.
            When we generate summaries we ALWAYS follow the below guidelines:
            <guidelines>
              - Each summary MUST be formatted in XML format.
              - Each summary must contain at least the following topics: 'user
                goals', 'assistant actions'.
              - Each summary, whenever applicable, MUST cover every topic and be
                placed between <topic name='$TOPIC_NAME'></topic>.
              - We ALWAYS output all applicable topics within <summary></summary>
              - If nothing about a topic is mentioned, DO NOT produce a summary
                for that topic.
              - We summarize in <topic name='user goals'></topic> ONLY what is
                related to the user, e.g., user goals.
              - We summarize in <topic name='assistant actions'></topic> ONLY what
                is related to the assistant, e.g., assistant actions.
              - NEVER start with phrases like 'Here's the summary...', provide
                directly the summary in the format described below.
            </guidelines>The XML format of each summary is as follows:
            <summary>
              <topic name='$TOPIC_NAME'>
                ...
              </topic>
              ...
            </summary>
            Here is the list of summaries we previously generated.
            <previous_summaries>
              $past_conversation_summary$
            </previous_summaries>
            And here is the current conversation session between a user and an AI
            assistant:
            <conversation>
              $conversation$
            </conversation>
            Please summarize the input conversation following the above guidelines
            plus the below additional guidelines:
            <additional_guidelines>
              - ALWAYS strictly follow the above XML schema and ALWAYS generate
              well-formatted XML.
              - NEVER forget any detail from the input conversation.
              - We also ALWAYS follow the below special guidelines for some of the topics.
              <special_guidelines>
                <user_goals>
                  - We ALWAYS report in <topic name='user goals'></topic> all details the user
                  provided in formulating their request.
                </user_goals>
                <assistant_actions>
                  - We ALWAYS report in <topic name='assistant actions'></topic> all details
                  about actions taken by the assistant, e.g., parameters used to invoke actions.
                </assistant_actions>
              </special_guidelines>
            </additional_guidelines>"
            }
        ]
    }
    

    其相关说明主要位于<guidelines/>部分,该部分要求智能体将结果放在<summary/>标签内返回。摘要内容按主题划分:与用户目标相关的信息归入名为user goals的主题,智能体自身的操作则归入assistant actions主题,每个主题都使用<topic name='$TOPIC_NAME'></topic>作为包装结构。期望的输出格式如下:

    <summary>
      <topic name='$TOPIC_NAME'>
        ...
      </topic>
      ...
    </summary>
    

    Bedrock通过$past_conversation_summary$变量将之前的摘要填充到<previous_summaries/>中,而实时对话则显示在<conversation>$conversationlt;/conversation>内。另外还有两个部分<additional_guidelines/>和<special_guidelines/>,可用于为摘要生成策略添加更多细节。

    其工作流程如下:

    1. 在代理配置中通过memory_configuration及enabled_memory_types = ["SESSION_SUMMARY"]启用内存功能。
    2. 调用方为每位用户提供唯一的memoryId来调用该代理。
    3. 会话因设置endSession = true或超时而结束。
    4. Bedrock在后台生成摘要。
  • 在后续使用相同 memoryId 的会话中,Bedrock 会将内存上下文放入诸如 $memory_content$ 这样的编排占位符中。
  • 这会带来两个后果:摘要并非即时生成,可能在会话结束一段时间后才出现;而且如果每次请求的 memoryId 都不同,就会导致上下文不连贯。

    警告: 不稳定的 memoryId 值,比如包含用户 ID 的连接 ID,会破坏内存的连贯性。应使用对每个用户或对话实体而言都保持不变的标识符。

    让摘要更具实用性

    默认的摘要只是简单的概括。这对通用型助手来说尚可,但对于专业型助手而言往往信息过于杂乱。对于小型新闻助手而言,优秀的摘要应包含:

    • 诸如人工智能新闻、气候或地缘政治等反复出现的主题;
  • 优先关注的地区和实体;
  • 用户在不同会话中持续关注的内容;
  • 明确的排除需求,例如“不要包含加密货币”。
  • 如果没有这种结构,记忆量会不断增加,而相关性却会下降。

    要启用该功能,首先需要在 Terraform 中配置代理资源:

    resource "aws_bedrockagent_agent" "agent" {
      # ... existing code above ...
      memory_configuration {
        enabled_memory_types = ["SESSION_SUMMARY"]
        storage_days = 30
        session_summary_configuration {
          max_recent_sessions = 5
        }
      }
      # ... existing code below ...
    }
    

    Bedrock 代理在此处仅接受 SESSION_SUMMARY 类型。AgentCore 的功能更为强大,它还能处理摘要、用户偏好、语义事实以及自定义记忆策略。storage_days 用于设定摘要的保存时长,而 max_recent_sessions 则控制要生成摘要的最新会话数量。

    每次调用都必须包含一个 memoryId,此处即为 user_id,这样各次对话才能关联到同一个实体:

    # Invoke with stable memoryId and explicit session end
    resp = bedrock_agent_runtime.invoke_agent(
      agentId=agent_id,
      agentAliasId=alias_id,
      sessionId=conversation_id,
      memoryId=user_id,     # stable per user
      inputText=message_text,
      endSession=is_last_turn
    )
    

    检索时使用相同的memoryId:

    # Retrieve summaries later (classic Agents API)
    memory = bedrock_agent_runtime.get_agent_memory(
      agentId=agent_id,
      agentAliasId=alias_id,
      memoryId=user_id,
      memoryType="SESSION_SUMMARY"
    )
    

    覆盖摘要生成提示

    开启记忆功能后,Bedrock 允许通过提示词覆盖来替代 MEMORY_SUMMARIZATION 阶段:将 prompt_type = "MEMORY_SUMMARIZATION" 更改为 prompt_creation_mode = "OVERRIDDEN"。传统智能体使用两个专用占位符:$past_conversation_summary$

    prompt_override_configuration {
      prompt_configurations = [{
        prompt_type = "MEMORY_SUMMARIZATION"
        prompt_state = "ENABLED"
        prompt_creation_mode = "OVERRIDDEN"
        parser_mode = "DEFAULT"
        base_prompt_template = <<-JSON
    {
      "system": "
      We are summarizing a news research conversation for future sessions.
      Capture:
        - recurring topics,
        - region focus,
        - entities and stories the user tracks,
        - explicit dislikes.
      Ignore:
        - greetings and small talk,
        - implementation questions about the assistant itself.
      Past summaries:
      <previous_summaries>
        $past_conversation_summary$
      </previous_summaries>
      Current session:
      <conversation>
        $conversation$
      </conversation>
      Return concise bullet points focused on user signal.",
      "messages": [
        {
          "role": "user",
          "content": [{ "text": "Generate a focused memory summary for personalization." }]
        }
      ]
    }
    JSON
      inference_configuration = [{
        temperature = 0.2
        top_k = 64
        top_p = 0.9
        max_length = 512
        stop_sequences = []
      }]
      # ... existing code below ...
    }
    ]
    }
    

    当通用总结无法提升后续回答质量、摘要需要较高的信噪比,或是内存令牌预算必须保持严格且可预测时,应覆盖默认流程。如果助手为通用型且不依赖个性化功能,则使用默认设置即可。

    常见错误包括:

    • memoryId值不稳定,例如用连接ID替代用户ID;
    • 会话永不结束,因而无法生成摘要;
    • 摘要提示过于冗长,实际上相当于重新生成完整对话记录;
    • 将摘要目标与用于后续处理的格式化目标混为一谈。

    约束机制:提示词无法强制执行的规则

    用户有时会表现粗鲁、滥用智能体,或询问内部细节、机密信息以及其他人的个人数据。防护机制可用于处理这些问题。其原则很简单:提示词引导行为,而防护机制则执行政策规定。利用提示词进行决策和格式控制,同时用防护机制对用户输入和智能体输出的内容安全设置限制。

    提示:在采取阻止措施之前,先在检测模式下测试防护机制。通过审核本应被拦截的内容,不会影响测试人员。

    Bedrock具备多种防护功能,只需将它们添加到列表中即可,无需额外设置:

    • 内容过滤器可过滤仇恨言论、侮辱性内容、暴力内容、色情内容、不当行为以及提示词攻击,同时针对输入和输出分别设定不同的过滤强度及处理方式。这类功能非常适合为普通用户流量提供持续的基础安全保障。
  • 禁止主题用于屏蔽特定主题领域,非常适合设定产品或监管相关的限制,例如“禁止讨论此主题”。
  • 词汇过滤器可管理预设及自定义的禁止词条,非常适合处理特定的业务术语、脏话或必须严格屏蔽的词汇。
  • 敏感信息过滤器能匹配个人身份识别信息及相关自定义正则表达式,可实现对账户标识、电子邮件和电话号码等隐私内容的管控。
  • 上下文约束检查可在信息检索流程中强制执行基于上下文的答案生成规则,这对于需要控制幻觉现象的RAG及知识库系统尤为重要。
  • 提示词攻击在标准层级中被归类为内容过滤器的一个子类别,包括越狱攻击、注入攻击和提示词泄露等类型。
  • 自动化推理检查可验证内容是否符合政策规则,从而确保回复的逻辑严谨性。
  • 大多数团队建议分阶段逐步实施:首先部署内容与敏感信息过滤器,接着是主题与词汇过滤器,等检索策略成熟后再加入基础推理功能。

    评估顺序

    安全机制会从两个方向进行检测:

    1. 输入。首先会对用户的提示语进行评估,所有已配置的政策会同时运行。若触发拦截规则,Bedrock会返回blocked_input_messaging并跳过推理过程。
    2. 输出。当输入通过检测后,模型会生成回复,其输出内容也会被检查。若触发拦截规则,系统会将回复替换为blocked_outputs_messaging,或在设置了敏感信息过滤时对相关内容进行遮蔽。
  • 如果两者均通过检测,输出内容将保持不变。
  • 顺序会影响成本。被拦截的输入仅需要支付防护机制的评估费用,无需进行模型推理。

    每个内容过滤器都有两个独立的设置。强度(inputStrength和outputStrength)可取值为NONE、LOW、MEDIUM或HIGH,用于控制检测违规行为的严格程度。操作方式(inputAction和outputAction)可为BLOCK,即拦截该内容并替换为默认提示信息;或为NONE,仅记录检测结果而不采取任何行动,实际上属于仅检测模式。

    一个合理的默认策略是:

    • 对于仇恨、暴力及不当行为等高风险类别,无论是输入还是输出都应从 MEDIUM 级开始;
    • 在测试或预发布阶段,应先将 outputAction = "NONE",以便统计误报情况;
    • 只有当追踪结果和测试集数据都符合预期时,才改为使用 BLOCK 级;
    • 低风险类别应保持为 LOW 或 MEDIUM 级,切勿在初始阶段就将所有类别都设置为 HIGH 级。

    内容过滤器也分为两个防护层级。CLASSIC 是基础过滤功能,而 STANDARD 则通过代码域分析及提示词攻击类别识别提供更强的保护。能够处理开发者风格的提示词、代码片段或混合文本与代码的助手,在 STANDARD 级下通常更为安全。

    版本控制

    护栏规则的发布应当明确且不可更改,每一次策略调整都应生成一个新版本。aws_bedrock_guardrail_version支持这一功能:可通过guardrail_configuration创建版本并将其关联到aws_bedrock_guardrail上。

    resource "aws_bedrock_guardrail" "guardrail" {
      name = "${var.environment}-news-guardrail"
      description = "Safety policies for news assistant"
      blocked_input_messaging = "Your request violates our usage policy."
      blocked_outputs_messaging = "This response was blocked by our safety policy."
      content_policy_config {
        filters_config {
          type = "HATE"
          input_strength = "MEDIUM"
          output_strength = "MEDIUM"
        }
        filters_config {
          type = "VIOLENCE"
          input_strength = "MEDIUM"
          output_strength = "MEDIUM"
        }
        filters_config {
          type = "MISCONDUCT"
          input_strength = "MEDIUM"
          output_strength = "MEDIUM"
        }
      }
    }
    
    resource "aws_bedrock_guardrail_version" "guardrail_version" {
      guardrail_arn = aws_bedrock_guardrail.guardrail.guardrail_arn
      description = "release-${substr(sha256(var.guardrail_policy_fingerprint), 0, 12)}"
      skip_destroy = true
    }
    
    resource "aws_bedrockagent_agent" "news_agent" {
      # ... instruction, model, prompt overrides ...
      guardrail_configuration {
        guardrail_identifier = aws_bedrock_guardrail.guardrail.guardrail_id
        guardrail_version = aws_bedrock_guardrail_version.guardrail_version.version
      }
      # ... existing code below ...
    }
    

    这种分离结构便于进行干净的回滚操作。提示信息保持不变,只有在需要调整策略时,防护版本才会随之变动。

    内部内容知识库

    假设智能体应依据公司政策或长期积累的知识来回答问题,并避免不必要的工具调用。Bedrock通过内置的知识库从检索到的文档中生成答案,一旦配置完成便会自动启用。它能够让智能体安全地访问公司的私有数据,从而使基础模型通过检索增强生成技术(RAG)提供更相关、更准确且量身定制的答案。

    当真实信息来源为内部或精心整理的文档、无需分钟级的实时更新,或是需要带有类似引用痕迹的可靠答案时,这种方案十分适用。

    注意:实时且由外部索引生成的信息更适合通过 API 工具来处理。

    Bedrock Knowledge Base 能够作为托管式的 RAG 流水线来处理您自己的内容。您无需自行构建检索基础设施,只需让 Bedrock 从连接器获取数据,解析并分割文档,生成嵌入向量,将向量存储在支持的存储系统中,然后在有请求时检索出相关的文档片段。在 KNOWLEDGE_BASE_RESPONSE_GENERATION 阶段,这些文档片段会被用于生成基于实据的响应。

    涉及两个流程。数据摄取可在离线模式下或同步模式下进行:首先解析文档,根据分块策略将其拆分成多个块,再通过嵌入模型将这些块转换为向量,并连同元数据映射一起写入向量索引中。运行时检索则是在每次请求时执行:对查询进行嵌入处理,向量存储返回语义上相关的块,Bedrock将这些块添加到模型的上下文中,随后在生成阶段产生有依据的输出;而在仅检索模式下则直接返回检索结果。

    这种分块方式具有实际意义,因为检索质量不佳往往是由于数据摄取或分块问题导致的,而非提示词的问题。

    不存在像SQL缓存中那样“以行形式插入”的操作。你需要添加或更新文件,或是基于连接器的数据源,之后再运行同步操作。

    警告:同步并非自动进行。将文档添加到 S3 后,必须手动触发同步,代理才能检索这些文档。

    运行时控制

    • numberOfResults 决定了返回的源数据块数量(默认为 5,范围为 1 到 100)。
    • overrideSearchType 可选择 SEMANTIC 仅进行向量搜索,或选择 HYBRID 同时进行向量和原始文本搜索,此功能仅支持在兼容的存储和索引上使用。若不设置,则由 Bedrock 自动决定。
    • 元数据 filter 以及逻辑组(andAll、orAll)可帮助实现精确检索。
    • 可选的重排序功能可用于优化数据块的最终顺序。

    由于采用分层数据块结构,实际返回的结果数量可能少于请求值,因为在响应时子数据块可能会被其父数据块替代。

    分块处理

    分块处理会显著影响检索质量。Bedrock提供默认的分块方式,即沿句子边界将文本分为约300个标记的片段;也有固定大小的分块方式,可明确指定标记数量及重叠比例;还有层次化的父子片段结构,既能提升精确度又能提供更广泛的上下文信息;此外还有基于模型边界定义的语义分块方式,以及不进行任何分块的处理方式——后者将每份文档视为一个独立片段,主要适用于已预分割的内容。

    一种合理的做法是:先从默认或固定大小的分块开始,因为这种方式较为简单。当需要更多上下文信息时再转为层次化分块;如果在提示词调整后精确度仍不佳,则可尝试语义分块。每次更改前后都应使用检索评估集进行对比。

    向量存储

    知识库可搭配多种向量后端使用,例如 OpenSearch Serverless 或托管集群、兼容 Aurora PostgreSQL 的版本、Pinecone、Redis Enterprise Cloud、MongoDB Atlas 以及 Neptune Analytics,具体取决于部署时的供应商支持情况。不同的后端选择会影响是否支持混合搜索、可用的元数据过滤功能,以及系统的运营复杂度和成本。OpenSearch Serverless 通常是最快捷的启动方式。以下是一个使用 Terraform 进行连接的示例:

    resource "aws_bedrockagent_knowledge_base" "news_kb" {
      name = "${var.environment}-news-kb"
      role_arn = aws_iam_role.bedrock_execution_role.arn
      knowledge_base_configuration {
        type = "VECTOR"
        vector_knowledge_base_configuration {
          embedding_model_arn = "arn:aws:bedrock:${data.aws_region.current.name}::foundation-model/amazon.titan-embed-text-v2:0"
        }
      }
      storage_configuration {
        type = "OPENSEARCH_SERVERLESS"
        opensearch_serverless_configuration {
          collection_arn = aws_opensearchserverless_collection.kb.arn
          vector_index_name = "news-kb-index"
          field_mapping {
            vector_field = "vector"
            text_field = "text"
            metadata_field = "metadata"
          }
        }
      }
      # ... existing code below ...
    }
    
    resource "aws_bedrockagent_data_source" "news_docs" {
      knowledge_base_id = aws_bedrockagent_knowledge_base.news_kb.id
      name = "${var.environment}-news-docs"
      data_source_configuration {
        type = "S3"
        s3_configuration {
         bucket_arn = aws_s3_bucket.news_docs.arn
        }
      }
      # ... existing code below ...
    }
    
    resource "aws_bedrockagent_agent_knowledge_base_association" "news_agent_kb" {
      agent_id = aws_bedrockagent_agent.agent[0].agent_id
      agent_version = "DRAFT"
      knowledge_base_id = aws_bedrockagent_knowledge_base.news_kb.id
      description = "Internal news assistant knowledge base for curated context and editorial guidelines"
      knowledge_base_state = "ENABLED"
      # ... existing code below ...
    }
    

    知识库与工具之间的路由

    引入知识库可为智能体提供第二条检索路径。为避免歧义,应在调度指令中明确规定知识库与API工具的路由规则,无论由哪个来源响应,都保持POST_PROCESSING接口的格式一致,并在检索置信度较低时指示智能体提出澄清问题。

    针对此架构的实用策略是:将政策文件、编辑标准及历史记录等内部静态内容通过知识库获取;将实时外部数据通过操作组获取;除非请求确实需要交叉验证,否则不要在单次处理中同时调用两者。

    在检索和生成操作中同样需要应用约束机制,这一点很重要,因为仅靠基于规则的检索无法完全消除安全风险。常见的做法是首先在检测模式下使用约束机制评估知识库路径,根据反馈调整误报和漏报情况,之后再在正式环境中采用阻断或屏蔽措施。

    内容导入

    • 初始数据集:若需一次性加载政策、常见问题解答及风格指南,可将相关文件上传到与数据源相连的S3存储桶并启动同步流程。
    • 持续更新:当产品或法律文本按预定时间表发生变化时,可通过夜间任务将新文件上传到S3,并为该数据源触发StartIngestionJob函数;若已启用自动同步功能,则可直接依靠该机制进行更新。
  • 大量小型文档:每个文件存储一个主题,例如 kb/topics/eu-ai-act-overview.md,这样分块边界与主题对应更清晰,引用也比使用一个巨大的 PDF 更易理解。
  • 连接器:SharePoint、Confluence、网络爬虫等工具可处理已存储在那里的内容。Bedrock 会按预定时间表进行数据获取和同步,具体选项因连接器类型和地区而异。
  • 程序化管道:当 CMS 或工单系统导出 HTML 或 JSON 时,Lambda 可将其转换为纯文本或 Markdown 格式,写入 S3 并调用 StartIngestionJob,以便知识库能够识别这些新内容。
  • 潜在问题

    • 将知识库视为实时数据源。其内容更新频率取决于同步计划。
    • 分块数量过多或过少都会影响精确度。
    • 元数据处理不当,导致过滤功能失效。
    • 将检索与格式化任务合并到同一个提示阶段中。
    • 误以为重新措辞提示就能弥补较差的检索效果。

    最终后果

    摘要只有在针对特定领域进行优化而非作为通用总结文本时才能发挥最佳作用。提示与约束机制属于不同层级,因此应分别处理其版本控制。知识库仅是一种检索选项,并不能替代实时API,因此提示中应明确说明何时由工具提供最新信息,何时由知识库提供精心整理的上下文。

    这些组件共同构成了该智能体的内部处理流程:能够将长对话浓缩为可重复使用的摘要的 MEMORY_SUMMARIZATION 提示词,用于拦截有害内容类别的 Bedrock Guardrail,以及基于 S3 数据源、用于检索增强生成的知识库。