官方更新 AWS ML Blog Thu, 20 Aug 2026 正文 ok 2945 words

在 Amazon Bedrock AgentCore 中通过自然语言编写 Dogwood 策略

英文标题:Authoring Dogwood policies from natural language in Amazon Bedrock AgentCore

Amazon Bedrock AgentCore 的 Policy Authoring 可将自然语言策略文档转换为语法和语义正确的 Dogwood 正式策略。新能力支持跨时间约束,包括速率限制、前置条件、工具调用顺序以及累计金额上限,并可调用 Amazon Bedrock Guardrails 检测自由文本中的不当内容。文章以零售银行客服 Agent 为例,展示了退款时段和金额限制、身份验证前置条件、转账累计上限、退款频率限制、敏感信息检测及主管审批等策略。对开发者而言,关键在于提供清晰、无歧义且与工具 Schema 对齐的自然语言规则,并审核生成的 Dogwood 策略是否准确表达原始意图。
打开原文相关 2.7 · 机会 2
media1.3 MB media101.1 KB media124.1 KB media345.0 KB media141.2 KB media346.5 KB media841.2 KB media666.1 KB media994.3 KB media210.8 KB

中文

AI Agent 可以自动化复杂工作流,但如果缺少适当控制,其采取的行动可能不符合组织政策或监管约束。为解决这一问题,我们构建了 Amazon Bedrock AgentCore 中的 Policy,使团队能够对运行在 Amazon Bedrock AgentCore 中的 Agent 实施统一控制。该功能近期新增了对跨越_时间维度_限制 Agent 行动的能力,支持速率限制、工具调用的前置条件和顺序要求,以及累计效应等策略。这些策略使用开源治理语言 Dogwood 表达,并由内置于 AgentCore Gateway(Amazon Bedrock AgentCore 的一项功能)中的 Dogwood monitor 对 Agent 操作进行实时应用。

作为此次新发布的一部分,我们扩展了 Policy Authoring 的能力。该 AI 驱动工具可将自然语言策略规范文档转换为语法和语义正确的 Dogwood 正式规范。借助这一新功能,您可以生成用于实施时序和轨迹约束的策略,调用 Amazon Bedrock Guardrails 服务检测自由文本语义中不当内容的策略,以及对工具输入参数施加限制的策略;后者已在此前版本的 AgentCore Policy 中提供。无论您的技术背景如何,都可以将以自然语言编写的策略文档直接导入 Amazon Bedrock AgentCore 中的 Policy,以保护已部署的 Agentic 系统。

本文将通过示例演示这一新能力,并提供在构建自然语言策略时采用最佳实践的指导。

将自然语言策略自动转换为 Dogwood

Dogwood 策略可以完全手工编写;对于一小组控制措施而言,这完全是合理的起点。当您已经以自然语言写好规则,眼前的工作是转录而非设计时,Policy Authoring 的效果最佳。您可以提供包含清晰规则集合的文档:策略列表、操作流程中的规则部分,或描述允许或受限操作的书面段落。Authoring 是翻译器而不是摘要器,因此,如果文档将规则与理由、背景和评论交织在一起,最好先将其精简为仅保留规则本身。

示例场景

为使示例更具体,我们考虑一家零售银行的客户服务 Agent。它验证来电者身份、提交争议、针对存在争议的扣款发放退款、在客户自己的账户之间转移资金,并且可以请求主管批准一笔扣款。其工具通过 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 }
file_dispute 创建争议案件 { account: String, description: String } { case_id: String }
request_approval 请求主管批准一笔扣款 { charge_id: String } { approved: Bool }

除策略文档外,Authoring 还接收一个准确承载上述信息的 Schema:工具名称、它们接受的参数以及返回的值。该 Schema 从 Agent 的 Model Context Protocol(MCP)工具清单生成,因此输出的策略引用的是 Agent 实际调用的相同名称。例如,生成策略中的 context.input.amount 就是前表中 amount 参数。Authoring 还会获得可用的 Amazon Bedrock Guardrails 检查集合,以及策略允许引用的身份声明。

