Elige lo que el modelo ve
La ingeniería de contexto consiste en decidir qué entra en la ventana del modelo en cada paso de una ejecución de agente. La ventana es finita, así que cada parte es una decisión.
Un agente que guarda todos los archivos y todos los resultados de herramientas llena la ventana en el sexto paso. Entonces las instrucciones importantes quedan enterradas bajo salidas que nadie necesita.
Al final de esta lección
Nota
Puedo decidir qué entra en la ventana en cada paso, dar un presupuesto a cada parte y decir qué se descarta primero.
Palabras clave
| Palabra | Qué significa |
|---|---|
| Ventana de contexto | todo lo que el modelo lee en una llamada |
| Presupuesto | el máximo de tokens que cabe en una ventana |
| Fijada | una parte que nunca se descarta |
| Resultado de herramienta | lo que imprimió una herramienta, devuelto al modelo |
| Memoria | notas que se guardan entre ejecuciones y se cargan al empezar |
| Orden de descarte | qué partes salen primero cuando la ventana está llena |
Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.
Una ventana se compone de partes
Cada llamada envía partes en un orden fijo. Las instrucciones dicen quién es el agente y qué puede hacer. La memoria guarda notas duraderas. Los archivos son aquello en lo que trabaja. Los resultados de herramientas son lo que imprimieron sus herramientas. El historial es la conversación hasta ahora.
Cómo se guarda la memoria es otro tema, el de construir agentes. Aquí la pregunta es solo cuánta cabe.
Qué descartar primero
Da un presupuesto a toda la ventana y descarta partes hasta que quepa. Descarta primero lo que cuesta menos reconstruir. Un resultado de herramienta antiguo se puede pedir de nuevo. Un archivo se puede volver a leer. La memoria es más lenta de rehacer.
No descartes nunca las instrucciones. Fija el archivo que se está editando. Deja de descartar en cuanto la ventana quepa, porque cada descarte de más pierde algo.
Orden de descarte de un agente de código
| Parte | Se descarta | Por qué |
|---|---|---|
| Resultados de herramientas | Primero, los más antiguos antes | La herramienta puede ejecutarse otra vez |
| Archivos | Después, salvo los fijados | El archivo se puede volver a leer |
| Memoria | En tercer lugar | Las notas son más lentas de rehacer |
| Historial | Al final | Contiene lo que pidió la persona |
| Instrucciones | Nunca | Sin ellas el agente no tiene reglas |
Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.
Un error frecuente
Advertencia
Mucha gente recorta por el principio y quita los mensajes más antiguos sea cual sea su contenido. Así se van primero las instrucciones. Descarta según lo que es una parte, no según su edad.
Escribe el ayudante del modelo, parte 1
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const base = process.env.AI_URL || \"http://ai:4000\";' '// Los tokens de una respuesta, o null si no trae ninguno.' 'function count(data, name) {' ' return data.usage && typeof data.usage[name] === \"number\" ? data.usage[name] : null;' '}' > model.js"Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
Escribe el ayudante del modelo, parte 2
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'async function ask(messages, sample, max) {' ' try {' ' const res = await fetch(base + \"/v1/chat/completions\", {' ' method: \"POST\",' ' headers: { \"content-type\": \"application/json\", authorization: \"Bearer lab\" },' ' body: JSON.stringify({ model: \"lab\", messages: messages, max_tokens: max || 100 }),' ' signal: AbortSignal.timeout(30000),' ' });' ' if (res.ok === false) throw new Error(\"estado \" + res.status);' ' const d = await res.json();' ' return { text: d.choices[0].message.content, inTokens: count(d, \"prompt_tokens\"), outTokens: count(d, \"completion_tokens\"), recorded: false };' ' } catch (error) {' ' console.log(\"Muestra grabada: ningún modelo respondió (\" + error.message + \").\");' ' return { text: sample, inTokens: null, outTokens: null, recorded: true };' ' }' '}' 'module.exports = { ask };' >> model.js"Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
Escribe el armador de ventana, parte 1
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const { ask } = require(\"./model\");' 'const BUDGET = 60;' '// Aquí un token es una palabra. Un tokenizador real cuenta de otra forma.' 'function size(text) { return text.split(\" \").length; }' 'const parts = [' ' { kind: \"instructions\", name: \"reglas\", text: \"Eres un asistente de código. Edita solo los archivos que se te muestran. Di qué has cambiado.\" },' ' { kind: \"memory\", name: \"notas\", text: \"El proyecto usa tabuladores. Las pruebas están en tests. Ejecútalas antes de cada commit.\" },' ' { kind: \"file\", name: \"app.py\", pinned: true, text: \"def total(items): return sum(items) # suma los precios de una cesta\" },' '];' > context1.js"Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
Escribe el armador de ventana, parte 2
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'parts.push({ kind: \"tool_result\", name: \"prueba 1\", text: \"3 pasaron 1 falló test_total esperado 12 obtenido 10 en tests barra test_app\" });' 'parts.push({ kind: \"tool_result\", name: \"prueba 2\", text: \"4 pasaron 0 fallaron\" });' 'parts.push({ kind: \"file\", name: \"leeme\", text: \"Un pequeño script de tienda que calcula el precio de las cestas e imprime un recibo.\" });' 'const DROP = [\"tool_result\", \"file\", \"memory\"];' 'function total(list) {' ' let sum = 0;' ' for (const p of list) sum = sum + size(p.text);' ' return sum;' '}' >> context1.js"Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
Escribe el armador de ventana, parte 3
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'function assemble(all, budget) {' ' const kept = all.slice();' ' const dropped = [];' ' for (const kind of DROP) {' ' for (const p of all) {' ' if (total(kept) <= budget) break;' ' if (p.kind === kind && p.pinned === undefined) {' ' kept.splice(kept.indexOf(p), 1);' ' dropped.push(p.name);' ' }' ' }' ' }' ' return { kept: kept, dropped: dropped, used: total(kept) };' '}' >> context1.js"Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
Escribe el armador de ventana, parte 4
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'async function main() {' ' const w = assemble(parts, BUDGET);' ' console.log(\"presupuesto \" + BUDGET + \", usado \" + w.used);' ' for (const p of parts) {' ' console.log((w.kept.includes(p) ? \"conservado \" : \"descartado \") + p.kind + \" \" + p.name + \" \" + size(p.text));' ' }' ' const body = w.kept.slice(1).map(function (p) { return p.name + \": \" + p.text; }).join(\" | \");' ' const question = body + \" | ¿Qué archivo editas primero? Una frase.\";' ' const messages = [{ role: \"system\", content: w.kept[0].text }, { role: \"user\", content: question }];' ' const reply = await ask(messages, \"Edito app.py primero, porque la prueba que falla es sobre total.\", 40);' ' console.log(\"modelo: \" + reply.text);' '}' 'main();' >> context1.js"Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
Ejecuta y mira qué se descartó
docker exec -w /home/node/project agent node context1.jsSe muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
Las palabras hacen de tokens
Una ventana real se cuenta en tokens con el tokenizador del modelo. Aquí una palabra es un token, así que cada estudiante imprime los mismos tamaños. La idea es la misma: sumar las partes y comparar con un límite.
El Run hace una llamada corta con lo conservado. Sin modelo imprime una muestra grabada.
Una salida posible
| Línea | Qué muestra |
|---|---|
| presupuesto 60, usado 58 | Ahora la ventana cabe |
| conservado instructions reglas 17 | Las instrucciones nunca se descartan |
| descartado tool_result prueba 1 13 | El resultado más antiguo sale primero |
| descartado tool_result prueba 2 4 | El descarte para en cuanto cabe |
| conservado file leeme 16 | No se descartó ningún archivo, así que el leeme se queda |
| modelo: ... | Una frase. Las palabras cambian en cada ejecución |
Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.