Construindo Conversas Multi-Turn com o Array de Mensagens
Uma conversa real com Claude não é uma única chamada de API - é uma sequência de chamadas, cada uma carregando todo o histórico até o momento.
Esta página cobre os mecanismos práticos de construção e manutenção desse histórico no código da aplicação.
Resumo
A API de Mensagens não tem memória do lado do servidor de requisições passadas.
Cada chamada é avaliada independentemente, usando apenas o array messages incluído naquela requisição específica.
Para construir uma conversa que pareça contínua, sua aplicação gerencia uma lista crescente de turnos e reenvia tudo a cada vez.
Este é um design deliberado: mantém a API simples e sem estado, e coloca você no controle total de exatamente qual contexto Claude vê em cada turno.
O contrapartida é que o gerenciamento de estado - adicionar turnos corretamente, na ordem certa, com o conteúdo certo - se torna responsabilidade da sua aplicação.
Receita
Cartão de receita de referência rápida - pronto para copiar e colar.
import anthropic
client = anthropic.Anthropic()
messages = []
def send(user_text: str) -> str:
messages.append({"role": "user", "content": user_text})
response = client.messages.create(
model="claude-opus-4-8",
max_tokens=1024,
system="Você é um assistente prestativo.",
messages=messages,
)
messages.append({"role": "assistant", "content": response.content})
return response.content[0].textQuando usar isso:
- Qualquer interface de chat onde o usuário envia mais de uma mensagem por sessão.
- Loops de agente onde as respostas de Claude (incluindo chamadas de ferramentas) precisam informar seu próximo turno.
- Serviços de backend que mantêm uma conversa por usuário, sessão ou ticket.
- Qualquer lugar onde você precise que Claude se refira a algo dito anteriormente na mesma interação.
Exemplo de Trabalho
import anthropic
client = anthropic.Anthropic()
class Conversation:
"""Gerencia o array de mensagens para uma conversa e o adiciona a cada turno."""
def __init__(self, system_prompt: str, model: str = "claude-opus-4-8"):
self.system_prompt = system_prompt
self.model = model
self.messages: list[dict] = []
def send(self, user_text: str, max_tokens: int = 1024) -> str:
self.messages.append({"role": "user", "content": user_text})
response = client.messages.create(
model=self.model,
max_tokens=max_tokens,
system=self.system_prompt,
messages=self.messages,
)
# Adiciona a lista completa de conteúdo, não apenas o texto extraído, para que quaisquer
# blocos não textuais (uso de ferramentas, etc.) sejam preservados para turnos futuros.
self.messages.append({"role": "assistant", "content": response.content})
# Concatena apenas os blocos de texto para o valor de retorno voltado para o chamador.
return "".join(block.text for block in response.content if block.type == "text")
if __name__ == "__main__":
convo = Conversation(system_prompt="Você é um assistente prestativo de planejamento de viagens.")
print(convo.send("Estou planejando uma viagem ao Japão em abril."))
print(convo.send("O que devo levar, considerando o que acabei de dizer?"))
print(convo.send("Quantas mensagens trocamos até agora?"))O que isso demonstra:
- Uma pequena classe
Conversationgerenciaself.messagespara que o estado não vaze para globais do módulo. send()adiciona o turno douserantes da chamada da API e o turno doassistantdepois, mantendo a alternância estrita de papéis intacta.- A lista completa
response.contenté armazenada, não uma string simples, para que blocos de uso de ferramentas ou outro conteúdo estruturado sobrevivam para turnos posteriores. system_prompté definido uma vez na construção e reutilizado sem alterações em cada chamada na conversa.
Mergulho Profundo
Como Funciona
- Cada chamada para
client.messages.create()é totalmente autônoma: a API não tem conceito de sessão, ID de conversa ou memória entre requisições. - A lista
messagesque você envia é percorrida em ordem; a API valida que ela começa comusere alterna estritamente os papéis a partir daí. - A resposta de Claude chega como um objeto
Messagecujo.contenté uma lista de blocos de conteúdo - tipicamente um blocotextpara uma resposta simples, mas potencialmente mais para uso de ferramentas ou conteúdo misto. - Adicionar
response.content(o objeto, não a string derivada) como o próximo turno doassistanté o que permite que Claude "veja" seu próprio raciocínio e chamadas de ferramentas anteriores na próxima requisição. - Como o array inteiro é reenviado, tanto o tamanho do payload da requisição quanto o número de tokens faturados aumentam à medida que a conversa se alonga - abordado mais detalhadamente na página de gerenciamento da janela de contexto.
Crescimento de Turnos por Troca
| Após | comprimento de messages | Papéis presentes |
|---|---|---|
| Primeira pergunta do usuário | 1 | user |
| Primeira resposta de Claude adicionada | 2 | user, assistant |
| Segunda pergunta do usuário | 3 | user, assistant, user |
| Segunda resposta de Claude adicionada | 4 | user, assistant, user, assistant |
Cada troca adiciona exatamente duas entradas: um user, um assistant.
Notas de Python
# Prefira uma estrutura tipada em vez de dicionários soltos se você estiver gerenciando muitas conversas.
from dataclasses import dataclass, field
@dataclass
class Turn:
role: str
content: str | list
@dataclass
class Session:
messages: list[Turn] = field(default_factory=list)
def as_api_payload(self) -> list[dict]:
return [{"role": t.role, "content": t.content} for t in self.messages]Usar um pequeno tipo wrapper (ou um dataclass, como acima) em vez de dicionários brutos espalhados pelo seu código torna muito mais fácil serializar uma conversa para armazenamento e recarregá-la mais tarde sem adivinhar a forma.
Armadilhas
- Adicionar apenas
response.content[0].textem vez da lista completa de conteúdo. Isso descarta blocostool_usee qualquer conteúdo estruturado, quebrando fluxos de uso de ferramentas no próximo turno. Correção: sempre adicioneresponse.contentcomo está para o turno do assistente; extraia o texto separadamente para exibição. - Esquecer de adicionar o turno do usuário antes de chamar a API. O array enviado para
messages.create()já deve incluir a pergunta mais recente. Correção: adicione o turno do usuário primeiro, depois chame a API, depois adicione a resposta. - Mutar a mesma string
systemno meio da conversa. Editar o prompt do sistema entre as chamadas funciona, mas invalida silenciosamente qualquer cache de prompt construído com base no prefixo anterior e pode mudar o comportamento de Claude no meio da conversa de maneiras confusas. Correção: mantenha o prompt do sistema congelado pela vida de uma conversa, a menos que você tenha um motivo explícito para alterá-lo. - Permitir que o array de mensagens cresça sem limites. Cada requisição reenvia todo o histórico, então uma conversa muito longa eventualmente corre o risco de exceder a janela de contexto do modelo e aumenta o custo em cada chamada. Correção: planeje aparar ou resumir turnos mais antigos antes que isso aconteça.
- Compartilhar uma lista
messagesmutável entre usuários concorrentes. Uma única lista global usada por várias conversas simultâneas misturará turnos não relacionados e quebrará a alternância. Correção: escopo uma listamessagespor conversa/sessão, nunca uma lista compartilhada em nível de módulo. - Assumir que a API deduplica ou ignora contexto repetido. Enviar a mesma informação duas vezes no array (por exemplo, duplicar um grande documento em dois turnos
userdiferentes) é faturado como tokens de entrada separados em ambas as vezes. Correção: inclua o contexto compartilhado uma vez e confie que ele permanecerá no histórico da conversa em vez de reenviá-lo.
Alternativas
| Alternativa | Usar Quando | Não Usar Quando |
|---|---|---|
| Armazenamento de sessão do lado do servidor (banco de dados/cache) com chave por ID de conversa | Múltiplos usuários, múltiplos dispositivos ou conversas que devem sobreviver a uma reinicialização do processo | Um script de curta duração ou ferramenta CLI de sessão única onde o estado em memória é suficiente |
| Compactação ou sumarização de turnos mais antigos | A conversa é de longa duração e se aproxima da janela de contexto | A conversa é curta e improvável de crescer |
Uma única requisição "one-shot" com todo o contexto em um único turno user | A tarefa é uma única pergunta sem necessidade de ida e volta | Você realmente precisa que Claude se refira às suas próprias respostas anteriores em vários turnos |
FAQs
A API de Mensagens armazena meu histórico de conversas para mim?
Não. A API é sem estado - ela não tem conceito de ID de conversa ou sessão. Sua aplicação é responsável por manter o array messages e reenviá-lo em cada chamada.
O que exatamente devo anexar após Claude responder?
Anexe response.content - a lista completa de blocos de conteúdo - como o content de uma nova entrada {"role": "assistant", ...}. Não armazene apenas a string de texto extraída.
Preciso reenviar o prompt do sistema em cada requisição?
Sim. Ao contrário de messages, system não é cumulativo - é um parâmetro de nível superior em cada chamada, então você passa o mesmo valor toda vez que deseja um comportamento consistente.
Posso editar um turno anterior no array antes de reenviá-lo?
Sim, nada impede você de modificar o histórico no lado do cliente - a API apenas valida a alternância de papéis e a estrutura, não que o conteúdo corresponda exatamente a uma resposta anterior. Editar o histórico é ocasionalmente útil para corrigir a própria saída anterior de Claude, mas faça isso deliberadamente, pois muda o que Claude "acredita" ter acontecido.
O que acontece se eu pular a adição da resposta do assistente e apenas adicionar minha próxima pergunta?
Você violará a regra de alternância - dois turnos user seguidos - e a próxima requisição falhará com um erro 400. Cada resposta do assistente deve ser adicionada antes que o próximo turno do usuário seja adicionado.
Existe um limite para quantos turnos posso ter em uma conversa?
Não há um limite fixo para o número de turnos, mas a contagem total de tokens de todos os turnos, mais o prompt do sistema, deve caber dentro da janela de contexto do modelo. Conversas longas eventualmente precisam de aparamento ou sumarização.
Devo armazenar o array de mensagens como JSON em um banco de dados?
Essa é uma abordagem comum e razoável para qualquer coisa além de um único script em memória - armazene o array (ou uma representação estruturada equivalente) com chave por ID de conversa ou sessão, e recarregue-o antes da próxima chamada.
O reenvio do array completo custa mais à medida que a conversa cresce?
Sim. Cada requisição é faturada por todos os tokens de entrada naquela requisição, incluindo o histórico reenviado, então o uso de tokens e o tamanho da requisição aumentam à medida que uma conversa se alonga.
Duas conversas diferentes podem compartilhar a mesma lista de mensagens com segurança?
Não. Uma lista mutável compartilhada usada por conversas concorrentes misturará turnos de diferentes usuários e quebrará a alternância necessária. Escopo uma lista por conversa.
Qual é o array de mensagens válido mínimo?
Uma única entrada com o papel user e conteúdo não vazio. O array deve começar com user, e uma requisição de turno único simples é uma chamada completamente válida.
Se Claude chamar uma ferramenta, isso muda como eu construo o array?
O mesmo padrão de adição se aplica - o turno do assistente solicitando a ferramenta é adicionado como de costume, e o resultado da ferramenta retorna como um bloco de conteúdo tool_result dentro do próximo turno user, mantendo a alternância intacta.
É seguro truncar ou descartar os primeiros turnos do array por conta própria?
Sim, e é uma técnica normal para conversas longas - apenas certifique-se de que o array que você envia ainda comece com um turno user e ainda alterne corretamente após o truncamento.
Relacionados
- Compreendendo Papéis e Turnos na API de Mensagens - o modelo conceitual no qual esta página se baseia.
- Noções Básicas da API de Mensagens - introdução mais curta e orientada a exemplos sobre os mesmos mecanismos.
- Gerenciando Histórico de Conversas e Aparamento para a Janela de Contexto - o que fazer quando o array fica muito grande.
- Referência de Papéis e Tipos de Blocos de Conteúdo da API de Mensagens - referência para cada tipo de bloco de conteúdo que você pode adicionar.
Versões de Stack: Escrito contra a linha de modelos Claude atual em ~junho de 2026 - Claude Fable 5, Claude Opus 4.8, Claude Sonnet 5 (o padrão), e Claude Haiku 4.5 - e o SDK oficial
anthropicpara Python (última versão 0.x). Nomes de modelos, versões de SDK e preços mudam rapidamente - verifique os detalhes atuais em platform.claude.com/docs antes de confiar neles.