该银行的合规团队以现有的人类员工使用形式,将控制要求维护为书面文档。以下规则取自该文档,每条规则后均附有 Policy Authoring 为其生成的 Dogwood 策略。有两项约定使输出更易于阅读。Dogwood 默认拒绝,且 forbid 会覆盖 permit,因此,授予某项能力的规则会成为带条件的 permit,而限制或设置上限的规则会成为 forbid。此外,条件既可以检查正在决策的调用,也可以检查同一会话中已发生的事情。以下示例两者皆有涉及。

策略转换示例

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

仅可在营业时间内发放退款;营业时间定义为 UTC 上午 9:00 至下午 5:00,且退款金额不得超过 2,500 美元。

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 是 Agent 发起 issue_refund 调用时的金额参数,并按照工具声明的单位进行比较。文档中的“2,500 美元”必须与工具的 amount 定义保持一致。时间比较读取的是作出调用决策时的时钟,两个条件都不依赖于 Agent 之前做过的事情。有关 duration 等基于时间函数的更多详情,请参阅 基于时间的策略支持。

必需的前置步骤

除非在此前 15 分钟内已针对同一账户验证来电者身份,否则不得发起转账。

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
    }
};

该规则无法仅根据转账请求本身确定,因此生成的条件会查看 Agent 已经执行的操作。formerly within 15m 会询问其所描述的事件是否在过去 15 分钟内任意时点发生过。这里指的是一次 verify_identity 调用的完成事件(::response,即结果,而非正在发起的调用),且其返回了 verified: true。在事件模式中,不带前缀的 input.account 表示此前事件的字段,而 context.input.account 表示正在决策的调用中的字段。将二者设为相等,才使“同一账户”具有精确含义。针对其他账户的验证,或虽尝试验证但结果未通过的验证,均不满足该规则。并且,由于所检查的历史记录属于当前会话,该规则不需要为来电者设置单独 ID,这也是 verify_identity 仅接受账户参数的原因。

累计上限

如果过去 12 小时内的转账总额将超过 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
    )
};

这里并非搜索历史记录,而是将其累加。策略获取过去 12 小时内每笔转账的金额参数,将其求和,并在累计总额超过 50,000 美元时拒绝当前调用。该时间窗口内的每笔单独转账都可能金额较小、看似无异常,但该条件限制的是它们的总量。还请注意文档中未明确的内容:它说的是“已转账”,却没有说明被阻止或失败的尝试是否计入。该翻译对 ::request 事件求和,意味着会计算 Agent 尝试过的每笔转账;对于上限而言,这是更安全的解读。但是,在文档中明确说明这一点就不再需要猜测,这也是随后第一条最佳实践的主题。

速率限制

Agent 在一小时内针对同一账户尝试退款不得超过三次。

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
    )
};

该策略与前一项策略具有相同结构,只是统计事件数量而非对字段求和。计数仅限于正在考虑的调用中所指定账户的退款,并且包含当前调用,因此一小时内的第四次尝试将被拒绝。该规则明确使用“尝试”,因此与前一个规则不同,它没有留下推断空间:被拒绝或失败的退款仍会计入限额。

对自由文本的检查

拒绝描述中包含社会保障号码的任何争议提交。

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

有些规则针对的是自由文本的语义,而非结构化值,因而无法通过字段比较来确定。对于这类规则,生成的策略会在规则指定的字段上内联调用 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
    }
};

该句包含以截然不同方式检查的两部分:当前调用参数的阈值,以及已发生事件的条件。两个子句都位于同一策略中。该规则收窄了既有许可:它拒绝金额超过 500 美元的退款,而 unless 子句是一个例外,当存在匹配的审批记录时,该例外会解除拒绝。与先前的前置条件示例一样,基于 charge_id 的关联可防止一笔扣款的审批授权另一笔扣款的退款。

最佳实践

