Python & AI Tutorials Logo
LangChain & LangGraph

4. Diseñar prompts reutilizables con plantillas

En el Capítulo 3, construimos una CLI de chat con streaming funcional donde los prompts estaban incrustados directamente en nuestro código Python. Esto funciona para prototipos rápidos, pero a medida que tus aplicaciones de IA crecen, los prompts codificados se convierten en una pesadilla de mantenimiento. Imagina actualizar la misma lógica de prompt en múltiples archivos, o intentar hacer pruebas A/B de diferentes variaciones de prompts sin volver a desplegar código.

Este capítulo te enseña cómo diseñar prompts reutilizables y mantenibles usando el sistema de plantillas de LangChain. Aprenderás a separar la lógica de prompts del código de la aplicación, aprovechar la mensajería basada en roles para un mejor control del LLM, externalizar prompts a archivos YAML para la colaboración en equipo, y validar plantillas antes de la ejecución para detectar errores temprano.

Lo que cubre este capítulo (y lo que no):

En este capítulo, trabajaremos con plantillas y prompts manualmente—renderizarás explícitamente plantillas a mensajes, luego enviarás esos mensajes al LLM usando llm.invoke(). Este enfoque práctico te ayuda a entender exactamente qué hacen las plantillas y cómo funcionan.

En el Capítulo 6, aprenderás LCEL (LangChain Expression Language), que te permite componer plantillas y LLMs en pipelines usando el operador |. Por ahora, nos estamos enfocando en los fundamentos de las plantillas sin esa capa de orquestación.

Al final de este capítulo, tendrás un sistema robusto de gestión de prompts que escala desde chatbots simples hasta flujos de trabajo complejos de múltiples agentes.

4.1) Separación de responsabilidades: Desacoplar código de prompts

¿Por qué separar prompts del código?

Cuando codificas prompts directamente en tu lógica de aplicación, creas un acoplamiento estrecho que conduce a varios problemas:

Carga de mantenimiento: Cambiar un prompt requiere modificar código Python, ejecutar pruebas y volver a desplegar. Los cambios de prompts típicamente ocurren con mucha más frecuencia que los cambios de código, haciendo este ciclo de modificación-prueba-redespliegue altamente ineficiente para lo que deberían ser simples ediciones de texto.

Desafíos de control de versiones: Cuando el código y los prompts están mezclados, el control de versiones se vuelve difícil. Los conflictos de fusión son más probables, y cada conflicto requiere resolución manual y refactorización.

Fricción de colaboración: Los miembros del equipo no técnicos (gerentes de producto, expertos de dominio) no pueden editar directamente prompts que viven en archivos .py y deben depender de la asistencia de desarrolladores. Esta dependencia hace que los ciclos de mejora de prompts sean significativamente más lentos.

Complejidad de pruebas: Probar diferentes variaciones de prompts significa copiar código, modificar cadenas y gestionar múltiples ramas—haciendo los experimentos lentos y propensos a errores.

Piensa en los prompts como consultas SQL en aplicaciones tradicionales. No codificarías cadenas SQL en todo tu código Python—usarías un ORM o al menos centralizarías las consultas. Los prompts merecen la misma disciplina arquitectónica.

El sistema de plantillas de LangChain

LangChain proporciona las clases PromptTemplate y ChatPromptTemplate para separar la estructura fija de tu prompt de los datos cambiantes. Escribe tu prompt una vez con {placeholders}, luego conecta diferentes valores cada vez—no más reconstrucción de prompts con f-strings o concatenación.

Sintaxis y uso de plantillas

Sintaxis de marcadores de posición

Las plantillas usan {nombre_variable} como marcadores de posición. En tiempo de ejecución, proporcionas un diccionario con claves coincidentes:

python
from langchain_core.prompts import PromptTemplate
 
# Define plantilla con marcadores de posición
template = PromptTemplate.from_template(
    "Translate {content} from {source_lang} to {target_lang}"
)
 
