AWS Machine Learning Blog

Authoring Dogwood policies from natural language in Amazon Bedrock AgentCore

8.5内容质量
Authoring Dogwood policies from natural language in Amazon Bedrock AgentCore

TL;DR · AI 摘要

AWS Bedrock AgentCore推出自然语言编写Dogwood策略功能,通过Policy Authoring工具实现策略自动化转换,强化AI代理合规控制。

核心要点

  • Policy Authoring工具可将自然语言策略转换为Dogwood正式规范
  • 新功能支持时间限制、轨迹约束及Guardrails内容检测
  • 策略可限制工具输入参数并控制代理行为轨迹

结构提纲

按章节快速跳转。

  1. 介绍AI代理需要政策控制以确保合规性,Bedrock AgentCore新增Dogwood策略功能

  2. AI驱动工具将自然语言策略转换为Dogwood规范,支持时间与轨迹约束

  3. 新增内容检测、参数限制及顺序控制等策略类型

  4. 以零售银行客服代理为例说明策略应用方式

  5. 建议精简规则文档,分离规则与背景说明

思维导图

用一张图看清主题之间的关系。

查看大纲文本(无障碍 / 无 JS 友好)
  • Bedrock AgentCore策略管理
    • Dogwood策略语言
      • 时间限制
      • 轨迹约束
    • Policy Authoring工具
      • 自然语言转换
      • Guardrails集成
    • 实施场景
      • 银行客服代理示例

金句 / Highlights

值得收藏与分享的关键句。

#AWS#Bedrock AgentCore#Dogwood#自然语言处理#策略管理
打开原文

从自然语言编写 Dogwood 策略:Amazon Bedrock AgentCore | 人工智能

从自然语言编写 Dogwood 策略:Amazon Bedrock AgentCore

AI 代理可以自动化复杂的工作流程,但如果在没有适当控制的情况下使用,可能会采取与组织政策或监管限制不一致的行动。为了解决这个问题,我们在 Amazon Bedrock AgentCore 中构建了 Policy 功能,使团队能够实施适用于 Amazon Bedrock AgentCore 中运行代理的控制措施。最近,我们扩展了这一功能,新增了用于强制执行跨时间限制代理行为的限制能力,支持速率限制、工具调用的前提条件和顺序排列、累积效应等策略。这些策略使用 Dogwood(一种开源治理语言)进行表达,并通过 AgentCore Gateway 内置的 Dogwood 监控器实时应用于代理操作,这是 Amazon Bedrock AgentCore 的一项功能。

此次新发布中,我们扩展了 Policy Authoring(一种 AI 驱动工具)的功能,该工具可将自然语言策略规范文档转换为语法和语义正确的 Dogwood 正式规范。通过这一新功能,您可以生成强制执行时间约束和轨迹约束的策略,调用 Amazon Bedrock Guardrails 服务以检测自由文本语义中的不当内容,以及对工具输入参数施加限制的策略(这些功能在 AgentCore 中 Policy 的早期版本中已提供)。无论您的技术背景如何,都可以直接将用自然语言撰写的策略文档导入 Amazon Bedrock AgentCore 中的策略,以保护已部署的代理系统。

在本文中,我们将通过示例演示这一新功能,并提供指导,帮助您在构建自然语言策略时遵循最佳实践。

自然语言策略到 Dogwood 的自动转换

Dogwood 策略可以完全手动编写,对于少量控制规则来说,这显然是一个合理的起点。当您已经拥有以散文形式撰写的规则,并且当前的工作是转录而非设计时,Policy Authoring 的效果最佳。您可以提供一份包含清晰规则的文档:策略列表、操作规程中的规则部分,或一段描述允许或限制操作的书面段落。Authoring 是一个翻译器而非摘要工具,因此如果文档中将规则与理由、背景和评论交织在一起,最好先将其精简为规则本身。

示例场景

为了使示例更加具体,我们考虑一个零售银行的客户服务代理。它验证来电者、处理争议、针对争议费用发放退款、在客户账户之间转移资金,并可以要求主管批准一笔费用。其工具通过 AgentCore Gateway 调用,每个工具接受少量参数并返回结果:

