Dia da Marmota para agentes de IA: padrões de cache de prompt para modelos OpenAI no Amazon Bedrock

O problema: agentes de IA vivem no Dia da Marmota

No filme Groundhog Day (Feitiço do Tempo), o personagem de Bill Murray acorda todo dia repetindo exatamente o mesmo 2 de fevereiro, do zero, sem carregar nada do que viveu no dia anterior. Agentes de Inteligência Artificial (IA) enfrentam um problema parecido: a cada passo da execução — cada chamada de ferramenta, cada resultado retornado, cada decisão tomada — o modelo precisa receber novamente todo o contexto acumulado da conversa.

Isso acontece porque Modelos de Linguagem de Grande Escala (LLMs) não têm memória entre requisições. Na virada do turno 20 de uma sessão de trabalho, o modelo relê os turnos 1 a 19 inteiros — e você paga por cada token repetido. A AWS publicou um guia técnico explicando como o prompt caching quebra esse ciclo no Amazon Bedrock, com foco em modelos OpenAI.

O que um agente envia ao modelo em cada turno

Antes de entender o cache, vale entender o que compõe uma requisição típica de agente:

  • Lista de ferramentas: as definições de cada função que o agente pode chamar. Muda quando há uma nova versão.
  • Instruções: o prompt de desenvolvedor com regras, persona, políticas e exemplos. Também muda por versão.
  • Histórico: tudo que aconteceu na sessão até agora — tarefa, chamadas de ferramentas e resultados. Cresce a cada turno.
  • Novo turno: o resultado mais recente de uma ferramenta ou a mensagem do usuário.

Sem cache, o agente reenvia tudo isso ao modelo a cada turno. O prompt caching resolve isso armazenando o estado processado do prompt e reutilizando-o nas requisições seguintes, a uma fração do preço de entrada normal.

Como o prompt caching funciona por dentro

Quando o modelo lê um prompt, ele executa uma etapa chamada prefill: processa cada token de entrada e constrói seus vetores internos de chave e valor (KV — Key-Value), que formam o estado de trabalho do modelo antes de gerar qualquer resposta. O prompt caching salva esse estado.

Quando uma requisição posterior começa com os mesmos tokens de entrada, o serviço carrega o estado salvo em vez de recomputá-lo. Apenas o conteúdo novo é processado.

O cache pode ser implícito — onde o Amazon Bedrock decide automaticamente onde termina o prefixo reutilizável — ou explícito — onde o código marca esse ponto usando prompt_cache_breakpoint. As duas abordagens podem ser combinadas, e a AWS recomenda isso para cargas de trabalho de agentes.

Primeiro cache: conteúdo estático

O primeiro ganho é cachear a parte estática do prompt: ferramentas e instruções que não mudam entre requisições. O prefixo reutilizável precisa ter pelo menos 1.024 tokens para ser elegível. Para conteúdo estático, o modo explícito é o mais indicado — você escolhe exatamente onde o prefixo salvo termina:

KEY = "support-agent:v7:gpt-6-astra"
INSTRUCTIONS = "Your full support policies and examples..."  # Rendered context: [provider context][TOOLS][INSTRUCTIONS] | [task]
# Cache starts at the beginning; the marker below chooses its end.
resp = client.responses.create(
    model="us.openai.gpt-6-astra",
    store=False,  # Response storage; prompt caching still works.
    include=["reasoning.encrypted_content"],
    prompt_cache_key=KEY,
    prompt_cache_options={"mode": "explicit", "ttl": "30m"},
    tools=TOOLS,  # Rendered before instructions; keep order stable.
    input=[
        {
            "role": "developer",
            "content": [{
                "type": "input_text",
                "text": INSTRUCTIONS,  # Save through this whole block, including TOOLS.
                "prompt_cache_breakpoint": {"mode": "explicit"},
            }],
        },
        # User message is processed but not cached.
        {"role": "user", "content": task},
    ],
)