# Llena marcadores de posición con diccionario
result = template.invoke({
    "content": "Hello world",
    "source_lang": "English", 
    "target_lang": "Korean"
})
 
print(result.text)

Salida:

Translate Hello world from English to Korean

Reglas clave:

  • Los nombres de marcadores de posición deben coincidir exactamente con las claves del diccionario
  • Todos los marcadores de posición deben ser proporcionados (las claves faltantes generan KeyError)
  • Las claves extra del diccionario son ignoradas
  • Usa invoke() para renderizar la plantilla con tus valores

PromptTemplate vs ChatPromptTemplate

PromptTemplate: Devuelve una cadena simple (envuelta en StringPromptValue)

  • Para completado de texto simple o modelos legacy
  • Salida: Cadena única como "Summarize: {content}"

ChatPromptTemplate: Devuelve mensajes estructurados con roles (envueltos en ChatPromptValue)

  • Para modelos de chat modernos (GPT-4, Claude, Gemini)
  • Salida: Mensajes separados por roles (system/user/assistant)
  • Opción preferida: Mejor para mantener instrucciones del sistema separadas de la entrada del usuario

¿Cuándo usar cuál?

  • Por defecto usa ChatPromptTemplate para modelos de chat—es más claro y mantenible
  • Usa PromptTemplate solo para completados simples o cuando la separación de roles no sea necesaria
python
# PromptTemplate - salida de cadena única
from langchain_core.prompts import PromptTemplate
 
template1 = PromptTemplate.from_template("Summarize: {content}")
result1 = template1.invoke({"content": "LangChain is a framework..."})  
print(result1)

Salida:

text='Summarize: LangChain is a framework...'
python
# ChatPromptTemplate - mensajes basados en roles
from langchain_core.prompts import ChatPromptTemplate
 
template2 = ChatPromptTemplate.from_messages([
    ("system", "You are a helpful assistant"),
    ("user", "{question}")
])
result2 = template2.invoke({"question": "What is LangChain?"})
print(result2)

Salida:

messages=[SystemMessage(content='You are a helpful assistant'), HumanMessage(content='What is LangChain?')]

La plantilla se define una vez. Puedes reutilizarla con diferentes valores sin modificar la definición de la plantilla. Tanto PromptTemplate.invoke() como ChatPromptTemplate.invoke() devuelven valores de prompt listos para ser enviados directamente a un LLM.

De formato de cadenas a plantillas

Refactoricemos un prompt codificado para usar plantillas. Aquí está la versión "antes" del Capítulo 3:

python
# Enfoque codificado (estilo Capítulo 3)
from langchain_openai import ChatOpenAI
 
llm = ChatOpenAI(model="gpt-4o-mini")
 
user_input = "Explain quantum computing"
# Lógica de prompt mezclada con código
prompt = f"You are a helpful assistant. Answer this question: {user_input}"
 
response = llm.invoke(prompt)
print(response.content)

Ahora con plantillas—usando el enfoque paso a paso que practicaremos a lo largo de este capítulo:

python
from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI
 
# Plantilla definida por separado
template = ChatPromptTemplate.from_messages([
    ("system", "You are a helpful assistant."),
    ("user", "{user_input}")
])
 
# Lógica de aplicación - ejecución paso a paso
llm = ChatOpenAI(model="gpt-4o-mini")
 
user_input = "Explain quantum computing"
 
# Paso 1: Renderizar plantilla a mensajes
messages = template.invoke({"user_input": user_input})
 
# Paso 2: Enviar mensajes al LLM
response = llm.invoke(messages)
print(response.content)