清晰且无歧义的策略能够带来更可预测的行为和更少的错误,无论实现者是人工用户还是自主 Agent。同时,这也会提升前文介绍的自然语言到 Dogwood Authoring 解决方案的性能。下面,我们将回顾构建自然语言策略的一些技巧和最佳实践。

  • 说明您指的是尝试还是结果。 “一次转账之后”含义不明确;“一次转账成功之后”则不含糊。尝试是指 Agent 发出的任意调用,包括被拒绝或失败的调用。只有已完成的调用才携带工具返回的值。速率限制和累计上限通常针对尝试,而前置条件和顺序规则通常针对结果。
  • 明确时间窗口。“最近”无法被转换;“过去 30 分钟内”则可以。时间窗口从正在决策的调用向后回溯,因此,如果规则意在按日历边界重置,而非随时钟滑动,请明确说明,因为这需要不同的控制方式。
  • 说明规则以什么作为关联键。“每小时不超过三笔转账”没有说明对象是谁:是该来电者的三笔,还是针对该账户的三笔?两者都可以表达,但它们是不同的策略,而该句没有选择其中任何一种。只要规则涉及计数、求和或关联,就应说明将事件连接起来的字段。
  • 给出阈值及其边界。“超过三次”和“至少三次”相差一次操作,而通常正是这一次操作是规则旨在阻止的。内容检查的置信度级别也是如此。
  • 审核生成的 Dogwood 策略是否正确。每项 Dogwood 策略都会与其来源语句一并返回,因此您可以并排阅读和比较。验证能够确认策略格式正确且锚定于正确的 Schema,但不能确认该策略表达了作者真正想表达的意思。这一判断仍由拥有该文档的人负责。

了解无法强制执行的内容

虽然 Authoring 服务可以筛除并高亮显示与 AgentCore Policy 强制执行机制不兼容的策略,但您也应了解一些常见问题。

  • 它不是关于某项操作的规则。“Agents shoul

English / 原文

AI agents can automate complex workflows but might take actions that don’t align with your organization’s policies or regulatory constraints if used without proper controls. To address this, we built Policy in Amazon Bedrock AgentCore so teams can implement controls that are applied across agents running in Amazon Bedrock AgentCore. This was recently expanded with new capabilities for enforcing restrictions that constrain agent actions across time , which support policies such as rate limiting, prerequisites and sequential ordering of tool calls, and cumulative effects. These policies are expressed in Dogwood, an open source governance language, and applied to agent actions in real time by the Dogwood monitor built into the AgentCore Gateway, a capability of Amazon Bedrock AgentCore.

As part of this new launch, we expand the capabilities of Policy Authoring, an AI-driven tool to convert natural language policy specification documents into syntactically and semantically correct Dogwood formal specifications. With this new feature, you can generate policies that enforce temporal and trajectory constraints, invoke Amazon Bedrock Guardrails services to detect inappropriate content in the semantic meaning of free-form text, as well as policies that place restrictions on the input parameters of tools which were available in the previous version of Policy in AgentCore. Whatever your technical background, you can import policy documents written in natural language directly into the policy in Amazon Bedrock AgentCore to safeguard your deployed agentic systems.

In this post, we demonstrate this new capability using examples and provide guidance on how you can use best practices when constructing natural language policies.

Automated translation of natural language policies to Dogwood

Dogwood policies can be written entirely by hand, and for a small set of controls that is a perfectly reasonable place to start. Policy Authoring works best when you already have rules written in prose and the work in front of you is transcription rather than design. You can provide a document containing a clean set of rules: a list of policies, the rules section of an operating procedure, or a written paragraph of permitted or restricted actions. Authoring is a translator rather than a summarizer, so a document that interleaves its rules with rationale, background, and commentary is better pared down to the rules themselves first.

Example setting

To keep the examples concrete, let us consider a customer-servicing agent at a retail bank. It verifies callers, files disputes, issues refunds against disputed charges, moves funds between a customer’s own accounts, and can ask a supervisor to approve a charge. Its tools are reached through the AgentCore Gateway, and each takes a small set of arguments and returns a result:

Tool Purpose Input Output
verify_identity Step-up verification of the caller { account: String } { verified: Bool }
initiate_transfer Moves funds between the customer’s accounts { account: String, dest_account: String, amount: Long } { confirmation: String }
issue_refund Reverses a disputed charge { account: String, charge_id: String, amount: Long } { refunded: Bool }
file_dispute Opens a dispute case { account: String, description: String } { case_id: String }
request_approval Asks a supervisor to approve a charge { charge_id: String } { approved: Bool }

Alongside the policy document, authoring takes a schema carrying exactly this information: the tool names, the arguments they accept, and the values they return. That schema is generated from the agent’s Model Context Protocol (MCP) tool manifest, so the policies that come out refer to the same names the agent actually calls. For example, context.input.amount in a generated policy is the amount argument in the preceding table. Authoring is also given the set of available Amazon Bedrock Guardrails checks, and identity claims that a policy is allowed to reference.

