De los prompts a los productos: el nuevo stack de producción
Ya no programas. Orquestas.
El cambio
“Reescribí en un solo agente LangGraph (con 4 tools) lógica que antes llevaba tres microservicios. En un fin de semana. En 2023 me habría llevado tres meses.”
— Yo, el mes pasado, aún asimilando
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.
Bienvenida(o) 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 stream con Vercel AI SDK
Tiempo: un fin de semana, una persona (yo)
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 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 (con riesgo de caja, claro).
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” (con criterio, tests y revisiones, no vibing solo).
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 (y, otra vez, hay que acotar y medir.)
Comparación de código (mismo rasgo, paradigmas distintos)
Fragmentos reales de un RAG; el texto de los prompts/comentarios en los bloques puede dejarse en inglés, que el código liga a librerías y equipos de verdad con inglés 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 no listados a mano, con riesgo medido
- • Razona sobre dato poco o nada estructurado
- • (Por ahora) gana velocidad a mercado sobre optimizar coste al milímetro, con plan de bajar CPM luego
✗ 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 groso 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) | A veces más barato afinar el prompt—no siempre, si hay evaluación seria |
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 lógica de negocio bajo riesgo.
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 solo “mandar a GPT-4” a todo.
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.
Cuesta, al inicio, retorcer el cerebro. Pero, una vez: produces más rápido, sostienes más complejidad, encajas producto imposible hace dos años—si acompañas con disciplina, no vibing.
El dev clásico no murió, pero en 2025, solo React+REST—sin criterio con datos no tabulares y caja de lenguaje—es un agujero. El salto, desde móvil, aún duele, pero pesa otra pista.
Aprende a orquestar. Deja de cazar solo control, busca sentido, dirección, evaluación en un sistema que, por su naturaleza, tira a probabilidad.