利用 LangChain 的防护机制与中间件构建安全的人工智能代理
了解 LangChain 中的确定性机制与基于模型的约束如何用于检测个人身份信息泄露、执行业务规则,以及为人工智能代理添加人工审批步骤。
什么是防护机制?
防护机制是一种用于监控人工智能系统行为并阻止其执行你不希望的操作的装置。
假设有一个配备了以下工具的智能体:
search()
sendEmail()
deleteUser()
makePayment()
该模型可能会认为调用deleteUser()是正确的做法。
但这真的是你希望允许的吗?
防护机制就位于智能体的决策与实际执行操作之间:
User
↓
Agent
↓
Guardrail
↓
Is this allowed?
├── Yes → Execute
└── No → Block / Ask for approval
防护机制通常用于:
- 防止个人身份信息泄露
- 识别并阻止提示注入企图
- 过滤掉有害或不适当的内容
- 应用业务逻辑和监管要求
- 确保输出符合质量与正确性标准
在 LangChain 中,防护机制主要是通过中间件实现的,它可以让你在执行流程的特定节点进行干预。
为什么 AI 智能体需要防护机制?
传统软件遵循开发者明确编写的逻辑。
例如:
if (!user.isAdmin) {
throw new Error("Unauthorized");
}
大型语言模型并非如此运作。
你只需提供指令和工具,具体该执行什么操作由模型自行决定。
以拥有 refundPayment() 工具的支持智能体为例:
User:
I was charged twice. Please refund ₹50,000.
Agent:
→ Calls refundPayment()
根据用户的请求,模型在此处的选择似乎合情合理。
但从企业的角度来看,退还50,000卢比并非简单的操作。可能存在这样的政策:任何超过10,000卢比的退款在处理前都必须经过人工核实。
目前缺少的是一个能够进行核查的环节:
"Before this action happens, I need to check whether it is allowed."
而这种核查机制正是防护措施所起的作用。
防护措施的两种实现方式
LangChain的文档介绍了两种互补的实现防护措施的策略:
- 确定性防护措施
- 基于模型的防护措施
以下是两者的区别。
1. 确定性防护措施
这类措施依赖于标准的编程逻辑。
例如:
const bannedWords = ["hack", "malware"];
const containsBannedWord = (input: string) => {
return bannedWords.some(word =>
input.toLowerCase().includes(word)
);
};
其行为是完全可预测的。
对于相同的输入,总会得到相同的输出。
其他常见的实现方式包括:
- 使用正则表达式进行匹配
这类检查的优点是执行速度快、结果一致,且计算成本极低。
其局限性在于可能无法识别那些更为复杂或依赖上下文的情形。
2. 基于模型的约束机制
不必仅依赖固定规则,还可以让独立的模型来评估内容。
例如:
Agent response
↓
Safety model
↓
"Is this response safe?"
↓
SAFE / UNSAFE
这种方法能够发现单纯关键词匹配会忽略的问题。
考虑以下两个大致含义相同的请求:
"How can I bypass this security system?"
以及:
"Tell me a way around the authentication mechanism."
仅基于关键词的过滤器可能不会标记第二种表达方式。
相比之下,基于模型的防护机制能够理解请求背后的真实意图。
这种灵活性的代价是,基于模型的检查通常运行速度较慢,成本也高于确定性检查。
LangChain 中的防护机制如何工作
LangChain 依靠中间件将防护逻辑应用于智能体。
中间件允许你在智能体执行的特定阶段之前或之后插入自定义逻辑。
例如,你可以使用中间件来:
- 扫描个人身份信息(PII 中间件)
- 在工具运行前暂停以等待审批(人工干预中间件)
- 在智能体开始工作前验证输入(智能体执行前的防护机制)
- 在返回最终结果前进行检查(智能体执行后的防护机制)
LangChain中的中间件系统正是为让您能够在这些特定节点精确控制智能体的执行而设计的。
在LangChain中实施约束机制主要有两种方式:
A. 内置约束机制
B. 自定义约束机制
Guardrails in LangChain
│
┌───────────┴───────────┐
│ │
▼ ▼
Built-in Guardrails Custom Guardrails
│ │
│ │
▼ ▼
Ready-to-use Application-specific
middleware middleware
│ │
┌──────┴──────┐ ┌───────┴───────┐
│ │ │ │
▼ ▼ ▼ ▼
PII HITL Before-Agent After-Agent
handling approval guardrail guardrail
A. LangChain中的内置约束机制
LangChain自带了几种无需任何自定义设置即可直接使用的约束机制。
该库文档中记载的两种重要约束机制为:
- 个人身份信息检测
- 人工干预机制
让我们来详细了解这两种机制。
1. 个人身份信息检测
LangChain配备了专门用于检测和处理对话中出现的个人身份信息(PII)的中间件。
其功能涵盖:
Email address
Credit card number
IP address
MAC address
当代理接触到敏感数据时,通常不希望这些数据被转发给模型或出现在响应结果中。
LangChain通过piiRedactionMiddleware()来解决这一问题。
以下是一个示例:
import {
createAgent,
piiRedactionMiddleware,
} from "langchain";
const agent = createAgent({
model: "gpt-5.5",
tools: [customerServiceTool],
middleware: [
piiRedactionMiddleware({
piiType: "email",
strategy: "redact",
applyToInput: true,
applyToOutput: true,
}),
],
});
const result = await agent.invoke({
messages: [{
role: "user",
content: "My email is john.doe@example.com"
}]
});
假设用户提交了如下内容:
My email is john.doe@example.com
中间件会在模型看到这些数据之前截获并重新编写它们:
My email is [REDACTED_EMAIL]
换言之,模型处理的是经过处理的占位符,而非原始的敏感数据。
注意: 设置
applyToOutput: true可确保即使模型在回复中生成了个人身份信息,中间件也会在响应到达用户之前将其删除。如果您的工具可能在结果中泄露个人身份信息,applyToToolResults: true可为工具输出提供同样的保护。
个人身份信息处理策略
LangChain 提供了四种不同的方式来处理检测到的个人身份信息:
为了了解其中的差异,让我们将每种策略应用到相同的示例输入上。
假设用户输入了:
My email is john.doe@example.com
1. redact
使用 redact 后,所有检测到的个人身份信息都会被完全替换为通用占位符。
piiRedactionMiddleware({
piiType: "email",
strategy: "redact",
applyToInput: true,
});
模型实际接收到的内容是:
My email is [REDACTED_EMAIL]
该策略适用于模型无需知晓底层实际值的场景。
2. mask
mask策略会隐藏部分值,同时保留足够的信息以供上下文使用。
piiRedactionMiddleware({
piiType: "email",
strategy: "mask",
applyToInput: true,
});
邮件显示的内容可能如下:
My email is j***@example.com
当模型或最终用户需要部分数据引用而不愿暴露全部内容时,此方法非常实用。
3. hash
hash策略会将个人信息替换为一致的、可确定的哈希值。
piiRedactionMiddleware({
piiType: "email",
strategy: "hash",
applyToInput: true,
});
处理后的邮件可能看起来像这样:
My email is 8f14e45fceea167a5a36dedd4bea2543...
由于哈希运算具有确定性,相同的输入总是会产生相同的哈希值。这使得无需暴露原始数据即可追踪或匹配相同值的重复出现情况。
4. block
与其他三种方式不同,block完全不会对敏感个人信息进行任何处理——一旦检测到此类信息,它会直接拒绝该请求。
例如,你可以配置自定义检测器来识别API密钥:
piiRedactionMiddleware({
piiType: "api_key",
detector: /sk-[a-zA-Z0-9]{32}/,
strategy: "block",
applyToInput: true,
});
如果用户随后发送:
My API key is sk-abcdefghijklmnopqrstuvwxyz123456
中间件会识别出API密钥的模式,并在其值进一步传播之前阻止该请求。
这种策略适用于那些某些类别的敏感数据——如API密钥、凭证及类似机密信息——从一开始就绝不能进入处理流程的情况。
2. 人工干预机制
某些操作风险过高,无法完全交由自主代理处理。
比如以下这类操作:
delete production database
send an external email
make a financial transaction
modify production data
与其直接禁止这些操作,不如让它们经过人工审批流程。
相应的处理流程如下:
Agent
↓
Tool call
↓
Guardrail
↓
Human approval
↓
┌───────────────┐
│ │
Approved Rejected
│ │
↓ ↓
Execute Stop
LangChain提供了humanInTheLoopMiddleware()来实现这种模式。
例如:
import { createAgent, humanInTheLoopMiddleware } from "langchain";
import { MemorySaver, Command } from "@langchain/langgraph";
const agent = createAgent({
model: "gpt-5.5",
tools: [
searchTool,
sendEmailTool,
deleteDatabaseTool,
],
middleware: [
humanInTheLoopMiddleware({
interruptOn: {
send_email: {
allowAccept: true,
allowEdit: true,
allowRespond: true,
},
delete_database: {
allowAccept: true,
allowEdit: true,
allowRespond: true,
},
search: false,
},
}),
],
// A checkpointer is required so the paused run can be resumed later
checkpointer: new MemorySaver(),
});
启用该机制后,代理会在执行send_email或delete_database之前暂停,并等待人工决策。要继续执行,需要同时提供线程ID和Command:
const config = { configurable: { thread_id: "some_id" } };
// First call pauses and waits for approval
await agent.invoke(
{ messages: [{ role: "user", content: "Send an email to the team" }] },
config
);
// Resume after a human approves the tool call
await agent.invoke(
new Command({ resume: { decisions: [{ type: "approve" }] } }),
config
);
重要提示:如果没有
checkpointer和thread_id,中间件就无从继续执行,暂停后再审批的机制也就无法正常工作。这是最常见的配置错误。
此处,配置实际上说明了:
search → automatically allowed
send_email → require human approval
delete_database → require human approval
这种模式对于在生产环境中运行的智能体尤为有用。
如果您想更深入地了解人工干预模式,有一篇专门的实操指南详细介绍了如何通过interrupt()暂停流程、等待人工决策,然后再通过Command继续执行。
B. 自定义约束规则
LangChain自带的中间件无法满足应用程序面临的所有场景需求。
当您的需求超出内置功能范围时,LangChain允许您编写自己的中间件,并实现自定义的约束规则逻辑。
有两个生命周期钩子特别适合此用途:
beforeAgent
afterAgent
这些钩子可以让您在智能体运行过程中的特定时刻注入约束规则逻辑。
1. 代理执行前的防护机制
beforeAgent钩子在代理被调用之初就会触发。你可以利用它来构建代理执行前的防护机制,在代理开始处理请求之前对其进行检查或过滤。
常见的应用场景包括:
- 身份验证
- 速率限制
- 输入过滤
- 拒绝不合适的请求
- 针对会话范围的检查
以下是示例:
import { createMiddleware, AIMessage } from "langchain";
const sensitiveDataFilterMiddleware = (sensitiveKeywords: string[]) => {
const keywords = sensitiveKeywords.map((kw) => kw.toLowerCase());
return createMiddleware({
name: "SensitiveDataFilterMiddleware",
beforeAgent: {
hook: (state) => {
// Check if messages exist
if (!state.messages || state.messages.length === 0) {
return;
}
// Get the first user message
const firstMessage = state.messages[0];
// Make sure the message is from the user
if (firstMessage._getType() !== "human") {
return;
}
const content = firstMessage.content.toString().toLowerCase();
// Check for sensitive keywords
for (const keyword of keywords) {
if (content.includes(keyword)) {
// Stop the agent before it starts processing
return {
messages: [
new AIMessage(
"I cannot process requests containing sensitive information. " +
"Please remove passwords, API keys, or secrets and try again."
),
],
jumpTo: "end",
};
}
}
// No sensitive content found
return;
},
canJumpTo: ["end"],
},
});
};
// Create the agent
import { createAgent } from "langchain";
const agent = createAgent({
model: "gpt-5.5",
tools: [searchTool, calculatorTool],
middleware: [
sensitiveDataFilterMiddleware([
"password",
"api_key",
"secret",
"private_key",
]),
],
});
// This request will be blocked
const result = await agent.invoke({
messages: [
{
role: "user",
content: "Show me how to store my production password securely.",
},
],
});
console.log(result);
流程如下:
User Request
│
▼
┌──────────────────────┐
│ beforeAgent Hook │
│ │
│ Check user message │
│ for sensitive words │
└──────────┬───────────┘
│
▼
┌─────────────────┐
│ Sensitive │
│ keyword found? │
└───────┬─────────┘
│
┌────────┴────────┐
│ │
YES NO
│ │
▼ ▼
┌──────────────────┐ ┌──────────────┐
│ Return blocked │ │ Continue to │
│ AIMessage │ │ the agent │
└────────┬─────────┘ └───────┬──────┘
│ │
▼ ▼
jumpTo: "end" Agent executes
│ │
▼ ▼
Final Response Final Response
当此钩子处于激活状态时,有问题的请求会在代理有机会运行或调用任何工具之前就被阻止。
2. 代理执行后的防护机制
afterAgent钩子在代理完成工作后会触发。它允许你实现代理执行后的防护机制,在代理的最终答案传递给用户之前对其进行检查或过滤。
常见应用包括:
- 安全检查
- 验证输出质量
- 合规性检查
- 过滤输出内容
- 由其他模型进行评估
例如,你可以将响应传递给另一个专门负责对其进行评估的模型:
import {
createMiddleware,
AIMessage,
initChatModel,
} from "langchain";
const toxicityGuardrailMiddleware = () => {
// Model used only for toxicity evaluation
const evaluatorModel = initChatModel("gpt-5.4-mini");
return createMiddleware({
name: "ToxicityGuardrailMiddleware",
afterAgent: {
hook: async (state) => {
// Get the final AI response
if (!state.messages || state.messages.length === 0) {
return;
}
const lastMessage =
state.messages[state.messages.length - 1];
if (lastMessage._getType() !== "ai") {
return;
}
const response = lastMessage.content.toString();
// Ask the evaluator model to check for toxicity
const evaluationPrompt = `
You are a toxicity detection system.
Analyze the following AI response and determine
whether it contains toxic, abusive, hateful, or
harassing language.
Respond with ONLY:
SAFE
or
TOXIC
AI response:
${response}
`;
const evaluation = await evaluatorModel.invoke([ { role: "user", content: evaluationPrompt, }, ]);
const result = evaluation.content
.toString()
.trim()
.toUpperCase();
// Replace the response if it is toxic
if (result === "TOXIC") {
return {
messages: [
new AIMessage(
"I'm unable to provide that response because it contains inappropriate language."
),
],
jumpTo: "end",
};
}
return;
},
canJumpTo: ["end"],
},
});
};
// Create the agent
import { createAgent } from "langchain";
const agent = createAgent({
model: "gpt-5.5",
tools: [
searchTool,
calculatorTool,
],
middleware: [
toxicityGuardrailMiddleware(),
],
});
// Invoke the agent
const result = await agent.invoke({
messages: [
{
role: "user",
content: "Give me a response to this angry customer.",
},
],
});
console.log(result);
User
↓
Main Agent (GPT-5.5)
↓
Generates response
↓
afterAgent hook
↓
Evaluator Model (GPT-5.4-mini)
↓
┌───────────────┐
│ Is it toxic? │
└───────┬───────┘
│
┌───┴───┐
↓ ↓
SAFE TOXIC
↓ ↓
Return Replace
response response
关键在于绝不能自动信任智能体的输出。相反,应先将生成的答案发送给评估模型,由其根据安全标准进行检查后再反馈给用户。
组合多种防护机制
在实际应用中,单一的防护机制往往不足以满足需求。
智能体执行的不同阶段需要不同类型的保障措施。请看以下流程:
1. Input filtering
2. PII protection
3. Tool approval
4. Output safety check
LangChain 允许你一次性将多个中间件组件附加到同一个智能体上。
以下是一个示例:
const agent = createAgent({
model: "gpt-5.5",
tools: [
searchTool,
sendEmailTool,
],
middleware: [
// 1. Before-agent guardrail for input filtering
sensitiveDataFilterMiddleware([
"password",
"api_key",
"secret",
"private_key",
]),
// 2. Built-in PII protection middleware
piiRedactionMiddleware({
piiType: "email",
strategy: "redact",
applyToInput: true,
applyToOutput: true,
}),
// 3. Built-in Human approval middleware for sensitive tools
humanInTheLoopMiddleware({
interruptOn: {
send_email: {
allowAccept: true,
allowEdit: true,
allowRespond: true,
},
},
}),
// 4. After-agent guardrail
toxicityGuardrailMiddleware(),
],
});
每个中间件层负责守护智能体执行路径中的不同部分。
综合起来,整体流程如下:
User Request
│
▼
┌──────────────────────┐
│ Before-agent │
│ guardrail │
│ │
│ Input filtering │
└──────────┬───────────┘
│
▼
┌──────────────────────┐
│ PII protection │
│ │
│ Redact sensitive │
│ information │
└──────────┬───────────┘
│
▼
AI Agent
│
▼
Tool call?
/ \
No Yes
│ │
│ ▼
│ ┌───────────────┐
│ │ Human approval│
│ └───────┬───────┘
│ │
│ ┌────┴────┐
│ │ │
│ Approved Rejected
│ │ │
│ ▼ ▼
│ Execute Stop
│ │
└───────┤
▼
Agent response
│
▼
┌──────────────────────┐
│ After-agent │
│ guardrail │
│ │
│ Toxicity check │
│ using evaluator model│
└──────────┬───────────┘
│
┌────┴────┐
│ │
SAFE TOXIC
│ │
▼ ▼
Return Replace
response response
这为智能体构建了一种分层防御策略。
核心思想是每个防护机制负责不同的检查点:
- 智能体执行前的防护机制用于验证传入的请求。
- PII 中间件负责保护敏感数据。
- 人工干预机制会在有人批准之前阻止有风险的工具调用。
- 智能体执行后的防护机制会在最终响应发送给用户之前对其进行检查。
不必依赖单一的安全机制,而是通过多层防护确保代理在每个阶段都能得到更全面的保护。
约束机制并不仅关乎安全
当人们听到“约束机制”这个词时,通常会联想到阻止有害或冒犯性内容。
但在生产系统中,约束机制还用于强制执行业务逻辑和特定于应用的政策。
例如:
Customer support agent
Can:
✓ Search orders
✓ Check delivery status
Cannot:
✗ Refund more than ₹10,000
✗ Delete customer account
✗ Change payment details
这些规则与检测有害内容毫无关系。
它们代表了定义代理可执行操作范围的应用政策。
由于大语言模型会在运行时动态做出决策,因此需要一个可靠的规则执行点,确保无论模型自行作出何种决定,这些规则都能得到遵守。
注意:大语言模型决定它想要做什么;而约束规则则决定了应用程序实际允许它执行哪些操作。
总结
构建人工智能代理并不仅仅是将大语言模型与少量工具连接起来而已。
一旦该代理开始处理真实用户的请求并操作真实的系统,就需要为其行为设定明确的边界。这正是约束规则的作用所在。
LangChain为此提供了多种构建模块:
- 用于处理必须可预测规则的确定性约束规则
- 用于基于含义和上下文进行校验的模型驱动型约束规则
- 用于处理敏感信息的个人身份信息中间件
- 用于涉及实际后果操作的“人在回路”中间件
- 用于输入和会话级别校验的代理前中间件
- 用于验证最终输出的结果后中间件
从这一切中得到的最重要教训是:不要仅依赖大语言模型来执行应用程序的规则。
让模型负责推理和决策,但将真正重要的边界条件编码在代码和中间件中,这样就可以直接进行检查和验证。
这才是让人工智能代理具备足够可靠性、适用于实际生产环境的关键。
参考资料
- 介绍防护机制概念的 LangChain 官方指南,地址为 https://docs.langchain.com/oss/javascript/langchain/guardrails
- 阐述中间件工作原理的 LangChain 官方参考文档,地址为 https://docs.langchain.com/oss/javascript/langchain/middleware/overview
相关阅读
- 使用 Qwen3.8-27B 和 Pi 构建本地版《愤怒的小鸟》克隆版本 — 了解如何利用 LM Studio 和 Pi agent 建立完全本地的 AI 编程工作流,从而使用 Qwen3.8-27B 创建可玩的《愤怒的小鸟》关卡。
- AI Agent 的结构化约束:ResolveFlow 流水线内部机制 — 阐述了基于 LangGraph 的 Agent 如何通过代码级检查而非提示指令来实现推理与执行的分离,同时介绍了在此过程中出现的一个检索错误。