The bank’s compliance team maintains its controls as a written document in the form it already uses for its human staff. The rules that follow are taken from that document, each followed by the Dogwood policy that Policy Authoring produced for it. Two conventions make the output more straightforward to read. Dogwood is default-deny and a forbid overrides a permit, so a rule that grants a capability becomes a permit carrying the conditions, while a rule that limits or caps something becomes a forbid. And a condition can examine either the call being decided or what has already happened in the same session. The following examples do both.

Policy translation examples

The following examples show how the autoformalizer translates natural language policies into Dogwood formulas.

Refunds might be issued only during business hours, defined as 9:00 AM–5:00 PM UTC, and only for amounts of $2,500 or less.

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 };

One sentence carrying two independent requirements becomes one policy with two conditions, both of which must hold for a refund to be permitted. context.input.amount is the amount argument of the issue_refund call as the agent issued it, compared in whatever units the tool declares. The document’s “$2,500” and the tool’s amount need to agree on that. The time comparison reads the clock at the moment the call is decided, and neither condition depends on anything the agent did earlier. For more detail on time-based functions like duration, see Time-based policy support.

A required prior step

Do not initiate a transfer unless the caller’s identity has been verified for that same account within the previous 15 minutes.

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
    }
};

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
    )
};

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
    )
};

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"))
};

Some rules are about the meaning of free-form text rather than a structured value, and no comparison on a field will decide them. For these, the generated policy calls an Amazon Bedrock Guardrails check inline, on the field the rule names, and compares the reported confidence against a threshold. This rule states no threshold, so the translation uses the default for that check. When a document does state one (for example, “with high confidence”, or a specific number), that value is carried through instead.

A rule that draws on more than one kind of condition

A refund of more than $500 requires a supervisor’s approval for that charge, recorded within the last 30 minutes.

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
    }
};

The sentence has two parts that are checked in quite different ways: a threshold on an argument of the current call, and a condition on what has already happened. Both clauses live in the same policy. The rule narrows an existing permission: it denies refunds over $500, and the unless clause is the exception that lifts the denial when a matching approval is on record. As in the earlier prerequisite example, the correlation on charge_id is what stops an approval for one charge from authorizing a refund on another.

Best practices

Clear and unambiguous policies result in more predictable behaviors and fewer errors, whether the implementer is a human user or an autonomous agent. As well, this improves the performance of the natural language to Dogwood authoring solution introduced earlier. Next, we review a handful of tips and best practices for constructing natural language policies.

  • Say whether you mean the attempt or the outcome. “After a transfer” is ambiguous. “After a transfer succeeds” is not. An attempt is any call the agent issued, including ones that were denied or failed. Only a completed call carries the values the tool returned. Rate limits and cumulative caps are usually about attempts, prerequisites and ordering rules about outcomes.
  • State the window. “Recently” has no translation. “Within the past 30 minutes” does. Windows look backward from the call being decided, so if a rule is meant to reset on a calendar boundary rather than slide with the clock, state that explicitly, because it requires a different control.
  • Name what the rule is keyed to. “No more than three transfers per hour” does not say whose: three by this caller, or three against this account? Both are expressible, they are different policies, and the sentence chooses neither. Wherever a rule counts, sums, or correlates, name the field that ties the events together.
  • Give the threshold and its boundary. “More than three” and “at least three” differ by one action, usually the one the rule exists to stop. The same applies to the confidence levels on content checks.
  • Review the generated Dogwood policies for correctness. Each Dogwood policy is returned alongside the sentence it came from, so that you can read the two side by side. While validation can establish that a policy is well-formed and anchored in the right schema, it doesn’t confirm that the policy says what its author meant. That judgment stays with the person who owns the document.

Knowing what can’t be enforced

