Hace unos meses nos tocó meter mano a un sistema que convertía texto libre —notas de comerciales, transcripciones de llamadas, correos reenviados— en registros estructurados de CRM: clientes, oportunidades, próximos pasos, resúmenes acumulativos. La lógica de negocio estaba bien pensada y el prompt lo había escrito alguien que sabía mucho del dominio comercial.
El problema era otro: tardaba entre 46 y 68 segundos por entrada.
Cincuenta y ocho segundos de media. Más de un minuto de spinner cada vez que un comercial guardaba una nota desde el móvil. Hoy ese mismo proceso responde en 8-12 segundos: hasta ocho veces más rápido, con la misma funcionalidad, el mismo modelo base y sin eliminar un solo campo del resultado.
Este artículo cuenta cómo bajamos ese proceso a menos de 20 segundos sin quitar ni una sola funcionalidad. No hay ningún truco de prompt. El cambio fue arquitectural, y la conclusión —que ya intuíamos, pero que aquí quedó medida— es que un prompt gigante no es una arquitectura, es un aplazamiento de la arquitectura.
El proyecto es de un cliente, así que todo lo que sigue está anonimizado: no hay nombres, ni prompts literales, ni datos de negocio. Lo que sí es real es la estructura del problema, las medidas y las decisiones.
Paso 1: todo en una sola llamada
El punto de partida era el clásico, y no lo digo con desprecio: es como empieza el 90% de las funcionalidades de IA que se validan primero en un chat. Una única llamada al modelo, con un system prompt de varios miles de palabras, que hacía todo esto a la vez:
- Detectar qué eventos comerciales había en el texto
- Clasificarlos por tipo
- Interpretar el subtexto y decidir cuál era el movimiento más importante
- Construir un JSON por evento con más de treinta campos
- Generar narrativa: fotos de estado, insights tácticos, briefings, guiones de llamada
- Consolidar los resúmenes acumulativos de cada cliente y cada oportunidad, respetando el histórico
Funcionaba. En un chat, con un ejemplo bonito, funcionaba bastante bien. Pero en producción, con temperatura 0.7 y diez peticiones seguidas, los tiempos eran estos:
| Métrica | Tiempo |
|---|---|
| Mínimo | 45,6 s |
| Máximo | 67,9 s |
| Media | 58,3 s |
| P95 | 67,9 s |
Bajar la temperatura a 0.2 —la sospecha habitual— no cambió nada: media de 59,1 s. Ahí ya sabíamos que el problema no era de configuración.
El punto de partida: una llamada, cinco responsabilidades, un output enorme y 58,3 segundos de espera.
Por qué una sola llamada era el problema
Hay tres razones, y conviene separarlas porque se arreglan de forma distinta.
La latencia la marca el output, no el input. Un LLM genera tokens de uno en uno. Puedes meterle 8.000 tokens de contexto y los procesará rápido, pero cada token que escribe cuesta tiempo. Un prompt que pide un JSON de treinta campos más cinco textos narrativos por cada evento detectado no tarda mucho porque piense mucho: tarda mucho porque escribe muchísimo. Y si el texto de entrada contiene tres eventos comerciales, el output se triplica.
La atención se diluye. Cuando metes reglas de formato, definiciones de campos, instrucciones de tono, criterios de priorización y lógica condicional en el mismo prompt, todas compiten por el mismo presupuesto de atención. Una regla de naming puede robarle capacidad de razonamiento a la detección de eventos. Es el mismo efecto que tiene pedirle a una persona que, en una sola pasada, lea un texto, lo clasifique, rellene un formulario de treinta campos, escriba tres párrafos de análisis estratégico y actualice un histórico. Puede hacerlo. Lo hará peor que si se lo pides por partes.
El razonamiento es opaco. Si el sistema detecta mal un evento, el error se arrastra a la estructuración, a la narrativa y a la consolidación. No hay forma de saber dónde falló ni de reintentar solo esa parte. Y tampoco puedes optimizar nada por separado: si la narrativa tarda mucho, no puedes quitarla sin romper el resto del output.
Ese tercer punto es el que más caro sale a medio plazo, y es el menos visible en una demo.
Paso 2: partir el prompt para ver, no para correr
El primer cambio no lo hicimos para optimizar. Lo hicimos para entender.
Separamos la llamada única en fases con entradas y salidas explícitas, medimos cada una por separado y dejamos el resto igual. Ni paralelización, ni modelos distintos, ni recortes de prompt. Solo trocear y cronometrar.
El resultado fue una mejora modesta —de 58 a 28,4 segundos, básicamente porque cada llamada generaba menos texto de golpe—, pero eso no era lo importante. Lo importante es que pasamos de una caja negra a un sistema observable. Por primera vez podíamos responder a “¿dónde se van los segundos?”.
Mismo trabajo, tres fases medibles. La ganancia de tiempo fue modesta; la de visibilidad, total.
Y la respuesta fue clara: el paso de estructuración seguía siendo un mini-cerebro completo. Detectaba, estructuraba, priorizaba, escribía narrativa y consolidaba entidades. Habíamos partido el monolito en trozos, pero uno de los trozos seguía siendo un monolito.
Este paso intermedio parece prescindible cuando lo cuentas después. No lo es. Si hubiéramos saltado directamente al diseño final, habríamos optimizado a ciegas, y probablemente el paso equivocado.
Paso 3: especializar, paralelizar y sacar del camino crítico
Con las medidas delante, el rediseño se hizo solo. Tres decisiones:
- Una responsabilidad por llamada. El paso grande se partió otra vez, separando lo operativo (datos que van a base de datos) de lo narrativo (textos que lee una persona).
- Lo que no depende de nadie, en paralelo. Varios pasos solo necesitaban la salida de la detección inicial, no la de los demás. No había razón para ejecutarlos en serie.
- Lo que no bloquea, al segundo plano. La narrativa es lo más lento de generar y lo que menos urge: nadie lee un briefing de reunión en el mismo segundo en que guarda una nota.
El pipeline quedó así:
| Paso | Qué hace | Modelo | Tiempo | ¿Bloquea al usuario? |
|---|---|---|---|---|
| 1. Detección | Identifica y clasifica los eventos del texto | Ligero | 3-5 s | Sí |
| 2. Estructuración | Convierte cada evento en un registro operativo | Principal | 5-10 s | Sí |
| 3. Consolidación | Actualiza los resúmenes acumulativos de cada entidad | Principal | 5-10 s | Sí, en paralelo con el 2 |
| 4. Acciones automáticas | Detecta cambios explícitos de datos aplicables sin criterio | Ligero | 2-4 s | Sí, en paralelo con 2 y 3 |
| 5. Enriquecimiento | Genera narrativa, insights y contenido de agenda | Principal | 10-15 s | No |
Los pasos 2, 3 y 4 arrancan a la vez en cuanto termina el 1, porque los tres consumen lo mismo: la lista de eventos detectados. El paso 5 se lanza después de haber respondido al usuario y actualiza los registros cuando termina.
El diseño final: un paso ligero que abre el grafo, tres pasos en paralelo y toda la narrativa fuera del camino crítico.
Los números
| Antes | Con pipeline secuencial | Con pipeline paralelo | |
|---|---|---|---|
| Tiempo total | 58,3 s | 28,4 s | 14,2 s |
| Tiempo percibido | 58,3 s | 28,4 s | 8-12 s |
Dicho en una frase: el proceso quedó más de 4 veces más rápido de punta a punta y hasta 8 veces más rápido en lo que el usuario percibe. La espera pasó de 58,3 segundos —más de un minuto delante de un spinner— a 8-12 segundos. Un 83% menos, con la misma funcionalidad y con el mismo modelo base.
Y ni siquiera es la comparación más dura. El peor caso medido en el sistema original fue de 67,9 segundos: más de un minuto para guardar una nota. Hoy el tramo más lento de todo el proceso —la narrativa, 10-15 segundos— ya no está en el camino crítico. El usuario no lo espera nunca.
Qué significan 48 segundos menos
Los porcentajes se olvidan; el tiempo acumulado no. Con 100 entradas al día, que es un volumen modesto para un equipo comercial mediano:
| Antes | Ahora | |
|---|---|---|
| Espera acumulada al día | 1 h 37 min | 17 min |
| Al mes (20 días laborables) | 32 horas | 6 horas |
Son 26 horas al mes —más de tres jornadas completas— que un equipo dejó de pasar mirando una pantalla que no responde.
Pero el cambio importante no es aritmético, es de comportamiento. Por encima de los 40 segundos, la gente lanza la petición y se va: abre otra pestaña, coge el coche, se olvida. Por debajo de los 20, se queda mirando el resultado — y si algo salió mal, lo corrige en caliente, cuando aún recuerda la conversación que acaba de tener. Esa diferencia no aparece en ninguna gráfica de latencia y es la que decide si la funcionalidad se usa o se abandona.
Y hay una parte que tampoco sale en la tabla: ahora sabemos qué paso falla cuando algo falla, podemos reintentar solo ese, podemos cambiar el modelo de un paso sin tocar los demás y podemos añadir un paso nuevo sin reescribir un prompt de 4.000 palabras.
Cómo quedaron los prompts en la práctica
Nada de esto se sostiene solo con arquitectura de cajas y flechas. Hay técnicas concretas de prompting detrás de cada paso, y son la parte que normalmente no se cuenta. No podemos enseñar el prompt real del cliente —es su propiedad—, pero sí el patrón, que es reutilizable en cualquier proyecto con esta forma.
Antes: un system prompt con cinco trabajos dentro
El prompt original tenía la forma clásica del “hazlo todo”: una lista larga de instrucciones, todas al mismo nivel, compitiendo por la misma atención y con el JSON de salida descrito en prosa al final.
Eres un asistente que procesa notas comerciales.
1. Detecta todos los eventos comerciales del texto (reunión, oferta, objeción, cierre...)
2. Para cada evento, clasifícalo por tipo y por urgencia
3. Construye un JSON con: cliente, oportunidad, tipo, fecha, importe, probabilidad,
próximos pasos, contactos mencionados... (30+ campos)
4. Escribe una narrativa de estado, un briefing de reunión y un guion de llamada
5. Actualiza el resumen acumulado del cliente y de la oportunidad, fusionando
con el histórico sin perder información previa
Responde siempre en JSON con este formato: {...}
Genéricamente correcto y muy difícil de mantener. Pedir “responde en JSON” en texto libre —en vez de forzar la salida por schema— provoca fallos de parseo con cierta regularidad, y cualquier ajuste de tono en el punto 4 arriesga romper el punto 3, porque los dos viven en el mismo bloque de texto.
Después: un prompt, un trabajo, una técnica distinta en cada uno
Paso 1 — Detección. Prompt corto, modelo ligero, temperatura 0, y salida forzada por tool calling en lugar de pedir JSON en prosa:
const result = await client.messages.create({
model: "modelo-ligero",
temperature: 0,
system: "Detecta los eventos comerciales del texto. Nada más.",
tools: [{
name: "eventos_detectados",
input_schema: {
type: "object",
properties: {
eventos: {
type: "array",
items: {
type: "object",
properties: {
tipo: { enum: ["reunion", "oferta", "objecion", "cierre"] },
fragmento: { type: "string" }
},
required: ["tipo", "fragmento"]
}
}
},
required: ["eventos"]
}
}],
tool_choice: { type: "tool", name: "eventos_detectados" }
});
Forzar la salida por schema en vez de pedir “responde en JSON” elimina de raíz una categoría entera de fallos: JSON mal cerrado, comentarios colados, campos inventados que no estaban en el contrato. El paso 3 del prompt original —el que más fallos silenciosos daba— desapareció casi entero solo con este cambio.
Paso 2 — Estructuración. Mismo patrón, schema más grande, pero un único trabajo: convertir un evento ya detectado en un registro. No decide qué es relevante ni escribe una sola línea de narrativa; eso ya lo resolvió el paso 1 y lo resolverá el paso 5.
Paso 5 — Enriquecimiento. Aquí cambia casi todo lo demás: temperatura alta (0.7-0.8), el modelo principal, y un system prompt que ya no compite por espacio con reglas de formato:
Eres [la voz del asistente]. Tu única tarea es escribir, a partir de este
registro ya estructurado y validado, una narrativa breve para el comercial:
qué pasó, qué se juega en el próximo paso, cómo abrir la próxima llamada.
Tono: directo, cercano, sin relleno corporativo.
No inventes datos que no estén en el registro.
Ese prompt no tiene reglas de formato de JSON, ni definiciones de campo, ni lógica de clasificación. Solo tono y una tarea. Es la diferencia entre pedirle a alguien que redacte bien y pedirle que redacte bien mientras rellena un formulario de treinta campos a la vez.
Tres técnicas que cambiaron más las cosas que el rediseño en sí
- Tool calling / structured outputs en cada paso operativo, nunca “responde en JSON” en texto libre. Es el cambio con mejor ratio esfuerzo-beneficio de todo el proyecto: convierte un fallo de parseo impredecible en un error de validación de schema, que se puede capturar y reintentar.
- Prompt caching del contexto compartido. Los pasos 2, 3 y 4 reutilizan buena parte del mismo contexto —el evento detectado, las reglas de negocio del dominio—. Cachear ese bloque evita pagarlo entero como tokens nuevos en cada una de las tres llamadas paralelas.
- Un dataset de evaluación por paso, no uno global. Antes de tocar un prompt en producción, lo pasamos por 20-30 casos de ejemplo con salida esperada para esa fase concreta. Sin esto, cada ajuste de prompt es un experimento sin control: puedes mejorar la detección y no enterarte de que rompiste la consolidación hasta que un cliente lo nota.
Nada de esto es exótico ni requiere una librería nueva. Es prompting básico —temperatura por tarea, schema en vez de prosa, contexto cacheado, evaluación por fase— aplicado de forma disciplinada, paso a paso, en vez de amontonado en un único bloque de texto.
Las reglas que sacamos en claro
Si tienes una funcionalidad de IA que hace demasiado en una sola llamada, este es el orden en el que la atacaríamos hoy:
1. Separa los datos de la narrativa. Es la división más rentable de todas y casi siempre existe. Los campos operativos son cortos, deterministas y validables contra un esquema. Los textos narrativos son largos, subjetivos y se benefician de un prompt con personalidad. Mezclarlos obliga al modelo a cambiar de registro a mitad de generación, y encarece cada regeneración: si solo quieres retocar el tono de un briefing, no deberías tener que volver a extraer los datos.
2. Manda al segundo plano todo lo que el usuario no lea ahora. El criterio no es la importancia, es el momento de consumo. Un resumen que se leerá mañana antes de una reunión puede tardar 20 segundos en generarse. Un dato que aparece en pantalla al guardar, no.
3. Paraleliza por dependencias reales, no por orden lógico. Es fácil encadenar los pasos en el orden en que los pensaste. Dibuja qué necesita cada paso de verdad: en nuestro caso, tres de los cinco solo dependían del primero.
4. Usa el modelo barato donde no hay criterio. Clasificar, detectar y extraer cambios explícitos es trabajo mecánico. No necesita el modelo más caro: necesita instrucciones claras y temperatura baja. Reserva el modelo bueno para donde hay interpretación.
5. Empieza midiendo, no optimizando. Trocear para observar es un paso con valor propio aunque no mejore los tiempos. Sin medidas por fase, optimizar es adivinar.
Lo que esta arquitectura te cobra
Cualquiera que te venda pipelines de agentes sin contarte esta parte te está vendiendo la mitad.
Más puntos de fallo. Cinco llamadas son cinco cosas que pueden dar timeout, devolver un JSON roto o quedarse a medias. Necesitas reintentos por paso, validación de esquema en cada salida y una política clara de qué hacer cuando falla un paso de background (¿reintentas?, ¿lo dejas vacío?, ¿avisas?). Ese código no existía cuando todo era una llamada.
Tokens de entrada repetidos. Cada paso necesita su parte de contexto, y hay contexto que viaja en varias llamadas. El coste de input sube. En nuestro caso lo compensó de sobra la bajada de tokens de salida —que son los caros— y la caché de prompt, pero es una cuenta que hay que hacer, no asumir.
Coherencia de voz. Al partir la narrativa en un paso propio, ese paso deja de ver el prompt completo con toda la personalidad del asistente. Si no le pasas suficiente contexto de tono, el resultado sale correcto pero plano. Nos costó un par de iteraciones ajustarlo.
Orquestación de verdad. Alguien tiene que gestionar el paralelismo, los timeouts, el orden de escritura en base de datos y qué pasa si el paso de background termina después de que el usuario haya editado el registro a mano. Es trabajo de ingeniería normal, pero es trabajo.
Ninguna de estas contrapartidas nos hizo dudar de la decisión. Todas nos hicieron dedicarle días que no habríamos dedicado a mantener el monolito.
El cambio más importante no fue técnico
Hay un beneficio que no aparece en ninguna métrica y que, con el tiempo, resultó el más valioso.
En un proyecto así hay dos tipos de conocimiento distintos. Está el de quien define qué es un evento comercial relevante y cómo se estructura, que es conocimiento de dominio y de datos. Y está el de quien define cómo habla el asistente, que es conocimiento de producto, de tono y de marca. Con un prompt monolítico, esas dos personas editan el mismo bloque de texto de 4.000 palabras y se pisan constantemente. Cualquier ajuste de tono obliga a revalidar la extracción de datos, y cualquier ajuste de extracción puede cargarse la voz.
Al separar los pasos, cada uno tiene dueño. La persona que define la voz itera sobre el prompt de enriquecimiento sin tocar nada de lo operativo. El equipo técnico ajusta la extracción sin reescribir la personalidad. Se puede versionar, comparar y revertir por separado.
Dicho de otra forma: dejamos de intentar que el modelo lo hiciera todo y empezamos a diseñar cómo queríamos que pensara. Y el efecto secundario fue que también quedó claro quién decide qué.
Cómo replicar esto en tu proyecto
Si estás en el punto de partida —una funcionalidad de IA que va bien en la demo y regular en producción— el orden que seguiríamos es:
- Instrumenta antes de tocar nada. Mide latencia, tokens de entrada y de salida por petición. Si no puedes decir cuántos tokens genera tu peor caso, aún no sabes cuál es tu problema.
- Lista las responsabilidades del prompt actual. Si al enumerarlas te salen más de tres verbos distintos (detectar, clasificar, redactar, consolidar…), tienes un pipeline escondido dentro de un prompt.
- Trocea y mide, sin optimizar. Acepta que la primera versión troceada mejore poco. El objetivo es ver.
- Dibuja el grafo de dependencias reales y paraleliza lo que no dependa entre sí.
- Saca del camino crítico todo lo narrativo y devuélvelo cuando esté listo.
- Fija una evaluación por paso antes de seguir optimizando: un conjunto de entradas de ejemplo con salidas esperadas, para cada fase. Sin esto, cada mejora de velocidad es una apuesta sobre la calidad.
El punto 6 es el que más gente se salta y el que más caro sale. Una arquitectura modular sin evaluación por módulo es una arquitectura modular que no sabes si funciona.
Nada de esto exigió un modelo mejor, ni más presupuesto de tokens, ni una librería de agentes. Exigió decidir qué tenía que pensar el modelo en cada momento — y ese cambio, medido, valió 48 segundos por entrada.
Si estás construyendo funcionalidades de IA sobre LLMs y te encuentras con tiempos de respuesta que no te encajan, o con un prompt que ya nadie del equipo se atreve a tocar, hablamos. Es un patrón que hemos visto lo suficiente como para reconocerlo rápido.
Y si te interesa la parte económica de esta decisión, en lo que cuesta realmente ejecutar LLMs en producción desglosamos los números de tokens, infraestructura y optimización con datos actuales.