Trilha de Engenharia de Prompt

Prompt Template Pattern com LangChain

Como separar a estrutura fixa do prompt das variáveis dinâmicas para criar prompts reutilizáveis, fáceis de manter e mais testáveis.

Parte 1 de 11 da trilha ~10 min de leitura Python LangChain IA Generativa

O problema dos prompts fixos

Quase todo projeto com LLM começa com o prompt escrito direto no código, montado com f-string:

python · o jeito que não escala
prompt = f"Você é um assistente que responde em estilo {estilo}. Pergunta: {pergunta}"
resposta = llm.invoke(prompt)

Funciona no primeiro dia. Depois aparecem os problemas: o mesmo prompt copiado em vários arquivos, ajustes de tom que exigem mexer no código, nenhuma forma de saber quais variáveis o prompt espera e nenhum aviso quando uma delas falta.

O Prompt Template Pattern resolve isso tratando o prompt como um componente: uma estrutura fixa com lacunas nomeadas, definida em um só lugar e preenchida em tempo de execução.

O que é o padrão

Em vez de uma string pronta, você define um template com placeholders entre chaves, como {style} e {question}. O template sabe quais variáveis precisa receber e só vira mensagem quando elas são preenchidas.

BenefícioNa prática
ReutilizaçãoO mesmo template atende perguntas, estilos e idiomas diferentes
ManutençãoMudou o formato do prompt? Altera em um único lugar
ValidaçãoO template conhece as variáveis esperadas e acusa as que faltam
ComposiçãoTemplates se combinam com modelos e parsers em chains
TestabilidadeDá para testar variações de prompt sem mexer na lógica de negócio

Seu primeiro template

No LangChain, o template para modelos de chat é o ChatPromptTemplate. Cada mensagem é uma tupla com o papel (system, user) e o texto com as variáveis:

python
from langchain_core.prompts import ChatPromptTemplate

system = ("system", "Você é um assistente que responde em estilo {style}.")
user = ("user", "{question}")

chat_prompt = ChatPromptTemplate.from_messages([system, user])

print(chat_prompt.input_variables)
# ['question', 'style']

Repare que o template já descobriu sozinho quais variáveis precisa. Para gerar as mensagens finais, preencha os valores:

python
messages = chat_prompt.format_messages(
    style="divertido",
    question="Quem foi Alan Turing?",
)

for m in messages:
    print(m.type, "->", m.content)
# system -> Você é um assistente que responde em estilo divertido.
# human -> Quem foi Alan Turing?

Dica: a mensagem de system define o comportamento do assistente; a de user carrega o pedido. Manter essa separação deixa claro o que é regra fixa e o que muda a cada chamada.

Montando a chain com LCEL

O padrão fica realmente útil quando o template entra numa chain. Com a LCEL (LangChain Expression Language), você liga os componentes com o operador |, como um pipe do terminal: o template gera as mensagens, o modelo responde e o parser transforma a resposta em texto.

python
from langchain.chat_models import init_chat_model
from langchain_core.output_parsers import StrOutputParser

llm = init_chat_model(model="gemini-2.5-flash", model_provider="google_genai")
parser = StrOutputParser()

chain = chat_prompt | llm | parser

result = chain.invoke(
    {"style": "formal", "question": "O que é uma Chain em IA Generativa?"}
)
print(result)

O init_chat_model cria o modelo a partir do nome e do provedor. Trocar Gemini por outro modelo é mudar dois parâmetros; o template e o resto da chain continuam iguais.

Como toda chain LCEL, ela ganha de graça outros modos de execução:

python
# Várias entradas de uma vez
respostas = chain.batch([
    {"style": "formal", "question": "O que é RAG?"},
    {"style": "didático", "question": "O que é um embedding?"},
])

# Resposta em streaming, pedaço por pedaço
for pedaco in chain.stream({"style": "curto", "question": "O que é LCEL?"}):
    print(pedaco, end="", flush=True)

Organizando no projeto

No repositório pattern_chain, o padrão está separado em três responsabilidades: os prompts, a montagem da chain e o ponto de entrada.

