El cambio
“Reescribí en un solo agente LangGraph (con 4 tools) lógica que antes habría separado en varios servicios. En un fin de semana. En 2023 me habría llevado meses.”
— Yo, en 2025, cuando esto todavía se sentía nuevo
El stack que aprendiste se está moviendo
¿Recuerdas cuando “full-stack” era React + Node + Postgres? Levantar Express, 15 REST, auth, validación, errores, logging…
Eso sigue en pie, pero asoma un paradigma distinto de raíz:
En vez de if/else interminable para el borde, orquestas con prompts a LLM para la ambigüedad. En vez de SQL clásico, búsqueda en vectores. En vez de toda la lógica a mano, flujos de agente.
Bienvenido al stack agéntico.
Comparación: antes y ahora
Stack clásico (2023)
Explícito, predecible, controlable. Pero: rígido, lento, se rompe con bordes no previstos.
Stack agéntico (2025)
Dinámico, tolera ambigüedad. Pero: probabilista, depura distinto, el coste por token importa a escala.
Ejemplo real: conocimiento interno de empresa
Diferencia con un proyecto a mano: sistema que responde sobre memoria de la compañía (docs, contratos, política, decisiones anteriores).
Enfoque clásico (2023)
- Clúster Elasticsearch, búsqueda full-text
- NER propio y extracción de entidades de dominio
- Parser de la consulta del usuario
- Algoritmo de ranking de relevancia
- Respuestas a partir de plantilla
- Manejo de errores para 20+ bordes…
Tiempo: 6 semanas, 2 ingenieras
Líneas (orden de magnitud): ~8.000
Mantenimiento: afinar sin fin; se quiebra con formas de preguntar nuevas
Enfoque agéntico (2025)
- Trozos + embeddings a pgvector (p. ej. vía LlamaIndex)
- Tool de recuperación con búsqueda semántica
- Prompt con contexto de dominio
- Flujo en LangGraph: pregunta → recupere → sintetice
- Respuesta en streaming con Vercel AI SDK
Tiempo: un fin de semana, una persona
Líneas (orden de magnitud): ~400
Mantenimiento: refinar el prompt, mucho más robusto a la variación
Remate: la vía agéntica sortea bordes que ni siquiera conté. Seguimiento, aclaraciones, cambiar de tema en plena vuelta… El esquema clásico puro se asfixia. (Siempre: control de coste, seguridad, alucinación—eso es otro capítulo.)
Qué cambia de verdad, debajo del capó
Más allá de “meter una API”, cambian abstracciones centrales:
1. De base a sistema de “memoria”
Antes: SELECT * WHERE…
Ahora: búsqueda con embeddings, top-k de fragmentos semánticamente parecidos
No abres filas, recuperas contexto por similitud. Ese cambio de cabeza es bruto.
2. De endpoint a tool
Antes: POST /api/... con esquema JSON rígido
Ahora: una herramienta tipo search_knowledge que el agente invoca cuando toca
Defines capacidad; el modelo elige cuándo y cómo. Orquestas, no microcontrolas cada rama.
3. De lógica en código a lógica en prompt
Antes: 200 if/else por intención de usuario
Ahora: “Eres asistente de conocimiento… responde y cita”
Cuentas comportamiento en lenguaje; el prompt es, en parte, el “programa”, sostenido con criterio y tests.
4. De máquina de estados a grafo de agente
Antes: transiciones fijas
Ahora: nodos LangGraph y aristas condicionales en tiempo de ejecución
Ramas dinámicas según contexto; hay que acotarlas y medirlas.
Comparación de código (mismo rasgo, paradigmas distintos)
Fragmentos representativos de un RAG. Este patrón —grafo de nodos con tools— dejó de ser vanguardia: hoy es tooling maduro y estándar. El texto de los prompts/comentarios en los bloques puede quedarse en inglés, porque muchas librerías y equipos técnicos operan así por defecto:
Enfoque clásico (Flask + Elasticsearch)
@app.route('/api/query', methods=['POST'])
def handle_query():
user_query = request.json['query']
# Entity extraction
entities = ner_model.extract(user_query)
# Build Elasticsearch query
es_query = {
"bool": {
"must": [{"match": {"content": user_query}}],
"should": [{"terms": {"entities": entities}}]
}
}
# Search
results = es.search(index="knowledge_base", body=es_query)
# Rank results
ranked = rank_by_relevance(results, entities)
# Generate response from template
if not ranked:
return {"answer": "No results found"}
top_result = ranked[0]
answer = f"Based on {top_result['title']}: {top_result['summary']}"
return {"answer": answer, "sources": ranked[:3]}
Rígido, paso a paso explícito, se tuerce con preguntas poco “esperadas”.
Enfoque agéntico (LangGraph + pgvector)
from langgraph.graph import StateGraph, START, END
from langchain_openai import ChatOpenAI
from typing_extensions import TypedDict
# Define state schema
class State(TypedDict):
query: str
context: str
answer: str
# Retrieval function using vector store
def retrieve(state: State) -> dict:
docs = vector_store.similarity_search(state["query"], k=5)
return {"context": docs}
def synthesize(state: State) -> dict:
response = llm.invoke(f"""
Context: {state["context"]}
Question: {state["query"]}
Provide a concise answer with citations.
""")
return {"answer": response}
# Build the graph
workflow = StateGraph(State)
workflow.add_node("retrieve", retrieve)
workflow.add_node("synthesize", synthesize)
workflow.add_edge(START, "retrieve")
workflow.add_edge("retrieve", "synthesize")
workflow.add_edge("synthesize", END)
# That's it. Handles variations, follow-ups, clarifications.
agent = workflow.compile()
response = agent.invoke({"query": user_input})
Describe qué (recupera, sintetiza), no todas las ramas. Se adapta mejor a intención; sigue haciendo falta observar y afinar.
Qué stack, cuándo
El agéntico no manda en todos lados. Marco que uso:
✓ Stack agéntico encaja si:
- Entrada ambigua o lenguaje natural y variables
- Requisitos se mueven rápido (modo startup, experimento con guardarraíl)
- Necesitas tolerar bordes que no listaste a mano
- Razona sobre dato poco o nada estructurado
- Gana velocidad a mercado sobre optimizar coste al milímetro
✗ Sigue con lo clásico si:
- Salida 100% determinista (pagos, cumplimiento estricto…)
- Latencia p95 < 100 ms: las llamadas LLM suman 1-3+ s, según ruta y modelo
- Cada céntimo por tramo importa a gran volumen
- Auditoría línea a línea de lógica
- Datos estructurados, reglas claras, caja cerrada: SQL razona bárbaro
La realidad híbrida (cómo vivo en producción)
Casi nunca es puro. Un RAG de empresa a menudo trae clásico + agéntico + otra vuelta de clásico:
- Clásico: autenticación, usuarios, sesión (Postgres)
- Agéntico / LLM: entendimiento, recuperación, redacción (p. ej. LangGraph + proveedor)
- Clásico otra vez: límite de tasa, caché, observación (Redis, APM…)
No tiras todo, eliges piso por piso y capa de riesgo.
Métricas que miré distinto
Cambian KPIs, no solo tráfico. Comparación a grosso modo:
| Métrica | Clásico | Agéntico |
|---|---|---|
| Tiempo de respuesta (orden) | 50–200 ms | 1–5 s (por latencia de LLM) |
| Coste / petición (orden; depende de modelo) | ~$0,001 (ejem.) | $0,02–0,10+ en tokens, según camino y cache |
| Bordes raros | Codificas; si falla, añadís rama | A veces encaja, a veces alucina, hay que atrapar y medir |
| Depurar | Stack trace, log | Trazas de prompt, trazas de cadenas, observar LLM |
| Velocidad de iterar | Más lento en cambio de lógica (código, deploy, test) | Suele ser más barato afinar el prompt que redeployar |
Habilidades, en orden aproximado
Si vas al stack agéntico, carga útil, en fases:
1. Ingeniería de prompts (sí, de veras)
Sistema, few-shot, cadenas de razonamiento: es, en capas, tu nueva lógica de negocio.
2. Bases vectoriales y embeddings
Similitud, troceo, búsqueda híbrida—tu límite frecuente a coste/retrieval.
3. Un framework, a fondo
Primitivos: agent, tool, graph. No 15 “hola mundo” distintos, uno prod-ready.
4. Observabilidad de LLM
LangSmith, Helicone, W&B… el console.log a solas no alcanza a escala real.
5. Coste y latencia
Caché, resúmenes, enrutar a modelo chico, cortar el camino—no mandar todo al modelo más grande por inercia.
Cierre
No solo sumamos herramienta: el paradigma carga. No orquestas cadenas 100% deterministas; conduces sistemas con componente estocástico—con riesgo, con eval, con criterio de negocio.
Y sí, al principio cuesta retorcer el cerebro. A toda la gente que me escribe “me quedo trabado, ¿por qué no sale de localhost?” le digo lo mismo: aprende las bases. No hay atajo. Cuando aterrizas los conceptos y entiendes qué te falta por aprender, es justo ahí donde te vuelves bueno—y entonces produces más rápido, sostienes más complejidad, y armas producto que hace dos años era imposible. Así fue con Gnosix, con Depadoc, con Elderhermit: duro al arranque, hasta que las bases aterrizan.
El dev clásico no murió, pero hoy, en 2026, quedarte solo en React+REST—sin criterio con datos no tabulares y caja de lenguaje—te deja fuera de la conversación. A mí me gusta ser ingeniero, me gusta el código. Pero cada vez trabajo más desde la mente, no desde el teclado: llevo el lenguaje casi hablado y dejo que el sistema ejecute.
Al final todo se reduce a una idea simple: input → IA en medio → output. ¿Cuál es el resultado que quieres generar? ¿Qué buscas exactamente? Si tú no sabes qué quieres, la IA menos. Ese es el trabajo real—saber qué quieres y ponerle rieles al sistema que lo ejecuta. Con eso, y con las bases, ya tienes todo.