工具 | 目的 | 输入 | 输出 ---|---|---|--- verify_identity | 对来电者进行增强验证 | { account: String } | { verified: Bool } initiate_transfer | 在客户账户之间转移资金 | { account: String, dest_account: String, amount: Long } | { confirmation: String } issue_refund | 逆转争议费用 | { account: String, charge_id: String, amount: Long } | { refunded: Bool }

code
file_dispute

开启争议案件

code
{ account: String, description: String }
code
{ case_id: String }
code
request_approval

请求主管批准费用

code
{ charge_id: String }
code
{ approved: Bool }

除了政策文件外,撰写过程还会使用一个包含这些信息的模式:工具名称、它们接受的参数以及返回的值。该模式是从代理的模型上下文协议(MCP)工具清单生成的,因此生成的策略引用的是代理实际调用的相同名称。例如,生成策略中的 context.input.amount 对应上表中的金额参数。撰写过程还会提供可用的 Amazon Bedrock Guardrails 检查项以及策略允许引用的身份声明。

银行的合规团队以现有用于人工员工的书面形式维护其控制措施。以下规则均来自该文件,每条规则后都附有 Dogwood 策略编写工具生成的对应策略。两个惯例使输出更易于阅读:Dogwood 默认拒绝策略,且禁止(forbid)规则会覆盖允许(permit)规则。因此,授予某种能力的规则会转化为带有条件的允许(permit),而限制或封顶某事物的规则会转化为禁止(forbid)。条件可以检查当前正在决策的调用,也可以检查同一会话中已经发生的内容。以下示例同时展示了这两种情况。

策略翻译示例

以下示例展示了自动形式化工具如何将自然语言策略转换为 Dogwood 公式。

对工具参数的限制

退款只能在工作时间(定义为 UTC 时间上午 9:00 至下午 5:00)发放,且金额不得超过 2500 美元。

code
permit ( principal, action == AgentCore::Action::"issue_refund", resource )
when { context.system.now.toTime() >= duration("9h")
    && context.system.now.toTime() <= duration("17h") }
when { context.input.amount <= 2500 };

一个包含两个独立要求的句子转化为一个策略,包含两个条件,两个条件都必须满足才能允许退款。context.input.amount 是代理发起的 issue_refund 调用的金额参数,单位由工具声明决定。文档中的“2500 美元”和工具的金额参数需要在单位上保持一致。时间比较会在调用决策时读取系统时间,且这两个条件都不依赖代理之前的操作。有关 duration 等基于时间的函数的更多细节,请参阅 时间策略支持 文档。

必须的前置步骤

在发起转账之前,必须确保调用者身份已在过去 15 分钟内针对同一账户完成验证。

code
permit ( principal, action == AgentCore::Action::"initiate_transfer", resource )
when temporal {
    formerly within 15m AgentCore::Action::"verify_identity"::response{
        input.account:   context.input.account,
        output.verified: true
    }
};
code

This rule cannot be settled from the transfer request alone, so the generated condition looks at what the agent has already done. formerly within 15m asks whether the event it describes occurred at any point in the past fifteen minutes. Here, the completion of a verify_identity call ( ::response , the result, rather than the call being made) that came back with verified: true . Inside an event pattern, a bare input.account names a field of that earlier event, while context.input.account names one on the call being decided. Setting the two equal is what makes “that same account” precise. A verification of some other account, or one that was attempted and came back unverified, does not satisfy the rule. And because the history examined is the current session’s, the rule needs no separate ID for the caller, which is why verify_identity takes only the account.

### A cumulative cap

Block a transfer if the total amount transferred in the past 12 hours would exceed $50,000.

forbid ( principal, action == AgentCore::Action::"initiate_transfer", resource ) when temporal { exists (total: Long). ( (sum a for (a: Long), (t: Timepoint). where ( formerly within 12h ( AgentCore::Action::"initiate_transfer"::request{ input.amount: a } && tp(t) ) )) == total && total > 50000 ) };

code

