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.
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.
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.