O prompt_cache_key indica ao serviço quais requisições devem compartilhar um cache. Em testes com esse padrão, um prefixo de cerca de 4.000 tokens escreveu 4.120 tokens na primeira requisição e leu esses mesmos 4.120 tokens nas requisições seguintes.

Um ponto de atenção importante: o prompt caching só funciona quando você passa exatamente o mesmo texto. Instruções e listas de ferramentas precisam ser serializadas da mesma forma sempre. Qualquer conteúdo variável — como data, nome do usuário ou ID de requisição — deve vir após o limite do cache.

Cache implícito para histórico crescente

À medida que a conversa cresce com ferramentas e conteúdos diferentes, o modo implícito permite que o serviço acompanhe o histórico automaticamente:

def ask_with_implicit_cache(history):
    return client.responses.create(
        model="us.openai.gpt-6-astra",
        store=False,
        include=["reasoning.encrypted_content"],
        prompt_cache_key=KEY,
        prompt_cache_options={"mode": "implicit", "ttl": "30m"},
        tools=TOOLS,
        input=history,  # Everything gets cached
    )

history = [
    {"role": "developer", "content": INSTRUCTIONS},
    {"role": "user", "content": task},
]

# Automatic endpoint: the end of the user message.
resp = ask_with_implicit_cache(history)

Para combinar os dois modos — um ponto de partida compartilhado explícito e um histórico crescente gerenciado implicitamente — basta adicionar o marcador no bloco do desenvolvedor e chamar a função com modo implícito no nível da requisição:

history = [
    {
        "role": "developer",
        "content": [{
            "type": "input_text",
            "text": INSTRUCTIONS,
            "prompt_cache_breakpoint": {"mode": "explicit"},
        }],
    },
    {"role": "user", "content": task},
]

# Still implicit mode, now with an explicit startup save point in the history.
resp = ask_with_implicit_cache(history)

Cacheando histórico e resultados de ferramentas

O loop de execução do agente acumula saídas do modelo e resultados de ferramentas no histórico. Em modo implícito, o Amazon Bedrock pode reutilizar o prefixo inalterado e cachear o conteúdo adicionado até o último resultado de ferramenta:

import json

# Map tool names to their Python implementations.
TOOL_HANDLERS = {
    "get_owner": get_owner,
    # Register your other initial tools here.
}

def execute_function(name, arguments):
    """Run the requested tool and return its result."""
    handler = TOOL_HANDLERS.get(name)
    if handler is None:
        return {"error": f"Unknown tool: {name}"}
    return handler(**arguments)

while True:
    # Preserve all output items, including encrypted reasoning.
    history.extend(resp.output)
    calls = [x for x in resp.output if x.type == "function_call"]
    if not calls:
        break
    for call in calls:
        result = execute_function(call.name, json.loads(call.arguments))
        history.append({
            "type": "function_call_output",
            "call_id": call.call_id,
            "output": json.dumps(result),
        })
    # The last result in this group supplies the automatic endpoint.
    resp = ask_with_implicit_cache(history)

Para o modo explícito puro, o loop marca o resultado final de cada lote com um prompt_cache_breakpoint, mantendo os marcadores anteriores no histórico. Consulte as regras atuais de lookup para conversas longas na documentação oficial.

Em testes com seis turnos, o GPT-6 Astra com breakpoints implícitos apresentou cerca de 62% de redução no custo de entrada em comparação com execução sem cache. O modo explícito com rolling breakpoints economizou cerca de 61% no mesmo modelo.

Quando as ferramentas mudam durante a sessão

Adicionar, remover ou reordenar definições de ferramentas invalida o prefixo cacheado. Para lidar com isso, a AWS descreve duas abordagens:

  • Ferramentas conhecidas em etapas diferentes: mantenha a lista TOOLS estável e use allowed_tools para limitar o que o modelo pode chamar em cada etapa, sem alterar o catálogo cacheado.
  • Novas ferramentas durante a sessão: os endpoints Amazon Bedrock Runtime e Amazon Bedrock Mantle aceitam um tipo de item de entrada chamado additional_tools para GPT-6 Astra, GPT-6.1 Sol e GPT-5.6 Luna. Para disponibilidade por região da AWS, consulte os modelos suportados por região da AWS no Amazon Bedrock.