Here, the history isn’t searched but added up. The policy takes the amount argument of every transfer in the past 12 hours, sums them, and denies the current call if the running total passes $50,000. Each individual transfer in that window might be small and unremarkable, but the condition instead constrains their aggregate. Note also what the document leaves open: it says “transferred” without saying whether a blocked or failed attempt counts. The translation sums ::request events, meaning every transfer the agent attempted, which is the safer reading for a cap. However, saying so in the document removes the guess, and that is the subject of the first best practice that follows.

### A rate limit

The agent might attempt no more than three refunds against the same account within one hour.

forbid ( principal, action == AgentCore::Action::"issue_refund", resource ) when temporal { exists (n: Long). ( (count for (t: Timepoint). where ( formerly within 1h ( AgentCore::Action::"issue_refund"::request{ input.account: context.input.account } && tp(t) ) )) == n && n > 3 ) };

code

This has the same shape as the previous policy, counting events rather than summing a field. The count is restricted to refunds against the account named in the call under consideration, and it includes that call, so the fourth attempt within the hour is the one that is denied. This rule says “attempt” explicitly, so unlike the previous one it leaves nothing to infer: a refund that was denied or that failed still counts against the limit.

### A check on free-form text

Reject any dispute filing whose description contains a Social Security number.

forbid ( principal, action == AgentCore::Action::"file_dispute", resource ) when { BedrockGuardrails::SensitiveInformation(["US_SOCIAL_SECURITY_NUMBER"], [context.input.description]) .maxConfidenceScore().greaterThanOrEqual(decimal("0.2")) };

code

一些规则涉及自由文本的含义而非结构化值,字段比较不会决定这些规则。对于这些规则,生成的策略会在规则名称字段中内联调用 Amazon Bedrock Guardrails 检查,并将报告的置信度与阈值进行比较。该规则未指定阈值,因此翻译时会使用该检查的默认值。当文档中明确指定阈值(例如“高置信度”或具体数值)时,会使用文档中指定的值。

### 依赖多种条件类型的规则

超过 500 美元的退款需要主管对相关费用进行审批,且该审批记录需在最近 30 分钟内生成。

forbid ( principal, action == AgentCore::Action::"issue_refund", resource ) when { context.input.amount > 500 } unless temporal { formerly within 30m AgentCore::Action::"request_approval"::response{ input.charge_id: context.input.charge_id, output.approved: true } };

code

该句子包含两个通过不同方式验证的部分:当前调用参数的阈值检查,以及对已发生事件的条件检查。这两个子句存在于同一策略中。该规则限制了现有权限:禁止金额超过 500 美元的退款,而 unless 子句则作为例外情况,当记录中存在匹配的审批时解除禁止。与之前的前提条件示例类似,charge_id 的关联性防止了某个费用的审批被用于授权其他费用的退款。

## 最佳实践

清晰明确的策略能带来更可预测的行为和更少的错误,无论执行者是人工用户还是自主代理。此外,这还能提升之前介绍的自然语言到 Dogwood 的编写解决方案的性能。接下来,我们将回顾一些构建自然语言策略的技巧和最佳实践。

- 明确说明是指尝试还是结果。"After a transfer" 是模糊的,"After a transfer succeeds" 则是明确的。尝试是指代理发出的任何调用,包括被拒绝或失败的调用。只有完成的调用才会携带工具返回的值。速率限制和累计上限通常针对尝试次数,而前提条件和排序规则则针对结果。

- 明确时间窗口。"Recently" 没有明确的时间范围,"Within the past 30 minutes" 则有具体定义。时间窗口从当前决策的调用向过去延伸,因此如果规则需要在日历边界重置而非随时间滑动,需明确说明,因为这需要不同的控制方式。

- 明确规则所关联的对象。"No more than three transfers per hour" 没有说明是针对哪个主体:是当前调用者的三次转账,还是针对该账户的三次转账?两者都可以表达,但属于不同的策略,而该句子未作明确选择。无论规则涉及计数、求和还是关联,都应明确说明将事件关联起来的字段。

- 明确阈值及其边界。"More than three" 和 "at least three" 差异在于一个动作,通常是规则旨在阻止的那个动作。置信度检查的置信度水平也遵循相同原则。

- 检查生成的 Dogwood 策略的准确性。每个 Dogwood 策略都会与生成它的句子一同返回,方便您并排查看。虽然验证可以确认策略格式正确且锚定在合适的模式中,但无法确认策略是否准确表达了作者的意图。该判断应由文档所有者进行。

