Amazon Bedrock AgentCore、自然言語による Dogwood ポリシー作成
本文の状態
日本語全文を表示中
詳細モードで約18分の本文を読めます。
同じ出来事の情報源
この情報源を基点に整理
AWS Machine Learning Blog
AWS は Amazon Bedrock AgentCore の新機能として、自然言語で記述されたポリシーを Dogwood という形式言語へ自動変換する機能を追加し、時間的制約やツール呼び出しの順序制御などを容易に実装可能にした。
AI深層分析を開く2026年8月21日 09:53
AI深層分析
キーポイント
自然言語から形式言語への自動変換機能の強化
AI を活用した Policy Authoring ツールにより、自然言語で記述されたポリシー文書を構文的・意味的に正しい Dogwood 形式仕様へ自動的に変換する機能が拡充された。
時間的制約と軌道制御のサポート
レート制限、前提条件、ツール呼び出しの順序付け、累積効果など、時間軸や実行経路にわたる複雑な制約をポリシーとして定義・適用できるようになった。
Guardrails との連携によるコンテンツ検知
生成されたポリシーにおいて、自由形式テキストの意味内容から不適切なコンテンツを検出するために Amazon Bedrock Guardrails サービスを呼び出す仕組みが追加された。
ポリシー作成の適切な活用場面
Dogwood ポリシーは手書きで記述可能であり、特に既存の規則を文章から転記する作業に適している。
入力ドキュメントの要件と処理方法
ポリシー作成ツールは要約ではなく翻訳を行うため、理由や背景説明が混在する文書は事前にルール部分に整理する必要がある。
重要な引用
Policy Authoring works best when you already have rules written in prose and the work in front of you is transcription rather than design.
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.
"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."
Dogwood is default-deny and a forbid overrides a permit
編集コメントを表示
編集コメント
自然言語によるポリシー記述の自動化は、AI エージェントの安全な実装において重要なステップとなる。技術的な形式言語への翻訳を担う AI ツールの進化が、現場でのガバナンス適用をより民主化する可能性を秘めている。
Source Article
元記事を日本語で読む
本文に関係しない購読案内、埋め込み通知、サイト内プロモーションは除いています。
AI エージェントは複雑なワークフローの自動化を可能にしますが、適切な制御手段がなければ、組織の方針や規制要件に反する行動をとってしまうリスクがあります。これを解決するため、私たちは Amazon Bedrock AgentCore におけるポリシー を構築しました。これにより、Amazon Bedrock AgentCore で実行されるすべてのエージェントに対して一貫した制御を適用することが可能になります。
最近では、この機能を拡張し、時間軸にわたる制限を強制する新しい機能が追加されました。これにより、レート制限やツール呼び出しの事前条件・順序付け、累積効果などを規定するポリシーの実装がサポートされています。これらのポリシーは、オープンソースのガバナンス言語である Dogwood で記述され、AgentCore Gateway に組み込まれた Dogwood モニターによって、エージェントのアクションに対してリアルタイムで適用されます。
今回の新機能の発表に伴い、自然言語で記述されたポリシー仕様書から、構文および意味的に正しい Dogwood 形式の仕様に自動変換する AI ドライブ型のツール「Policy Authoring」の機能が拡張されました。
この新機能により、時間的制約や軌道(トラジェクトリ)制約を適用するポリシーの生成が可能になりました。また、自由記述テキストの意味内容に不適切な要素が含まれていないかを検出するために Amazon Bedrock Guardrails サービスを呼び出すポリシーや、以前のバージョンで利用可能だったツールの入力パラメータに対する制限を課すポリシーも作成できます。
技術的なバックグラウンドに関わらず、自然言語で記述されたポリシー文書を直接 Amazon Bedrock AgentCore のポリシーとしてインポートできるため、展開中のエージェントシステムを安全に保護することができます。
本稿では、この新機能の具体例を示しながら、自然言語によるポリシー構築におけるベストプラクティスの活用方法について解説します。
自然言語ポリシーから Dogwood への自動変換
Dogwood ポリシーは手書きで完全に記述することも可能です。制御項目が少数であれば、そこから始めるのは十分に理にかなったアプローチです。ポリシー作成(Policy 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 } |
file_dispute | 紛争ケースを開設する | { account: String, description: String } | { case_id: String } |
request_approval | 請求の承認を監督者に依頼する | { charge_id: String } | { approved: Bool } |
ポリシー文書と並行して、作成プロセスではツール名、受け付ける引数、および返却値という正確な情報を含むスキーマも扱われます。このスキーマは、エージェントの Model Context Protocol (MCP) ツールマニフェストから生成されるため、生成されたポリシーで参照される名称は、実際にエージェントが呼び出す名称と一致します。例えば、生成されたポリシー内の context.input.amount は、前述の表にある amount 引数を指します。
また、作成時には利用可能な Amazon Bedrock Guardrails チェックの一覧や、ポリシー内で参照が許可されるアイデンティティクレームの情報も提供されます。
同銀行のコンプライアンスチームは、既存の人事用ドキュメントフォーマットをそのまま流用し、統制ルールを文書として管理しています。以下に示す規則は、その文書から抜粋したものであり、各項目には「Policy Authoring」が生成した Dogwood ポリシーが続いています。
出力をより読みやすくするために、以下の 2 つの規約を採用しています。
Dogwood はデフォルトで拒否(deny)であり、forbid 規則は permit 規則よりも優先されます。そのため、特定の機能を許可するルールは、条件付きの permit として機能します。
制限や上限を設けるルールは、forbid として扱われます。
条件には、決定しようとしている呼び出しと、同じセッション内で既に発生した事柄の両方を評価できます。以下の例では、これら両方のケースを示します。
ポリシー翻訳の例
以下の例では、自動形式化ツールが自然言語のポリシーを Dogwood 数式に変換する方法を示しています。
ツールの引数に対する制約
返金処理は、日本時間午前 9 時から午後 5 時(UTC)の営業時間内のみ実施され、対象額は 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 };独立した要件を2つ含む一文は、両方の条件が満たされた場合にのみ返金が可能となる1つのポリシーとなります。context.input.amount は、エージェントが発行した issue_refund コールの引数であり、ツールが宣言する単位で比較されます。ドキュメントに記載された「$2,500」とツールの amount が、その単位において一致している必要があります。時間条件は、決定が行われた瞬間の時刻を読み取るものであり、いずれの条件もエージェントが以前に行った操作には依存しません。
「duration」などの時間ベースの関数に関する詳細は、Time-based policy support をご覧ください。
必須の前段階
*通話者の身元が直近 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
}
};このルールは転送リクエストのみからは確定できないため、生成された条件ではエージェントがこれまでに何を行ったかを参照します。formerly within 15m は、記述されているイベントが過去 15 分以内に一度でも発生したかどうかを問うものです。
ここでは、verified: true という結果を返した verify_identity 呼び出しの完了(呼び出しそのものではなく、::response としての結果)を示しています。
イベントパターン内では、裸の 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 時間に行われたすべての送金における amount 引数の値を集計し、その累積額が 50,000 ドルを超えた場合に現在の呼び出しを拒否します。その期間内の個々の送金は少額で目立たないものでも、この条件はそれらの合計に制約を課すものです。
また、ドキュメントがあえて留保している点にも注目してください。「transferred(転送された)」と記述されているだけで、ブロックや失敗した試行がカウントされるかどうかについては言及していません。翻訳側では ::request イベントを集計しており、エージェントが試行したすべての送金が含まれます。これは上限設定に対してより安全な解釈です。ただし、ドキュメントに明記することで推測の余地をなくせるため、これが次のベストプラクティスの主題となります。
レート制限
*エージェントは、1 時間以内に同一アカウントに対して最大 3 回までの返金処理を試行できます。
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
)
};これは先ほどのポリシーと同様の構造で、フィールドの合計ではなくイベント数をカウントします。カウント対象は、現在検討中の呼び出しで指定されたアカウントに対する返金に限定され、その呼び出し自体も含まれます。つまり、1 時間以内に 4 回目の試行が行われた場合に拒否されます。
このルールでは「attempt(試行)」と明記されているため、先ほどのポリシーとは異なり推測の余地がありません。拒否された返金や失敗した返金であっても、制限カウントに含まれます。
フリーテキストの確認
*説明に社会保障番号が含まれる紛争申し立てはすべて拒否します。
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
}
};この文は、2 つの異なる方法で検証される部分から成り立っています。1 つ目は現在の呼び出し引数に対する閾値条件、2 つ目は過去の事象に関する条件です。両方の節は同じポリシー内に含まれています。
このルールは既存の権限を制限するもので、500 ドルを超える払い戻しを拒否します。ただし unless 節には例外が定義されており、該当する承認記録が存在する場合はその拒否を取り消すことができます。前述の前提条件の例と同様、charge_id による相関付けが行われることで、ある請求に対する承認が別の請求の払い戻しを無効にすることを防いでいます。
ベストプラクティス
明確で曖昧さのないポリシーは、実行者が人間ユーザーであっても自律型エージェントであっても、より予測可能な動作をもたらすとともにエラーを減らします。また、これにより前述の自然言語から Dogwood への記述ソリューションのパフォーマンスも向上します。ここでは、自然言語によるポリシー構築のためのいくつかのヒントとベストプラクティスをご紹介します。
- 試行(attempt)か結果(outcome)かを明確にする。「転送後」という表現は曖昧です。一方、「転送が成功した後」であれば明確になります。試行とは、エージェントが発行したすべての呼び出しを指し、拒否されたものや失敗したものも含まれます。ツールが返す値を持つのは完了した呼び出しのみです。レート制限や累積上限は通常「試行」に関するものであり、前提条件や順序ルールは「結果」に関するものです。
- 適用範囲(ウィンドウ)を明示する。「最近」という表現には明確な定義がありませんが、「過去 30 分以内」であれば明確です。ウィンドウは判断対象の呼び出しから遡って設定されるため、カレンダーの境界でリセットされるべきルールである場合、時計に連動してスライドするのではなく明示的に記述する必要があります。これは異なる制御手段を必要とするからです。
- ルールの対象となる要素を特定する。「1 時間あたり最大 3 回の転送」という表現だけでは、誰に対するものかが不明確です。この呼び出し元による 3 回なのか、このアカウントに対する 3 回なのか。両方の表現は可能ですが、異なるポリシーであり、文章自体がどちらを選んでいるかを示していません。ルールがカウントや合計、相関関係を含む箇所では、イベントを結びつけるフィールド名を必ず明記してください。
閾値とその境界を明確に定義してください。「3 つ以上」と「少なくとも 3 つ」は 1 つの行動の違いであり、通常はそのルールが阻止しようとする行為に関わります。同様に、コンテンツチェックにおける信頼度のレベルについても注意が必要です。
生成された Dogwood ポリシーの正確性をレビューしてください。各 Dogwood ポリシーには、その出典となった文が併記されるため、両者を並べて確認できます。バリデーションによってポリシーが構文上正しく、適切なスキーマに紐付いていることは保証されますが、それが著者の意図を正しく反映しているかまで検証するものではありません。その判断は、ドキュメントの所有者が行うべきです。
強制できないことを理解する
作成サービスでは、AgentCore の Policy で強制することができないポリシーをフィルタリングしてハイライト表示できますが、よくある問題点も把握しておく必要があります。
- 特定の行動に関するルールではない。「エージェントは常に顧客の最善の金融利益のために行動し、健全な専門的判断を下すべきである」といった記述です。ここでは特定の行動、フィールド、または主体に対する条件が設定されていません。これは重要な要件であり、認可エンジンに含めるのではなく、エージェントの指示や評価、トレーニングに明記すべき内容です。
このポリシーは、判断を下すのではなく「アクション」を要求するものです。例えば、「紛争の記述に社会保障番号が含まれている場合は、記録前にそれを削除してください」といった指示です。ポリシーエンジンが呼び出しを許可または拒否するのは事実ですが、それ自体を変更することはありません。社会保障番号を含む申請書を拒否するという隣接ルールは明確に表現可能であり、前述の例にも含まれています。一方、データ削除(redaction)は異なる制御手段であり、パイプライン上の別のポイントで適用されるものです。
また、この言語が表現できる範囲を超えた要求も存在します。「土曜日および米国連邦銀行祝日に送金取引を拒否する」といったルールです。Dogwood の日付・時刻サポートは特定の時点やオフセット、差分を扱うことはできますが、「曜日を取得する機能」や「祝日カレンダー」は用意されていません。Dogwood 言語のガイドには利用可能な構文が詳細に記されており、ポリシー作成サービスによってルールが無効とされた際には、そのギャップを確認し、代替となる表現が可能かを確認するために必ず参照すべきです。
さらに、適用範囲の外にある要求もあります。「顧客は 1 日あたり最大 10 件の送金を行うことができる。これはすべての同時セッションを合算してカウントする」といったルールです。Dogwood の適用(enforcement)評価は、あくまで 1 つのセッション内の軌跡を対象とするため、複数のセッションにまたがる上限値のような要件は、別の表現方法では再現できません。
いずれの場合も、有用な出力はポリシーそのものではなく、「Dogwood への変換ができない」ことを示すラベルです。これは、代替手段を検討する必要があることを意味します。具体的には、記述の書き直し、別の制御項目への移動、あるいは人間による処理として継続することの受容が考えられます。
ポリシー作成の仕組み
自動形式化パイプラインは、4 つのステップで実行されます。まず文書を「分解(decompose)」することから始まります。読者向けに書かれたルールは複合的な場合が多く、番号付き条項には複数の独立した義務が含まれていることがよくあります。また、1 つの文に 2 つの義務が込められていることもあります。前述の営業時間に関する例のようにです。
分解により、各条項は原子レベルのルールへと分割されます。これは特定のツール、あるいはツールのセットに対して個別に適用可能なステートメントです。次に、これらのルールを「ルーティング(routed)」します。Dogwood とその構成要素であるモニターで表現可能かどうかが判断され、不可能なものは翻訳せずに一旦保留とされます。保留される主な理由は、前節で述べた 4 つの要因によるものです。
ここで翻訳を試みる前にフィルタリングを行うことで、表現できないルールが「正しく検証されても誤ったものを強制する」ようなポリシーになるのを防ぎます。残されたルールは、文書と共に提供されたツールスキーマを基盤として Dogwood へと「自動形式化(autoformalized)」されます。
最後に、生成されたすべての候補ポリシーは、オープンソース言語に同梱されている Dogwood コマンドラインツール を用いて検証されます。これは判断を要する部分ではなく、コンパイラが厳密かつ決定論的な権威として機能します。つまり、ポリシーが構文解析可能か、そしてその中のすべての名前がスキーマ内に存在するかを判定するのはコンパイラの役割です。
候補が却下された場合、その診断情報が返され、そのエラーを踏まえてルールは再度翻訳されます。このプロセスは所定のラウンド数まで繰り返されます。
原文を表示
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.
A constraint on a tool’s arguments
*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.
<
関連記事
今日のまとめ
AIデイリーブリーフで今日の重要ニュースをまとめ読み