TOOL_HANDLERS["get_due_date"] = get_due_date

NEW_TOOL = {
    "type": "function",
    "name": "get_due_date",
    "description": "Return the due date for a support case.",
    "strict": True,
    "parameters": {
        "type": "object",
        "properties": {"case_id": {"type": "string"}},
        "required": ["case_id"],
        "additionalProperties": False,
    },
}

history.append({
    "type": "additional_tools",
    "role": "developer",
    "tools": [NEW_TOOL],
})
history.append({
    "role": "user",
    "content": "Find the due date for case C-123.",
})

# Implicit mode saves through this new user message, including NEW_TOOL.
resp = ask_with_implicit_cache(history)
d = resp.usage.input_tokens_details
print("old prefix read", d.cached_tokens, "new write", d.cache_write_tokens)
# Example output:
# old prefix read 2048 new write 256

Subagentes e chamadas paralelas

Ao delegar tarefas a agentes auxiliares, é possível compartilhar o prefixo cacheado entre eles. A recomendação da AWS é fazer um “aquecimento” (warm-up) antes de disparar os helpers em paralelo:

shared = {"role": "developer", "content": [{
    "type": "input_text",
    "text": INSTRUCTIONS,
    "prompt_cache_breakpoint": {"mode": "explicit"},
}]}

warm = client.responses.create(
    model="us.openai.gpt-6-astra",
    store=False,
    tools=TOOLS,
    prompt_cache_key=KEY,
    prompt_cache_options={"mode": "implicit", "ttl": "30m"},
    max_output_tokens=16,
    input=[shared, {"role": "user", "content": "Reply READY."}],
)

d = warm.usage.input_tokens_details
if not (d.cache_write_tokens or d.cached_tokens):
    raise RuntimeError("Shared prefix was not cached.")

from concurrent.futures import ThreadPoolExecutor

# helper_tasks contains the task text for each helper.
helper_inputs = [
    [shared, {"role": "user", "content": task}]
    for task in helper_tasks
]

with ThreadPoolExecutor(max_workers=4) as pool:
    helpers = list(pool.map(ask_with_implicit_cache, helper_inputs))

Para chamadas laterais como sumarizadores ou juízes, basta copiar o histórico completo do agente pai e adicionar a instrução específica do helper:

from copy import deepcopy

# history is the completed history built by the agent loop above.
# Copy it so the helper can add instructions without changing the parent.
helper_history = deepcopy(history)
helper_history.append({
    "role": "developer",
    "content": "Summarize this conversation in one sentence. Do not call tools.",
})

summary = ask_with_implicit_cache(helper_history)
d = summary.usage.input_tokens_details
print("history read", d.cached_tokens, "write", d.cache_write_tokens)
print(summary.output_text)
# Example output (assumed values and conversation):
# history read 4096 write 32
# Case C-123 is due on October 31, 2026.

Pausas, reinícios e chaves de cache

Prefixos cacheados têm um tempo de vida (TTL — Time to Live) mínimo de 30 minutos, renovado a cada reutilização bem-sucedida. Para novas sessões que precisam reutilizar o mesmo prefixo, a chave de cache deve ser estável. Uma chave específica por sessão isola as entradas de cache mesmo quando os prompts coincidem.

A AWS recomenda derivar a chave de cache a partir do conteúdo das instruções usando um hash:

import hashlib

def cache_key(app, model, prompt_version, instructions):
    digest = hashlib.sha256(instructions.encode()).hexdigest()[:12]
    return f"{app}:{prompt_version}:{model}:{digest}"

KEY = cache_key("support-agent", "us.openai.gpt-6-astra", "v7", INSTRUCTIONS)