## 了解无法强制执行的内容

尽管编写服务可以过滤并突出显示与AgentCore中策略强制执行不兼容的策略,但你也应意识到常见的问题。

- 这不是关于某个操作的规则。"代理应始终以客户的最佳财务利益行事,并运用良好的专业判断。" 这里没有任何关于操作、字段或主体的条件。这是一个真实的要求,应包含在代理的指令、评估和培训中,而不是授权引擎中。

- 它要求执行某个操作,而非做出判断。"当争议描述包含社会保险号码时,在备注存储前应将其删除。" 策略引擎允许或拒绝某个调用,但不会修改它。前面示例中提到的拒绝包含社会保险号码的文件的规则是可表达的。删除操作是另一种控制方式,应用于流程的不同阶段。

- 它超出了语言表达的范围。"在周末和美国联邦银行假日禁止电汇。" Dogwood的日期和时间支持涵盖时间点、偏移量和差异,但没有提供"周几"访问器或节假日日历。Dogwood语言指南详细列出了可用的构造,当策略编写服务将规则排除在外时,建议仔细阅读该指南,确认存在哪些缺口,并查看是否有相近的表达方式可用。

- 它超出了强制执行的范围。"客户每天最多可发起十次转账,该统计涵盖所有并发会话。" 强制执行仅评估会话内的轨迹,因此跨会话的限制无法通过其他措辞来实现。

在每种情况下,有用的输出不是策略本身,而是表明其无法转换为Dogwood的标签。这提示你需要考虑替代方案:重写规则、将其转移到其他控制机制,或接受其作为人工流程保留。

## 策略编写的工作原理

自动形式化流程分为四个步骤。首先从分解文档开始。为读者撰写的规则通常较为复杂:一个编号条款经常包含多个独立义务,单个句子有时也会包含两个,如前文提到的营业时间示例。分解过程会将它们拆分为原子规则,每个规则都是关于特定工具或工具集的陈述,且可以独立执行。每个规则随后会被路由处理。一个规则要么可以使用Dogwood及其构成的监控器表达,要么不能,而无法表达的规则会被搁置而非翻译,通常是因为前一节提到的四个原因。在翻译尝试前进行过滤,可以防止无法表达的规则变成验证通过但执行错误的策略。剩余的规则会被自动形式化为Dogwood,依据文档一同提供的工具模式进行锚定。最后,每个候选策略都会使用与开源语言随附的Dogwood命令行工具进行验证。这是流程中不涉及判断的部分:编译器是判断策略是否可解析以及策略中每个名称是否存在于模式中的精确且确定性权威。当候选策略被拒绝时,其诊断信息会被返回,并在可见这些错误的情况下重新翻译规则,进行有限轮次的重复。

最终输出包含两组内容:通过语法和环境模式兼容性验证的策略及其生成的原子自然语言规则,以及被搁置的原子自然语言规则。

## 结论

本文展示了Policy Authoring如何将书面政策文件转换为Dogwood策略。您已经看到了涵盖工具参数约束、前提条件、累积上限、速率限制以及自由格式内容检查的翻译示例。此外,您还了解了使自然语言规则良好翻译的属性:明确的时间窗口、命名的主体、显式的阈值,以及在尝试和结果之间清晰的选择。Dogwood可以直接编写,对于更喜欢使用该语言的团队,他们可以继续这样做。Authoring功能针对的是规则已以文本形式存在的常见情况,以缩短从您已维护的文档到可审查和部署的策略集的路径。

要开始使用,请参阅AgentCore文档中的《Policy》部分,了解如何创建策略引擎以及如何从文档编写策略,如果希望自行阅读或扩展生成的策略,请参阅Dogwood语言指南。如需了解更多关于这些策略在运行时如何被解释和执行的信息,请参阅《Amazon Bedrock AgentCore中使用时间策略保护AI代理》和《Dogwood:AI代理的运行时验证》。

我们还想感谢团队中其余的Applied Scientists:Shang Chao、Shahriar Sadat、Du Wanyu和Kulshreshtha Devang对此次发布的贡献。

## 作者简介