¿Qué cambió?

  1. Definición de plantilla: La estructura del prompt se define una vez en template, separada de la lógica de ejecución.
  2. Sintaxis de marcador de posición: {user_input} es un marcador de posición que se llena en tiempo de ejecución.
  3. Ejecución paso a paso: Renderizamos explícitamente la plantilla (template.invoke()), luego enviamos el resultado al LLM (llm.invoke()). Este proceso de dos pasos te ayuda a entender qué hacen realmente las plantillas.
  4. Reutilizabilidad: La misma template puede usarse para cualquier pregunta del usuario sin modificación.
  5. Estructura de mensajes: template.invoke() devuelve un ChatPromptValue correctamente formateado que el LLM espera.

¿Por qué el enfoque paso a paso?

A lo largo de este capítulo, verás este patrón repetidamente:

python
messages = template.invoke(inputs)  # Paso 1: Renderizar plantilla
response = llm.invoke(messages)     # Paso 2: Enviar al LLM

Estamos usando este enfoque de dos pasos intencionalmente para el aprendizaje—muestra exactamente qué hacen las plantillas: transformar datos de entrada en mensajes estructurados. En el Capítulo 6, aprenderás el patrón de producción del mundo real: combinar estos pasos con pipelines LCEL (template | llm). Pero entender cada paso por separado primero construye una base sólida.

Validación de plantillas

Las plantillas detectan errores temprano. Si referencias un marcador de posición que no existe, LangChain genera un error antes de hacer una llamada a la API:

python
template = PromptTemplate.from_template("Summarize: {text}")
 
# Esto fallará - falta la clave 'text'
try:
    template.invoke({"content": "Some text"})  # Nombre de clave incorrecto
except KeyError as e:
    print(f"Template error: {e}")

Salida:

Template error: "Input to PromptTemplate is missing variables {'text'}.  Expected: ['text'] Received: ['content']

Esta validación ocurre en el momento de renderizado de la plantilla, no durante la ejecución del LLM—ahorrándote tanto tiempo como costos de API.

4.2) Plantillas de prompts conscientes de roles (System, User, Assistant)

Entendiendo los roles de mensajes

Los LLMs modernos (GPT-4, GPT-5, Claude, Gemini) entienden estructura conversacional a través de roles de mensajes. Cada mensaje tiene un rol específico que le dice al modelo cómo interpretarlo.

Los tres roles principales:

System: Define cómo debe comportarse la IA

  • Propósito: Establece la personalidad, experiencia y reglas operacionales de la IA
  • Ejemplo: "Eres un experto en Python que escribe ejemplos de código concisos"
  • Cuándo se aplica: Se establece una vez al inicio, influye en todas las respuestas
  • Piénsalo como: El manual de instrucciones de la IA

User: Representa la entrada humana

  • Propósito: Hace preguntas o solicitudes
  • Ejemplo: "¿Cómo leo un archivo en Python?"
  • Cuándo se aplica: Cada vez que un humano envía un mensaje
  • Piénsalo como: Las preguntas que haces

Assistant: Representa las respuestas previas de la IA

  • Propósito: Proporciona historial de conversación
  • Ejemplo: "Puedes usar la función open() para leer archivos"
  • Cuándo se aplica: Cuando necesitas conversaciones de múltiples turnos
  • Piénsalo como: La memoria de la IA de respuestas anteriores

Mensajes del sistema: El mecanismo de control

El mensaje del sistema le dice a la IA quién es y cómo debe operar—antes de cualquier interacción del usuario.

Lo que puedes controlar:

  1. Experiencia: "Eres un desarrollador senior de Python"
  2. Formato de salida: "Siempre responde en formato JSON"
  3. Reglas de comportamiento: "Si no estás seguro, di 'No lo sé'"
  4. Estilo de respuesta: "Sé conciso y técnico"

Por qué esto importa:

Sin mensaje del sistema → respuestas genéricas y verbosas

Con mensaje del sistema → comportamiento consistente y personalizado

Mensajes del sistema en acción

