Создание безопасных ИИ-агентов с механизмами контроля LangChain и промежуточным программным обеспечением
Узнайте, как в LangChain функционируют детерминистичные и основанные на моделях механизмы контроля для выявления утечек персональных данных, обеспечения соблюдения бизнес-правил и добавления этапов утверждения со стороны человека в агентов ИИ.
Что такое ограничительные механизмы?
Ограничительный механизм — это средство, которое отслеживает действия ИИ-системы и предотвращает её выполнение действий, которые вам не нужны.
Рассмотрим агента, оснащённого следующими инструментами:
search()
sendEmail()
deleteUser()
makePayment()
Модель может решить, что вызов deleteUser() — правильный шаг.
Но действительно ли вы хотите это разрешить?
Ограничительный механизм находится между решением агента и фактическим выполнением этого действия:
User
↓
Agent
↓
Guardrail
↓
Is this allowed?
├── Yes → Execute
└── No → Block / Ask for approval
Ограничительные механизмы часто используются для:
- Предотвращения утечки персональных данных
- Обнаружения и блокировки попыток вставки промптов
- Фильтрации вредоносного или неподходящего контента
- Применения бизнес-логики и нормативных ограничений
- Проверки соответствия результатов стандартам качества и корректности
В LangChain механизмы контроля создаются в основном с помощью промежуточного программного обеспечения, что позволяет вмешиваться в процесс выполнения агента в определенных моментах.
Зачем ИИ-агентам нужны механизмы контроля?
Традиционное программное обеспечение следует логике, явно заданной разработчиком.
Например:
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:
А. Встроенные механизмы контроля
Б. Пользовательские механизмы контроля
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
А. Встроенные механизмы контроля в 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 и ожидает решения человека. Чтобы возобновить выполнение, требуются идентификатор потока и объект 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у middleware нет возможности возобновить работу, и механизм временной остановки с последующим утверждением просто не будет функционировать. Это, пожалуй, самая распространенная ошибка конфигурации.
Здесь конфигурация фактически гласит:
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
Это создаёт стратегию многоуровневой защиты для агента.
Основная идея заключается в том, что каждый механизм защиты отвечает за разный контрольно-проверочный пункт:
- Механизм защиты до запуска агента проверяет входящий запрос.
- Промежуточный компонент для обработки персональных данных защищает конфиденциальную информацию.
- Проверка с участием человека блокирует рискованные вызовы инструментов до их одобрения человеком.
- Механизм защиты после работы агента проверяет окончательный ответ перед его отправкой пользователю.
Вместо того чтобы полагаться на один единственный механизм безопасности, используется несколько уровней защиты, чтобы агент был максимально защищен на каждом этапе.
Ограничения — это не только вопрос безопасности
Когда люди слышат термин «ограничения», они обычно представляют себе блокировку вредоносного или оскорбительного контента.
Однако в производственных системах ограничения также служат для обеспечения соблюдения бизнес-логики и правил, специфичных для приложения.
Например:
Customer support agent
Can:
✓ Search orders
✓ Check delivery status
Cannot:
✗ Refund more than ₹10,000
✗ Delete customer account
✗ Change payment details
Эти правила не связаны с обнаружением вредоносного контента.
Они представляют собой правила приложения, определяющие границы того, что агенту разрешено делать.
Поскольку большие языковые модели принимают решения динамически во время выполнения, необходим точный механизм обеспечения соблюдения правил, которые должны действовать независимо от решений самой модели.
Примечание: большая языковая модель сама решает, что делать; ограничения определяют, что приложение фактически позволяет ей делать.
Заключительные мысли
Создание ИИ-агента включает не только подключение большой языковой модели к нескольким инструментам.
Как только агент начинает обрабатывать реальные запросы пользователей и взаимодействовать с реальными системами, необходимо четко определить границы его поведения. Именно для этого и существуют ограничения.
LangChain предоставляет несколько компонентов для этой цели:
- Детерминистические ограничения для правил, которые должны быть предсказуемыми
- Ограничения, основанные на моделях, для проверок, зависящих от смысла и контекста
- Промежуточное программное обеспечение для обработки персональных данных для защиты конфиденциальной информации
- Промежуточное программное обеспечение с участием человека для действий, имеющих реальные последствия
- Промежуточное программное обеспечение до выполнения действий агентом для проверок на уровне входных данных и сессии
- Промежуточное программное обеспечение после выполнения действий агентом для проверки окончательного результата
Самый важный урок из всего этого заключается в следующем: не полагайтесь исключительно на LLM для соблюдения правил вашего приложения.
Позвольте модели заниматься логическими рассуждениями и принятием решений, но сохраняйте наиболее важные ограничения в коде и промежуточном программном обеспечении, где вы сможете их напрямую проверять.
Именно это превращает ИИ-агента в достаточно надежный инструмент для реальной производственной среды.
Ссылки
- Официальный руководство LangChain, посвященное концепциям ограничений, доступно по адресу https://docs.langchain.com/oss/javascript/langchain/guardrails
- Официальная документация LangChain, описывающая принципы работы промежуточного программного обеспечения, доступна по адресу https://docs.langchain.com/oss/javascript/langchain/middleware/overview
Связанная литература
- Создание локальной копии Angry Birds с использованием Qwen3.8-27B и Pi — Узнайте, как настроить полностью локальный рабочий процесс для разработки ИИ с помощью LM Studio и агента Pi для создания играбельного уровня Angry Birds с использованием Qwen3.8-27B.
- Структурные ограничения для агентов ИИ: внутри пайплайна ResolveFlow — Рассматривается, как агент на основе LangGraph обеспечивает разделение процессов рассуждения и выполнения с помощью проверок на уровне кода вместо инструкций из промптов, а также описывается ошибка поиска данных, возникшая на этапе разработки.