While the authoring service can filter out and highlight policies that are incompatible with enforcement by Policy in AgentCore, you should also be aware of the common issues.

  • It isn’t a rule about an action. “Agents should always act in the customer’s best financial interest and exercise sound professional judgment.” There’s no condition here on any action, field, or principal. This is a real requirement, and it belongs in the agent’s instructions, its evaluations, and its training, rather than in an authorization engine.
  • It asks for an action, not a verdict. “When a dispute description contains a Social Security number, redact it before the note is stored.” A policy engine permits or denies a call. It doesn’t modify one. The neighboring rule that denies a filing containing a Social Security number is expressible and appears among the preceding examples. Redaction is a different control, applied at a different point in the pipeline.
  • It is outside what the language expresses. “Deny wire transfers on weekends and U.S. federal bank holidays.” The date and time support in Dogwood covers points in time, offsets, and differences. There is no day-of-week accessor and no holiday calendar. The Dogwood language guide sets out in detail which constructs are available, and it is worth reading through that guidance when a rule is set aside by the policy authoring service, both to confirm the gap and to see whether a nearby formulation is supported.
  • It’s outside the scope of enforcement. “A customer might initiate at most ten transfers per day, counted across all of that customer’s concurrent sessions.” Enforcement evaluates a trajectory within a session, so a cap that pools across sessions isn’t something a different phrasing can recover.

In each case the useful output is not a policy but a label indicating that it cannot be translated into Dogwood, which tells you that you should consider alternatives: rewrite it, move it to a different control, or accept that it stays a human process.

How policy authoring works

The autoformalization pipeline runs in four steps. It begins by decomposing the document. Rules written for a reader are often compound: a numbered clause frequently carries several independent obligations, and a single sentence sometimes carries two, as the preceding business-hours example does. Decomposition splits them into atomic rules, each one a statement about a specific tool or set of tools that can be enforced on its own. Each rule is then routed. A rule is either expressible in Dogwood and its constituent monitors, or it is not, and the ones that aren’t are set aside instead of translated, generally because of the four reasons in the previous section. Filtering here before translation attempt keeps an inexpressible rule from becoming a policy that validates cleanly and enforces the wrong thing. The rules that remain are autoformalized into Dogwood, anchored in the tool schema supplied alongside the document. Finally, every candidate policy is validated using the same Dogwood command-line tools that ship with the open source language. This is the part of the pipeline that isn’t a matter of judgment: the compiler is a precise and deterministic authority on whether a policy parses and whether every name in it exists in the schema. Where a candidate is rejected, its diagnostics are handed back and the rule is translated again with those errors in view, for a bounded number of rounds.

What comes out is two collections: the policies that validated for syntax and compatibility with the environment schema along with the atomic natural language rules that generated them, and the atomic natural language rules that were set aside.

Conclusion

This post demonstrated how Policy Authoring turns a written policy document into Dogwood policies. You’ve seen examples of translations covering constraints on tool arguments, prerequisites, cumulative caps, rate limits, and checks on free-form content. As well, you’ve reviewed the properties that make a natural language rule translate well: a stated window, a named subject, an explicit threshold, and a clear choice between the attempt and the outcome. Dogwood can be written directly, and teams who prefer to work in the language are welcome to keep doing so. Authoring is there for the common case where the rules already exist in prose, to shorten the path from a document you already maintain to a set of policies you can review and deploy.

To get started, see the Policy in AgentCore documentation for creating a policy engine and authoring policies from a document, and the Dogwood language guide if you would like to read or extend the generated policies yourself. For more on how these policies are interpreted and enforced at runtime, see Securing AI agents with temporal policies in Amazon Bedrock AgentCore and Introducing Dogwood: runtime verification for AI agents.

We would also like to acknowledge the remaining Applied Scientists in our team Chao Shang, Sadat Shahriar, Wanyu Du, and Devang Kulshreshtha for their contributions to this launch.


About the authors

Sandesh Swamy

Sandesh is a Senior Applied Scientist at AWS. During his 9-year tenure at Amazon, Sandesh has made key contributions to Alexa, Amazon Q Developer in chat applications, Amazon Q and Bedrock Guardrails with extensive experience in building models as well as safeguarding applications built on top of Large Language Models. He’s currently focused on Agentic safety with both stochastic and deterministic methods.

Min Bai

Min is a Senior Applied Scientist at AWS Agentic AI where he works on safety mechanisms to improve the reliability and rule following of agentic AI systems. Previously during his 5 years at AWS, Min also has worked on Ground Truth services and LLM Guardrails for multimodal use-cases.

Rui Dong

Rui is a Senior Applied Scientist at AWS Agentic AI. During her 5 years at AWS, Rui has been a key contributor to several AWS products including Amazon Kendra, Amazon Q Business, and Policy in Amazon Bedrock AgentCore.