Agentes de IA con confirmación humana: por qué la IA no debería actuar sola sobre tus datos

Los agentes de IA son la evolución natural del chat: en lugar de solo responder, hacen cosas. Registran un gasto, crean un presupuesto, mandan un correo o actualizan un registro. Y ahí aparece la pregunta incómoda: ¿qué pasa cuando se equivocan?
En Fino tomé una decisión desde el diseño: Fino AI puede registrar gastos, crear presupuestos e importar estados de cuenta desde el chat, pero siempre con tu confirmación antes de cada acción. En este artículo te explico el patrón detrás de esa decisión y cómo implementarlo.
El problema: el modelo no sabe cuándo duda
Un LLM puede leer “gasté 20 en el super” y registrar $20 en “Supermercado”… o $200, o en la cuenta equivocada. Y lo hará con el mismo tono seguro en ambos casos. Cuando la acción toca dinero, datos personales o algo difícil de deshacer, un error silencioso es mucho peor que un paso extra.
El patrón: proponer → confirmar → ejecutar
La idea es separar dos cosas que normalmente van juntas:
- El modelo propone una acción estructurada (qué herramienta, con qué parámetros).
- La app la muestra al usuario de forma legible y la guarda como pendiente.
- El usuario confirma (o corrige, o cancela).
- El servidor ejecuta, validando todo de nuevo.
El modelo nunca ejecuta nada directamente: solo produce intenciones.
1. Define herramientas con parámetros estrictos
Con tool calling (o function calling), le describes al modelo qué acciones existen y qué parámetros aceptan:
const tools = [
{
name: 'create_expense',
description: 'Registra un gasto del usuario.',
parameters: {
type: 'object',
properties: {
amount: { type: 'number', description: 'Monto positivo' },
currency: { type: 'string', enum: ['USD'] },
category: { type: 'string' },
accountId: { type: 'string' },
date: { type: 'string', format: 'date' },
},
required: ['amount', 'currency', 'category', 'accountId'],
},
},
];Mientras más cerrado sea el esquema (enums, formatos, campos requeridos), menos espacio tiene el modelo para inventar.
2. Convierte la llamada en una acción pendiente
Cuando el modelo responde con una llamada a herramienta, no la ejecutes. Valídala y guárdala:
import { z } from 'zod';
import { randomUUID } from 'node:crypto';
const CreateExpense = z.object({
amount: z.number().positive().max(100_000),
currency: z.literal('USD'),
category: z.string().min(1),
accountId: z.string(),
date: z.string().date().optional(),
});
async function proposeAction(userId: string, toolName: string, rawArgs: unknown) {
const args = CreateExpense.parse(rawArgs); // falla si el modelo inventó algo
// Verifica contra tus datos, no contra lo que dijo el modelo
await assertAccountBelongsToUser(userId, args.accountId);
return db.pendingActions.insert({
id: randomUUID(),
userId,
tool: toolName,
args,
status: 'pending',
expiresAt: Date.now() + 10 * 60 * 1000, // 10 minutos
});
}3. Muestra la acción en lenguaje humano
La confirmación solo sirve si la persona entiende lo que va a aprobar. Nada de JSON en pantalla:
Registrar gasto $20.00 · Supermercado · Cuenta principal · Hoy [Confirmar] [Editar] [Cancelar]
Permitir editar antes de confirmar es clave: convierte un error del modelo en una corrección de dos segundos.
4. Ejecuta con idempotencia
Cuando llega la confirmación, el servidor ejecuta usando el ID de la acción pendiente como llave de idempotencia. Si el usuario toca dos veces “Confirmar” o la red reintenta, la acción se aplica una sola vez:
async function confirmAction(userId: string, actionId: string) {
return db.transaction(async (tx) => {
const action = await tx.pendingActions.findForUpdate(actionId);
if (!action || action.userId !== userId) throw new Error('Acción no encontrada');
if (action.status === 'done') return action.result; // idempotente
if (action.expiresAt < Date.now()) throw new Error('La acción expiró');
const result = await executeTool(tx, action.tool, action.args);
await tx.pendingActions.update(actionId, { status: 'done', result });
await tx.auditLog.insert({ userId, actionId, tool: action.tool, args: action.args });
return result;
});
}5. Deja auditoría
Cada acción ejecutada queda registrada: quién, qué, cuándo y con qué parámetros. Si algo sale mal, puedes explicarlo y revertirlo.
¿Siempre hay que confirmar?
No necesariamente. Mi criterio:
| Tipo de acción | ¿Confirmación? |
|---|---|
| Leer o consultar datos | No |
| Sugerir algo (sin efecto) | No |
| Crear o modificar datos del usuario | Sí |
| Mover dinero, enviar mensajes, borrar | Sí, y con detalle |
La fricción debe ser proporcional al riesgo. Confirmar una lectura es molesto; no confirmar un borrado es peligroso.
Conclusión
Los agentes de IA más útiles no son los más autónomos, sino los más confiables. Separar proponer de ejecutar te da lo mejor de ambos mundos: la comodidad de pedir las cosas en lenguaje natural y la tranquilidad de que nada pasa sin tu visto bueno.
Comparto más sobre IA y desarrollo de productos en TikTok: @andydev22.