August 23, 2026
AWSLLM Guardrails

Notebook

How to Apply Amazon Bedrock Guardrails to Only Part of an Input

This note explains how to limit the scope of Amazon Bedrock Guardrails to part of an input text by using InvokeModel input tags and Converse guardContent blocks.

Amazon BedrockAmazon Bedrock GuardrailsLLM
日本語

Guide

Contents

  1. Prerequisites
  2. Guard Only Part of the Input with InvokeModel
  3. InvokeModel Result
  4. Guard Only Part of the Input with Converse
  5. Converse Result
  6. Summary

When using Bedrock Guardrails, you may want to inspect only user input, but system-side instructions or trusted context can also become part of the evaluation target.

The trigger for this note was an issue I hit while using Claude Code Action through Bedrock. An account-level Guardrails configuration was applied to system-side instructions, and part of the system prompt was treated as prompt injection.

In that environment, selective_content_guarding.system was set to comprehensive, so not only user input but also the system prompt context was broadly evaluated. As a result, Claude Code Action could barely run.

While investigating the issue, I found that it can be avoided by making the Guardrails scope selective and explicitly marking the part of the user prompt that should be evaluated.

In a real application, a single prompt often contains several kinds of text.

  • System-side instructions
  • Trusted context retrieved by RAG or tool execution
  • Previous conversation history
  • The current user input

Sometimes you only want Guardrails to evaluate the current user input.
In Bedrock, the way to specify that scope differs by API.

APIHow to specify the guarded scope
InvokeModelUse <amazon-bedrock-guardrails-guardContent_xxx> input tags
ConverseUse a guardContent block in message content

InvokeModel and InvokeModelWithResponseStream support XML-style input tags. Converse does not use XML tags, and instead uses the guardContent field.

The official documentation covers this behavior here.

Prerequisites

This example uses a Bedrock API key and sends it as Authorization: Bearer $AWS_BEARER_TOKEN_BEDROCK.

export AWS_REGION=us-east-1
export MODEL_ID='us.anthropic.claude-haiku-4-5-20251001-v1:0'
export GUARDRAIL_ID='xxxxxxxx'
export GUARDRAIL_VERSION='DRAFT'
export AWS_BEARER_TOKEN_BEDROCK='your-api-key-here'

Set GUARDRAIL_ID to the ID of an existing Bedrock Guardrails resource.
In this example, the guardrail detects PII such as addresses and names.

I configured the guardrail to detect addresses as PII, with Mask for input and Detect for output.

Address PII input masking and output detection settings

Guard Only Part of the Input with InvokeModel

With InvokeModel, wrap the text you want to evaluate with <amazon-bedrock-guardrails-guardContent_${TAG_SUFFIX}>.
Then set the same suffix in amazon-bedrock-guardrailConfig.tagSuffix in the request body.

TAG_SUFFIX="$(openssl rand -hex 6)"
 
curl -s "https://bedrock-runtime.${AWS_REGION}.amazonaws.com/model/${MODEL_ID}/invoke" \
  -H "Authorization: Bearer ${AWS_BEARER_TOKEN_BEDROCK}" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -H "X-Amzn-Bedrock-GuardrailIdentifier: ${GUARDRAIL_ID}" \
  -H "X-Amzn-Bedrock-GuardrailVersion: ${GUARDRAIL_VERSION}" \
  -H "X-Amzn-Bedrock-Trace: ENABLED" \
  --data-binary @- <<EOF
{
  "anthropic_version": "bedrock-2023-05-31",
  "max_tokens": 512,
  "messages": [
    {
      "role": "user",
      "content": [
        {
          "type": "text",
          "text": "My company address is 1-1 Chiyoda, Chiyoda-ku, Tokyo. Please respond politely to the customer.\\nHere is the user's question.\\n\\n<amazon-bedrock-guardrails-guardContent_${TAG_SUFFIX}>I live at 2-8-1 Nishi-Shinjuku, Shinjuku-ku, Tokyo. Please tell me your company address and how to get there from my home.</amazon-bedrock-guardrails-guardContent_${TAG_SUFFIX}>"
        }
      ]
    }
  ],
  "amazon-bedrock-guardrailConfig": {
    "tagSuffix": "${TAG_SUFFIX}"
  }
}
EOF

In this example, only the text wrapped by the tag is evaluated by Guardrails:

I live at 2-8-1 Nishi-Shinjuku, Shinjuku-ku, Tokyo. Please tell me your company address and how to get there from my home.

InvokeModel Result

With this configuration, the address inside the tagged user input was anonymized as {ADDRESS}.
In the response text, the model interprets {ADDRESS} as a missing address, which shows that the input-side address was masked before being passed to the model.

{
  "content": [
    {
      "type": "text",
      "text": "# Thank you for your inquiry.\n\nOur company address is as follows:\n\n**1-1 Chiyoda, Chiyoda-ku, Tokyo, Japan**\n\nHowever, I apologize, but I'm unable to provide specific directions from your location since the address you mentioned appears to be incomplete in the information available to me."
    }
  ],
  "amazon-bedrock-guardrailAction": "INTERVENED"
}

In the Guardrails trace, the address inside the tag is marked as ANONYMIZED on the input side.

