Um prompt de tradução bom resolve o caso genérico até aparecer terminologia específica, formatação que precisa sobreviver, ou um cliente exigindo consistência entre entregas. O Mt-service, sistema real que uso em produção integrado ao SDL Trados, passa cada documento por cinco passadas — cada uma com um objetivo — antes do checkpoint humano final. O resultado é 15 vezes mais rápido e 95% mais barato que o processo manual, com duas passadas de QA e 100% dos jobs saindo com relatório.

A diferença entre "parece traduzido" e "pronto para entregar" é o número de passadas, cada uma com objetivo específico. Vale a pena montar cinco passadas em vez de confiar num único prompt bom? Vale, a partir do momento em que existe glossário para respeitar e alguém vai conferir o resultado.

O que você precisa

  • Python 3.11+
  • Docker e Docker Compose
  • Chave de API de pelo menos um provedor de LLM — a arquitetura permite mais de um, um por passada
  • Glossário do cliente: termos que não podem variar
  • Familiaridade com FastAPI e APIs REST
  • Tempo: mais de uma semana para as 5 passadas ponta a ponta

Passo 1: estruture o projeto e o ambiente Docker

Organize em api/ (main.py, pipeline.py, models.py), passes/ — um arquivo por passada — e config/settings.py.

mkdir traducao-pipeline && cd traducao-pipeline
python -m venv .venv && source .venv/bin/activate
pip install fastapi uvicorn litellm pydantic python-dotenv

Um Dockerfile e um docker-compose.yml sobem tudo com um comando. Resultado: docker compose up sobe a API vazia respondendo em /health.

LEIA TAMBÉM · ExplicaçãoO que separa um protótipo de IA de um sistema em produção são três peças baratasMostrar IA funcionando uma vez é fácil. O difícil é deixar rodando todo dia sem ninguém olhando. Três peças simples — Docker para empacotar, healthcheck para vigiar, agendador para disparar — fazem quase todo o trabalho.

Passo 2: implemente a passada 1 (tradução bruta)

O prompt precisa do idioma de origem e destino, pedir para preservar formatação — Markdown ou HTML —, listar os termos que não devem ser traduzidos (nomes próprios, marcas) e definir o tom. O LiteLLM abstrai o provedor: trocar de modelo é trocar uma string.

import litellm

async def translate_pass(text: str, config: dict) -> dict:
    response = await litellm.acompletion(
        model=config["model"],  # ex.: "openai/gpt-4o"
        messages=[
            {"role": "system", "content": config["system_prompt"]},
            {"role": "user", "content": text},
        ],
        temperature=0.3,
    )
    return {
        "translated_text": response.choices[0].message.content,
        "tokens_used": response.usage.total_tokens,
    }

temperature=0.3 mantém a saída consistente entre execuções. Resultado: um texto curto de teste sai traduzido, com a contagem de tokens junto.

Passo 3: implemente a passada 2 (revisão linguística)

Um segundo LLM — pode ser mais barato que o da passada 1 — revisa gramática, naturalidade e fluência, recebendo o texto de origem e o traduzido lado a lado. Reservar o modelo caro só para a passada 1 é o que mantém o pipeline barato mesmo com cinco chamadas por documento. Resultado: texto revisado, mais uma lista de correções para rastrear depois.

Passo 4: implemente a passada 3 (verificação de consistência com glossário)

Esta passada compara os termos traduzidos contra o glossário do cliente — um JSON ou CSV de termo original para termo obrigatório — e corrige antes de seguir, nunca depois. A lógica varre o texto revisado, checa cada termo do glossário e substitui as divergências encontradas. Resultado: um relatório de quantos termos foram encontrados e corrigidos.

Passo 5: implemente a passada 4 (adaptação cultural)

Expressões idiomáticas e referências culturais sobrevivem à tradução literal, mas soam estranhas no idioma de destino; o prompt pede para ajustar isso mantendo o sentido original, sem adicionar informação nova. O risco é a passada mudar o sentido do original — por isso a próxima passada existe. Resultado: texto adaptado, rastreável até a versão anterior.

Passo 6: implemente a passada 5 (validação final e métricas)

A validação verifica completude — nada foi omitido — comparando a estrutura da origem com a do texto final: contagem de parágrafos, marcações preservadas. Ao final, gera as métricas do job: modelo usado em cada passada, tokens consumidos, termos de glossário corrigidos, tempo total. Resultado: um JSON de "relatório do job" — a base do "100% dos jobs com relatório".

Passo 7: orquestre as 5 passadas na API e suba com Docker

pipeline.py encadeia as cinco em sequência, expostas num único endpoint:

@app.post("/traduzir")
async def traduzir(doc: DocumentoIn):
    resultado = await run_pipeline(doc.texto, doc.config)
    return resultado
docker compose up -d

Resultado: uma chamada a /traduzir devolve o texto final e o relatório do job com as cinco passadas aplicadas.

LEIA TAMBÉM · Caso realO Mt-service traduz 15× mais rápido porque o QA roda duas vezes em todo jobUm sistema recebe o texto a traduzir, aplica o vocabulário próprio de cada cliente, traduz, revisa, confere duas vezes e só então manda para uma pessoa aprovar. Cada trabalho sai com um relatório do que foi feito.

O que pode dar errado

Pipeline caro e lento se as cinco passadas usam o modelo mais forte — reserve o caro só para a passada 1.

Termo do glossário não respeitado pela tradução bruta — a passada 3 corrige antes de seguir, nunca depois.

Adaptação cultural mudando o sentido do original — a validação final (passada 5) compara contra o texto de origem.

Container sobe mas a chave de API não está no .env — confira as variáveis de ambiente antes do docker compose up.

Próximos passos

Um dashboard de métricas de qualidade por cliente e idioma, a partir dos relatórios acumulados. Trocar o modelo de uma passada isolada sem tocar nas outras — é troca literal de string. Rodar em paralelo as passadas sem dependência direta entre si, como consistência e adaptação cultural.

Cinco passadas custam mais que um prompt. A diferença é ter QA rastreável por job, não uma tradução que só parece boa.