Veamos el impacto real de los mensajes del sistema comparando la misma pregunta con y sin uno. Presta atención a cómo cambia dramáticamente la respuesta—no solo en longitud, sino en tono, complejidad y enfoque de enseñanza.

Sin mensaje del sistema:

python
from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI
 
template = ChatPromptTemplate.from_messages([
    ("user", "What is Python?")
])
 
llm = ChatOpenAI(model="gpt-4o-mini")
messages = template.invoke({})
response = llm.invoke(messages)
print(response.content)

Salida:

Python is a high-level, interpreted programming language known for its readability and simplicity. 
It was created by Guido van Rossum and first released in 1991. 
Python emphasizes code readability, allowing programmers to express concepts in fewer lines of code compared to languages such as C++ or Java.
 
Key features of Python include:
...

Con mensaje del sistema:

python
from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI
 
# Controla la personalidad y estilo de salida con mensaje System
template = ChatPromptTemplate.from_messages([
    ("system", """You are a senior Python instructor with 15 years of teaching experience.
Your students are complete beginners who have never programmed before.
 
Teaching style:
- Use simple, everyday analogies
- Avoid technical jargon
- Show practical examples from daily life
- Be encouraging and patient"""),
    ("user", "What is Python?")
])
 
llm = ChatOpenAI(model="gpt-4o-mini")
messages = template.invoke({})
response = llm.invoke(messages)
print(response.content)

Salida:

Great question! Think of Python like a really helpful tool in your toolbox. 
Just like a hammer or a screwdriver helps you build or fix things around the house, Python helps you create software or automate tasks on a computer.
 
Imagine you wanted to bake a cake. 
You need a recipe to follow, right? In this analogy, Python is like that recipe. 
It tells the computer what steps to take to achieve a goal, whether it's doing math, organizing files, or even running a game.
...

La diferencia:

Sin mensaje del sistema:

  • La IA usa su comportamiento predeterminado: educada, informativa, pero genérica
  • Las respuestas son enciclopédicas y formales—optimizadas para audiencias amplias
  • Sin personalidad consistente: cada respuesta puede variar en tono y estilo
  • Sin restricciones: la IA decide por sí misma qué tan detallada o técnica ser

Con mensaje del sistema:

  • La IA sigue tus instrucciones específicas: personalidad, estilo y reglas que definiste
  • Las respuestas son consistentes y predecibles—cada respuesta coincide con tus requisitos
  • Personalidad clara mantenida: actúa como el rol que asignaste (maestro, experto, asistente)
  • Restricciones explícitas aplicadas: formato de salida, nivel de lenguaje y límites de comportamiento que estableciste

Punto clave: Sin un mensaje del sistema, obtienes el modo predeterminado de la IA. Con un mensaje del sistema, obtienes tu IA—adaptada a las necesidades de tu aplicación. El mensaje del sistema transforma la IA de una herramienta de propósito general en un asistente especializado que se comporta exactamente como quieres, cada vez.

Roles User y Assistant: Construyendo conversaciones

Pregunta única (solo User):

python
template = ChatPromptTemplate.from_messages([
    ("system", "You are a Python expert."),
    ("user", "{question}")
])
 
llm = ChatOpenAI(model="gpt-4o-mini")
messages = template.invoke({"question": "How do I read a CSV?"})
response = llm.invoke(messages)

Funciona bien para preguntas independientes.

Múltiples turnos con contexto (User + Assistant):

Sin historial:

python
template = ChatPromptTemplate.from_messages([
    ("system", "You are a Python expert."),
    ("user", "How does it work?")  # "it" = ???
])

La IA no sabe a qué se refiere "it".

Con historial:

python
template = ChatPromptTemplate.from_messages([
    ("system", "You are a Python expert."),
    ("user", "What's the pandas library?"),
    ("assistant", "Pandas is a data analysis library."),
    ("user", "How does it work?")  # Ahora "it" = pandas
])