estrutura
src/
├── main.py                 # ponto de entrada
└── prompt_template/
    ├── __init__.py         # exports públicos
    ├── prompts.py          # templates
    └── chain.py            # montagem da chain

prompts.py guarda os templates, cada um numa função com nome descritivo:

src/prompt_template/prompts.py
from langchain_core.prompts import ChatPromptTemplate


def create_qa_prompt() -> ChatPromptTemplate:
    """Prompt de perguntas e respostas com as variáveis style e question."""
    system = ("system", "you are an assistant that answers questions in a {style} style")
    user = ("user", "{question}")
    return ChatPromptTemplate.from_messages([system, user])


def create_formatter_prompt() -> ChatPromptTemplate:
    """Prompt para formatar um conteúdo no formato pedido."""
    system = ("system", "Format the following content as {format_type}")
    user = ("user", "{content}")
    return ChatPromptTemplate.from_messages([system, user])

chain.py recebe qualquer template e devolve a chain pronta. O modelo é um parâmetro, então a mesma função serve para qualquer prompt e qualquer provedor:

src/prompt_template/chain.py
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import ChatPromptTemplate
from langchain.chat_models import init_chat_model


def create_chain(
    prompt: ChatPromptTemplate,
    model_name: str = "gemini-2.5-flash",
    model_provider: str = "google_genai",
):
    """Cria uma chain LCEL com prompt, modelo e parser."""
    llm = init_chat_model(model=model_name, model_provider=model_provider)
    parser = StrOutputParser()
    return prompt | llm | parser


def create_basic_chain():
    """Chain básica usando o prompt de perguntas e respostas."""
    from prompt_template.prompts import create_qa_prompt

    return create_chain(create_qa_prompt())

main.py só carrega as variáveis de ambiente e executa:

src/main.py
from dotenv import load_dotenv

load_dotenv()

from prompt_template import create_basic_chain


def main():
    chain = create_basic_chain()
    result = chain.invoke(
        {"style": "formal", "question": "What is Chain in Generative AI?"}
    )
    print(result)


if __name__ == "__main__":
    main()

Para rodar, copie o .env.example para .env, preencha a GOOGLE_API_KEY e execute:

bash
poetry install
python src/main.py

Boas práticas

1. Dê nomes claros às variáveis

{user_question} e {context} dizem o que esperam; {x} e {input2} não. O nome da variável é a documentação de quem vai preencher o template.

2. Documente cada template

Uma docstring curta dizendo o propósito e os valores esperados evita que o template seja usado para algo que ele não cobre.

3. Deixe o template validar a entrada

Se faltar uma variável, a chain falha antes de gastar uma chamada ao modelo, com uma mensagem que diz exatamente o que faltou:

python
chat_prompt.invoke({"style": "formal"})
# KeyError: Input to ChatPromptTemplate is missing variables {'question'}.
# Expected: ['question', 'style'] Received: ['style']

Para regras de negócio (um estilo dentro de uma lista permitida, um texto com tamanho máximo), valide antes de chamar a chain.

4. Fixe valores que não mudam com partial

Quando uma variável tem o mesmo valor em todo um fluxo, preencha-a antes e deixe só o que muda em aberto:

python
formal_prompt = chat_prompt.partial(style="formal")
print(formal_prompt.input_variables)
# ['question']

5. Use chaves duplas para texto literal

Tudo entre { } vira variável. Se o prompt precisa mostrar um JSON de exemplo, escape as chaves com {{ }}; senão o template vai exigir variáveis que não existem.

6. Mantenha os prompts fora da lógica de negócio

Prompts num módulo próprio (como o prompts.py) podem ser revisados, versionados e testados sem tocar no resto do código.

Resumo e próximos passos

  • Um template separa a estrutura fixa do prompt das variáveis que mudam a cada chamada.
  • O ChatPromptTemplate descobre as variáveis sozinho e acusa as que faltam.
  • Com LCEL, prompt | llm | parser vira uma chain com invoke, batch e stream.
  • Separar prompts, chains e ponto de entrada deixa o projeto pronto para crescer.

O passo natural depois disso é encadear vários templates, em que a saída de um alimenta o próximo. É o assunto do tutorial Sequential Chain Pattern na prática.

Referências