Creación de agentes de IA seguros con medidas de seguridad y middleware de LangChain
Aprenda cómo funcionan las restricciones deterministas y basadas en modelos en LangChain para detectar fugas de PII, hacer cumplir las reglas empresariales y agregar pasos de aprobación humana a los agentes de IA.
¿Qué son las barreras de seguridad?
Una barrera de seguridad es un mecanismo que supervisa lo que hace un sistema de IA y evita que realice acciones que no se desean.
Imaginemos un agente equipado con las siguientes herramientas:
search()
sendEmail()
deleteUser()
makePayment()
El modelo podría decidir que llamar a deleteUser() es la opción adecuada.
Pero, ¿es realmente algo que se quiera permitir?
Una barrera de seguridad se interponga entre la decisión del agente y la ejecución real de esa acción:
User
↓
Agent
↓
Guardrail
↓
Is this allowed?
├── Yes → Execute
└── No → Block / Ask for approval
Las barreras de seguridad se utilizan comúnmente para:
- Evitar que la información de identificación personal se filtre
- Detener e impedir intentos de inyección de comandos
- Filtrar contenido dañino o inapropiado
- Aplicar lógica empresarial y restricciones regulatorias
- Verificar que las salidas cumplan con los estándares de calidad y corrección
En LangChain, las medidas de control se implementan principalmente a través del middleware, lo que permite intervenir en el flujo de ejecución del agente en puntos específicos.
¿Por qué necesitan los agentes de IA medidas de control?
El software tradicional sigue la lógica que un desarrollador escribió de forma explícita.
Por ejemplo:
if (!user.isAdmin) {
throw new Error("Unauthorized");
}
Los LLM no funcionan de esta manera.
Usted proporciona instrucciones y herramientas, pero el modelo mismo decide qué acción tomar.
Tomemos como ejemplo a un agente de soporte que tiene acceso a la herramienta refundPayment():
User:
I was charged twice. Please refund ₹50,000.
Agent:
→ Calls refundPayment()
La elección del modelo en este caso podría parecer razonable teniendo en cuenta lo que solicitó el usuario.
No obstante, desde el punto de vista empresarial, reembolsar ₹50,000 no es una operación trivial. Puede existir una política que exija que cualquier reembolso superior a ₹10,000 pase por verificación manual antes de ser procesado.
Lo que falta es una capa que pregunte:
"Before this action happens, I need to check whether it is allowed."
Esa capa es exactamente lo que proporcionan las barreras de seguridad.
Dos enfoques para las barreras de seguridad
La documentación de LangChain describe dos estrategias complementarias para implementar barreras de seguridad:
- Barreras de seguridad deterministas
- Barreras de seguridad basadas en modelos
A continuación se explica cómo difieren.
1. Barreras de seguridad deterministas
Estas se basan en la lógica de programación estándar.
Por ejemplo:
const bannedWords = ["hack", "malware"];
const containsBannedWord = (input: string) => {
return bannedWords.some(word =>
input.toLowerCase().includes(word)
);
};
El comportamiento aquí es completamente predecible.
Con el mismo ingreso, siempre se obtiene el mismo resultado.
Otros patrones comunes incluyen:
- Comparación con expresiones regulares
La ventaja es que este tipo de verificación se ejecuta rápidamente, produce resultados consistentes y requiere muy poca capacidad de cálculo.
La limitación es que estas verificaciones pueden no detectar casos más sutiles o dependientes del contexto.
2. Límites basados en modelos
En lugar de depender únicamente de reglas fijas, se puede utilizar un modelo separado para evaluar el contenido.
Por ejemplo:
Agent response
↓
Safety model
↓
"Is this response safe?"
↓
SAFE / UNSAFE
Este enfoque puede detectar cosas que la simple coincidencia de palabras clave pasaría por alto.
Considere estas dos solicitudes, que significan más o menos lo mismo:
"How can I bypass this security system?"
y:
"Tell me a way around the authentication mechanism."
Un filtro basado únicamente en palabras clave podría no marcar la segunda formulación.
Por el contrario, una barrera basada en modelos puede interpretar la intención subyacente de la solicitud.
El costo de esta flexibilidad es que las verificaciones basadas en modelos tienden a ejecutarse más lentamente y costar más que las deterministas.
Cómo funcionan las barreras en LangChain
LangChain se basa en middleware para integrar la lógica de las barreras alrededor de un agente.
El middleware permite insertar lógica personalizada antes o después de etapas específicas de la ejecución del agente.
Por ejemplo, puedes usar el middleware para:
- Escanear en busca de PII (middleware para PII)
- Pausar la ejecución de una herramienta en espera de aprobación (middleware con intervención humana)
- Validar la entrada antes de que el agente comience a trabajar (barrera antes del agente)
- Revisar la salida final antes de devolverla (barrera después del agente)
El sistema de middleware en LangChain fue creado específicamente para permitirte controlar la ejecución de un agente en estos puntos exactos.
Existen dos formas principales de aplicar medidas de control en LangChain:
A. Medidas de control integradas
B. Medidas de control personalizadas
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. Medidas de control integradas en LangChain
LangChain incluye varias medidas de control listas para usar sin necesidad de configuración adicional.
Dos de las más destacadas documentadas por la biblioteca son:
- Detección de PII
- Intervención humana
Analicemos ambas.
1. Detección de PII
LangChain cuenta con middleware diseñado específicamente para detectar y gestionar la Información de Identificación Personal (PII) que aparece en una conversación.
Esto puede abarcar cosas como:
Email address
Credit card number
IP address
MAC address
Cuando un agente tiene acceso a datos sensibles, generalmente no se desea que esos datos sean enviados al modelo ni reflejados en la respuesta.
LangChain aborda esto con piiRedactionMiddleware().
He aquí un ejemplo:
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"
}]
});
Supongamos que un usuario envía:
My email is john.doe@example.com
El middleware intercepta y reescribe esto antes de que el modelo lo vea:
My email is [REDACTED_EMAIL]
En otras palabras, el modelo trabaja con sustitutos sanitizados en lugar de los datos sensibles en bruto.
Nota: Al establecer
applyToOutput: true, se garantiza que, incluso si el modelo genera información de identificación personal en su respuesta, el middleware la elimine antes de que la respuesta llegue al usuario. Si sus herramientas podrían filtrar información de identificación personal en sus resultados,applyToToolResults: trueextiende la misma protección a las salidas de las herramientas también.
Estrategias para manejar la información de identificación personal
LangChain admite cuatro formas distintas de tratar la información de identificación personal detectada:
Para ver la diferencia, apliquemos cada estrategia al mismo ejemplo de entrada.
Supongamos que el usuario ingresa:
My email is john.doe@example.com
1. redact
Con redact, toda la información de identificación personal detectada es reemplazada completamente por un marcador genérico.
piiRedactionMiddleware({
piiType: "email",
strategy: "redact",
applyToInput: true,
});
Lo que realmente recibe el modelo es:
My email is [REDACTED_EMAIL]
Esta estrategia es adecuada para situaciones en las que el modelo no necesita realmente conocer el valor subyacente.
2. mask
La estrategia mask oculta parte del valor mientras deja lo suficiente visible para el contexto.
piiRedactionMiddleware({
piiType: "email",
strategy: "mask",
applyToInput: true,
});
El correo electrónico podría verse así:
My email is j***@example.com
Esto es útil cuando tanto el modelo como el usuario final necesitan una referencia parcial a los datos sin exponerlos en su totalidad.
3. hash
hash reemplaza la PII por un valor de hash consistente y determinista.
piiRedactionMiddleware({
piiType: "email",
strategy: "hash",
applyToInput: true,
});
El correo electrónico transformado podría verse algo así:
My email is 8f14e45fceea167a5a36dedd4bea2543...
Dado que el hashing es determinista, las entradas idénticas siempre generan hashes idénticos. Esto permite rastrear o comparar ocurrencias repetidas del mismo valor sin revelar nunca los datos originales.
4. block
A diferencia de los otros tres, block no transforma en absoluto la PII; simplemente rechaza la solicitud de inmediato una vez que se detecta ese tipo de PII.
Por ejemplo, se puede configurar un detector personalizado para detectar claves API:
piiRedactionMiddleware({
piiType: "api_key",
detector: /sk-[a-zA-Z0-9]{32}/,
strategy: "block",
applyToInput: true,
});
Si luego un usuario envía:
My API key is sk-abcdefghijklmnopqrstuvwxyz123456
el middleware reconoce el patrón de la clave API y detiene la solicitud antes de que su valor pueda propagarse más allá.
Esta estrategia es adecuada para casos en los que ciertas categorías de datos sensibles —claves API, credenciales y secretos similares— nunca deben llegar al flujo de trabajo del agente en primer lugar.
2. Intervención humana
Ciertas operaciones conllevan demasiado riesgo como para ser entregadas completamente a un agente autónomo.
Considere acciones como:
delete production database
send an external email
make a financial transaction
modify production data
En lugar de prohibir directamente estas acciones, puedes enrutarlas a través de un paso de aprobación humana.
El flujo resultante se ve así:
Agent
↓
Tool call
↓
Guardrail
↓
Human approval
↓
┌───────────────┐
│ │
Approved Rejected
│ │
↓ ↓
Execute Stop
LangChain ofrece humanInTheLoopMiddleware() para implementar este patrón.
Por ejemplo:
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(),
});
Con esto en su lugar, el agente se detiene antes de ejecutar send_email o delete_database y espera una decisión humana. Para reanudar la ejecución posteriormente, la llamada necesita tanto un ID de hilo como un 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
);
Importante: sin un
checkpointery unthread_id, no hay nada desde donde el middleware pueda reanudarse, y el mecanismo de pausa seguida de aprobación simplemente no funcionará. Este es, con diferencia, el error de configuración más común.
Aquí, la configuración establece de manera efectiva:
search → automatically allowed
send_email → require human approval
delete_database → require human approval
Este patrón resulta especialmente valioso para los agentes que operan en entornos de producción.
Si desea conocer más a fondo el patrón human-in-the-loop, hay una guía práctica separada que explica exactamente cómo el gráfico hace una pausa mediante interrupt(), espera la decisión de un humano y luego continúa con la ejecución a través de Command.
B. Límites personalizados
El middleware LangChain que viene preinstalado no se adaptará a todos los escenarios con los que se enfrente su aplicación.
Cuando sus requisitos van más allá de lo que ya está incluido, LangChain le permite escribir su propio middleware e integrar comportamientos de límites personalizados.
Hay dos ganchos de ciclo de vida que son especialmente útiles para este propósito:
beforeAgent
afterAgent
Estos ganchos le brindan la posibilidad de insertar lógica de límites en momentos específicos durante el funcionamiento de un agente.
1. Límites de seguridad antes del agente
Un gancho beforeAgent se dispara al inicio exacto de la invocación de un agente. Puedes utilizarlo para crear un límite de seguridad antes del agente que inspeccione o filtre una solicitud entrante antes incluso de que el agente comience a trabajar en ella.
Los casos de uso típicos incluyen:
- Autenticación
- Límite de frecuencia
- Filtrado de entradas
- Rechazo de solicitudes inapropiadas
- Verificaciones limitadas a una sesión
Aquí hay un ejemplo:
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);
Flujo:
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
Con este gancho activo, una solicitud problemática se detiene antes de que el agente tenga la oportunidad de ejecutar o invocar ninguna herramienta.
2. Límites de seguridad después del agente
Un gancho afterAgent se dispara una vez que el agente ha completado su trabajo. Te permite implementar un límite de seguridad después del agente que verifique o filtre la respuesta final del agente antes de que llegue al usuario.
Las aplicaciones comunes incluyen:
- Verificaciones de seguridad
- Validación de la calidad de la salida
- Verificaciones de cumplimiento
- Filtrado de la salida
- Evaluación realizada por otro modelo
Por ejemplo, se podría enrutar la respuesta a través de un segundo modelo cuya única función es evaluarla:
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
La conclusión clave aquí es que nunca se debe confiar automáticamente en la salida del agente. En su lugar, se envía la respuesta generada a través de un modelo evaluador cuya tarea es verificarla según los criterios de seguridad antes de devolverla al usuario.
Combinación de múltiples medidas de seguridad
En la práctica, rara vez es suficiente una sola medida de seguridad para una aplicación del mundo real.
Diferentes fases de la ejecución de un agente requieren diferentes tipos de salvaguardas. Considere esta secuencia:
1. Input filtering
2. PII protection
3. Tool approval
4. Output safety check
LangChain permite adjuntar varios componentes de middleware a un único agente al mismo tiempo.
Aquí hay un ejemplo:
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(),
],
});
Cada capa de middleware es responsable de proteger una parte distinta del camino de ejecución del agente.
Juntos, el flujo general se ve así:
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
Esto genera una estrategia de defensa en capas para el agente.
La idea central es que cada medida de protección se encarga de un punto de control diferente:
- La medida de protección antes del agente valida la solicitud recibida.
- El middleware para datos PII protege la información sensible.
- La revisión humana bloquea las llamadas a herramientas riesgosas hasta que una persona las apruebe.
- La medida de protección después del agente verifica la respuesta final antes de que llegue al usuario.
En lugar de depender de un único mecanismo de seguridad, se aplican múltiples capas para proteger al agente de manera más exhaustiva en cada etapa.
Las medidas de seguridad no se refieren solo a la protección
Cuando la gente escucha el término “medidas de seguridad”, suele pensar en bloquear contenido dañino u ofensivo.
No obstante, en los sistemas de producción, estas medidas también sirven para hacer cumplir la lógica empresarial y las políticas específicas de la aplicación.
Por ejemplo:
Customer support agent
Can:
✓ Search orders
✓ Check delivery status
Cannot:
✗ Refund more than ₹10,000
✗ Delete customer account
✗ Change payment details
Estas reglas no tienen nada que ver con la detección de contenido dañino.
Representan políticas de la aplicación que definen los límites de lo que el agente está autorizado a hacer.
Dado que un LLM toma decisiones de forma dinámica en tiempo de ejecución, se necesita un punto de aplicación fiable para las reglas que deben mantenerse independientemente de lo que el modelo decida por sí mismo.
Nota: el LLM decide qué quiere hacer; las directrices determinan lo que la aplicación realmente le permite hacer.
Pensamientos finales
Construir un agente de IA implica más que simplemente conectar un LLM a un puñado de herramientas.
Una vez que ese agente comienza a manejar solicitudes reales de usuarios y a interactuar con sistemas reales, es necesario establecer límites claros en su comportamiento. Ese es exactamente el papel que desempeñan las directrices.
LangChain ofrece varios componentes para este fin:
- Directrices deterministas para reglas que deben ser predecibles
- Directrices basadas en modelos para verificaciones que dependen del significado y el contexto
- Middleware de PII para manejar información sensible
- Middleware con intervención humana para acciones con consecuencias reales
- Middleware previo al agente para verificaciones a nivel de entrada y sesión
- Middleware posterior al agente para validar la salida final
La lección más importante de todo esto es: no confíe únicamente en el LLM para hacer cumplir las reglas de su aplicación.
Deje que el modelo se encargue del razonamiento y la toma de decisiones, pero mantenga los límites realmente importantes codificados en el código y el middleware, donde pueda inspeccionarlos y verificarlos directamente.
Eso es lo que convierte a un agente de IA en algo lo suficientemente fiable para un entorno de producción real.
Referencias
- La guía oficial de LangChain sobre los conceptos de protección, disponible en https://docs.langchain.com/oss/javascript/langchain/guardrails
- La referencia oficial de LangChain que describe cómo funciona el middleware en general, disponible en https://docs.langchain.com/oss/javascript/langchain/middleware/overview
Lecturas relacionadas
- Construyendo un clon local de Angry Birds con Qwen3.8-27B y Pi — Aprenda cómo configurar un flujo de trabajo de programación con IA completamente local utilizando LM Studio y el agente Pi para crear un nivel jugable de Angry Birds con Qwen3.8-27B.
- Restricciones estructurales para agentes de IA: Dentro del pipeline ResolveFlow — Explica cómo un agente basado en LangGraph impone una separación entre el razonamiento y la ejecución a través de verificaciones a nivel de código en lugar de instrucciones en el prompt, incluyendo un error de recuperación que surgió durante el proceso.