El historial de conversación (pregunta anterior del usuario + respuesta del asistente) proporciona contexto. La IA ahora entiende que "it" significa pandas.

Ejemplo: Construyendo una conversación con historial

Ahora construyamos un ejemplo que recuerda intercambios previos. Esta función mantiene el historial de conversación y lo pasa a la IA con cada nueva pregunta:

python
llm = ChatOpenAI(model="gpt-4o-mini")
 
def chat_with_history(user_input: str, history: list):
    messages = [("system", "You are a Python expert.")]
    
    # Agregar historial
    for msg in history:
        messages.append((msg["role"], msg["content"]))
    
    # Agregar entrada actual
    messages.append(("user", user_input))
    
    # Usa formato mustache para evitar errores cuando el contenido contiene {llaves}
    template = ChatPromptTemplate.from_messages(messages, template_format="mustache")
    
    formatted = template.format()
    response = llm.invoke(formatted)
    return response.content
 
# Uso
history = []
 
# Turno 1
resp1 = chat_with_history("What's a Python dictionary?", history)
print(resp1)
 
history.append({"role": "user", "content": "What's a Python dictionary?"})
history.append({"role": "assistant", "content": resp1})
 
# Turno 2 - usa contexto
resp2 = chat_with_history("Show an example.", history)
print(resp2)

Reglas de orden de mensajes

Los LLMs esperan una estructura de conversación específica: System → User → Assistant → User → Assistant → ...

¿Por qué este orden?

Este patrón refleja conversaciones naturales humano-IA:

  1. System viene primero (opcional): Porque establece reglas de comportamiento que se aplican a toda la conversación, debe definirse antes de que comience cualquier interacción. Así como briefeas a alguien antes de que comience a trabajar, no en medio de una tarea.

  2. User luego Assistant se alternan: En conversaciones reales, los humanos hablan (User), la IA responde (Assistant), los humanos hacen seguimiento (User), la IA responde de nuevo (Assistant). Este patrón de turnos es cómo se entrenó la IA, por lo que espera esta estructura.

  3. Debe terminar con User: La IA genera una respuesta al último mensaje User. Si la conversación termina con Assistant, no hay nada a lo que la IA pueda responder.

Ejemplos válidos:

python
# System + User único
[("system", "..."), ("user", "...")]
 
# System + conversación
[("system", "..."), ("user", "..."), ("assistant", "..."), ("user", "...")]

Patrones problemáticos:

python
# Assistant antes de User - la IA se confunde sobre el contexto
[("system", "..."), ("assistant", "..."), ("user", "...")]
# La IA ve una respuesta sin una pregunta. Podría alucinar a qué pregunta 
# estaba respondiendo, llevando a respuestas irrelevantes o confusas.
python
# Dos mensajes User seguidos - falta respuesta de la IA
[("system", "..."), ("user", "..."), ("user", "...")]
# La IA no sabe a qué mensaje User responder, o podría fusionarlos 
# torpemente. Pierde el flujo conversacional.
python
# Termina con Assistant - nada a lo que responder
[("system", "..."), ("user", "..."), ("assistant", "...")]
# La conversación está completa. La IA no tiene nada que generar ya que no hay 
# pregunta User pendiente. Probablemente producirá un error o respuesta vacía.

Punto clave: Estos patrones no siempre causan errores duros, pero confunden a la IA porque rompen la lógica conversacional en la que fue entrenada. La IA podría generar respuestas, pero serán poco confiables o sin sentido. Siempre sigue el patrón esperado para un comportamiento predecible.

Más allá del historial simple: Patrones de producción (Vista previa)

Nota importante: El patrón de historial de conversación que acabas de aprender es una gran base, pero los sistemas de producción usan enfoques más sofisticados.

El problema con el historial sin procesar:

