Fundamentos de Manutenção de Software
10 Manutenção Usando IA 🔗
The hottest new programming language is English. — Andrej Karpathy (post no X, em 24/01/2023)
Este capítulo discute como Inteligência Artificial pode apoiar atividades de manutenção de software. Inicialmente, apresentamos os chamados assistentes de código, isto é, ferramentas capazes de sugerir código de forma eficiente (Seção 10.2). Em seguida, avançamos para um modelo mais sofisticado de automação: os agentes de código, que possuem autonomia para planejar tarefas, chamar ferramentas externas, editar arquivos e validar mudanças de forma iterativa (Seção 10.3). Depois, na Seção 10.4, tratamos da integração de aplicações de IA com sistemas externos, usando protocolos como MCP (Model Context Protocol) ou ferramentas de linha de comandos. Para concluir, na Seção 10.5, tratamos de um tema central para o uso de modelos de linguagem: a escrita de prompts claros e eficazes, especialmente no contexto de manutenção e evolução de software.
10.1 Introdução 🔗
Há pelo menos 20 anos, existe um volume consistente de pesquisas envolvendo o uso de Inteligência Artificial para automatizar tarefas de Engenharia de Software. Especificamente, em Manutenção de Software sempre tivemos pesquisas voltadas à automatização de tarefas como as seguintes:
- Compreensão de código
- Geração de documentação
- Identificação de problemas de design e arquitetura
- Atualização automática de bibliotecas
- Localização e correção de bugs
- Predição de bugs
- Priorização de tarefas de manutenção
- Recomendação de refatorações
- Remodularização arquitetural
- Extração de microsserviços
- Identificação e priorização de dívida técnica
- Migração de linguagens de programação
- Reengenharia e modernização de sistemas legados
- Recomendação de revisores de código
No entanto, em 2016, um trabalho muito interessante foi publicado pelos pesquisadores Abram Hindle, Earl Barr, Mark Gabel, Zhendong Su e Prem Devanbu. Nesse trabalho, intitulado On the Naturalness of Software (link), os autores mostraram por meio de técnicas de modelagem estatística que a seguinte hipótese é válida no contexto de software:
Os programas que pessoas reais escrevem são, em sua maioria, simples e repetitivos e, por isso, possuem propriedades estatísticas previsíveis e úteis, que podem ser capturadas por modelos estatísticos de linguagem e exploradas em diversas tarefas de Engenharia de Software.
Portanto, nesse artigo, os autores descobriram que seria viável construir modelos estatísticos de linguagem e aplicá-los com sucesso no contexto de desenvolvimento e manutenção de software. Na verdade, eles chegaram a construir um protótipo de uma ferramenta simples de recomendação de código (code completion), que se mostrou mais efetiva que uma ferramenta muito usada na época.
Dois anos depois, a OpenAI começou a liberar seus primeiros modelos de linguagem: GPT-1 (2018), GPT-2 (2019) e GPT-3 (2020). Em seguida, em novembro de 2022, a empresa disponibilizou o ChatGPT, que fez um grande sucesso e alcançou 100 milhões de usuários em apenas dois meses. Para comparar, o Instagram levou dois anos e meio para atingir o mesmo número de usuários.
Em junho de 2022, isto é, alguns meses antes do lançamento do ChatGPT, o GitHub lançou publicamente o GitHub Copilot, confirmando que a indústria rapidamente percebeu o potencial do uso de modelos de linguagem no domínio de software. Iremos comentar mais sobre assistentes de código, como o GitHub Copilot em sua primeira versão, na Seção 10.2.
Continuando com nossa cronologia, em um artigo publicado em 2023, Shunyu Yao e colegas propuseram a abordagem ReAct (Reasoning + Acting), que definiu uma nova forma para usar LLMs, centrada em um ciclo de raciocínio e ação (link). Em vez de apenas retornar imediatamente uma resposta final, na abordagem ReAct, o modelo planeja os próximos passos, executa ações (por exemplo, chama ferramentas externas) e analisa os resultados obtidos. Com isso, tornou-se possível resolver tarefas mais longas e complexas com LLMs, o que inspirou a arquitetura dos agentes modernos de IA, incluindo agentes de código. Iremos comentar mais sobre esses agentes na Seção 10.3.
Em seguida, na Seção 10.4, trataremos da integração de aplicações de IA com sistemas externos, usando protocolos como MCP (Model Context Protocol). Na seção final do capítulo (Seção 10.5), apresentaremos recomendações para a escrita de prompts mais eficazes e, portanto, capazes de incrementar a qualidade dos resultados produzidos por modelos de IA.
Nota: Os avanços em IA estão ocorrendo em grande velocidade. Por isso, neste capítulo, vamos apresentar conceitos e técnicas que consideramos já estabilizados e que, portanto, não devem mudar de forma relevante. Assim, evitaremos tratar de tecnologias que ainda estão sendo testadas. Também evitaremos citar, de forma deliberada, nomes de sistemas e modelos. O motivo é que diversas soluções e sistemas bastante comentados há dois ou três anos já não são mais relevantes. Em resumo, apesar de desafiador, esperamos que o conteúdo deste capítulo, ou pelo menos de parte dele, permaneça relevante por um tempo e que, portanto, o investimento em sua leitura valha a pena.
10.2 Assistentes de Código 🔗
O principal representante desse tipo de ferramenta de IA é o GitHub Copilot, lançado em 2022 pelo GitHub em parceria com a OpenAI. Em sua primeira versão, essa ferramenta se propunha a atuar como um assistente do desenvolvedor, de forma semelhante ao revisor em uma sessão de programação pareada (pair programming). Daí também o uso do termo pareamento com IA para o estilo de desenvolvimento proposto por assistentes de código. De forma concreta, esses assistentes funcionam integrados a uma IDE e sugerem blocos de código para completar o código que está sendo escrito de forma manual.
Suponha que você começou a implementar um método de ordenação e escreveu a sua assinatura (ou cabeçalho):
public void selectionSort(int[] arr) {
Nesse momento, um assistente de código percebe que você está começando a implementar um novo método e pode sugerir o restante do código, ou seja:
for (int i = 0; i < arr.length - 1; i++) {
int minIndex = i;
for (int j = i + 1; j < arr.length; j++) {
if (arr[j] < arr[minIndex]) {
minIndex = j;
}
}
int temp = arr[i];
arr[i] = arr[minIndex];
arr[minIndex] = temp;
}
}
Cabe ao desenvolvedor analisar esse código antes de aceitá-lo. Se não quiser aceitar a sugestão, ele pode continuar escrevendo o código manualmente.
A implementação de um assistente de código não é tão complexa. Basicamente, ele monitora continuamente a escrita do código e, em paralelo, envia prompts para uma LLM completar as partes pendentes. No exemplo anterior, após a escrita da primeira linha do método, o assistente pode submeter o seguinte prompt para a LLM integrada à ferramenta:
You are a coding assistant. Continue this Java function:
public void selectionSort(int[] arr) {
Na prática, os prompts usados por assistentes de código são mais completos do que esse e, por exemplo, incluem as linhas anteriores à posição atual do cursor na IDE, as linhas que seguem essa posição, comentários próximos ao cursor e bibliotecas importadas no início do arquivo.
A vantagem do modelo de pareamento com IA é que o desenvolvedor continua sendo o protagonista da sessão, cabendo a ele analisar e aceitar os trechos sugeridos pelo assistente de código. Por outro lado, nesse tipo de ferramenta o modelo de IA não possui autonomia para atuar simultaneamente em múltiplas partes do código.
Antes de concluir, é importante lembrar que, após a sua primeira versão, o GitHub Copilot incorporou novos recursos, tais como um chat para submissão de tarefas e também uma versão baseada em agentes, modelo este que será descrito na próxima seção.
10.3 Agentes de Código 🔗
Um agente de código é caracterizado pela sua autonomia. Ele recebe uma tarefa (por exemplo, realizar uma refatoração) e então, para realizá-la, ele pode solicitar a execução de ferramentas ou scripts na máquina do cliente para, por exemplo, ler arquivos, realizar mudanças em arquivos, executar testes, criar commits, abrir pull requests, etc. Nesta seção, vamos começar explicando as funcionalidades e a arquitetura de agentes de código. Em seguida, vamos tratar de como configurar esses agentes para seguir regras específicas de um projeto.
10.3.1 Arquitetura 🔗
Normalmente, usamos LLMs da seguinte forma: enviamos um prompt com uma tarefa, o modelo de linguagem realiza essa tarefa e devolve um resultado. No entanto, a implementação de um agente de código é mais sofisticada e consiste, essencialmente, de um loop, como mostrado na próxima figura.
Assim como no modelo tradicional, o usuário escreve um prompt solicitando que o agente realize uma determinada tarefa. No entanto, o agente não precisa realizar essa tarefa de uma só vez. Em vez disso, o modelo pode pedir para o agente executar primeiro uma ferramenta e enviar o resultado dessa execução para sua análise. Esse loop (modelo → ferramenta → modelo → …) pode ser executado diversas vezes, até que o modelo decida dar uma resposta definitiva para a tarefa que foi inicialmente solicitada. Quando isso acontece, o loop termina.
No contexto de agentes de código, o loop descrito no parágrafo anterior é chamado de loop agêntico (agentic loop). Existe ainda o termo arcabouço agêntico (agentic harness), que é usado para denominar todo o código do agente, exceto aquele que é parte do modelo. Logo, o loop que mencionamos, o disparo da execução das ferramentas e até mesmo a interface com o usuário do agente fazem parte do seu arcabouço (ou harness, em inglês).
Portanto, em termos arquiteturais, um agente de código possui dois componentes principais: harness e o modelo de linguagem (sendo esse último, na verdade, um componente externo acessado via uma API). O código responsável por tudo que acontece antes e após a chamada do modelo faz parte do componente de harness, conforme ilustrado na próxima figura.
Podemos entender também da seguinte forma:
Agente = Harness + Modelo de Linguagem
Harness = Loop Agêntico + UI + Controle de Segurança + etc
O módulo de harness é responsável por chamar as ferramentas que serão executadas na máquina do desenvolvedor que está usando o agente. Existem pelo menos quatro grupos de ferramentas que podem ser chamadas pelo harness:
Manipulação de arquivos (ler, editar, criar novos arquivos, etc.).
Busca em arquivos (listar arquivos cujo nome segue um padrão, buscar conteúdo de arquivos usando expressões regulares, etc.).
Execução de comandos shell (iniciar servidores, executar testes, comandos git, etc.).
Acesso à Web (pesquisar na web, acessar documentação, buscar mensagens de erro, etc.).
Exemplo: Para ilustrar o funcionamento do loop
agêntico, considere uma tarefa de refatoração que envolva substituir o
uso do tipo Vector pelo tipo ArrayList em um
sistema. Apesar de ser simples, essa tarefa pode exigir sete iterações
do loop, conforme explicado a seguir:
Iteração 1:
- Modelo: solicita buscar arquivos com ocorrências de
Vector. - Harness: executa ferramenta de busca; envia resultados para o Modelo.
Iteração 2:
- Modelo: solicita leitura dos arquivos relevantes.
- Harness: executa ferramenta de leitura; envia conteúdo para o Modelo.
Iteração 3:
- Modelo: identifica onde
Vectoré usado como tipo; solicita renomeação. - Harness: executa ferramenta de edição para trocar
VectorporArrayList; envia confirmação da mudança para o Modelo.
Iteração 4:
- Modelo: solicita execução dos testes.
- Harness: executa ferramenta de testes; envia resultados para o Modelo.
Iteração 5:
- Modelo: solicita correções no código, com base no resultado dos testes que falharam.
- Harness: executa ferramenta de edição e reexecuta testes; envia resultados para o Modelo.
Iteração 6:
- Modelo: como os testes passaram, solicita criação de commit e Pull Request (PR).
- Harness: executa comandos de commit e de abertura de PR; envia resultados para o Modelo.
Iteração 7:
- Modelo: retorna resposta final.
Chamamos de trajetória o registro completo de todas as ações executadas pelo loop agêntico para realizar uma determinada tarefa. Esse registro inclui as ações tomadas pelo modelo, bem como as ferramentas executadas e seus resultados.
10.3.2 Memória e Prompt de Sistema 🔗
Uma característica importante de um loop agêntico é o seu caráter stateful: cada iteração inclui o histórico da conversa até aquele momento. Na prática, isso significa que, a cada nova chamada ao modelo, o harness envia não apenas o resultado mais recente, mas também o resultado de todas as ferramentas executadas anteriormente e todas as respostas produzidas pelo modelo (possivelmente, com alguma compactação ou sumarização, para economizar tokens). Esse histórico completo, também chamado de memória do agente, permite que o modelo se lembre do que já foi feito, raciocine sobre os resultados obtidos e decida qual ferramenta acionar a seguir.
Também faz parte da configuração do agente um prompt de sistema, que o agente repassa para o modelo. Esse prompt descreve o papel do modelo, as ferramentas disponíveis e as restrições que deve respeitar. Logo, ele é usado para instruir um modelo genérico a se comportar como um agente de código. Por exemplo, sem esse prompt, o modelo não saberia que pode solicitar a execução de ferramentas, nem quais ferramentas estão à sua disposição. Apenas para ilustrar e ficar mais claro, seguem as primeiras linhas do prompt de sistema do agente de código Codex da OpenAI (esse agente possui uma licença aberta e seu código pode ser publicamente acessado neste repositório; a versão mostrada é de abril de 2026).
You are GPT-5.1 running in the Codex CLI, a terminal-base
coding assistant. Codex CLI is an open source project led
by OpenAI. You are expected to be precise, safe, and helpful.
Your capabilities:
- Receive user prompts and other context provided by the
harness, such as files in the workspace.
- Communicate with the user by streaming thinking &
responses, and by making & updating plans.
- Emit function calls to run terminal commands and apply
patches. Depending on how this specific run is configured,
you can request that these function calls be escalated to
the user for approval.
Como podemos ver, esse prompt define o papel do agente, o ambiente em que atua e suas principais capacidades. Ele informa que o agente pode receber contexto adicional provido pelo harness, interagir com o usuário e chamar ferramentas para executar comandos e editar arquivos. Ele também deve pedir esclarecimentos quando a tarefa solicitada não estiver clara.
10.3.3 Configuração de Agentes 🔗
Para configurar um agente com regras e políticas específicas de um
projeto, desenvolvedores devem criar um arquivo, no formato Markdown.
Normalmente, os agentes chamam esse arquivo de AGENTS.md
(embora, uma exceção seja o Claude Code, que usa o nome
CLAUDE.md)
Por exemplo, esse arquivo de configuração pode ser usado para
descrever a arquitetura do sistema que será criado pelo agente,
incluindo seus módulos e suas interfaces. Um exemplo é mostrado no
trecho a seguir (extraído do projeto evstack/ev-node):
## Code Architecture
### Core Package Structure
The project uses a zero-dependency core package pattern:
- core/ - Contains only interfaces and types
- block/ - Block management, creation, and synchronization
- p2p/ - Networking layer built on libp2p
...
### Key Interfaces
- Executor (core/executor.go) - Handles state transitions
- Sequencer (core/sequencer.go) - Orders transactions
- DA (core/da.go) - Data availability layer abstraction
Em arquivos AGENTS.md, pode-se também documentar
ferramentas específicas que o agente pode chamar, como no exemplo a
seguir (extraído do projeto PrefectHQ/marvin):
### Finding Things
- Use `rg` for searching, not grep
...
Como último exemplo, pode-se também documentar práticas de teste,
como no exemplo a seguir (extraído de apache/airflow):
## Testing Standards
- Add tests for new behavior - cover success, and edge cases
- Use pytest patterns, not unittest.TestCase
- Use spec/autospec when mocking
- Use time_machine for time-dependent tests
- Use @pytest.mark.parametrize for multiple similar inputs
- Use @pytest.mark.db_test for tests that require database
access
- Do not use caplog in tests, prefer checking logic
Normalmente, o arquivo AGENTS.md é carregado no início
de qualquer sessão. Por isso, ele deve incluir somente regras cuja
ausência pode levar a erros do modelo. Particularmente, o principal caso
de uso desses arquivos é registrar conhecimento de domínio que o modelo
não consegue inferir diretamente a partir do projeto.
Arquivos AGENTS.md muito grandes e detalhados também
podem ser ignorados pelos modelos. Assim, recomenda-se analisar
periodicamente esses arquivos, removendo tudo que for óbvio ou que possa
ser facilmente descoberto. Por exemplo, uma regra como Siga o guia de
estilo PEP 8
é desnecessária. O motivo é que LLMs, em geral, já
geram código Python aderente a essa convenção de estilo. Logo, incluí-la
costuma ser redundante e não justifica o uso de espaço na memória do
agente.
Literatura Científica: Em 2026, junto com Helio
Santos, Vitor Costa e João Eduardo Montandon, realizamos um dos
primeiros estudos sobre a configuração de agentes de código (link). Para isso, analisamos
328 arquivos CLAUDE.md. Concluímos que as seções mais
comuns desses arquivos tratam de arquitetura (presente em 72% dos
arquivos), regras de desenvolvimento (45%), visão geral dos projetos
(39%) e testes (35%).
10.3.4 Perguntas Frequentes 🔗
Um agente de código pode executar um comando que vai corromper meu sistema? Esse risco é mitigado por diversas camadas de proteção. Primeiro, no prompt do sistema, os modelos são instruídos a não solicitar a execução de comandos perigosos. Segundo, o componente de harness valida comandos e solicitações de uso de ferramentas antes da execução. Por último, e mais importante, é comum que os agente operem em um ambiente isolado, chamado sandbox. Por exemplo, as ferramentas podem ser executadas em um container Docker ou em uma máquina virtual, reduzindo as chances de que uma eventual ação destrutiva alcance a máquina do cliente.
Alguns agentes possuem um modo de planejamento. Para que ele serve? Nesse modo de trabalho, o modelo primeiro elabora um plano de execução antes de realizar mudanças no sistema. Ele divide a tarefa em etapas, identifica arquivos que serão modificados e define critérios de verificação. Esse plano deve ser revisado e modificado pelo desenvolvedor, antes de ser executado. Portanto, o modo de planejamento é útil principalmente quando o agente deve executar uma tarefa complexa e abrangente. Antes que ele comece a modificar diversos arquivos, o desenvolvedor deve então aprovar sua proposta de mudança.
O agente pode entrar em loop infinito? Na verdade, o agente pode até repetir buscas ou ações sem qualquer progresso. Porém, para evitar um loop infinito, o harness define limites de iterações, tempo e custo. Ao atingir esses limites, o loop agêntico é abortado.
10.3.5 Vantagens e Desvantagens 🔗
Para concluir, a principal vantagem do modelo de agentes é sua autonomia e capacidade de dividir um problema complexo em passos menores. Para resolver esses passos, o agente pode precisar de informações locais, que são obtidas por meio da execução de ferramentas. Por outro lado, a principal desvantagem também decorre desse modelo centrado em autonomia: agentes podem gerar uma grande quantidade de código, o que dificulta o entendimento e a revisão por humanos. Em outras palavras, com agentes, pode-se produzir código mais rapidamente do que a capacidade humana de compreensão e revisão, gerando o que está sendo chamado por alguns autores de dívida cognitiva.
10.4 Integração com Sistemas Externos 🔗
Muitas vezes, uma aplicação de IA, como um agente ou um chat, precisa de uma informação que não pode ser obtida localmente por meio de uma ferramenta disponível na própria máquina. Por exemplo, quando perguntamos para um modelo de IA qual é a capital de Minas Gerais, ele responde imediatamente Belo Horizonte. O motivo é que ele foi treinado com documentos que continham essa informação. Porém, quando perguntamos para o mesmo modelo quais os bugs estão abertos no sistema proprietário de bugs da nossa empresa, ele não vai conseguir responder baseado apenas nos seus dados de treinamento. Ou seja, para responder essa pergunta, ele precisa consultar um sistema externo. Por exemplo, o modelo deve acessar uma API do referido sistema de bugs, a qual possui um endpoint que retorna a lista de bugs abertos.
Para facilitar a integração entre LLMs e sistemas externos, existem certos protocolos, sendo Model Context Protocol (MCP) o mais conhecido deles. Como o nome sugere, o objetivo de MCP é padronizar a forma com que modelos acessam contexto externo para responder perguntas. No nosso exemplo, o contexto externo é obtido por meio de uma chamada a uma API com informações sobre os bugs de uma empresa. Porém, MCP pode ser usado para acessar diversos sistemas externos, como sistemas de arquivo (como Google Drive ou Dropbox), sistemas de mensagens (como Slack ou GMail), sistemas de gestão, sistemas de relacionamento com clientes, etc.
10.4.1 Arquitetura MCP 🔗
O protocolo MCP pressupõe uma arquitetura cliente/servidor, com quatro componentes principais (veja também na figura da próxima página):
Host: nome dado por MCP para a aplicação de IA, a qual pode ser um chat, um agente de código ou qualquer outra aplicação baseada em modelos de linguagem.
Cliente MCP: responsável por conectar o host aos servidores MCP, enviar solicitações, receber respostas e repassar ao host informações sobre as ferramentas e recursos disponíveis.
Servidor MCP: responsável por publicar ferramentas, recursos e prompts de um sistema externo em um formato padronizado, atendendo às requisições feitas pelo cliente MCP.
Sistema Externo: sistema que armazena e disponibiliza as informações de que o host precisa.
Ainda sobre a figura anterior, é importante explicar também que um cliente MCP costuma ser genérico, isto é, ele pode se conectar com diversos servidores MCP. Além disso, o sistema externo não precisa estar na mesma máquina do servidor MCP, podendo ser executado em outro servidor ou ambiente separado. Por fim, veja que a comunicação entre o cliente e o servidor MCP ocorre por meio da troca de mensagens em JSON.
Na próxima página, mostramos uma figura com o fluxo completo de uso
de MCP. Inicialmente, o cliente MCP consulta o servidor MCP para
descobrir quais ferramentas estão disponíveis, como por exemplo uma
função get_bugs(status: String). Em seguida, o usuário faz
uma solicitação em linguagem natural ao host (a aplicação de IA): Quais
bugs estão abertos? A aplicação interpreta o pedido e decide chamar a
ferramenta apropriada por meio do cliente MCP, que encaminha a
requisição ao servidor MCP. Esse, por sua vez, acessa o sistema externo
responsável pelos dados desejados. Por fim, o resultado retorna pelo
mesmo caminho até a aplicação de IA (host), que o converte em uma
resposta final para o usuário.
Além de ferramentas, MCP também pode ser usado para acessar outros recursos externos, como arquivos e bancos de dados.
Exemplo: O Jira — um conhecido sistema de gerenciamento de issues — possui um servidor MCP, semelhante ao que usamos como exemplo. Esse servidor disponibiliza ferramentas para buscar issues, criar ou atualizar tickets, consultar sprints e quadros, listar projetos, ler comentários, etc. Com isso, usuários podem submeter prompts como os seguintes em aplicações de IA: (1) liste os bugs abertos do projeto mobile com prioridade alta; (2) crie um ticket para registrar um bug no carrinho de compras após atualização do app; (3) liste as tarefas pendentes da sprint atual e seus responsáveis.
10.4.2 Integração Usando Interfaces de Linha de Comando 🔗
No entanto, temos também que lembrar que, pelo menos no momento
atual, não existe um amplo consenso sobre a necessidade de protocolos
como MCP. Por exemplo, uma alternativa a esse protocolo consiste em
disponibilizar ferramentas para acesso a um sistema externo baseadas em
uma Interface de Linha de Comando (ou CLI, na sigla em
inglês). Essas ferramentas devem ser instaladas na máquina local do
usuário e usadas para acessar o serviço remoto. Assim, um agente de
código pode chamar essas ferramentas da mesma forma como chama comandos
shell, como ls, grep, find ou
curl, ou comandos git, como git diff,
git commit e git push.
Exemplo: Fizzy é uma ferramenta de gerenciamento de projetos baseada em Kanban, que está sendo desenvolvida pela empresa 37signals para apoiar equipes no acompanhamento visual de tarefas e fluxos de trabalho. A ferramenta ainda não tem um servidor MCP, mas já possui uma interface CLI que permite executar, em um terminal, comandos como os seguintes:
fizzy board list # lista quadros Kanban do projeto
fizzy card list # lista cartões do quadro default
fizzy card show 42 # mostra detalhes do cartão ID 42
fizzy card create --board ID --title "..." # cria cartão
Assim, um agente de código pode integrar-se ao Fizzy sem usar um servidor MCP, bastando invocar esses comandos como faria com outra ferramenta local. Logo, esse exemplo ilustra que, em muitos casos, interfaces de linha de comando podem constituir uma alternativa mais simples para integrar aplicações de IA com sistemas externos.
10.5 Engenharia de Prompts 🔗
Especialistas em LLMs, como Andrej Karpathy, na citação com a qual iniciamos este capítulo, sugerem que linguagens naturais (como inglês e português) são agora também uma linguagem de programação. O motivo é que elas são usadas para escrever os prompts que constituem a principal entrada para modelos de linguagem.
Por esse motivo, não é surpresa que tenham sido realizados estudos para classificar e catalogar diferentes tipos de prompts, conforme descrito a seguir:
Zero-Shot Prompting: Quando se solicita ao modelo que realize uma tarefa sem fornecer qualquer exemplo de resposta.
One-Shot Prompting: Quando se fornece ao modelo um exemplo de resposta para a tarefa desejada. Se for fornecido mais de um exemplo, o prompt é chamado de Few-Shot Prompting.
Chain-of-Thought (CoT) Prompting: Quando se orienta o modelo a realizar passos intermediários de raciocínio antes de gerar a resposta final. Por exemplo, pode-se adicionar uma frase como a seguinte no prompt: Pense passo a passo ou Explique sua decisão. Adicionalmente, pode-se especificar, no próprio prompt, a lista de passos que o modelo deve seguir. Em qualquer caso, o objetivo é forçar o modelo a
pensar em voz alta
antes de dar uma resposta final.
Com a popularização de modelos de linguagem, surgiu também o termo Engenharia de Prompts, usado para designar boas práticas para a escrita de prompts.
No entanto, os modelos de linguagem evoluíram bastante nos últimos anos. Além disso, surgiram arquiteturas mais sofisticadas, como aquelas usadas por agentes de código. Como consequência, a importância de prompts elaborados diminuiu um pouco, já que parte do conhecimento e dos objetivos antes explicitados textualmente foi incorporada aos modelos mais avançados e a novas arquiteturas. No entanto, isso não significa que prompts se tornaram irrelevantes. Pelo contrário, prompts claros e bem estruturados continuam sendo úteis para obter respostas precisas e alinhadas aos objetivos dos usuários de modelos de linguagem.
Especificamente, podemos assumir que existem três tipos principais de prompts para manutenção de software:
Prompts para Modificação de Código, por exemplo, corrigir um bug, refatorar uma parte do código, implementar uma nova funcionalidade ou atualizar o sistema para usar um novo recurso de uma linguagem de programação.
Prompts para Compreensão de Código, isto é, quando usamos o modelo de linguagem para entender uma parte do código.
Prompts para Recomendação de Melhorias, por exemplo, quando queremos identificar oportunidades de refatoração ou prospectar novas funcionalidades para um sistema.
No restante desta seção, vamos apresentar boas práticas para escrita desses tipos de prompts, bem como mostrar alguns exemplos.
10.5.1 Prompts para Modificação de Código 🔗
No caso de prompts para modificação de código, dois tipos de informação são importantes. A primeira diz respeito aos critérios de verificação da tarefa que queremos realizar com o modelo. Por exemplo, ao solicitar a correção de um bug, podemos informar que os testes automatizados precisam continuar passando. O segundo tipo de informação diz respeito ao contexto para realização da tarefa. Nesse caso, podemos incluir nos prompts informações como mensagens de erro, elementos do código nos quais o modelo deve atuar e links para referências externas, como documentação de APIs.
A seguir, apresentamos exemplos de prompts para modificação de código:
Prompt para correção de bugs: A totalização do valor do carrinho de compras está incorreta quando há mais de um item no carrinho. Exemplo: 2 × R$ 10 + 1 × R$ 5 deveria ser R$ 25, mas está mostrando R$ 20. Verifique o cálculo em
CartService.calculateTotal. Primeiro, escreva um teste que reproduza o bug. Em seguida, use esse teste para verificar a correção do bug.Prompt para refatoração: Refatore o método
calculateTotalemShoppingCart, que está grande e com muitas responsabilidades. Por exemplo, extraia métodos para cálculo de subtotal, descontos e frete. Antes de concluir, garanta que os testes continuem passando.Prompt para implementação de novas funcionalidades: Implemente uma função que aplique um desconto no total de um carrinho de compras. Exemplo: cupom
15OFF→ 15% de desconto. Se for informado um cupom inválido, não altere o total. Implemente testes para cupons válidos e inválidos.Prompt para atualização de linguagem de programação: Refatore
switchparaswitch expressionapenas quando cadacasepossuir uma única expressão sem efeitos colaterais e oswitchseja usado para retornar ou atribuir um valor. Adicionedefaultquando necessário. Considere a refatoração completa somente se todos os testes passarem sem modificação de asserções.
Como o leitor deve ter percebido, esses prompts descrevem, com certo nível de detalhe, a modificação de código que deve ser realizada e, muitas vezes, incluem exemplos. Logo, usando a classificação anterior, eles são um híbrido de prompts One-Shot (ou Few-Shot) e Chain-of-Thought.
10.5.2 Prompts para Compreensão de Código 🔗
Por natureza, esses prompts descrevem tarefas mais abertas, cujo objetivo é entender alguma parte ou funcionalidade do código. Portanto, eles, normalmente, não incluem critérios de verificação nem informações detalhadas de contexto. A seguir, mostramos alguns exemplos.
Como o mecanismo de login funciona?
Por que este código chama a função
calculateDiscount()e não a funçãogetDefaultDiscount()na linha 1525?Explique por que a classe
OrderServiceviola o princípio da responsabilidade única.Explique o propósito do método a seguir. Antes de propor uma resposta, siga os seguintes passos: (1) Identifique as entradas do método; (2) Identifique as saídas produzidas; (3) Resuma a lógica principal executada; (4) Identifique dependências externas, como chamadas a outros métodos, APIs ou bibliotecas. Por fim, forneça uma explicação concisa do propósito do método. [Segue o código do método]
Dada a natureza de tarefas de compreensão de código, em sua maioria, os prompts acima são do tipo Zero-Shot. No entanto, podemos usar também prompts do tipo Chain-of-Thought (CoT), como no caso do último exemplo.
10.5.3 Prompts para Recomendação de Melhorias 🔗
Esses prompts são usados para explorar oportunidades de melhoria, modernização, evolução ou manutenção de um sistema. Veja alguns exemplos:
Analise o arquivo
UserService.javae identifique oportunidades de refatoração para melhorar legibilidade e manutenção.Analise o arquivo legado
LegacyPaymentProcessor.phpe proponha alguns passos para modernizá-lo gradualmente.Recomende refatorações para facilitar testes automatizados das funções do arquivo
inventory_management.py.Sugira melhorias de desempenho para
RecommendationEngine.ts.
Normalmente, como observado nos exemplos acima, esses prompts são do tipo Zero-Shot.
Bibliografia 🔗
Anthropic. Best Practices for Claude Code. https://code.claude.com/docs/ en/best-practices
Michael Bolin. Unrolling the Codex agent loop. https://openai.com/index/ unrolling-the-codex-agent-loop
Shunyu Yao; Jeffrey Zhao; Dian Yu; Nan Du; Izhak Shafran; Karthik Narasimhan; Yuan Cao. ReAct: Synergizing Reasoning and Acting in Language Models. ICLR 2023.
Chip Huyen. AI Engineering: Building Applications with Foundation Models. O’Reilly, 2025.
Exercícios 🔗
1. Compare assistentes e agentes de código, conforme discutido neste capítulo. Explique a principal diferença entre esses dois modelos de uso de IA no desenvolvimento de software.
2. Agentes de código costumam demandar uma grande quantidade de tokens para resolver uma tarefa (tokens são as unidades básicas de texto processadas por modelos de linguagem, podendo se referir a palavras, partes de palavras ou símbolos). Por que esse consumo de tokens tende a ser tão alto? Baseie sua resposta na arquitetura de agentes de código apresentada na Seção 10.3.
3. No contexto de LLMs, pesquise o significado do conceito de janela de contexto (context window). Qual é o tamanho da janela de contexto de algumas LLMs conhecidas? Qual é o impacto desse conceito no funcionamento de agentes de código? Qual recurso agentes de código implementam para reduzir esse impacto?
4. Um agente de código recebeu a tarefa de implementar uma certa funcionalidade em um sistema. Cite três tipos de ferramentas externas que ele poderia utilizar durante a execução da tarefa e explique brevemente o propósito de cada uma delas.
5. Explique a diferença entre as técnicas Chain-of-Thought (CoT) e ReAct (Reasoning + Acting) no contexto de modelos de linguagem.
6. Qual a diferença entre dívida técnica e dívida cognitiva?
Este livro foi formatado com a ferramenta Pandoc, que foi usada para converter Markdown em LaTeX e, depois, para gerar um arquivo PDF. A fonte usada foi Computer Modern, 11 pt. As versões EPUB e HTML foram geradas a partir dos mesmos arquivos Markdown, também usando a ferramenta Pandoc.