ruta backend / módulo 6

M6 — AI / Agentic

Outcome del módulo: un mini-RAG o agente demostrable, con evals, integrado como feature real (no demo aislada). Explicar por qué cada decisión (chunking, modelo, retrieval) y probarlo con una eval.

Regla del módulo: cualquiera llama a un LLM; el valor está en lo que lo rodea — retrieval, evals, control de costo/latencia y confiabilidad. Ahí está el diferenciador de backend, no en el prompt.


Concepto 1 — Fundamentos LLM (tokens, context, structured output, tool calling)

Idea núcleo: un LLM predice tokens dado un contexto acotado. Para backend, lo que importa es controlar entrada/salida: límites de contexto, salida estructurada y llamadas a funciones.

Entender (capa 1)

Texto:

Visual:

flowchart LR
    P[prompt + contexto] --> LLM[LLM]
    LLM -->|texto| T[respuesta libre]
    LLM -->|schema| S[JSON/pydantic validado]
    LLM -->|tool call| F[pide ejecutar función args]

Video: How LLMs Work - Tokens, Context Windows, and API Costs Explained — Data Whisperer Academy

Ejemplo (structured output):

from pydantic import BaseModel
class Extraction(BaseModel):
    name: str
    amount: float
    currency: str
# pedir al LLM que devuelva EXACTA esta estructura → parseable, integrable
result: Extraction = llm.extract(text, schema=Extraction)

Fijar (capa 2)

Nota atómica:

Feynman: “Texto libre es pedirle a alguien que te cuente algo y transcribirlo a mano. Structured output es darle un formulario: llena los campos exactos que tu sistema espera.”

Aplicar (capa 3)

Hacé una extracción con structured output (pydantic) sobre un texto. Contá los tokens de entrada/salida. Definí una tool y hacé que el modelo la invoque.

Límites


Concepto 2 — RAG (Retrieval-Augmented Generation)

Idea núcleo: en vez de meter todo al prompt, recuperás solo los fragmentos relevantes de tu base de conocimiento y se los das al modelo. Respuestas fundadas en tus datos, sin reentrenar.

Entender (capa 1)

Texto: pipeline: (1) chunking — partir documentos en fragmentos; (2) embeddings — convertir cada chunk en un vector; (3) guardarlos en un vector store; (4) en la query, embeber la pregunta y traer los chunks más cercanos (retrieval); (5) opcional reranking para ordenar mejor; (6) pasar esos chunks como contexto al LLM.

Visual:

flowchart LR
    subgraph Ingesta
      D[docs] --> CH[chunking] --> EMB[embeddings] --> VS[(vector store)]
    end
    subgraph Query
      Q[pregunta] --> QE[embedding] --> RET[retrieve top-k] --> RR[rerank] --> CTX[contexto] --> LLM[LLM] --> A[respuesta fundada]
    end
    VS --> RET

Video: What is Retrieval-Augmented Generation (RAG)? — IBM Technology

Ejemplo (conceptual):

# ingesta
for chunk in chunk_document(doc, size=500, overlap=50):
    store.add(embed(chunk), metadata={"source": doc.id})
# query
hits = store.search(embed(pregunta), top_k=5)
contexto = "\n".join(h.text for h in hits)
respuesta = llm.answer(pregunta, context=contexto)   # fundada en TUS datos

Fijar (capa 2)

Nota atómica:

Feynman: “RAG es un examen a libro abierto: no memorizás toda la enciclopedia (reentrenar) ni la cargás entera al escritorio (prompt gigante). Buscás las 3 páginas que necesitás y respondés con eso a la vista.”

Aplicar (capa 3)

Armá un mini-RAG sobre un set de docs público: chunking + embeddings + pgvector + retrieval top-k. Probá con 5 preguntas y observá qué chunks trae.

Límites


Concepto 3 — Vector stores

Idea núcleo: una DB especializada en búsqueda por similitud de vectores (nearest neighbor), no por igualdad exacta.

Entender (compacto):


Concepto 4 — Agentes (LangGraph, tool-calling, estado)

Idea núcleo: un agente es un LLM en un loop que decide acciones (llamar tools), observa resultados y sigue, hasta cumplir un objetivo. LangGraph modela ese loop como un grafo de estados.

Entender (capa 1)

Texto: a diferencia de una llamada única, el agente itera: piensa → elige una tool → ejecuta → observa → decide el siguiente paso. LangGraph te da un grafo explícito (nodos = pasos, edges = transiciones) con estado compartido, en vez de un loop implícito difícil de controlar/debuggear.

Visual:

flowchart TD
    START[objetivo] --> THINK[LLM decide]
    THINK -->|usar tool| TOOL[ejecuta tool] --> OBS[observa resultado] --> THINK
    THINK -->|listo| END[respuesta final]

Video: Introduction to LangGraph — LangChain (oficial)

Ejemplo (conceptual):