Simplemente pasar todo el historial de conversación a la IA tiene limitaciones:

  1. Desperdicio de tokens: Cada mensaje (incluso los antiguos) cuenta para tu límite de tokens y costos
  2. Pérdida de enfoque: La IA podría distraerse con conversaciones anteriores irrelevantes
  3. Sin tarea explícita: La IA infiere qué hacer del historial, en lugar de recibir instrucciones claras

Un mejor enfoque:

Los sistemas de producción separan el contexto de las instrucciones:

Enfoque de historial simple (lo que acabamos de aprender):

python
messages = [
    ("system", "You are a Python expert."),
    ("user", "What's a dictionary?"),
    ("assistant", "A dictionary is a key-value data structure."),
    ("user", "Show an example.")
]

Enfoque de producción (viene en capítulos posteriores):

python
messages = [
    ("system", "You are a Python expert."),
    ("user", """Context: The user previously asked about Python dictionaries and learned they are key-value structures.
 
Task: Provide a code example demonstrating dictionary usage.""")
]

La diferencia:

  • Historial sin procesar: La IA ve la conversación completa y descubre qué hacer
  • Patrón de producción: La IA recibe contexto resumido + instrucción explícita

Beneficios de la separación:

  • Menos tokens (menor costo, respuestas más rápidas)
  • Comportamiento más confiable (instrucciones claras)
  • Mejor control (tú decides qué contexto importa)

Dónde aprenderás esto:

  • Capítulo 8: Gestión de estado de conversación y memoria
  • Capítulo 11: Recuperación contextual (combinando RAG con memoria de conversación)
  • Capítulo 16: Enrutamiento dinámico basado en contexto de conversación

Por ahora, entender el historial sin procesar es esencial—es la base para estos patrones avanzados. Pero ten en cuenta: lo que acabas de aprender es una herramienta de enseñanza, no la solución final.

4.3) Externalizar prompts: Gestionar archivos de plantillas (.yaml)

¿Por qué externalizar prompts?

A medida que tu aplicación de IA crece, gestionar prompts en código Python se vuelve difícil de manejar. Externalizar prompts a archivos YAML proporciona:

Colaboración no técnica: Gerentes de producto, expertos de dominio e ingenieros de prompts pueden editar archivos YAML sin tocar código Python o entender conceptos de programación.

Claridad en control de versiones: Rastrea cambios de prompts separadamente de cambios de código. No más commits mixtos donde ajustes de prompts y actualizaciones de lógica aparecen juntos.

Prompts específicos por entorno: Diferentes prompts para desarrollo, staging y producción sin cambios de código.

Pruebas A/B: Prueba variaciones de prompts cargando diferentes archivos—no se necesitan cambios de código.

Piensa en los archivos YAML de prompts como archivos de configuración en aplicaciones tradicionales—definen comportamiento sin requerir cambios de código o redespliegue.

¿Qué es YAML?

YAML es un formato de datos legible por humanos comúnmente usado para archivos de configuración. Si nunca has visto YAML antes, piénsalo como una alternativa más limpia a JSON—usa indentación en lugar de corchetes y es más fácil de leer y editar.

Estructura de prompts YAML

LangChain define una estructura de archivo YAML estándar para prompts. Veamos ejemplos:

Ejemplo 1: Prompt sin variables

Cuando un prompt no necesita valores en tiempo de ejecución, establece input_variables como una lista vacía:

yaml
# prompts/system_prompt.yaml
_type: prompt
input_variables: []
template: |
  You are a helpful assistant.
  Please answer in a friendly and encouraging tone.

El símbolo | te permite escribir texto multilínea, y los saltos de línea se preservan.

Ejemplo 2: Prompt con variables

Cuando un prompt necesita valores en tiempo de ejecución, lístalos en input_variables:

yaml
# prompts/user_prompt.yaml
_type: prompt
input_variables:
  - user_input
template: |
  User question: {user_input}
  Please provide a clear answer.

En tiempo de ejecución, el marcador de posición {user_input} se reemplaza con el valor real.

