上一篇我们介绍了 Human-in-the-loop,它解决的是很具体的问题:当 Agent 准备执行高风险操作(如退款、发送邮件、修改数据等)时,可以暂停执行,把最终决定交给人工。
如果继续深入 Agent 安全性,会发现 HITL 只是 Agent 安全控制中的一种方式。用户输入中的敏感信息、模型生成的内容、Tool 的参数,以及哪些操作允许继续执行,都需要有对应的控制边界。
这些边界可以统称为 Guardrails。
在 LangChain 中,Guardrails 主要通过 Middleware 插入 Agent 的不同执行阶段,对输入、输出和工具行为进行检查、限制或拦截。本文重点介绍 Guardrails 的主要类型、应该放在哪里,以及实际项目中怎样组合使用。
一、Guardrails介绍
Guardrails 的字面含义是护栏,可以理解为运行在 Agent 执行过程中的安全检查与约束机制。它关注的是 Agent 从接收请求到返回结果的整个过程中,哪些内容能够通过、哪些操作能够执行,以及出现风险时应该怎样处理。
常见场景包括:
- 防止个人敏感信息泄露;
- 检测异常或恶意输入;
- 拦截不合适的模型输出;
- 执行业务规则和合规要求;
- 限制高风险工具调用。
LangChain 主要通过 Middleware 实现这些 Guardrails。因为 Middleware 可以进入 Agent 执行过程中的不同位置,以实现在相应位置进行安全检查与控制。
例如:
用户请求
↓
输入检查
↓
PII 脱敏
↓
模型推理
↓
工具调用检查
↓
模型最终回答
↓
输出检查
↓
返回用户
这里每一个检查点,都可以看作一道 Guardrail。风险发生在哪里,就应该尽可能在靠近风险的位置设置边界。
有些安全要求当然也可以写进 System Prompt,但 Prompt 更适合告诉模型“应该怎么做”;对于权限、隐私和高风险操作这类不能被绕过的规则,更适合在代码和 Middleware 层进行限制。
二、两类Guardrails
根据判断方式不同,Guardrails 可以分成确定性 Guardrails 和模型型 Guardrails。
确定性 Guardrails 使用普通程序逻辑完成判断,例如:
正则表达式
关键词匹配
权限判断
金额阈值
白名单
参数校验
比如:
if user.role != "admin":
reject_operation()
if amount > 1000:
require_review()
这种方式最大的特点是结果可预测。
相同输入通常会得到相同结果,而且执行快,不需要额外调用模型。邮箱、信用卡号、用户权限、金额阈值,都很适合这种方式。
确定性 Guardrails 不适合复杂语义。
例如:
帮我设计一种绕过公司内部审核流程的方法。
真正需要判断的是意图,而不是某几个固定关键词。
另一个 Guardrials 是模型型Guardrails,这类 Guardrail 会调用 LLM 或分类模型进行语义判断,例如:
这段回答是否符合金融合规规则?
这个请求是否包含隐蔽的恶意意图?
最终回复是否泄露内部信息?
它更适合复杂语义,但会增加模型调用、延迟和成本,而且结果仍然具有概率性。
在实际项目中可以遵循一个简单原则:能通过代码明确判断的,不交给模型;只有规则难以描述时,再使用模型判断。
三、Guardrails放在哪里
前面的文章中,我们多次提到 Agent 通常运行在一个循环中:
用户输入
↓
模型调用
↓
模型决定是否使用工具
↓
工具执行
↓
结果返回模型
↓
继续推理
↓
最终回答
Guardrails 要控制这个过程,就需要插入不同阶段。
在 LangChain 中,Middleware 提供了多个 Hook:
before_agent
↓
before_model
↓
Model
↓
after_model
↓
Tool
↓
再次进入 Model
↓
after_agent
其中,before_agent 和 after_agent 围绕一次完整 Agent 调用执行;before_model 和 after_model 位于 Agent Loop 内部,因此可能执行多次。
另外还有 wrap_model_call 和 wrap_tool_call,可以直接包裹模型或工具调用。
不同位置适合处理不同问题。
身份和请求级权限,可以放在 before_agent;不允许进入模型的数据,应在模型调用前处理;工具权限和参数校验,应靠近 Tool;最终内容审核,则适合放在 after_agent。
四、处理敏感信息
LangChain 内置了 PIIMiddleware,用于检测和处理个人身份信息(Personally Identifiable Information,PII)。
当前内置类型包括邮箱、信用卡、IP 地址、MAC 地址和 URL,也支持通过正则表达式或自定义检测函数扩展。
检测到敏感信息后,可以选择:
| 策略 | 处理方式 |
|---|---|
redact | 用占位符替换 |
mask | 隐藏部分内容 |
hash | 转换成稳定哈希 |
block | 阻止继续执行 |
例如,不希望用户邮箱直接发送给模型:
from langchain.agents import create_agent
from langchain.agents.middleware import PIIMiddleware
agent = create_agent(
model="gpt-5.5",
tools=[],
middleware=[
PIIMiddleware(
"email",
strategy="redact",
apply_to_input=True,
)
],
)
result = agent.invoke({
"messages": [{
"role": "user",
"content": "我的邮箱是 test@example.com,请帮我记录。"
}]
})
经过 PIIMiddleware 处理后,模型实际接收到的输入已经发生变化。
处理后类似于:
我的邮箱是 [REDACTED_EMAIL],请帮我记录。
也就是说,敏感信息是在进入模型之前被处理,而不是等模型回答之后再删除。
PIIMiddleware 还可以通过 apply_to_output 检查模型输出,通过 apply_to_tool_results 检查工具返回结果。
五、自定义安全边界
PII 只是一个通用场景,实际项目中的 Guardrails 往往需要自己实现。
例如,一个内部 Agent 不允许处理包含特定危险指令的请求,可以在 before_agent 中拦截。
from typing import Any
from langchain.agents import create_agent
from langchain.agents.middleware import AgentState, before_agent
from langgraph.runtime import Runtime
@before_agent(can_jump_to=["end"])
def content_filter(
state: AgentState,
runtime: Runtime,
) -> dict[str, Any] | None:
if not state["messages"]:
return None
message = state["messages"][0]
content = str(message.content).lower()
if "drop database" in content:
return {
"messages": [{
"role": "assistant",
"content": "该请求已被安全策略阻止。"
}],
"jump_to": "end",
}
return None
agent = create_agent(
model="gpt-5.5",
tools=[],
middleware=[content_filter],
)
调用:
result = agent.invoke({
"messages": [{
"role": "user",
"content": "请执行 DROP DATABASE production"
}]
})
运行结果:
该请求已被安全策略阻止。
这里比较重要的是:
"jump_to": "end"
它意味着 Middleware 不只是修改内容,还可以直接改变 Agent 的执行路径。
before_agent
↓
发现违规
↓
jump_to="end"
↓
结束执行
此时模型不会继续被调用。
当然,这个关键词示例只是为了说明机制。实际项目如果要限制 SQL 删除操作,更可靠的方式通常是在 SQL Tool 或工具调用附近检查真正准备执行的语句。
六、检查最终输出
有些风险只有 Agent 完成任务以后才能判断。
例如研究 Agent 调用了多个工具,最终生成一段完整回答,这时可能需要检查:
最终内容是否符合公司政策?
是否泄露敏感信息?
是否满足业务规范?
这类检查适合放在 after_agent。
如果规则比较复杂,可以使用模型型 Guardrail:
from typing import Any
from langchain.agents.middleware import AgentState, after_agent
from langchain.chat_models import init_chat_model
from langchain.messages import AIMessage
from langgraph.runtime import Runtime
safety_model = init_chat_model("gpt-5.4-mini")
@after_agent(can_jump_to=["end"])
def safety_guardrail(
state: AgentState,
runtime: Runtime,
) -> dict[str, Any] | None:
last_message = state["messages"][-1]
if not isinstance(last_message, AIMessage):
return None
result = safety_model.invoke([{
"role": "user",
"content": f"""
判断下面的回答是否安全。
只返回 SAFE 或 UNSAFE。
回答:
{last_message.content}
"""
}])
if "UNSAFE" in str(result.content):
last_message.content = "该回答无法提供。"
return None
模型判断具有一定随机性,运行结果:
如果安全模型返回:
UNSAFE
用户最终看到:
该回答无法提供。
这就是模型型 Guardrail。
它能够理解复杂语义,但代价是额外模型调用。
七、高风险行为控制
前面的 Guardrails 主要控制数据和内容,但 Agent 更大的风险往往来自行为本身。
例如模型判断:
订单 A1024 应该退款 899 元。
即使这个判断完全正确,也不意味着 Agent 应该自动拥有退款权限。
对于发送邮件、修改数据库、删除文件、退款、转账等行为,需要控制的是:谁拥有最终执行权。
上一篇已经重点介绍了 Human-in-the-loop,这里不再展开 Interrupt、Resume 等具体流程。
从 Guardrails 的角度看,HITL 可以理解为一种行为边界:当某个 Tool 属于高风险操作时,通过 HumanInTheLoopMiddleware 暂停执行,把最终决定交给人工。
普通查询 Tool
↓
自动执行
退款 Tool
↓
Guardrail
↓
人工 approve / edit / reject
↓
决定是否执行
因此 Guardrails 和 HITL 不是两个完全独立的概念。
更准确地说:
Guardrails
├── 输入检查
├── PII保护
├── 输出检查
├── 业务规则
├── 模型安全判断
└── Human-in-the-loop
HITL 是 Guardrails 中针对高风险行为的一种控制方式。
八、组合多层Guardrails
真实项目通常不会只有一道 Guardrail。
不同 Middleware 可以分别承担自己的安全职责。例如一个客服 Agent:
用户请求
↓
请求检查
↓
PII脱敏
↓
模型推理
↓
工具规则检查
↓
必要时人工审批
↓
工具执行
↓
最终输出检查
↓
返回用户
代码结构可能类似:
agent = create_agent(
model="gpt-5.5",
tools=[
search_tool,
get_order_tool,
send_email_tool,
],
middleware=[
content_filter,
PIIMiddleware(
"email",
strategy="redact",
apply_to_input=True,
),
HumanInTheLoopMiddleware(
interrupt_on={
"send_email": True,
}
),
safety_guardrail,
],
)
运行结果
普通查询可能经过:
输入检查
→ PII检查
→ Model
→ Search Tool
→ Model
→ 输出检查
→ 返回
高风险操作则可能经过:
输入检查
→ PII检查
→ Model
→ 高风险 Tool
→ HITL
→ Tool执行
→ Model
→ 输出检查
→ 返回
从这个过程可以看到,一个完整的 Guardrails 体系并不是最后统一加一个“安全模型”。
更合理的判断方式是:
能否通过确定规则判断?
↓
能 → 使用代码规则
不能
↓
是否属于高风险行为?
↓
是 → 考虑人工审核
不是
↓
使用模型做语义判断
同时还要确认这个问题:风险真正出现在哪里?
如果敏感数据不能进入模型,就在模型调用前处理;如果风险来自 Tool,就应该在 Tool 附近设置边界。
总结
Guardrails 是分布在 Agent 执行过程中的多道安全边界。确定性问题优先交给代码,复杂语义再使用模型,高风险行为则可以结合 HITL。
设计时最重要是判断风险发生在哪里、谁应该负责判断,以及应该在什么位置阻止它继续传播或执行。把边界放在真正产生风险的位置,Guardrails 才能成为可靠的系统约束。
社区讨论
参与讨论
有问题或想法?欢迎继续讨论。