# nodos con estado compartido; el grafo controla el flujo
from langgraph.graph import END        # sentinel real del nodo terminal (no el string "END")
graph.add_node("plan", plan_step)
graph.add_node("act", call_tool)
graph.add_edge("plan", "act")
graph.add_conditional_edges("act", lambda s: "plan" if not s.done else END)

Fijar (capa 2)

Nota atómica:

Feynman: “Una llamada al LLM es preguntarle algo una vez. Un agente es un empleado con tareas: prueba una herramienta, ve el resultado, decide el siguiente paso. LangGraph es el organigrama que define qué puede hacer y cuándo parar, para que no se quede dando vueltas.”

Aplicar (capa 3)

Armá un agente mínimo con 1-2 tools (ej: buscar en tu RAG + una calculadora) usando LangGraph. Ponele un límite de pasos para evitar loops.

Límites


Concepto 5 — MCP (Model Context Protocol)

Idea núcleo: estándar para exponer tools/recursos a modelos/agentes de forma interoperable, en vez de integrar cada tool a mano por proveedor.

Entender (compacto):


Concepto 6 — Evals (medir que no alucina)

Idea núcleo: sin evals, “funciona” es una opinión. Las evals miden la calidad de las respuestas de forma repetible — el paso que separa un demo de un producto.

Entender (capa 1)

Texto: una eval = un dataset de casos (pregunta → respuesta esperada / criterios) + una métrica. Para RAG: relevancia del contexto recuperado, fidelidad (la respuesta se apoya en el contexto, no inventa), correctitud. La técnica central es LLM-as-judge (otro modelo puntúa) — y frameworks como RAGAS la automatizan (faithfulness, answer relevancy, context precision/recall son LLM-as-judge por dentro). Combinalo con métricas deterministas (exact match, similitud de embeddings) donde apliquen.

Visual:

flowchart LR
    DS[dataset de casos] --> RUN[correr el sistema] --> SCORE[métricas: relevancia / fidelidad / correctitud] --> GATE{¿supera umbral?}
    GATE -->|sí| SHIP[deploy]
    GATE -->|no| FIX[mejorar chunking/prompt/modelo]

Video: AI Agent Evaluation with RAGAS — James Briggs

Fijar (capa 2)

Nota atómica:

Feynman: “Sin evals, evaluás tu IA como quien prueba la sopa una vez y dice ‘buena’. Las evals son la receta con medidas: probás 50 platos con criterio y sabés si esta tanda salió peor que la anterior.”

Aplicar (capa 3)

Armá un dataset chico (10 preguntas con respuesta esperada) para tu mini-RAG y corré una eval de fidelidad/relevancia. Cambiá el chunk size y volvé a medir para ver el efecto.

Límites


Concepto 7 — Costo y latencia

Idea núcleo: en producción, un LLM es un componente con costo por token y latencia real que hay que gestionar y defender ante el negocio.

Entender (compacto):


Concepto 8 — AI-native development workflow

Idea núcleo: las herramientas de IA asistida (Cursor, Copilot, Claude Code, Devin, agent frameworks) son velocidad real de ingeniería cuando reemplazan trabajo mecánico verificable — scaffolding, refactors, schemas, migraciones — y cuando además se usan para construir agentes internos que automatizan tareas repetitivas del propio equipo; son un juguete cuando se usan para generar código que nadie revisa o para tareas donde el costo de un error supera el tiempo ahorrado.

Entender (capa 1)

Texto:

Visual:

flowchart TD
    T[tarea repetitiva de eng] --> D{es scaffolding/refactor/schema puntual, o se repite seguido?}
    D -->|puntual| A[asistente interactivo: Cursor/Copilot/Claude Code]
    D -->|se repite seguido| B[agente interno: corre solo, pega sistemas]
    A --> V[verificar output: tests + lint + review humano]
    B --> V
    V -->|pasa| M[merge / velocidad real]
    V -->|falla| F[descartar o corregir manual]

Video: Coding Agents Explained: How Claude Code, Codex & Cursor Actually Work — ByteByteAI

Ejemplo (agente interno mínimo — bot de revisión de PR):

# agente que corre en CI: lee el diff, corre checks, comenta resumen en el PR
def review_pr(pr_number: int) -> None:
    diff = github.get_diff(pr_number)
    lint_result = run_linter(diff.changed_files)
    test_result = run_tests(diff.changed_files)
    summary = llm.summarize(
        diff=diff,
        context={"lint": lint_result, "tests": test_result},
        schema=PRReviewSummary,   # structured output (Concepto 1)
    )
    github.comment(pr_number, summary.to_markdown())
    # el humano decide el merge; el agente solo acelera el triage

Fijar (capa 2)

Nota atómica:

Feynman: “Un asistente de IA es un aprendiz muy rápido: le das la tarea repetitiva (armar el CRUD, mover el schema) y la entrega en minutos, pero un ingeniero senior sigue revisando el trabajo antes de firmarlo. Un agente interno es contratar a ese aprendiz de planta para una tarea que se repite todos los días — solo tiene sentido si esa tarea realmente se repite lo suficiente como para justificar el sueldo.”