{
  "amazon-bedrock-trace": {
    "guardrail": {
      "input": {
        "<guardrail-id>": {
          "sensitiveInformationPolicy": {
            "piiEntities": [
              {
                "type": "ADDRESS",
                "match": "2-8-1 Nishi-Shinjuku, Shinjuku-ku, Tokyo",
                "action": "ANONYMIZED",
                "detected": true
              }
            ]
          },
          "invocationMetrics": {
            "guardrailCoverage": {
              "textCharacters": {
                "guarded": 122,
                "total": 247
              }
            }
          }
        }
      },
      "outputs": [
        {
          "<guardrail-id>": {
            "sensitiveInformationPolicy": {
              "piiEntities": [
                {
                  "type": "ADDRESS",
                  "match": "1-1 Chiyoda, Chiyoda-ku, Tokyo, Japan",
                  "action": "NONE",
                  "detected": true
                }
              ]
            }
          }
        }
      ],
      "actionReason": "Guardrail masked.\nNo action."
    }
  }
}

The company address outside the tag, 1-1 Chiyoda, Chiyoda-ku, Tokyo, was not ANONYMIZED on the input side. Only the user input wrapped by amazon-bedrock-guardrails-guardContent was masked.
The guardrailCoverage.textCharacters values also show that only part of the input, not the whole input, was evaluated by Guardrails.
On the output side, the company address in the model response is detected, but because the output action is Detect, the action is NONE.

tagSuffix must be 1 to 20 alphanumeric characters.
Using a fixed value can make the tag structure easier for a user to guess and abuse in prompt injection, so AWS recommends using a random value for each request.

If you want to evaluate multiple sections, you can use the same suffix multiple times.
Nested tags are not supported.

<amazon-bedrock-guardrails-guardContent_${TAG_SUFFIX}>
First guarded section
</amazon-bedrock-guardrails-guardContent_${TAG_SUFFIX}>
 
Unguarded context
 
<amazon-bedrock-guardrails-guardContent_${TAG_SUFFIX}>
Second guarded section
</amazon-bedrock-guardrails-guardContent_${TAG_SUFFIX}>

Guard Only Part of the Input with Converse

With Converse, use a guardContent block instead of XML input tags.

curl -s "https://bedrock-runtime.${AWS_REGION}.amazonaws.com/model/${MODEL_ID}/converse" \
  -H "Authorization: Bearer ${AWS_BEARER_TOKEN_BEDROCK}" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  --data-binary @- <<EOF
{
  "guardrailConfig": {
    "guardrailIdentifier": "${GUARDRAIL_ID}",
    "guardrailVersion": "${GUARDRAIL_VERSION}",
    "trace": "enabled"
  },
  "inferenceConfig": {
    "maxTokens": 512
  },
  "messages": [
    {
      "role": "user",
      "content": [
        {
          "text": "My company address is 1-1 Chiyoda, Chiyoda-ku, Tokyo. Please respond politely to the customer.\\nHere is the user's question."
        },
        {
          "guardContent": {
            "text": {
              "text": "I live at 2-8-1 Nishi-Shinjuku, Shinjuku-ku, Tokyo. Please tell me your company address and how to get there from my home."
            }
          }
        }
      ]
    }
  ]
}
EOF

In Converse, set the Guardrails ID and version in guardrailConfig, and put the content you want to evaluate inside guardContent.

When a guardContent block exists in messages, Guardrails evaluates the content inside that block.
Here, the company address in the normal text block is outside the evaluation scope, and only the user input is placed in guardContent.

Converse Result

With this configuration, the address inside guardContent was also anonymized as {ADDRESS}.

{
  "output": {
    "message": {
      "content": [
        {
          "text": "# Thank you for your inquiry.\n\nOur company address is as follows:\n\n**1-1 Chiyoda, Chiyoda-ku, Tokyo**\n\nUnfortunately, I am unable to provide specific directions from your home to our office, as the address field in your message appears to be incomplete or wasn't filled in properly."
        }
      ],
      "role": "assistant"
    }
  },
  "stopReason": "end_turn"
}

In trace.guardrail.inputAssessment, the address in the guardContent user input is marked as ANONYMIZED.

{
  "trace": {
    "guardrail": {
      "actionReason": "Guardrail masked.\nNo action.",
      "inputAssessment": {
        "<guardrail-id>": {
          "sensitiveInformationPolicy": {
            "piiEntities": [
              {
                "type": "ADDRESS",
                "match": "2-8-1 Nishi-Shinjuku, Shinjuku-ku, Tokyo",
                "action": "ANONYMIZED",
                "detected": true
              }
            ]
          },
          "invocationMetrics": {
            "guardrailCoverage": {
              "textCharacters": {
                "guarded": 122,
                "total": 245
              }
            }
          }
        }
      }
    }
  }
}

On the output side, the company address is detected, but because the output action is Detect, the action is NONE.

{
  "trace": {
    "guardrail": {
      "outputAssessments": {
        "<guardrail-id>": [
          {
            "sensitiveInformationPolicy": {
              "piiEntities": [
                {
                  "type": "ADDRESS",
                  "match": "1-1 Chiyoda, Chiyoda-ku, Tokyo",
                  "action": "NONE",
                  "detected": true
                }
              ]
            }
          }
        ]
      }
    }
  }
}

With Converse as well, the company address in the normal text block was not anonymized on the input side. Only the user input inside guardContent was masked.
Because this example uses Mask-based anonymization, stopReason is end_turn, not guardrail_intervened, and the model response is still returned.

Summary

Bedrock Guardrails lets you specify which part of an input message should be evaluated by the guardrail.
The way to specify the scope depends on the API.

  • InvokeModel uses <amazon-bedrock-guardrails-guardContent_${TAG_SUFFIX}> input tags
  • Converse uses a guardContent block in message content
  • tagSuffix should be a random value for each request

If you evaluate system-side instructions and trusted context together with user input, Guardrails can intervene in places you did not intend.
By explicitly marking only user input as the evaluation target, you can keep the necessary protection while making model calls easier to run reliably.

Related notes

Read next