· 16 min de leitura
AI Engineer (Para Software Engineers)
Antes de tudo, um disclaimer: neste artigo, estou usando o termo AI Engineer para me referir ao profissional capaz de projetar e construir plataformas e sistemas baseados em IA, especialmente aqueles que utilizam LLMs, RAG, embeddings, inference e fine-tuning, integrando esses componentes a sistemas de software reais.
Ou seja, não vamos treinar um modelo de Machine Learning, ou simular o treinamento de um modelo de LLM, mas ao término desse texto, espero que você seja capaz de construir um agente que resolve um problema real.
Esse artigo também não é um tutorial de código, aqui é conceito.
Parte 1: Fundação
Entenda Language Models
Uma frase que eu sempre digo é: LLM não deve ser caixa preta pra engenheiro de software, você precisa entender como os dados são coletados e os modelos são treinados, precisa estudar profundamente esse tema, talvez eu escreva um artigo completo sobre isso, mas se você tiver 3 horinhas disponível, eu recomendo muito esse video aqui:
https://www.youtube.com/watch?v=7xTGNNLPyMI
O Andrej Karpathy, que fez parte do primeiro time da OpenAI, explica de uma forma muito didática todo o processo: desde a coleta e preparação dos dados, passando pelo treinamento dos modelos, até chegarmos aos modelos que conhecemos hoje.
É praticamente uma aula completa sobre LLMs, com um conteúdo de valor enorme e, o melhor, disponível gratuitamente no YouTube, fala sério o quanto você não pagaria para assistir uma palestra dessa?
Depois disso você é introduzido ao mundo, a partir daí você já vai saber o que procurar ou no que se aprofundar.
Vamos então seguir a definição mais simples possível: Um modelo de LLM recebe uma sequência de tokens e prevê qual deve ser o próximo token, sendo token um pedaço de uma palavra (Tokens têm uma ideia um pouco mais diferente do que ser apenas um pedaço de palavra, mas essa definição por si só já é o suficiente).
Qual modelo usar?
Saber escolher o modelo pra coisa certa é o que vai tornar a sua plataforma economicamente viável (Ou não). Diferentes atividades podem ser realizadas por diferentes modelos, mas para facilitar, vou criar um tier:
Gold
Neste tier estão os chamados modelos de fronteira (frontier models): modelos que representam o estado da arte em capacidade geral e apresentam desempenho elevado em tarefas complexas de raciocínio, programação, uso de ferramentas e resolução de problemas.
São modelos que você provavelmente reservaria para tarefas em que qualidade e capacidade de raciocínio justificam o custo.
Exemplos:
- GPT-6 Astra
- Claude Fable 5.1
- Claude Opus 5
- GPT-5.6 Sol
- Gemini 3.1 Pro
Silver
Aqui ficam modelos com alta capacidade geral, mas posicionados abaixo dos modelos de fronteira em capacidade, custo ou ambos. São excelentes candidatos para tarefas que exigem raciocínio, programação, análise de documentos e uso de ferramentas, mas que não necessariamente precisam do modelo mais poderoso disponível.
Exemplos:
- Claude Sonnet 5
- GPT-5.6 Terra
- Gemini 3 Flash
- DeepSeek V4 Pro
- Qwen de maior porte
Bronze
Aqui ficam os modelos menores, rápidos e baratos, voltados principalmente para tarefas simples e de alto volume. O objetivo não é obter o máximo de capacidade possível, mas encontrar o menor modelo capaz de resolver adequadamente a tarefa.
Exemplos:
- Claude Haiku 4.5
- GPT-5.6 Luna
- Gemini 3.1 Flash-Lite
- modelos small, mini e nano
- Llama de menor porte
- Qwen de menor porte
- Gemma de menor porte
- Mistral Small
- modelos especializados em classificação ou extração
Imagine, por exemplo, que sua plataforma receba 10 milhões de documentos e precise apenas identificar:
"Este documento é uma folha de pagamento?"
→ SIM
→ NÃO
Seria desperdício utilizar um modelo de fronteira para essa tarefa.
Um modelo pequeno pode fazer esse trabalho por uma fração do custo.
Por isso que: Todo lançamento importa, quando a Google lança um modelo flash novo com alta capacidade e mais barato, talvez ele seja a cereja do bolo pra destravar o faturamento de um produto.
Entendendo o custo
O custo de uma API de LLM pode parecer contraintuitivo, principalmente porque estamos acostumados a pagar por APIs por chamada ou por uma mensalidade fixa.
O modelo mental mais próximo seria imaginar que você contratou um escritor para escrever um livro, mas ele cobra por quantidade de palavras que lê e escreve.
Então imagine que eu mande uma requisição:
Request
user: "Olá tudo bem"
Response
assistant: "Estou ótimo e você?"
Aí a coisa começa a ficar interessante, na próxima requisição você precisa mandar tudo, e é importante que você saiba que, modelos de LLM são stateless.
Request
user: "Olá tudo bem"
assistant: "Estou ótimo e você?"
user: "Que bom, como está o clima?"
Response
assistant: "O clima está bom!"
O que eu mando são os input tokens e o que eu recebo são os output tokens.
Uma das estratégias que os provedores usam para baratear o custo é pré armazenar os tokens, você continua mandando tudo na requisição, mas o provedor já tem o contexto, então ele apenas processa o que é novo.
Preços de Setembro/26
| Tier | Modelo | Provedor | 📥 Input / 1M | 💾 Cached Input / 1M | 📤 Output / 1M |
|---|---|---|---|---|---|
| 🥇 GOLD | GPT-6 Astra | OpenAI | US$ 10,00 | US$ 1,00 | US$ 50,00 |
| 🥇 GOLD | Claude Fable 5.1 | Anthropic | US$ 10,00 | US$ 0,25 | US$ 50,00 |
| 🥇 GOLD | Claude Opus 5 | Anthropic | US$ 5,00 | US$ 0,50 | US$ 25,00 |
| 🥇 GOLD | GPT-5.6 Sol | OpenAI | US$ 4,00 | US$ 0,40 | US$ 20,00 |
| 🥇 GOLD | Gemini 3.1 Pro | US$ 2,00 | US$ 0,20 | US$ 12,00 | |
| 🥈 SILVER | Claude Sonnet 5 | Anthropic | US$ 2,00 | US$ 0,20 | US$ 10,00 |
| 🥈 SILVER | GPT-5.6 Terra | OpenAI | US$ 2,00 | US$ 0,20 | US$ 12,00 |
| 🥈 SILVER | Gemini 3 Flash | US$ 0,50 | US$ 0,05 | US$ 3,00 | |
| 🥈 SILVER | DeepSeek V4 Pro | DeepSeek | ~US$ 0,60 | ~US$ 0,003–0,006 | ~US$ 3,60 |
| 🥉 BRONZE | Claude Haiku 4.5 | Anthropic | US$ 1,00 | US$ 0,10 | US$ 5,00 |
| 🥉 BRONZE | GPT-5.6 Luna | OpenAI | US$ 0,20 | US$ 0,02 | US$ 1,20 |
| 🥉 BRONZE | Gemini 3.1 Flash-Lite | US$ 0,10 | US$ 0,025 | US$ 0,40 |
A nossa pequena conversa acima praticamente não teria custo: estamos falando de algo em torno de 25 tokens.
O problema aparece quando a conversa começa a crescer.
A cada nova mensagem, o histórico anterior pode voltar a fazer parte do contexto enviado ao modelo. Assim, uma conversa que começou com poucos tokens pode rapidamente chegar a milhares ou até milhões de tokens.
Usando seu primeiro modelo: primeiras inferências
Existem muitos frameworks para fazer o que vamos fazer agora, mas o objetivo aqui é justamente eliminar as camadas de abstração pra que você consiga ter uma visão completa sobre o que está acontecendo.
Para não ficarmos presos a um único provedor, como OpenAI ou Anthropic, vou utilizar uma interface mais genérica. Isso nos permitirá experimentar diferentes modelos, comparar suas capacidades e entender como integrar essas tecnologias sem acoplar nossa aplicação a um único fornecedor.
Para isso, vamos utilizar o OpenRouter, que nos permitirá acessar diferentes modelos por meio de uma interface única (OpenRouter, paga nóis).
Existe uma interface comum, adotada por diversos provedores, para interagir com modelos de linguagem. Ela ficou conhecida como Chat Completions.
A ideia é simples: em vez de cada modelo exigir uma forma completamente diferente de enviar uma conversa, utilizamos uma estrutura padronizada de mensagens, com elementos como role e content.
O OpenRouter implementa essa interface de forma compatível com a API da OpenAI, permitindo que nossa aplicação utilize diferentes modelos sem precisar mudar fundamentalmente a forma como fazemos as chamadas.
A interface Chat Completions pode ser resumida em alguns conceitos:
model— identifica qual modelo será utilizado.messages— lista de mensagens que compõem o contexto enviado ao modelo.role— identifica a origem da mensagem, comouser,assistant,systemetool.content— conteúdo da mensagem.usage— informa o consumo de tokens, normalmente separando tokens de entrada e saída e, quando disponível, tokens em cache.
Um exemplo completo utilizando o OpenRouter, com a requisição e a resposta:
Até aqui
Essa é a parte legal: o modelo, por si só, não tem estado, não tem memória persistente e não faz nada no mundo. Ele apenas recebe um contexto e gera texto.
Por mais impressionante que isso pareça, isoladamente, ele é “burro”.
O que transforma esse simples gerador de texto em algo capaz de realizar inúmeras tarefas é a nossa velha e boa engenharia de software.
Estado, memória, ferramentas, bancos de dados, APIs, filas, workflows, regras, loops de execução, gerenciamento de contexto e, claro, outros modelos.
O LLM é apenas uma peça do sistema.
Parte 2: Arquitetando com LLMs
Você pode ter recebido uma demanda como:
“Vamos colocar IA no nosso produto.”
Eu costumo dizer que isso é um mau uso do recurso.
Usar a potência de um LLM não deveria significar simplesmente adicionar uma interface diferente para interagir com o produto. Significa repensar a forma como o produto funciona.
Não estamos falando apenas de colocar um chat ao lado de uma tela existente, adicionar um botão de “Gerar com IA” ou substituir alguns cliques por uma conversa.
Estamos falando de uma mudança conceitual em como o sistema, como um todo, opera.
É construir software que não precisa necessariamente ser operado diretamente por um humano.
Um sistema em que humanos, outros sistemas e agentes de IA podem interagir com as mesmas capacidades, cada um utilizando a interface mais adequada para a tarefa.
E é por isso que vamos criar um exemplo do zero aqui, um agente que, baseado em documentos de empresas abertas na bolsa, no meu patrimônio e na minha tolerância a risco, recomenda quanto devo alocar em cada investimento.
O contexto
Contexto é o conjunto de informações que o modelo recebe para produzir uma determinada resposta. Ele ajuda a direcionar a atenção do modelo para melhorar a inferência dos resultados.
Modelos de LLM podem gerar texto sobre qualquer coisa, por isso é importante dar um caminho inicial para ele.
Ex:
“Você é um agente financeiro, use a linguagem formal, nunca execute uma operação de transação na conta do cliente”
“Você é um classificador de chamados, você recebe o chamado e classifica o grau de insatisfação do cliente e retorna a prioridade, que pode ser: BAIXA, MÉDIA, ALTA. ”
A quantidade de contexto está ligada ao LLM que você está usando, modelos de fronteira lidam bem com uma quantidade menor, mas modelos com menos capacidade podem precisar de um contexto maior.
Há também uma armadilha no contexto, ele vai junto da mensagem e seu tamanho pode aumentar consideravelmente a quantidade de tokens. Alguns frameworks vão pedir pra você definir coisas como, escopo, personalidade, contexto do negócio, contexto global, tudo isso no final é um texto que será enviado antes da mensagem e tudo isso pesa.
Pro nosso agente, vou manter um contexto bem enxuto:
“Você é um especialista em recomendação de investimento, analisa relatórios de empresas listadas na bolsa e recomenda o melhor investimento dado o apetite a risco, o cliente se chama Gustavo, ele prefere retornos imediatos e de curto prazo, atualmente não tem um investimento alocado.”
Legal, com o contexto nós já demos um grande passo, o modelo já tem um ponto de partida, mas ainda falta algo, precisamos dar pra ele capacidade de fazer as coisas, essas capacidades são chamadas de tools.
Tools
Ok, até agora estamos mandando isso para o modelo:
"System prompt: Você é um especialista em recomendação de investimento, analisa relatórios de empresas listadas na bolsa e recomenda o melhor investimento dado o apetite a risco, o cliente se chama Gustavo, ele prefere retornos imediatos e de curto prazo, atualmente não tem um investimento alocado.
User: Olá!"
Ele provavelmente irá responder algo como:
Olá Gustavo, como posso te ajudar? Que tal saber mais sobre investimento?
E daí podemos ir conversando, mas isso só vai se resumir a uma conversa com pura inferência do modelo, mas a gente quer ir além, a gente quer dar opção do agente consultar meu saldo, ler documentos específicos e calcular uma alocação.
Para dar ao modelo a capacidade de interagir com o mundo externo, precisamos informar quais ferramentas estão disponíveis.
System prompt:
Você é um especialista em recomendação de investimento.
Analisa relatórios de empresas listadas na bolsa e recomenda o melhor
investimento dado o apetite a risco.
O cliente se chama Gustavo.
Ele prefere retornos imediatos e de curto prazo.
Atualmente não possui nenhum investimento alocado.
Tools disponíveis:
1. get_balance
Consulta o saldo disponível do cliente.
2. search_company_documents
Pesquisa informações e relatórios de empresas listadas na bolsa.
3. calculate_allocation
Calcula uma sugestão de alocação considerando o patrimônio,
perfil de risco e objetivo do cliente.
User:
Olá!
Estou tratando todas essas informações como contexto. Isso é proposital: do ponto de vista do modelo, são informações que ele recebe para realizar a inferência.
Porém, quando utilizamos uma API como a do OpenRouter, esse contexto não é enviado necessariamente como um único texto. A API recebe uma estrutura padronizada que separa as mensagens, as ferramentas disponíveis e outros parâmetros da requisição.
Um payload simplificado poderia ser:
{
"model": "openai/gpt-5.6-sol",
"messages": [
{
"role": "system",
"content": "Você é um especialista em recomendação de investimento..."
},
{
"role": "user",
"content": "Olá!"
}
],
"tools": [
{
"type": "function",
"function": {
"name": "get_balance",
"description": "Consulta o saldo disponível do cliente.",
"parameters": {
"type": "object",
"properties": {}
}
}
}
]
}
Onde essa tool é executada? (Tool Calling)
Essa é a parte em que a Engenharia de Software entra em ação, lembra que eu disse lá em cima que o modelo é burro? É burro porque seu único papel é gerar texto, mas na geração de texto ele pode gerar uma chamada para uma ferramenta, essa por exemplo, seria a resposta da API dizendo, olha “Executa essa função e me dá o resultado por favor”
{
"choices": [
{
"message": {
"role": "assistant",
"content": null,
"tool_calls": [
{
"id": "call_abc123",
"type": "function",
"function": {
"name": "get_balance",
"arguments": "{}"
}
}
]
},
"finish_reason": "tool_calls"
}
]
}
Agora, o seu código deve interpretar essa tool call e executá-la. O algoritmo é bem simples:
- Envie messages e tools para o modelo.
- Se a resposta vier com tool_calls:
- Para cada tool solicitada, execute-a com os argumentos recebidos.
- Adicione o resultado em messages, como resposta da ferramenta.
- Volte ao primeiro passo.
- Se vier com finish_reason "stop", o content é a resposta final.
É isso, sem mais complexidade, executar uma tool nada mais é do que executar uma função no seu programa e dizer para o modelo qual foi a resposta que a ferramenta retornou.
Isso é harness, isso é tudo que dá poder pra um modelo de LLM, ele não tem consciência, ele não executa nada sozinho. Tudo que ele faz foi autorizado por alguém.
O problema é quando a gente dá acesso a um Bash para um LLM. Aí, realmente, ele pode fazer qualquer coisa.
RAG (Retrieval-Augmented Generation)
Um modelo não aguentaria ter todos os documentos sobre investimentos, relatórios de empresas e tudo mais no seu contexto ao mesmo tempo. Por isso, precisamos permitir que ele consulte essas informações incrementalmente e adicione ao contexto apenas o que for relevante.
Apesar de parecer algo supercomplexo, a ideia é simples: permitir que o modelo recupere informações de fontes externas antes de gerar uma resposta. O desafio está em fazer essa busca de forma eficiente, encontrando os documentos certos sem desperdiçar tempo, dinheiro ou contexto. Existem várias estratégias para isso, e vamos entender as principais e como elas podem ser combinadas para melhorar os resultados.
Busca textual (Full-text search)
É a forma mais tradicional de busca: procurar palavras e termos presentes nos documentos. É especialmente útil quando precisamos encontrar correspondências exatas, como nomes de empresas, códigos de ações ou termos financeiros específicos.
Imagine que queremos encontrar relatórios que mencionam literalmente “PETR4” ou “EBITDA”. Uma busca textual pode resolver isso sem precisar de modelos de IA.
Tecnologias como Elasticsearch e PostgreSQL Full-Text Search oferecem recursos para esse tipo de consulta.
Embeddings
Os embedding models transformam textos em vetores numéricos que representam aspectos do seu significado. Isso permite comparar uma pergunta com documentos e encontrar conteúdos semanticamente relacionados, mesmo quando eles não utilizam exatamente as mesmas palavras.
Imagine perguntar “Quais empresas podem sofrer com o aumento dos juros?”. Um relatório que fala sobre endividamento elevado e dependência de financiamento pode ser relevante mesmo sem mencionar a palavra “juros”. Os embeddings ajudam a encontrar esse tipo de relação por meio da similaridade entre vetores.
Busca vetorial (Vector Search)
Depois de gerar os embeddings dos documentos, precisamos armazená-los e consultá-los de forma eficiente. É aí que entra a busca vetorial, que permite encontrar os vetores mais próximos daquele gerado a partir da pergunta.
Por exemplo, podemos transformar milhares de trechos de relatórios financeiros em embeddings e indexá-los em um banco vetorial. Quando o usuário fizer uma pergunta, o sistema gera um embedding para ela e recupera os trechos mais semelhantes. Tecnologias como pgvector, Qdrant e Pinecone oferecem recursos para isso.
Vale lembrar que embeddings e busca vetorial não são a mesma coisa: os embeddings representam o conteúdo em vetores, enquanto a busca vetorial utiliza esses vetores para recuperar informações semelhantes.
Busca híbrida (Hybrid Search)
Por que escolher entre busca textual e busca semântica se podemos combinar as duas? A busca híbrida reúne os resultados de uma busca por palavras-chave com os de uma busca vetorial, aproveitando os pontos fortes de cada abordagem.
No nosso exemplo financeiro, a busca textual pode encontrar relatórios que mencionam exatamente uma empresa ou um indicador, enquanto a busca vetorial pode recuperar documentos que discutem conceitos relacionados sem utilizar os mesmos termos. Combinadas, elas podem oferecer resultados mais completos.
Filtragem por metadados
Nem sempre queremos pesquisar em todos os documentos disponíveis. Às vezes, já sabemos que precisamos de informações de uma empresa específica, de um determinado setor ou de um período fiscal.
Os metadados permitem aplicar filtros antes ou durante a recuperação. Por exemplo, podemos restringir a busca aos relatórios da Petrobras publicados em 2026 ou às empresas do setor bancário. Isso reduz o universo de pesquisa e ajuda a recuperar informações mais relevantes.
Reranking
As estratégias de busca podem retornar muitos documentos, mas isso não significa que todos sejam úteis para responder à pergunta. O reranking consiste em reavaliar os resultados recuperados e reorganizá-los por relevância.
Imagine que a busca inicial encontre 50 trechos sobre empresas, dívidas e juros. Um modelo de reranking pode analisar a relação entre cada trecho e a pergunta original e priorizar os cinco mais relevantes para serem enviados ao LLM. Isso ajuda a melhorar a qualidade do contexto, embora adicione custo e latência ao processamento.
Chunking (divisão de documentos)
Antes de pesquisar documentos extensos, precisamos decidir como dividi-los em partes menores, chamadas chunks. Essa etapa é importante porque, na maioria dos casos, não queremos recuperar um relatório inteiro quando apenas um parágrafo contém a informação necessária.
Imagine um relatório anual com centenas de páginas. Podemos dividi-lo em trechos que preservem seções e parágrafos relacionados, indexá-los e recuperar apenas as partes relevantes para cada pergunta. O tamanho dos chunks e a forma como são divididos influenciam tanto a qualidade da busca quanto a quantidade de contexto enviada ao modelo.
Query rewriting (reescrita da consulta)
Nem sempre a pergunta do usuário está formulada da melhor maneira para uma busca. Podemos reformulá-la ou expandi-la para incluir conceitos relacionados e aumentar a chance de encontrar informações relevantes.
Por exemplo, a pergunta “Quais empresas são vulneráveis aos juros?” pode gerar consultas adicionais sobre endividamento, custo da dívida, refinanciamento e despesas financeiras. Essas consultas ajudam a explorar diferentes formas de expressar o mesmo problema.
Knowledge Graphs (grafos de conhecimento)
Em alguns cenários, não basta encontrar documentos semelhantes: precisamos entender as relações entre entidades. Um grafo de conhecimento representa entidades e suas conexões de forma estruturada.
No contexto financeiro, podemos representar empresas, subsidiárias, fornecedores, credores e participações societárias como nós conectados por relações. Assim, se quisermos entender como um problema em uma empresa pode afetar outras organizações, podemos percorrer essas conexões e recuperar informações que uma busca textual ou vetorial isolada talvez não encontre.
Nem todo sistema RAG precisa de todas essas estratégias. Uma busca simples pode ser suficiente para alguns casos, enquanto sistemas com grandes volumes de documentos e perguntas complexas podem se beneficiar da combinação de várias técnicas. O importante é escolher a abordagem de acordo com o problema que queremos resolver.
High level
E com as tools certas e um RAG bem feito, nosso agente pode ter a capacidade de ser útil e resolver um problema real
Conclusão (O fim é só o começo)
Com tudo que fizemos aqui, já é possível entregar um agente inteligente que de fato gera valor.
Não existe mágica. É tudo engenharia de software: os fundamentos, o que você estudou na faculdade, os algoritmos, as estruturas de dados, os grafos e tudo aquilo que você aprendeu ao longo da sua jornada como engenheiro.
O LLM é uma peça nova nesse quebra-cabeça, mas todo o resto continua existindo. Precisamos pensar em arquitetura, estado, persistência, concorrência, segurança, observabilidade, custos e resiliência. Precisamos entender como conectar as peças, definir limites e garantir que o sistema se comporte como esperamos.
A diferença é que agora temos um componente capaz de interpretar linguagem natural, lidar com informações não estruturadas e decidir quais ferramentas utilizar para resolver um problema. E isso abre um universo de possibilidades que antes exigiria muito mais código e esforço para construir.
Mas não confunda isso com deixar a IA fazer tudo sozinha. O modelo pode ser excelente em interpretar uma intenção, analisar documentos e propor caminhos, mas continua sendo responsabilidade nossa construir os mecanismos que garantem que essas ações sejam executadas de forma segura, previsível e eficiente.
E é justamente aí que está a oportunidade para nós, engenheiros de software. Não precisamos reinventar tudo o que aprendemos até aqui. Precisamos entender como aplicar esses fundamentos a uma nova forma de construir sistemas.
Este artigo é apenas o começo. Ainda existem muitos assuntos para explorar: avaliação de modelos, fine-tuning, inferência local, sistemas multiagentes, otimização de custos e tantas outras possibilidades.
O mais importante é não ficar apenas na teoria. Escolha um problema real, construa algo, meça os resultados, descubra onde falha e melhore. É assim que transformamos essa tecnologia em software que realmente entrega valor.
No fim das contas, não estamos deixando de fazer engenharia de software. Estamos descobrindo novas formas de fazê-la.