Aplicar (capa 3)

Elegí una tarea repetitiva real de tu flujo (ej: generar el boilerplate de un endpoint nuevo siguiendo el patrón del repo, o resumir diffs de PR). Hacela una vez con un asistente IA (Cursor/Copilot/Claude Code) midiendo tiempo total (generar + verificar) contra hacerla a mano. Si se repite seguido, prototipá un agente interno simple (script + LLM call) que la automatice y corré structured output para que el resultado sea consumible por otro sistema (Concepto 1).

Límites


Concepto 9 — Producción rigurosa: golden datasets, prompt injection y control del loop

Idea núcleo: en 2025-2026 esto ya se pregunta en entrevistas de backend general, no solo roles ML — medir calidad no determinística, defenderse de input hostil, y poder auditar/pausar un agente son responsabilidades de arquitectura, no detalles de implementación.

Entender (capa 1)

Texto — golden dataset (la base real de una eval, no solo “10 preguntas”): un dataset versionado construido con 4 buckets: muestra estratificada de tráfico real, casos adversariales, edge cases deliberados, y replays de fallos que ya llegaron a producción. Tamaños de referencia: 50-100 (mínimo viable) · 200-500 (producción) · 1000+ (sistema maduro). El LLM-as-judge (Concepto 6) no se usa “porque sí” — se valida primero contra ese dataset exigiendo 75-90% de acuerdo con etiquetas humanas antes de confiar en sus scores. El mismo evaluador corre en dev, en gate de pre-release y en producción para que los números sean comparables entre etapas.

Texto — prompt injection, mitigación de arquitectura (no “sanitizar el input”): el patrón de referencia es el Dual LLM (Simon Willison): un LLM privilegiado tiene acceso a las tools pero nunca lee contenido no confiable directamente. Un segundo LLM “en cuarentena” procesa ese contenido (un email, una página web, un doc) pero no tiene acceso a ninguna tool. Un controlador de código convencional media entre los dos: el LLM en cuarentena devuelve referencias simbólicas ($VAR1) en vez de texto crudo, y el controlador sustituye el valor real recién al ejecutar la tool — así la instrucción inyectada nunca llega al modelo que puede actuar. El propio autor del patrón lo llama defensa “bastante deficiente” — quedan vectores de ingeniería social y de encadenar outputs. Es una capa de defense-in-depth, no reemplazo de scopes mínimos de tool ni de aprobación humana en acciones destructivas.

Texto — auditoría/replay y estados explícitos del loop: se loguea cada llamada al LLM (prompt + versión de modelo), cada tool call con su respuesta, diffs de estado y un correlation ID por ejecución — con eso se reproduce exactamente una secuencia fallida para depurarla. Los estados explícitos (PLANNING → EXECUTING → WAITING_FOR_APPROVAL) no son un patrón con nombre fijo — es aplicar máquina de estados a un loop de agente: enumerar estados y transiciones fuerza a definir casos no considerados, y da el punto natural para pausar antes de una acción irreversible (transacción, borrado) y esperar aprobación humana.

Visual:

flowchart TD
    UNTRUSTED[contenido no confiable<br/>email/web/doc] --> Q[LLM en cuarentena<br/>SIN acceso a tools]
    Q -->|devuelve $VAR1, no texto crudo| CTRL[controlador de código]
    CTRL -->|sustituye valor real solo al ejecutar| PRIV[LLM privilegiado<br/>CON tools]
    PRIV --> ACT[ejecuta tool]

    S1[PLANNING] --> S2[EXECUTING]
    S2 -->|acción irreversible detectada| S3[WAITING_FOR_APPROVAL]
    S3 -->|humano aprueba| S2
    S2 -->|termina| DONE[fin]

Video: The Dual LLM pattern for building AI assistants that can resist prompt injection — Simon Willison (artículo, no video)

Fijar (capa 2)

Nota atómica:

Feynman: “El LLM en cuarentena es un traductor que solo puede señalar objetos con el dedo, nunca describirlos con palabras que el que manda pueda malinterpretar como una orden. Los estados del agente son semáforos en una avenida: sin ellos, el agente cruza cualquier intersección a la velocidad que traía; con ellos, hay un punto obligado donde frena y mira antes de cruzar donde de verdad importa.”

Aplicar (capa 3)

Tomá tu agente del Concepto 4 y agregale un estado WAITING_FOR_APPROVAL antes de cualquier tool que escriba/borre datos — que el grafo pause y solo continúe con una señal externa. Por separado, diseñá (no hace falta implementar) cómo aplicarías el patrón Dual LLM si tu agente tuviera que leer el contenido de una página web para resumirla y luego actuar con esa info.

Límites


Checklist de dominio M6

Marcás cuando podés explicar (Feynman) + aplicar:

Salida verificable del módulo: un mini-RAG sobre docs públicos (pgvector) con structured output, un dataset de eval (10 casos) que mide fidelidad/relevancia, y medición de tokens/latencia. Bonus: un agente con 1-2 tools y límite de pasos.