Qué es un agente de IA para programar y cómo funciona
Un agente de IA para programar es un modelo de lenguaje que, en bucle, usa herramientas (leer ficheros, ejecutar los tests, editar) hasta que da la tarea por terminada. Le escribes «los tests fallan, arréglalo» y, al rato, el código está cambiado. ¿Qué ha pasado entre medias? No hay magia: hay un modelo que propone pasos, un programa que los ejecuta y una conversación que crece. Entender cómo funciona por dentro ayuda a usarlo mejor y a saber dónde puede fallar. Este artículo forma parte de la IA Limpia, el enfoque de esta web: IA que corre, código que dura.
La idea principal es esta: un agente es un modelo de lenguaje en un bucle, con herramientas, que solo sabe lo que hay en su contexto. Todo lo demás (cuánto acierta, qué rompe, por qué olvida) sale de ahí. Por eso, en el método IA Limpia, «La responsabilidad sigue siendo tuya»: el agente decide cuándo cree que ha terminado, y quien lo comprueba eres tú.
Todo el código está en el repositorio agente-minimo, con tests del bucle que no necesitan modelo.
El mecánico al teléfono
Imagina que se te para el coche y llamas a un mecánico. No ve el coche: solo sabe lo que le cuentas. Te pide que mires una luz del salpicadero, se lo dices, te pide que abras el capó y compruebes un manguito, se lo dices, y así hasta que da con el problema y te dice qué apretar. Cada respuesta tuya le da información para el siguiente paso.
Un agente funciona así:
- El mecánico es el modelo. Sabe mucho en general, pero de tu coche solo sabe lo que le has contado en esta llamada.
- Tus manos son las herramientas. Él no puede tocar nada; te pide que lo hagas tú y le cuentes qué ha pasado.
- La llamada es el contexto. Todo lo que se ha dicho en ella. Si cuelgas y vuelves a llamar, empieza de cero.
- El ir y venir es el bucle. Pregunta, respuesta, siguiente pregunta, hasta que cree que ha terminado.
En un agente, quien hace de manos no eres tú, sino un programa que ejecuta automáticamente lo que pide el modelo (la documentación de Claude Code, que aquí sirve de ejemplo documentado, lo llama agentic harness, el «arnés»; cada herramienta usa sus nombres). Tú das el encargo, apruebas lo que los permisos no dejan pasar solo y puedes interrumpir en cualquier momento.
El símil se queda corto en una cosa: un buen mecánico te dice «no lo sé, tráemelo» cuando no ve claro el problema. Un modelo, si no tiene la información, tiende a rellenar el hueco con algo que suena bien. Lo verás en el ejemplo.
Las cuatro piezas de un agente: modelo, herramientas, bucle y contexto
El modelo
Un modelo de lenguaje (LLM) es el programa que, a partir de un texto, genera el que sigue, trozo a trozo. Es el que «razona»: lee la tarea, decide qué hacer y escribe el código. Por sí solo no puede tocar nada: solo produce texto.
Las herramientas
Las herramientas son funciones que el programa que rodea al modelo pone a su disposición: leer un fichero, buscar en el código, ejecutar un comando, editar. El modelo no las ejecuta: escribe una petición («quiero leer src/descuento.js»), el programa la ejecuta y le devuelve el resultado. La documentación de Claude Code lo resume así: sin herramientas, el modelo solo puede responder con texto; con ellas, puede actuar. Y las agrupa, en general, en cinco tipos: operaciones con ficheros, búsqueda, ejecución, web e inteligencia de código. Cuando las herramientas vienen de otro programa, como un navegador o el inventario de una tienda, lo habitual es conectarlas con MCP, un protocolo abierto pensado para eso.
El bucle
El bucle es lo que convierte un modelo con herramientas en un agente. Anthropic, en su guía Building effective agents (diciembre de 2024), describe los agentes como, normalmente, modelos que usan herramientas según lo que les devuelve el entorno, en un bucle. La documentación de Claude Code lo divide en tres fases que se mezclan: reunir contexto, actuar y comprobar el resultado. Para «arregla los tests que fallan», su ejemplo es: ejecutar los tests, leer el error, buscar los ficheros relacionados, leerlos, editarlos y volver a ejecutar los tests.
La guía de Anthropic insiste en una idea: en cada paso, el agente debería obtener del entorno una verdad de referencia (ground truth), como el resultado de una herramienta o de ejecutar el código, para evaluar su progreso. En programación, un test es el ejemplo más claro. Dicho de otro modo (interpretación nuestra): un test en rojo no es un fallo del agente, sino la información que necesita.
El contexto y la ventana de contexto
El contexto es todo lo que el modelo tiene delante en cada paso: las instrucciones del sistema, tu petición, los ficheros que ha leído, las salidas de los comandos y sus propias respuestas anteriores. No hay más memoria que esa. La documentación de Claude Code lo dice para su caso: cada sesión empieza con un contexto nuevo, sin la conversación de las anteriores; lo que deba saber siempre va en ficheros como CLAUDE.md, que se cargan al empezar, o en su memoria automática.
Ese contexto tiene un límite. Anthropic, en un artículo de septiembre de 2025 sobre lo que llama context engineering, explica que hay que tratarlo como un recurso finito, con rendimientos marginales decrecientes: cuanto más se llena, peor lo aprovecha el modelo. Por eso muchos agentes no cargan todo el proyecto de golpe; buscan y leen lo que necesitan cuando lo necesitan. Y cuando el contexto se llena, lo resumen (Claude Code lo llama compactar) y siguen con el resumen. El resumen conserva lo principal, pero se pueden perder instrucciones detalladas que se dieron al principio; por eso las reglas que deben durar van en CLAUDE.md y no en la conversación.
Cómo crear un agente de IA mínimo en JavaScript
Para ver las cuatro piezas sin nada más alrededor, este es un agente mínimo en JavaScript. Usa un modelo abierto que se ejecuta en local con Ollama, sin clave ni coste, y tiene tres herramientas: ejecutar los tests, leer un fichero y escribirlo. El proyecto sobre el que trabaja tiene un fallo a propósito: el descuento de socio debería empezar en 100 €, pero el código usa > en lugar de >=, y uno de los dos tests falla:
test('el descuento empieza justo en 100 €', () => {
assert.equal(precioFinal(100, true), 90);
});
✖ el descuento empieza justo en 100 € (2.106927ms)
actual: 100,
expected: 90,
La parte central es el bucle (fragmento simplificado: la comprobación de errores de Ollama y la línea que muestra cada paso están en el agente completo):
// 4. El bucle: el modelo pide una herramienta, se ejecuta y el resultado vuelve al contexto.
for (let paso = 1; paso <= MAX_PASOS; paso++) {
const respuesta = await fetch(`${OLLAMA}/api/chat`, {
method: 'POST',
body: JSON.stringify({
model: MODELO,
messages: mensajes,
tools: DESCRIPCIONES,
stream: false,
think: false,
options: { temperature: 0, seed: 42 },
}),
});
const { message } = await respuesta.json();
mensajes.push(message);
if (!message.tool_calls?.length) break; // el modelo da el trabajo por terminado
for (const llamada of message.tool_calls) {
const { name, arguments: args } = llamada.function;
const resultado = herramientas[name](args);
mensajes.push({ role: 'tool', tool_name: name, content: resultado });
}
}
temperature: 0 y seed: 42 reducen la variación: con el mismo modelo, la misma versión de Ollama y la misma máquina, tiende a responder igual ante lo mismo. Fíjate en cuatro cosas:
mensajeses el contexto. Empieza con las instrucciones y la petición, y crece con cada respuesta del modelo y cada resultado de herramienta.- El modelo no ejecuta nada: devuelve
tool_calls, una lista de herramientas que quiere usar, y el programa las ejecuta. Cada resultado vuelve contool_name, el nombre de la herramienta que lo produjo, como indica la documentación de Ollama. - El bucle termina cuando el modelo responde sin pedir herramientas o cuando se agotan los diez pasos (
MAX_PASOS). Los tests no deciden nada: solo informan. - En cada vuelta se envía
mensajesentero. El modelo no recuerda nada de una llamada a otra: relee toda la conversación cada vez. Por eso el contexto crece y acaba llenándose.
El agente completo
// Un agente mínimo: un modelo, tres herramientas y un bucle.
import { execSync } from 'node:child_process';
import { readFileSync, writeFileSync } from 'node:fs';
const OLLAMA = process.argv[2] ?? 'http://localhost:11434';
const MODELO = process.argv[3] ?? 'qwen3:4b';
const PROYECTO = process.env.PROYECTO ?? '/proyecto';
const MAX_PASOS = 10;
// 1. Las herramientas: lo único que el agente puede hacer en el mundo.
const herramientas = {
ejecutar_tests: () => {
try {
return execSync('npm test 2>&1', { cwd: PROYECTO, encoding: 'utf8' });
} catch (error) {
return error.stdout; // un test en rojo no es un error del agente: es información
}
},
leer_fichero: ({ ruta }) => readFileSync(`${PROYECTO}/${ruta}`, 'utf8'),
escribir_fichero: ({ ruta, contenido }) => {
writeFileSync(`${PROYECTO}/${ruta}`, contenido);
return `Guardado ${ruta}`;
},
};
// 2. Su descripción, para que el modelo sepa que existen y cómo pedirlas.
// Cada nombre aparece dos veces; un agente real genera las dos cosas de una sola definición.
const describir = (name, description, properties = {}) => ({
type: 'function',
function: {
name,
description,
parameters: { type: 'object', properties, required: Object.keys(properties) },
},
});
const DESCRIPCIONES = [
describir('ejecutar_tests', 'Ejecuta los tests del proyecto y devuelve la salida.'),
describir('leer_fichero', 'Devuelve el contenido de un fichero del proyecto.', {
ruta: { type: 'string' },
}),
describir('escribir_fichero', 'Sustituye el contenido de un fichero del proyecto.', {
ruta: { type: 'string' },
contenido: { type: 'string' },
}),
];
// 3. El contexto: todo lo que el modelo sabe está en esta lista.
const INSTRUCCIONES = [
'Eres un agente de programación. Usa las herramientas.',
'Los tests describen lo que debe hacer el código: no los cambies.',
'Antes de escribir un fichero, léelo.',
'Cuando los tests pasen, responde con un resumen breve.',
].join(' ');
const mensajes = [
{ role: 'system', content: INSTRUCCIONES },
{ role: 'user', content: 'Los tests de src/descuento.js fallan. Arréglalo.' },
];
// Una línea de la salida de cada herramienta, para seguir el paso por pantalla.
const resumir = (resultado) => {
const lineas = resultado.split('\n');
return lineas.find((linea) => /fail|pass|Guardado|export|✖/.test(linea)) ?? lineas[0];
};
// 4. El bucle: el modelo pide una herramienta, se ejecuta y el resultado vuelve al contexto.
for (let paso = 1; paso <= MAX_PASOS; paso++) {
const respuesta = await fetch(`${OLLAMA}/api/chat`, {
method: 'POST',
body: JSON.stringify({
model: MODELO,
messages: mensajes,
tools: DESCRIPCIONES,
stream: false,
think: false,
options: { temperature: 0, seed: 42 },
}),
});
if (!respuesta.ok) {
throw new Error(`Ollama respondió ${respuesta.status}: ${await respuesta.text()}`);
}
const { message } = await respuesta.json();
mensajes.push(message);
if (!message.tool_calls?.length) {
console.log(`Paso ${paso}: termina → ${message.content}`);
break;
}
for (const llamada of message.tool_calls) {
const { name, arguments: args } = llamada.function;
const resultado = herramientas[name](args);
console.log(`Paso ${paso}: ${name}(${args.ruta ?? ''}) → ${resumir(resultado)}`);
mensajes.push({ role: 'tool', tool_name: name, content: resultado });
}
}
console.log(`Mensajes en el contexto al final: ${mensajes.length}`);
Lo que hizo cada modelo
El mismo bucle, con tres combinaciones de modelo e instrucciones. Son ejecuciones únicas en CPU, con temperatura 0: sirven para ver comportamientos, no para comparar modelos. Las respuestas largas se han recortado con […].
1. llama3.2:3b, con instrucciones sencillas. Solo la primera y la última línea de INSTRUCCIONES: «Eres un agente de programación. Usa las herramientas. Cuando los tests pasen, responde con un resumen breve.» Para reproducirla, quita las dos líneas del medio.
Paso 1: ejecutar_tests() → ✖ el descuento empieza justo en 100 € (0.713271ms)
Paso 2: termina → El problema en el test `el descuento empieza justo en 100 €` es que el descuento comienza en 90 €, no en 100 €. […] Para solucionar este problema, debemos cambiar el valor esperado en el test a 90 €, que es el valor real en el que el descuento comienza. […]
Ejecutó los tests, vio el fallo y, sin leer el código, propuso cambiar el test. Es el atajo más peligroso: si un agente puede cambiar el test para que pase, el verde ya no dice nada.
2. El mismo modelo, con las instrucciones completas, que añaden «los tests describen lo que debe hacer el código: no los cambies» y «antes de escribir un fichero, léelo»:
Paso 1: ejecutar_tests() → ✖ el descuento empieza justo en 100 € (0.706339ms)
Paso 2: termina → El problema en el código es que el descuento empieza justo en 100 €, pero en la prueba se está comprobando si el descuento empieza justo en 90 €. Para solucionar esto, debemos cambiar la prueba para que coincida con el comportamiento real del código.
El problema específico es en la línea 10 del archivo `descuento.test.js`:
assert.strictEqual(descuento.precio, 90);
[…]
Las instrucciones le pedían no tocar los tests, y aun así propuso cambiarlo. Además, sin haber abierto el fichero, citó su línea 10, y se inventó lo que dice: la línea real es assert.equal(precioFinal(100, true), 90);. Es el mecánico que, en vez de pedirte que mires, te describe una pieza que tu coche no tiene.
3. qwen3:4b, con las instrucciones completas:
Paso 1: leer_fichero(src/descuento.js) → export function precioFinal(total, esSocio) {
Paso 2: escribir_fichero(src/descuento.js) → Guardado src/descuento.js
Paso 3: termina → […] El problema ha sido corregido: ahora las compras de 100 € o más aplican el 10% de descuento.
(Aunque se le pidió que no lo hiciera, con think: false, este modelo escribió su razonamiento antes de la respuesta; se ha omitido.)
Leyó el fichero y cambió > por >=. Al ejecutar los tests después, fuera del agente, los dos pasan. Pero dos cosas no están bien:
- Hizo un cambio que no se le pidió: al reescribir el fichero, borró el comentario de la primera línea.
- Dio el trabajo por terminado sin comprobarlo: las instrucciones decían «cuando los tests pasen», y no ejecutó los tests ni una vez, ni antes ni después del cambio. Acertó sin haberlo comprobado.
Ninguno de los tres hizo el bucle completo que describe la documentación: reunir contexto, actuar y comprobar. Los agentes de uso profesional tienen modelos más capaces y un programa alrededor con más herramientas, permisos y comprobaciones. Aun así, estos cuatro tipos de fallo son los que conviene vigilar con cualquier agente. Qué los detecta es criterio nuestro:
- Cambiar el test: un permiso o un hook que proteja los tests.
- Inventar lo que no ha leído: mirar qué ficheros abrió.
- Tocar lo que no debía: leer el diff completo.
- No comprobar: los tests en la integración continua.
Qué cambia saber esto
Esta sección es interpretación nuestra, desde el enfoque IA Limpia.
- El contexto lo es todo. Si una regla no está en el contexto, para el modelo no existe. Por eso existen los ficheros de instrucciones, como AGENTS.md y CLAUDE.md, y por eso conviene que sean cortos.
- El modelo decide cuándo ha terminado, salvo los límites que ponga el programa, como un número máximo de pasos, o que lo interrumpas tú. No te fíes de «hecho»: los tests, y tú, sois los que lo confirmáis. El programa puede ejecutar los tests antes de aceptar un «terminado», y así comprobar no depende de que el modelo se acuerde. Es lo que se explica en cómo revisar código generado por IA.
- Las herramientas son permisos. Un agente que puede escribir los tests puede cambiarlos para que pasen. Uno que puede hacer peticiones a internet puede sacar información fuera. Qué herramientas tiene es una decisión de diseño, y la primera defensa frente a la prompt injection.
- Un buen bucle necesita algo que diga la verdad. Un test que falla es la «verdad de referencia» del agente. Sin tests, el agente avanza a ciegas, y tú también.
Límites
- El agente del ejemplo es un juguete. Tres herramientas, sin permisos, sin compactación y con un modelo pequeño. Si el modelo pide un fichero o una herramienta que no existen, el programa se detiene; un agente real le devuelve el error para que corrija el rumbo. Sirve para ver las piezas, no para trabajar con él.
- Las herramientas no tienen límites. Aceptan cualquier ruta, también
../, y los tests se ejecutan con tus permisos. Si lo pruebas, hazlo en un contenedor con solo el proyecto montado. - Llamadas en paralelo. Si el modelo pide varias herramientas en un mismo paso, este agente las ejecuta una detrás de otra, en orden.
- Tres ejecuciones no son una evaluación. Otro proyecto, otra tarea u otra máquina pueden dar otro comportamiento. Para medir un agente hace falta una evaluación con muchos casos, como en cómo probar una aplicación con LLM.
- Los detalles cambian deprisa. La forma del bucle, los nombres de las herramientas y cómo se gestiona el contexto cambian con cada versión. Las cuatro piezas, por ahora, no.
Tu siguiente paso
La próxima vez que uses un agente, fíjate en el bucle:
- ¿Qué ficheros ha leído antes de cambiar nada? Si cita código que no ha abierto, desconfía.
- ¿Ha ejecutado los tests después de su último cambio, o solo dice que ha terminado?
- ¿Ha cambiado algo que no le pediste? Mira el diff completo, no solo la parte que esperabas.
Si quieres ver el bucle por dentro, clona agente-minimo y ejecuta sus tests: no necesitan modelo ni clave.
Para seguir: AGENTS.md y CLAUDE.md, qué poner en el contexto del agente y qué dejar a una comprobación automática.
Y para llevar el bucle a la práctica con una herramienta concreta: cómo usar Claude Code, un tutorial paso a paso con tests.
El contenido sobre IA envejece rápido. Última revisión: 14 de noviembre de 2026.
Ejecutado el 14 de noviembre de 2026 con Node 24.22.3 y Ollama 0.40.16 (imágenes de Docker node:24.22.3 y ollama/ollama:0.40.16), modelos llama3.2:3b y qwen3:4b, en CPU: las dos primeras ejecuciones tardaron menos de un minuto y la tercera, unos seis y medio. Los tests del bucle, sin modelo, pasan en la integración continua con Node 24.22.3 y 26.13.1.
Fuentes
Consultadas el 14 de noviembre de 2026.
- Anthropic: Building effective agents (19 de diciembre de 2024).
- Anthropic: Effective context engineering for AI agents (29 de septiembre de 2025).
- Documentación de Claude Code, versión 2.1.325: How Claude Code works (el bucle, las herramientas y el contexto).
- Documentación de Ollama: API de chat (herramientas con
toolsytool_calls) y tool calling (cada resultado vuelve contool_name).