Volver al portafolio
sep. 20258 min de lectura

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)

Frontend: React, Vue, Svelte
Backend: Node.js, Python Flask/FastAPI, Go
Base de datos: PostgreSQL, MongoDB
API: REST o GraphQL
Lógica de negocio: if/else a mano, máquinas de estado
Despliegue: EC2, Vercel, contenedores…

Explícito, predecible, controlable. Pero: rígido, lento, se rompe con bordes no previstos.

Stack agéntico (2025)

Frontend: Next.js + Vercel AI SDK (UI en stream)
Orquestación: LangGraph, LangChain, LlamaIndex
LLM: OpenAI, Anthropic, modelos abiertos vía Together, Replicate…
Memoria: base vectorial (Pinecone, pgvector, Weaviate…)
Lógica de negocio: agentes + tools + prompts
Despliegue: Vercel, Modal, Lambdas…

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)

  1. Clúster Elasticsearch, búsqueda full-text
  2. NER propio y extracción de entidades de dominio
  3. Parser de la consulta del usuario
  4. Algoritmo de ranking de relevancia
  5. Respuestas a partir de plantilla
  6. 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)

  1. Trozos + embeddings a pgvector (p. ej. vía LlamaIndex)
  2. Tool de recuperación con búsqueda semántica
  3. Prompt con contexto de dominio
  4. Flujo en LangGraph: pregunta → recupere → sintetice
  5. 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étricaClásicoAgéntico
Tiempo de respuesta (orden)50–200 ms1–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 rarosCodificas; si falla, añadís ramaA veces encaja, a veces alucina, hay que atrapar y medir
DepurarStack trace, logTrazas de prompt, trazas de cadenas, observar LLM
Velocidad de iterarMá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.

Ulises Arellano
CEO e ingeniero de IA, Gnosix
GitHub