Componentes clave:

  • _type: prompt: Identifica esto como una plantilla de prompt
  • input_variables: Lista todos los marcadores de posición usados en la plantilla (lista vacía [] si no hay ninguno)
  • template: El texto real del prompt con {placeholders}

Cargar y usar prompts YAML

Carga básica:

Ahora carguemos los archivos YAML que creamos y usémoslos con un LLM:

python
from langchain_core.prompts import load_prompt, ChatPromptTemplate
from langchain_openai import ChatOpenAI
 
# Cargar prompts desde archivos YAML
system_prompt_template = load_prompt("prompts/system_prompt.yaml")
user_prompt_template = load_prompt("prompts/user_prompt.yaml")
 
# Combinar prompts cargados en una plantilla de chat
chat_template = ChatPromptTemplate.from_messages([
    ("system", system_prompt_template.template),
    ("user", user_prompt_template.template)
])
 
llm = ChatOpenAI(model="gpt-4o-mini")
 
# Paso 1: Renderizar plantilla con valores en tiempo de ejecución
messages = chat_template.invoke({"user_input": "What is LangChain?"})
 
# Paso 2: Enviar al LLM
response = llm.invoke(messages)
print(response.content)

Verificar plantillas cargadas:

Antes de usar una plantilla, verifica que se cargó correctamente:

python
from langchain_core.prompts import load_prompt
 
# Cargar la plantilla
user_prompt_template = load_prompt("prompts/user_prompt.yaml")
 
# Verificar qué variables espera
print("Input variables:", user_prompt_template.input_variables)
 
# Ver el texto de la plantilla
print("Template:", user_prompt_template.template)

Salida:

Input variables: ['user_input']
Template: User question: {user_input}
Please provide a clear answer.

Errores comunes en YAML

Error 1: Indentación inconsistente

YAML requiere indentación consistente (típicamente 2 espacios). Cada nivel debe usar la misma cantidad de espaciado:

Incorrecto:

yaml
_type: prompt
input_variables:
- user_input      # Incorrecto: los elementos de lista deben estar indentados
  - question      # Incorrecto: niveles de indentación mixtos

Correcto:

yaml
_type: prompt
input_variables:
  - user_input    # Correcto: ambos elementos al mismo nivel de indentación
  - question

Error 2: Desajuste de marcadores de posición

Los marcadores de posición en template deben coincidir con input_variables:

Incorrecto:

yaml
input_variables:
  - user_input
template: "Question: {question}"  # '¡question' no está en input_variables!

Correcto:

yaml
input_variables:
  - user_input
template: "Question: {user_input}"

LangChain generará un error si los marcadores de posición no coinciden con las variables declaradas.

4.4) Vista previa y validación de plantillas antes de la ejecución

¿Por qué previsualizar plantillas?

La ingeniería de prompts es iterativa. Ajustas la redacción, ajustas la estructura, agregas ejemplos—y cada iteración cuesta tokens de API y tiempo. Previsualizar plantillas antes de la ejecución te permite:

Ahorrar tiempo y dinero: Detecta errores antes de hacer llamadas costosas a la API.

Verificar corrección: Asegúrate de que las variables se llenan correctamente y el formato es el esperado.

Depurar eficientemente: Ve el prompt exacto enviado al LLM, con todas las variables llenadas y el formato aplicado.

Piensa en la vista previa de plantillas como depuración con print—inspeccionas el estado intermedio antes de la ejecución para verificar la corrección.

Vista previa básica de plantillas

Inspeccionar estructura de plantilla:

Antes de usar una plantilla con un LLM, inspecciona su estructura y previsualiza cómo se renderiza con datos de muestra:

python
from langchain_core.prompts import ChatPromptTemplate
 
template = ChatPromptTemplate.from_messages([
    ("system", "You are a {role}."),
    ("user", "{user_input}")
])
 
