【核心主旨】:Guardrails(安全护栏)是 AI Agent 系统中不可或缺的“防波堤”,以中间件(Middleware)的形式串联在 Agent 运行管线的各个生命周期中,提供输入过滤、数据脱敏、高危工具人工审批及输出合规质检,构建纵深防御体系。
一、 为什么 Agent 需要 Guardrails?
在生产级应用中,单纯依赖 Prompt(系统提示词)来约束大模型是脆弱且不可靠的(极易受越狱、提示词注入、幻觉或复杂上下文漂移影响)。Guardrails 将安全合规逻辑从 Prompt 中抽离,通过工程化的中间件流水线(Pipeline)实施强制控制。
1. 核心防护维度
- 输入端拦截:过滤提示词注入(Prompt Injection)、非法恶意意图、违禁关键词。
- 隐私保护(PII):自动掩码脱敏身份证、邮箱、银行卡号、API Key 等敏感隐私。
- 高危工具审计(HITL):对转账、删库、外部邮件群发等操作引入人工确认(Human-in-the-Loop)。
- 输出端合规质检:杜绝敏感信息漏出,自动注入行业合规声明(如医疗/金融/法律免责声明)。
2. 两种实现思路对比
| 维度 | 确定性护栏(Deterministic) | 模型驱动护栏(Model-Based) |
|---|---|---|
| 底层原理 | 正则匹配、关键词字典、AST 语法树分析、规则引擎 | 引入轻量级小模型或专用分类器进行语义判断 |
| 执行速度 | 极快(毫秒级,纯 CPU 计算) | 较慢(依赖网络请求与模型推理延时) |
| 调用成本 | 零成本(无 Token 开销) | 有额外 API 调用费用 |
| 语义理解 | 差(只能机械匹配,容易被同义词/混淆字符绕过) | 强(能够理解隐晦越狱、语境讽刺与复杂意图) |
| 推荐阶段 | 放置在最前置链路,进行第一道低成本粗筛 | 放置在深度审查环节或复杂输出质检环节 |
二、 确定性 vs 模型驱动基础实现
1. 确定性拦截(正则与关键词匹配)
def deterministic_guardrail(text: str) -> bool:
"""命中黑名单关键词则阻断,返回 True"""
banned_keywords = ["hack", "exploit", "malware", "bomb"]
return any(kw in text.lower() for kw in banned_keywords)
test_inputs = [
"How do I hack into a database?",
"What is the capital of France?",
"Explain how malware spreads"
]
for inp in test_inputs:
status = "🚫 BLOCKED" if deterministic_guardrail(inp) else "✅ ALLOWED"
print(f"{status}: {inp}")
2. 模型驱动语义判决(LLM 作为裁判)
from langchain_groq import ChatGroq
model = ChatGroq(model="openai/gpt-oss-20b", temperature=0)
def model_based_guardrail(text: str) -> str:
"""使用轻量模型评估内容安全性,仅返回 SAFE 或 UNSAFE"""
prompt = f"""Is the following user input safe to process?
Reply with only 'SAFE' or 'UNSAFE'.
Input: {text}"""
result = model.invoke([{"role": "user", "content": prompt}])
return result.content.strip()
for inp in test_inputs:
verdict = model_based_guardrail(inp)
status = "🚫 UNSAFE" if "UNSAFE" in verdict else "✅ SAFE"
print(f"{status}: {inp}")
三、 内置 PII 隐私信息检测与脱敏中间件
LangChain 提供了开箱即用的 PIIMiddleware,可自动针对输入或输出流中的个人隐私数据进行脱敏。
1. 支持类型与处理策略
- 支持实体:
email(邮箱)、credit_card(信用卡)、ip(IP 地址)、mac_address、url及自定义正则(如api_key)。 - 处理策略(Strategy):
redact:替换为占位符(如[REDACTED_EMAIL])mask:掩码打码(如****-****-****-5100)hash:哈希单向加密block:检测到时立即抛出异常阻断
2. 代码实现
from langchain.agents import create_agent
from langchain.agents.middleware import PIIMiddleware
from langchain_core.tools import tool
@tool
def customer_lookup(query: str) -> str:
"""查询客户信息"""
return f"Customer record found for query: {query}"
agent = create_agent(
model=model,
tools=[customer_lookup],
middleware=[
# 1. 邮箱进行替换脱敏
PIIMiddleware("email", strategy="redact", apply_to_input=True),
# 2. 信用卡进行末尾保留掩码
PIIMiddleware("credit_card", strategy="mask", apply_to_input=True),
# 3. 检测到 API Key 时直接异常阻断
PIIMiddleware("api_key", detector=r"sk-[a-zA-Z0-9]{32}", strategy="block", apply_to_input=True),
],
)
四、 内置 Human-in-the-Loop(人在回路审批)
对于带有副作用或高风险的工具调用,通过 HumanInTheLoopMiddleware 配合 Checkpointer 实现执行暂停与状态等待。
1. 核心流程
- 中断挂起:Agent 生成 ToolCall 时,中间件捕获该请求并触发
Interrupt,返回会话当前快照。 - 外部介入:人工审核人员查看调用参数并决定批准(
approve)、编辑修改(edit)或驳回(reject)。 - 会话恢复:通过传入带有相同
thread_id的Command(resume=...)恢复图流转。
2. 代码实现
from langchain.agents import create_agent
from langchain.agents.middleware import HumanInTheLoopMiddleware
from langgraph.checkpoint.memory import InMemorySaver
from langgraph.types import Command
from langchain_core.tools import tool
@tool
def search_web(query: str) -> str:
"""搜索公开信息"""
return f"Search results for: {query}"
@tool
def send_email(to: str, subject: str, body: str) -> str:
"""发送外部邮件(高危)"""
return f"Email sent to {to} with subject: {subject}"
@tool
def delete_records(table: str, condition: str) -> str:
"""删除数据库记录(高危)"""
return f"Deleted records from {table} where {condition}"
# 组装人在回路 Agent(必须提供 checkpointer 支撑中断恢复)
hitl_agent = create_agent(
model=model,
tools=[search_web, send_email, delete_records],
middleware=[
HumanInTheLoopMiddleware(
interrupt_on={
"send_email": True, # 需要人工确认
"delete_records": True, # 需要人工确认
"search_web": False, # 低风险,直接放行
}
),
],
checkpointer=InMemorySaver(),
)
3. 中断触发与恢复决策
config = {"configurable": {"thread_id": "session_001"}}
# 步骤 1:触发调用,Agent 在调用 send_email 前自动挂起
result = hitl_agent.invoke(
{"messages": [{"role": "user", "content": "Send an email to team@company.com about the Q4 results"}]},
config=config
)
# 步骤 2A:人工审批通过,恢复执行
approved = hitl_agent.invoke(
Command(resume={"decisions": [{"type": "approve"}]}),
config=config
)
# 步骤 2B:或者人工驳回
# rejected = hitl_agent.invoke(
# Command(resume={"decisions": [{"type": "reject", "reason": "参数不合规,暂缓执行"}]}),
# config=config
# )
五、 自定义前置护栏(Before-Agent Hook)
利用 before_agent 钩子在 Agent 完全不消耗任何 LLM Token 的情况下完成快速粗筛,拦截违规请求。
from typing import Any
from langchain.agents.middleware import AgentMiddleware, AgentState, hook_config
from langgraph.runtime import Runtime
class ContentFilterMiddleware(AgentMiddleware):
"""
确定性前置输入拦截器:
命中黑名单关键词直接短路跳转到 end,完全不产生 LLM 调用开销
"""
def __init__(self, banned_keywords: list[str]):
super().__init__()
self.banned_keywords = [kw.lower() for kw in banned_keywords]
@hook_config(can_jump_to=["end"])
def before_agent(self, state: AgentState, runtime: Runtime) -> dict[str, Any] | None:
if not state["messages"]:
return None
first_message = state["messages"][0]
if first_message.type != "human":
return None
content = first_message.content.lower()
for keyword in self.banned_keywords:
if keyword in content:
print(f"🚫 Blocked — keyword detected: '{keyword}'")
return {
"messages": [{
"role": "assistant",
"content": "抱歉,您的请求包含不适宜的内容,已被系统拦截。"
}],
"jump_to": "end"
}
return None
六、 自定义后置护栏(After-Agent Hook)
利用 after_agent 钩子拦截 Agent 生成的最终回复,在返回给终端用户前进行质量审查与安全兜底。
from langchain_core.messages import AIMessage
class SafetyGuardrailMiddleware(AgentMiddleware):
"""
模型驱动后置质检护栏:
使用轻量判决模型核验生成文本安全性,若不安全则替换为保底安全文案
"""
def __init__(self, check_model):
super().__init__()
self.safety_model = check_model
@hook_config(can_jump_to=["end"])
def after_agent(self, state: AgentState, runtime: Runtime) -> dict[str, Any] | None:
if not state["messages"]:
return None
last_message = state["messages"][-1]
if not isinstance(last_message, AIMessage):
return None
safety_prompt = f"""Evaluate if this AI response is safe and appropriate for users.
Respond with only 'SAFE' or 'UNSAFE'.
Response to evaluate:
{last_message.content}"""
result = self.safety_model.invoke([{"role": "user", "content": safety_prompt}])
if "UNSAFE" in result.content.upper():
print("⚠️ Output flagged as UNSAFE — replacing with safe fallback")
last_message.content = "非常抱歉,该回答未通过安全合规检测,无法直接提供,请调整提问或联系人工支持。"
return None
七、 多层组合防御管线(Layered Guardrails)
在生产环境中,多个中间件按顺序组合到 middleware 列表中,形成由粗到精、由快到慢的严密防御链路:
graph TD
A[用户输入] --> B[Layer 1: ContentFilterMiddleware<br/>确定性前置输入过滤/0成本]
B --> C[Layer 2: PIIMiddleware input<br/>输入敏感隐私脱敏]
C --> D[Layer 3: HumanInTheLoopMiddleware<br/>高危工具调用人工审批]
D --> E[Layer 4: PIIMiddleware output<br/>输出敏感隐私脱敏]
E --> F[Layer 5: SafetyGuardrailMiddleware<br/>模型级最终输出质检]
F --> G[安全合规响应]
完整装配代码
production_agent = create_agent(
model=model,
tools=[search_tool, send_email_tool],
middleware=[
# 1. 确定性输入拦截(零 Token 成本优先阻断)
ContentFilterMiddleware(banned_keywords=["hack", "exploit", "malware"]),
# 2. 输入信用卡号掩码
PIIMiddleware("credit_card", strategy="mask", apply_to_input=True),
# 3. 高危工具调用中断人工审批
HumanInTheLoopMiddleware(interrupt_on={"send_email_tool": True, "search_tool": False}),
# 4. 输出邮箱脱敏(防止模型内部吐露敏感信息)
PIIMiddleware("email", strategy="redact", apply_to_output=True),
# 5. 输出安全模型终审
SafetyGuardrailMiddleware(check_model=model),
],
checkpointer=InMemorySaver(),
)
八、 生产级实战案例:医疗问诊助理
在医疗问诊场景中,系统需要同时满足:
- 违禁阻断:自残、毒品制造、武器等输入彻底阻断,并提供急救指引。
- 患者隐私:电子病历中出现的邮箱与银行卡自动脱敏。
- 动作风控:预约挂号属于现实动作,必须由人工审核通过。
- 合规提示:任何输出均需强制附带医疗免责声明。
from typing import Any
from langchain.agents.middleware import AgentMiddleware, AgentState, hook_config, PIIMiddleware, HumanInTheLoopMiddleware
from langgraph.runtime import Runtime
from langchain.agents import create_agent
from langchain_core.tools import tool
from langgraph.checkpoint.memory import InMemorySaver
from langchain_core.messages import AIMessage
# 1. 医疗合规前置输入过滤器
class HealthcareSafetyFilter(AgentMiddleware):
BLOCKED_TOPICS = ["drug synthesis", "self-harm", "suicide method", "weapon", "hack"]
@hook_config(can_jump_to=["end"])
def before_agent(self, state: AgentState, runtime: Runtime) -> dict[str, Any] | None:
if not state["messages"] or state["messages"][0].type != "human":
return None
content = state["messages"][0].content.lower()
for topic in self.BLOCKED_TOPICS:
if topic in content:
return {
"messages": [{
"role": "assistant",
"content": "我是健康咨询助手,仅提供通用健康咨询与就医建议。如果您正处于紧急危机状态,请立即拨打 120/110 或当地紧急求助电话。"
}],
"jump_to": "end"
}
return None
# 2. 医疗免责声明后置追加器
class MedicalOutputValidator(AgentMiddleware):
DISCLAIMER = "\n\n⚕️ *免责声明:以上内容仅供健康科普参考,不能替代执业医师的面诊与处方,如有身体不适请及时就医。*"
@hook_config(can_jump_to=["end"])
def after_agent(self, state: AgentState, runtime: Runtime) -> dict[str, Any] | None:
if not state["messages"]:
return None
last_message = state["messages"][-1]
if isinstance(last_message, AIMessage):
if "免责声明" not in last_message.content:
last_message.content += self.DISCLAIMER
return None
# 3. 业务工具定义
@tool
def search_symptoms(symptoms: str) -> str:
"""查询症状常规常识"""
return f"针对【{symptoms}】的常规科普信息..."
@tool
def book_appointment(patient_name: str, date: str, doctor: str) -> str:
"""预约专家号(涉及系统写入,属于高危操作)"""
return f"已成功为 {patient_name} 预约 {date} 的 {doctor} 医生门诊"
@tool
def get_medication_info(medication: str) -> str:
"""查询药品基本信息与用法须知"""
return f"关于药品【{medication}】的说明书摘要..."
# 4. 构建医疗机器人
healthcare_bot = create_agent(
model=model,
tools=[search_symptoms, book_appointment, get_medication_info],
middleware=[
HealthcareSafetyFilter(), # 护栏 1: 危险意图前置过滤
PIIMiddleware("email", strategy="redact", apply_to_input=True), # 护栏 2: 隐私邮箱脱敏
PIIMiddleware("credit_card", strategy="mask", apply_to_input=True),
HumanInTheLoopMiddleware(interrupt_on={ # 护栏 3: 挂号操作人工审批
"book_appointment": True,
"search_symptoms": False,
"get_medication_info": False
}),
MedicalOutputValidator(), # 护栏 4: 强制追加合规免责声明
],
checkpointer=InMemorySaver(),
system_prompt="你是一位专业、富有同理心的医疗健康咨询助理。请根据用户需求提供科学建议,并提醒就医。"
)
九、 核心总结与生产工程守则
- Guardrails 即 Middleware:在现代 Agent 架构中,护栏应始终以插件化中间件的方式解耦组织,而不是混杂在 Prompt 或业务工具函数中。
- 先确定性规则,后模型审查(Cheap Checks First):
- 永远将零成本的确定性过滤(正则表达式、敏感词库)放在最前端;
- 过滤 80% 的初级探测与恶意攻击,只有合法合规的输入才放行进入 LLM 推理环节。
- 人在回路(HITL)的核心前提是持久化存储:
- 任何涉及中断等待的操作都必须配备可靠的
checkpointer(开发测试用InMemorySaver,生产环境切为 Postgres 或 Redis 状态仓库)。
- 任何涉及中断等待的操作都必须配备可靠的
- 进出双向脱敏:
- 输入脱敏保障下游大模型厂商不接触企业隐私;
- 输出脱敏防止大模型记忆诱导攻击与隐私外泄。