| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288 |
- ---
- title: "Input Guardrails"
- description: "Validate incoming messages before they reach the agent."
- icon: "arrow-right-to-bracket"
- ---
- Input guardrails validate incoming messages **before** they reach the agent. They screen both user input and inter-agent communication.
- ## Simplified Input Processing
- Agency Swarm automatically extracts text content from messages, so your guardrails receive clean text instead of full message objects.
- ## Function Signature
- Each input guardrail receives three parameters:
- ```python
- from agency_swarm import Agent, GuardrailFunctionOutput, RunContextWrapper, input_guardrail
- @input_guardrail
- async def my_input_guardrail(
- context: RunContextWrapper,
- agent: Agent,
- user_input: str | list[str],
- ) -> GuardrailFunctionOutput:
- """Validate user input."""
- return GuardrailFunctionOutput(output_info="", tripwire_triggered=False)
- ```
- **Parameters:**
- - `context`: Run context wrapper with access to shared state.
- - `agent`: The Agent instance receiving the input.
- - `user_input`: Extracted text content.
- - Single message: a string containing the message content.
- - Multiple consecutive messages: a list of strings, one per message.
- **Return:**
- - `GuardrailFunctionOutput` with:
- - `tripwire_triggered` (bool): `True` if validation failed.
- - `output_info` (str): Guidance message returned to the caller.
- <Note>
- File and image inputs inside messages are not passed to input guardrails.
- </Note>
- ## Input Types
- When a user sends multiple messages:
- ```json
- [
- {"role": "user", "content": "Hi"},
- {"role": "user", "content": "How are you?"}
- ]
- ```
- Your guardrail receives:
- ```python
- ["Hi", "How are you?"]
- ```
- This allows you to process each new input message individually or validate them as a group.
- ## Basic Input Guardrail
- ```python
- from agency_swarm import Agent, GuardrailFunctionOutput, RunContextWrapper, input_guardrail
- @input_guardrail
- async def require_task_prefix(
- context: RunContextWrapper, agent: Agent, user_input: str | list[str]
- ) -> GuardrailFunctionOutput:
- text = user_input if isinstance(user_input, str) else " ".join(user_input)
- blocked = not text.startswith("Request:")
- return GuardrailFunctionOutput(
- output_info="Prefix your request with 'Request:' describing what you need." if blocked else "",
- tripwire_triggered=blocked,
- )
- agent = Agent(
- name="CustomerSupportAgent",
- instructions="You are a helpful customer support agent.",
- input_guardrails=[require_task_prefix],
- )
- ```
- ## Practical Example: Filtering Off-Topic Questions
- Use input guardrails to keep agents focused on their domain. This example delegates relevance decisions to an evaluator agent:
- ```python
- from agency_swarm import (
- Agent,
- GuardrailFunctionOutput,
- ModelSettings,
- Reasoning,
- RunContextWrapper,
- input_guardrail,
- )
- from pydantic import BaseModel
- class RelevanceDecision(BaseModel):
- is_relevant: bool
- reason: str
- guardrail_agent = Agent(
- name="GuardrailAgent",
- instructions=(
- "You screen incoming messages for a customer-support assistant. "
- "Treat questions about account access, billing, and troubleshooting as relevant. "
- "Flag any unrelated requests as irrelevant."
- ),
- model="gpt-5.4-mini",
- model_settings=ModelSettings(reasoning=Reasoning(effort="low")),
- output_type=RelevanceDecision,
- )
- @input_guardrail
- async def require_support_topic(
- context: RunContextWrapper, agent: Agent, user_input: str | list[str]
- ) -> GuardrailFunctionOutput:
- candidate = user_input if isinstance(user_input, str) else "\n".join(user_input)
- guardrail_result = await guardrail_agent.get_response(candidate, context=context.context)
- decision = RelevanceDecision.model_validate(guardrail_result.final_output)
- if not decision.is_relevant:
- return GuardrailFunctionOutput(
- output_info="Only support questions are allowed. Ask about billing, account access, or troubleshooting.",
- tripwire_triggered=True,
- )
- return GuardrailFunctionOutput(output_info="", tripwire_triggered=False)
- support_agent = Agent(
- name="CustomerSupportAgent",
- instructions="You help customers resolve account, billing, and troubleshooting issues.",
- model="gpt-5.4-mini",
- input_guardrails=[require_support_topic],
- raise_input_guardrail_error=False, # Non-strict mode: guidance returned as assistant message
- )
- ```
- See the full example at [`examples/guardrails_input.py`](https://github.com/VRSEN/agency-swarm/blob/main/examples/guardrails_input.py).
- ## Non-strict vs Strict Mode
- Input guardrails support two modes that control how guidance is delivered. Use `raise_input_guardrail_error` to control this behavior.
- ### Non-strict Mode (Default)
- **Setting:** `raise_input_guardrail_error=False`
- In non-strict mode, guardrail guidance flows naturally as assistant output:
- - Guidance is returned as `final_output` (non-streaming) or `message_output_created` event (streaming).
- - No exception is raised.
- - Guidance persists as an assistant message with `message_origin="input_guardrail_message"`.
- ### Strict Mode
- **Setting:** `raise_input_guardrail_error=True`
- In strict mode, guardrail failures abort the turn immediately:
- - `InputGuardrailTripwireTriggered` is raised.
- - Guidance persists as a system message with `message_origin="input_guardrail_error"`.
- - The turn is aborted before the agent processes input.
- - The caller must handle the exception.
- <Accordion title="Strict mode usage example">
- ```python
- from agency_swarm import Agent, InputGuardrailTripwireTriggered
- agent = Agent(
- name="CustomerSupportAgent",
- instructions="You are a helpful customer support agent.",
- input_guardrails=[require_task_prefix],
- raise_input_guardrail_error=True,
- )
- try:
- response = await agency.get_response("Hello!")
- except InputGuardrailTripwireTriggered as exc:
- print(f"Validation failed: {exc.guardrail_result.output_info}")
- ```
- </Accordion>
- ### Comparison Table
- | Mode | `raise_input_guardrail_error` | Caller sees | Persisted entry | Role | Use case |
- |------|-------------------------------|-------------|-----------------|------|----------|
- | **Non-strict** | `False` (default) | Guardrail text as `final_output` or streaming event | Assistant message (`input_guardrail_message`) | `assistant` | Conversational flows, helpful guidance |
- | **Strict** | `True` | `InputGuardrailTripwireTriggered` exception | System message (`input_guardrail_error`) | `system` | Hard requirements, compliance, security |
- <Accordion title="Should I use non-strict or strict mode?">
- **Use non-strict mode when:**
- - You want a conversational user experience.
- - Agents are communicating with each other internally.
- - Guardrail feedback is helpful guidance, not a hard block.
- - You do not want to write exception handling code.
- **Use strict mode when:**
- - You are enforcing non-negotiable requirements.
- - Security or compliance rules must block processing.
- - You want explicit control over error handling.
- - The caller should know immediately that validation failed.
- </Accordion>
- <Accordion title="Streaming behavior example">
- ```text
- RunItemStreamEvent(
- name='message_output_created',
- item=MessageOutputItem(
- raw_item=ResponseOutputMessage(
- id='msg_input_guardrail_guidance',
- content=[ResponseOutputText(text="Prefix your request...")],
- role='assistant',
- status='completed'
- )
- )
- )
- ```
- </Accordion>
- ## Guardrails in Message History
- Each guardrail trigger is recorded in chat history with a guidance entry. Every entry carries `message_origin` to identify which guardrail fired.
- For most use cases, `role`, `content`, and `message_origin` are enough. Additional metadata is mainly for tracing multi-agent runs.
- ### Message Origin Values
- - `input_guardrail_message`: Input guardrail in non-strict mode.
- - `input_guardrail_error`: Input guardrail in strict mode.
- - `output_guardrail_error`: Output guardrail failure (always a system message).
- ### Persistence Behavior
- | Mode | `raise_input_guardrail_error` | Streaming Event | Persisted Entry |
- |------|-------------------------------|-----------------|-----------------|
- | **Non-strict** | `False` (default) | `message_output_created` with guidance text | Assistant message, `message_origin="input_guardrail_message"` |
- | **Strict** | `True` | `{"type": "error", "content": guidance}` | System message, `message_origin="input_guardrail_error"` |
- <Note>
- `validation_attempts` does not apply to input guardrails. Input guardrails trigger immediately on validation failure.
- </Note>
- ### Message History After Guardrails Trip
- When an input guardrail trips, agent-to-agent request messages remain in history alongside guardrail guidance. This preserves context so calling agents can adjust their approach.
- Output guardrail messages also persist in history to guide retry attempts.
- <Accordion title="Example message history entries (illustrative)">
- ```json
- [
- {
- "role": "assistant",
- "content": "Please, prefix your request with 'Support:' describing what you need.",
- "message_origin": "input_guardrail_message",
- "agent": "CustomerSupportAgent"
- },
- {
- "role": "assistant",
- "content": "When chatting with this agent, provide your name first.",
- "message_origin": "input_guardrail_message",
- "agent": "DatabaseAgent",
- "callerAgent": "CustomerSupportAgent"
- },
- {
- "role": "system",
- "content": "Do not include email addresses in your response.",
- "message_origin": "output_guardrail_error",
- "agent": "DatabaseAgent",
- "callerAgent": "CustomerSupportAgent"
- }
- ]
- ```
- </Accordion>
- ## Internal Agent Communication
- For many agent-to-agent flows, non-strict mode (`raise_input_guardrail_error=False`) is easier to work with because guidance is returned inline instead of raising exceptions mid-chain.
- <Warning>
- Due to the nature of handoffs, using `Handoff` for agent-to-agent communication can bypass input guardrails between agents.
- </Warning>
|