# Previsualizar estructura de plantilla
print("Input variables:", template.input_variables)
print("Message count:", len(template.messages))
 
# Previsualizar con datos de muestra
prompt_value = template.invoke({
    "role": "Python programming expert",
    "user_input": "What is Python?"
})
 
print("\nPreview:")
for msg in prompt_value.to_messages():
    print(f"{msg.type}: {msg.content}")

Salida:

Input variables: ['role', 'user_input']
Message count: 2
 
Preview:
system: You are a Python programming expert.
human: What is Python?

Esto muestra exactamente qué se enviará al LLM, permitiéndote verificar el prompt antes de la ejecución.

Validar plantillas: Detectar variables faltantes

El error de plantilla más común es variables requeridas faltantes. Aquí hay una función de validación reutilizable que detecta variables faltantes:

python
from langchain_core.prompts import ChatPromptTemplate
 
def preview_template(template: ChatPromptTemplate, inputs: dict):
    """Previsualiza plantilla con entradas dadas, detectando errores."""
    try:
        prompt_value = template.invoke(inputs)
        
        print("TEMPLATE PREVIEW")
        print("=" * 60)
        
        for i, msg in enumerate(prompt_value.to_messages(), 1):
            print(f"Message {i} ({msg.type.upper()}):")
            print(msg.content)
            print("-" * 60)
                
    except KeyError as e:
        print(f"ERROR: {e}")
        print(f"Required variables: {template.input_variables}")
 
# Uso
template = ChatPromptTemplate.from_messages([
    ("system", "You are a {role}."),
    ("user", "{user_input}")
])
 
# Entradas válidas
preview_template(template, {
    "role": "Python programming expert",
    "user_input": "What is Python?"
})
 
# Variable faltante
preview_template(template, {
    "user_input": "What is Python?"  # Falta 'role'
})

Salida:

TEMPLATE PREVIEW
============================================================
Message 1 (SYSTEM):
You are a Python programming expert.
------------------------------------------------------------
Message 2 (HUMAN):
What is Python?
------------------------------------------------------------
 
ERROR: "Input to ChatPromptTemplate is missing variables {'role'}.
Expected: ['role', 'user_input'] Received: ['user_input']
...
Required variables: ['role', 'user_input']

Flujo de trabajo de validación:

Aquí está el proceso típico de validación de plantillas:

Errores

Válido

No

Definir plantilla

Cargar datos de muestra

Validar entradas

Corregir plantilla/datos

Previsualizar mensajes

¿Listo?

Ejecutar con LLM

Este proceso iterativo ayuda a detectar errores antes de llamadas costosas al LLM.

Lista de verificación pre-ejecución

Antes de enviar plantillas a producción:

  • Todas las input_variables están declaradas en YAML/plantilla
  • Los datos de muestra se renderizan sin errores
  • Los prompts multilínea se muestran correctamente
  • Los marcadores de posición coinciden exactamente con los nombres de variables
  • Prueba con casos extremos (cadenas vacías, texto largo)

Resumen del capítulo:

Has aprendido a diseñar prompts mantenibles y reutilizables usando el sistema de plantillas de LangChain:

  1. Separación de responsabilidades: Desacopla prompts del código para un mantenimiento e iteración más fáciles
  2. Plantillas conscientes de roles: Usa mensajes system, user y assistant para interacciones estructuradas con LLM con jerarquía de instrucciones adecuada
  3. Prompts externalizados: Gestiona prompts en archivos YAML para colaboración no técnica y control de versiones
  4. Vista previa y validación: Detecta errores temprano y verifica plantillas antes de la ejecución

Próximos pasos:

En el Capítulo 5, verás cómo las plantillas permiten la toma de decisiones autónoma en ejemplos de agentes de vista previa. Luego, en el Capítulo 6, aprenderás LCEL (LangChain Expression Language) para componer estas plantillas en pipelines poderosos usando el operador |.