Un sistema RAG chiquito, completo y medido, para entender de qué se habla cuando se habla de flujos agénticos.
Material de la charla "Workflows Agénticos: Guía de Supervivencia" (Interaction Design Foundation, septiembre 2026).
Movés los diales —cómo se corta el texto, cuánto se solapa, cuántos pedazos se traen— y mirás moverse las métricas. Ningún número está inventado: todos salen de correr esto.
git clone https://github.com/glaterza/asado-rag
cd asado-rag
./instalar.sh
./correr.sh web # → http://127.0.0.1:8000La primera vez baja el modelo de embeddings (220 MB, queda en caché). Después arranca en segundos y funciona sin internet.
Probado en Linux. instalar.sh usa uv si está,
y si no python3 -m venv. Necesita Python 3.10+.
./correr.sh # corte fijo, sin solape
./correr.sh --tamano 260 --solape 60 # con solape
./correr.sh --estrategia oracion # cortado por oración
./correr.sh --k 5 # traer 5 pedazos en vez de 3
./correr.sh --contextual # + resumen situador por pedazo (necesita credencial)Sin credencial se mide toda la búsqueda y no se gasta un peso. Con credencial se agrega la respuesta, el costo, la latencia y la verificación de la cita:
export ANTHROPIC_API_KEY=…
./correr.sh webTambién se puede cargar desde el front, en el panel de arriba: queda en memoria de ese proceso, no se escribe a disco ni se devuelve al navegador. El botón Olvidar la borra.
Si preferís un archivo, .env en esta carpeta (ya está en .gitignore):
ANTHROPIC_API_KEY=sk-ant-…
Usa claude-haiku-4-5, el modelo más chico. Correr las seis preguntas cuesta
del orden de medio centavo.
La casilla Contexto agrega adelante de cada pedazo una oración generada que lo sitúa en el documento. No es una tercera forma de cortar: se combina con cualquiera de las dos, y por eso es una casilla y no una opción del desplegable.
Cuesta una llamada al modelo por pedazo, una sola vez al armar el índice — el front te dice cuánto costó. Después queda cacheado hasta que muevas un dial.
⚠ La verdad de referencia se sigue calculando contra los pedazos sin el contexto agregado. Si midiéramos contra el texto contextualizado, el resumen podría "contener" el dato sin que el fragmento lo tenga, y la métrica se estaría mintiendo a sí misma.
1 · Un corte puede volver una pregunta imposible.
Con pedazos de 260 caracteres y sin solape, el texto se parte justo en
…se cortan en tiras de unos 5 a 8 cm de ✂ ancho llamadas "tira de asado".
La medida queda de un lado y el nombre del otro: ningún pedazo tiene el dato entero.
La respuesta estaba en el texto. Se perdió al cortar, no al buscar.
2 · Más solape no es mejor. Medido, con k=3:
| estrategia | recall@3 | precisión@3 | MRR | hit@3 |
|---|---|---|---|---|
| fija, sin solape | 0,750 | 0,250 | 0,583 | 0,750 |
| fija, solape 60 | 0,625 | 0,250 | 0,500 | 0,750 |
| por oración | 1,000 | 0,333 | 0,708 | 1,000 |
El solape empeoró el recall, por dos efectos opuestos: arregló la pregunta rota (0,00 → 0,50) pero fabricó casi-duplicados que empujaron el pedazo bueno de otra pregunta fuera del top 3 (1,00 → 0,00).
Y contextual retrieval sobre un corte malo no lo arregla: medido sobre los mismos 8 pedazos rotos dio recall@3 0,750 —igual que sin contexto— con un costo 3,8× mayor. Mejoró el orden (MRR 0,750, el mejor de los cuatro), pero no encuentra lo que se perdió al cortar. No se compra la salida de un mal corte con una técnica más cara.
3 · La búsqueda nunca devuelve cero. Preguntale por el precio del asado, o por un asado de cocodrilo: no está en el texto y igual trae tres pedazos, con puntajes de 0,24 a 0,32 — indistinguibles de una pregunta cuya respuesta sí está y saca 0,371. Ningún umbral las separa solo.
4 · La métrica se puede hacer trampa a sí misma. Si sacaras del promedio la pregunta que el corte volvió imposible, recall@3 daría 1,000. El código la cuenta como cero y avisa por qué.
Python plano, sin frameworks de agentes. Se lee entero en diez minutos.
rag.py |
el núcleo: cortar, vectorizar, buscar, medir, responder |
app.py |
el front (FastAPI + un HTML, sin build ni node) |
cli.py |
la misma máquina desde la terminal |
textos/asado.txt |
el corpus |
Embeddings: paraphrase-multilingual-MiniLM-L12-v2 vía
fastembed — 384 dimensiones, corre local
sobre ONNX, sin GPU y sin llamadas a ninguna API.
La verdad de referencia marca qué texto hace falta para contestar, no qué pedazo — porque los pedazos cambian con cada estrategia y la verdad no. Un pedazo cuenta como correcto sólo si contiene todos los fragmentos requeridos.
La cita se verifica con código. Se le pide al modelo que cierre con
[FUENTE: n] y se chequea que ese n estuviera entre los fragmentos enviados.
Sin opinión, sin otro modelo, sin costo.
El modelo de embeddings es chico (384 dimensiones); los de producción usan 768–1024 y andan mejor. El patrón —el corte que rompe un dato, el puntaje que nunca da cero— no cambia con el tamaño. Los números puntuales sí.
El texto de textos/asado.txt viene del artículo
Asado de Wikipedia en español,
bajo CC BY-SA 4.0.
El código, MIT. Ver LICENSE.