prefix = {"role": "developer", "content": [{
    "type": "input_text",
    "text": INSTRUCTIONS,
    "prompt_cache_breakpoint": {"mode": "explicit"},
}]}

for label, question in [
    ("session A", "Explain the refund policy without calling tools."),
    ("session B", "Explain the escalation policy without calling tools."),
]:
    # A fresh conversation for each session; the startup prefix matches.
    session_history = [prefix, {"role": "user", "content": question}]
    result = ask_with_implicit_cache(session_history)
    d = result.usage.input_tokens_details
    print(label, "read", d.cached_tokens, "write", d.cache_write_tokens)

Na primeira execução a frio, a sessão A escreve o prefixo compartilhado. A sessão B pode lê-lo mesmo com uma tarefa diferente. A reutilização do cache exige o mesmo modelo, conta AWS e região.

O que o prompt caching entrega em números

Para um agente baseado em GPT-6 Astra com um prefixo inicial de 8.000 tokens e seis turnos adicionando 2.000 tokens cada, os resultados modelados pela AWS foram:

  • Cache estático de uma vez: custo reduzido a US$ 0,50 por sessão.
  • Rolling breakpoints explícitos: redução de 64% no custo de entrada modelado — de US$ 0,87 para US$ 0,32 por sessão.

Esses são resultados de uma carga de trabalho específica. A AWS recomenda medir os ganhos do seu próprio agente.

Como verificar se o cache está funcionando

O campo cached_tokens no uso da resposta indica tokens lidos do cache; cache_write_tokens indica tokens escritos. Para monitoramento em produção no endpoint Amazon Bedrock Runtime, as métricas CacheReadInputTokenCount e CacheWriteInputTokenCount estão disponíveis no Amazon CloudWatch. Para custos, filtre o AWS Cost Explorer pelo Amazon Bedrock e agrupe por tipo de uso — leituras, escritas e entradas não cacheadas aparecem como entradas separadas por modelo.

Lista de verificação rápida

  • Mantenha o prefixo reutilizável estável: ferramentas e instruções compartilhadas primeiro, histórico e novos turnos depois.
  • Use cache implícito para histórico crescente, marcadores explícitos para prefixos compartilhados, ou ambos.
  • No modo explícito puro, mantenha os marcadores de turnos anteriores para que requisições futuras encontrem os prefixos salvos.
  • Mantenha o catálogo inicial de ferramentas inalterado; use allowed_tools para restringir chamadas e additional_tools para adicionar novas ferramentas.
  • O prefixo cacheável precisa ter pelo menos 1.024 tokens e permanece elegível por pelo menos 30 minutos após a última escrita ou reutilização bem-sucedida.
  • Aqueça o conteúdo compartilhado antes de disparar helpers em paralelo.
  • Use chaves de cache estáveis dentro de cada grupo de compartilhamento.
  • Escritas de cache custam 1,25 vez a taxa normal de entrada; leituras dependem do modelo.

Conclusão

Assim como Bill Murray só escapa do Dia da Marmota quando para de repetir os mesmos erros e começa a carregar o que aprendeu de um dia para o outro, agentes de IA saem do ciclo de reprocessamento quando mantêm o início do prompt estável e usam limites de cache automáticos ou explícitos à medida que o histórico cresce.

A combinação de cache implícito para conversas em evolução e marcadores explícitos para conteúdo compartilhado cobre a maioria dos cenários. Para aplicar esses padrões, a AWS disponibiliza a documentação de prompt caching para modelos OpenAI no Amazon Bedrock e informações sobre disponibilidade de modelos por região e endpoint.

Fonte

Groundhog Day for AI agents: Prompt caching patterns for using OpenAI models on Amazon Bedrock (https://aws.amazon.com/blogs/machine-learning/groundhog-day-for-ai-agents-prompt-caching-patterns-for-using-openai-models-on-amazon-bedrock/)

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *