A travel assistant should not answer a question about football results or offer hotel advice for a region its data does not cover. A guardrail is an application-level check that keeps an agent within its intended scope and policies. It inspects a request or response at a chosen workflow stage and, when a check fails, changes the normal path—for example, by refusing the request or asking for clarification. section 14.2 section 14.2.1
Choose the check and its location together:
- At the router, classify the user's latest question before sending it to a travel or booking agent. Accept travel topics; send unrelated questions directly to a polite refusal path. This avoids unnecessary downstream work, though classification itself still uses a lightweight LLM. section 14.2.1
- At each agent, apply a narrower check before its main model call. The Cornwall travel and booking agents accept only Cornwall-related travel questions, even if someone calls an agent directly rather than through the router. section 14.2.2
- After generation, consider checking answers before delivery or before a downstream action. Possible checks include outdated attractions, sensitive contact details, tone, and the format of booking information; these are proposed extensions, not part of the demonstrated input filters. section 14.3.3
Listing 14.5 Restricting questions to travel-related topics
class GuardrailDecision(BaseModel): #1
is_travel: bool = Field(
...,
description=(
"""True if the user question is about travel information:
destinations, attractions,
lodging (hotels/BnBs), prices, availability,
or weather in Cornwall/England."""
),
)
reason: str = Field(...,
description="Brief justification for the decision.")
GUARDRAIL_SYSTEM_PROMPT = ( #2
"""You are a strict classifier. Given the user's
last message, respond with whether it is
travel-related. Travel-related queries
include destinations, attractions, lodging (hotels/BnBs),
room availability, prices, or weather in Cornwall/England."""
)
REFUSAL_INSTRUCTION = ( #3
"""You can only help with travel-related questions
(destinations, attractions, lodging, prices,
availability, or weather in Cornwall/England).
The user's request is not travel-related.
Politely refuse and briefly explain what
topics you can help with."""
)
llm_guardrail = llm_model.with_structured_output(
GuardrailDecision) #4
The router policy gives the classifier explicit travel categories, returns a structured decision with a reason, and defines the refusal instruction. Defining scope first makes the routing decision testable. section 14.2.1
Listing 14.9 Agent-level guardrail function
def pre_model_guardrail(state: dict):
messages = state.get("messages", [])
last_msg = messages[-1] if messages else None
if not isinstance(last_msg, HumanMessage): #1
return {}
user_input = last_msg.content
classifier_messages = [ #2
SystemMessage(content=AGENT_GUARDRAIL_SYSTEM_PROMPT),
HumanMessage(content=user_input),
]
decision = llm_guardrail.invoke(classifier_messages)
if decision.is_travel: #3
# Allow normal flow; do not modify inputs
return {}
return {"llm_input_messages":
[SystemMessage(content=AGENT_REFUSAL_INSTRUCTION),
*messages]} #4
The agent-level function checks that the latest message is from the user, classifies it, and leaves valid input unchanged. On rejection, it supplies a refusal instruction through the pre-model hook rather than taking the router's early-exit path. section 14.2.2
Two different boundaries
“Can you give me the latest results of Inter Milan?” fails the travel router check and receives a travel-only refusal. “Can you give me some travel tips for Liverpool?” is travel-related but fails the narrower Cornwall agent check. The second check matters because the travel agent's vector store contains Cornwall data only. section 14.2.1 section 14.2.2
Guardrails can use explicit rules, approved-source checks, or classification and moderation models. Layering them helps catch different failures: an entry check limits irrelevant requests, an agent check protects a component reused elsewhere, and an output check can catch a plausible-looking but unsuitable answer. A pre-model check cannot, by itself, verify what the model eventually says. section 14.2 section 14.3.3













