Pular para o conteúdo
Novo

DeepSeek V4.1 Flash grátis por 7 dias: 10M tokens por dia após qualquer recarga. Até 30/11.

Como funciona

Agentes de IA e uso de ferramentas: como um modelo chama funções

Como funciona o uso de ferramentas: o modelo devolve uma chamada estruturada, o programa executa a ferramenta e regras de aprovação decidem o que roda.

Iniciante12 min de leituraPor NotHoly

Um modelo de chat produz texto. Um agente é um sistema que faz o modelo ir além disso: ele lê um pedido, percebe que precisa de uma informação ou de uma ação fora da conversa, pede a um programa hospedeiro que faça o trabalho e segue adiante com o resultado. Quase todo agente se apoia no mesmo mecanismo, o uso de ferramentas, também chamado de chamada de função.

Este artigo explica esse mecanismo passo a passo, com os formatos de requisição e resposta descritos na documentação pública de APIs. Você vai ver por que o programa hospedeiro, e não o modelo, decide o que realmente roda, por que ações com efeito colateral precisam de uma decisão humana e o que a lista da OWASP chama de excesso de agência.

Não é preciso laboratório. Uma pequena simulação local, no fim do texto, roda o ciclo com um modelo de mentira, para que você veja o fluxo de controle sem enviar nada a uma API.

Do que um agente é feito

Um agente tem três partes.

O modelo recebe texto e devolve texto. No uso de ferramentas, ele também pode produzir um pedido estruturado que diz "chame esta função com estes argumentos".

O hospedeiro é o programa que você escreve ou executa. Ele envia as requisições ao modelo, mantém a conversa, lê a saída e decide o que fazer com ela. É no hospedeiro que fica o código real de cada ferramenta.

As ferramentas são funções que o hospedeiro aceita rodar em nome do modelo: consultar um arquivo, ler um banco de dados, buscar uma página, enviar uma mensagem. Cada ferramenta tem um nome, uma descrição e um esquema para os argumentos.

O modelo propõe e o hospedeiro decide. Boa parte das questões de segurança deste artigo sai dessa divisão.

Por que o ciclo importa

Um modelo sem ferramentas só responde com o que tem no contexto. Pergunte a ele a cotação de hoje e ele vai chutar ou dizer que não sabe. Com uma ferramenta de clima, ele pede ao hospedeiro o valor, recebe a resposta e escreve a partir dela.

O ciclo também permite vários passos. O modelo pode pesquisar, ler um resultado, achar que não basta, abrir um link que encontrou e então resumir. Cada passo é uma ida e volta separada, e o hospedeiro controla todas: quantas são permitidas, quais ferramentas são oferecidas e o que acontece quando uma chamada falha.

Como uma chamada de ferramenta funciona

Toda troca com ferramentas segue a mesma sequência, qualquer que seja a API.

  1. O hospedeiro envia a conversa e uma lista de definições de ferramentas ao modelo.
  2. O modelo responde com texto ou devolve uma chamada estruturada, marcando a resposta como uma parada para ferramenta.
  3. O hospedeiro lê a chamada, valida e executa a função correspondente, se ela existir e a política permitir.
  4. O hospedeiro acrescenta o resultado à conversa, referindo-se à chamada pelo seu ID.
  5. O hospedeiro envia a conversa atualizada de volta. O modelo chama outra ferramenta ou escreve a resposta final.

O ciclo termina quando o modelo devolve texto simples, quando o hospedeiro atinge um limite de turnos ou quando o hospedeiro o interrompe.

Definições de ferramenta são JSON Schema

Uma definição de ferramenta tem nome, descrição escrita para o modelo e um esquema de entrada. O esquema usa JSON Schema para declarar o tipo de cada argumento e quais são obrigatórios. O modelo escolhe ferramentas lendo as descrições, então uma descrição vaga gera chamadas erradas.

O modelo devolve uma chamada estruturada

O modelo não executa nada. Ele devolve um dado que informa o nome da função e os argumentos em um objeto JSON. Como a saída é estruturada, o hospedeiro não precisa adivinhar o nome da função a partir de texto livre.

Estruturado não quer dizer correto. O modelo pode citar uma ferramenta que não existe, ou passar um valor que respeita o esquema mas não faz sentido naquela situação.

O hospedeiro executa a ferramenta

Essa etapa é só do hospedeiro. Ele procura o nome no próprio registro. Se o nome não existe, responde com erro. Se existe, ainda precisa decidir se aquela chamada, com aqueles argumentos, para aquele usuário, pode rodar. Só então executa o código.

O resultado volta

O resultado vai como uma mensagem que carrega o ID da chamada original. O modelo lê o resultado como dado e decide o próximo passo. Trate esse conteúdo como dado, não como instrução. Uma página web buscada pode conter frases que parecem comandos. Esse é o cerne da injeção de prompt, e o hospedeiro não pode deixar que um conteúdo devolvido altere as regras dele.

Formatos de requisição e resposta anotados

Os formatos abaixo seguem duas referências públicas sobre uso de ferramentas, consultadas em 08/11/2026. Os identificadores de modelo são marcadores. Os nomes dos campos são exatos.

Uma requisição com uma ferramenta

json
{
  "model": "<model-id>",
  "max_tokens": 1024,
  "tools": [
    {
      "name": "get_weather",
      "description": "Get the current weather for a given location.",
      "input_schema": {
        "type": "object",
        "properties": {
          "location": {
            "type": "string",
            "description": "City and state, e.g. San Francisco, CA"
          }
        },
        "required": ["location"]
      }
    }
  ],
  "messages": [
    { "role": "user", "content": "What's the weather in San Francisco?" }
  ]
}
  • tools é a lista completa de funções que o modelo pode chamar. O que não está nela não existe para o modelo.
  • name é o que o modelo devolve, e é com ele que o hospedeiro escolhe a função.
  • description é lida pelo modelo, então deve dizer o que a ferramenta faz e quando usá-la.
  • input_schema é JSON Schema. required informa quais argumentos o modelo precisa fornecer.

Uma resposta que pede a ferramenta

json
{
  "stop_reason": "tool_use",
  "content": [
    {
      "type": "tool_use",
      "id": "toolu_01A09q90qw90lq917835lq9",
      "name": "get_weather",
      "input": { "location": "San Francisco, CA" }
    }
  ]
}
  • stop_reason com o valor tool_use indica que o modelo espera um resultado. Um hospedeiro que só lê texto deixa essa chamada passar despercebida.
  • Um bloco com type igual a tool_use é a chamada estruturada. O id precisa voltar na resposta.
  • input deve respeitar o esquema. Valide mesmo assim, porque o modelo não é um motor de esquemas.

A mensagem seguinte com o resultado

json
{
  "role": "user",
  "content": [
    {
      "type": "tool_result",
      "tool_use_id": "toolu_01A09q90qw90lq917835lq9",
      "content": "15 degrees Celsius, partly cloudy"
    }
  ]
}
  • tool_use_id precisa ser igual ao id da chamada. Se não for, o pareamento quebra.
  • Mantenha content curto. Despejos grandes gastam contexto e podem carregar texto injetado.

O mesmo ciclo no formato de chamada de função

Uma segunda referência usa outros nomes para a mesma ideia. A ferramenta tem type igual a function, e o modelo devolve um item function_call cujo campo arguments é uma string JSON que o hospedeiro precisa interpretar.

json
{
  "type": "function",
  "name": "get_weather",
  "description": "Retrieves current weather for the given location.",
  "parameters": {
    "type": "object",
    "properties": {
      "location": { "type": "string" }
    },
    "required": ["location"],
    "additionalProperties": false
  },
  "strict": true
}
json
{
  "type": "function_call",
  "id": "fc_12345xyz",
  "call_id": "call_12345xyz",
  "name": "get_weather",
  "arguments": "{\"location\":\"Paris, France\"}"
}
json
{
  "type": "function_call_output",
  "call_id": "call_12345xyz",
  "output": "{\"temperature\":\"25\",\"unit\":\"C\"}"
}

Essa referência afirma a regra com clareza: quando o modelo chama uma função, a sua aplicação precisa executá-la e devolver o resultado. Os nomes dos campos mudam entre endpoints e versões, então confira antes de copiar um formato.

Por que o hospedeiro decide o que roda

É tentador imaginar o modelo "usando" uma ferramenta como uma pessoa usa uma calculadora. Essa imagem esconde o fato principal. O modelo escreve um texto que descreve uma chamada. É o hospedeiro que decide se esse texto vira ação.

Isso dá ao hospedeiro três funções. Ele é o único lugar onde o código executa, então uma função de apagar só roda quando o hospedeiro a executa. É também o único lugar onde a política pode ser aplicada: uma frase no prompt dizendo "não escreva arquivos" é um pedido, enquanto a ausência de uma ferramenta de escrita ou um papel de banco somente leitura é um controle. E ele controla o escopo, porque escolhe quais ferramentas são oferecidas a cada turno. Oferecer menos ferramentas é o controle mais barato que você tem.

A especificação do MCP descreve a mesma divisão em termos de protocolo. O host é a aplicação com LLM, os clientes são conectores dentro dela, e os servidores expõem ferramentas. A especificação diz que hosts devem obter consentimento explícito do usuário antes de invocar qualquer ferramenta, e que descrições de comportamento de ferramentas, como as anotações, devem ser tratadas como não confiáveis, salvo se vierem de um servidor confiável. A página da especificação que eu verifiquei em 08/11/2026 aponta para uma revisão datada de 28/07/2026. Leia a revisão atual antes de construir sobre ela.

Por que a aprovação importa em efeitos colaterais

Algumas chamadas só leem. Uma busca ou uma consulta não muda nada fora do hospedeiro. Outras mudam algo: enviam uma mensagem, alteram um registro, gastam dinheiro ou apagam dados.

Uma leitura errada gera uma resposta errada que alguém pode conferir. Uma escrita errada pode ser permanente, pode atingir outras pessoas e pode passar despercebida por um tempo. Por isso um padrão sensato é separar as ferramentas pelo efeito e exigir uma decisão explícita de uma pessoa antes de qualquer chamada que cause efeito colateral. O hospedeiro mostra a ação em linguagem simples, com os argumentos reais, e espera um sim.

Aprove a chamada específica, não a sessão. Um sim dado uma vez para "enviar e-mail" não deve cobrir em silêncio as próximas cem mensagens com destinatários diferentes. A aprovação também falha quando quem revisa não olha com atenção, então combine com argumentos visíveis, limites para o que uma chamada pode fazer e registros.

Excesso de agência, pela visão da OWASP

A OWASP Top 10 para aplicações com LLM lista o excesso de agência como LLM06:2025. A entrada descreve uma vulnerabilidade que permite ações danosas em resposta a saídas inesperadas, ambíguas ou manipuladas do modelo, qualquer que seja a causa. Alucinação e injeção de prompt, direta ou indireta, são gatilhos comuns.

Ela aponta três causas raiz: funcionalidade excessiva (ferramentas que a tarefa não precisa), permissões excessivas (ferramentas que podem mais do que a tarefa exige) e autonomia excessiva (ações de alto impacto sem nenhuma verificação). As mitigações decorrem delas. Ofereça só as ferramentas necessárias. Prefira ferramentas estreitas a shells gerais ou leitores de URL arbitrárias. Imponha o acesso mínimo no sistema de destino, por exemplo com papéis de banco ou escopos OAuth restritos. Execute ações sob a autorização do próprio usuário. Exija aprovação humana para ações de alto impacto. Aplique a autorização no sistema de destino, em vez de confiar que o modelo decida.

Registro, monitoramento e limite de taxa aparecem como medidas para limitar danos. Eles não impedem o problema.

Uma simulação local do ciclo

Esta simulação não tem provedor, chamada de rede nem segredo. O "modelo" é um stub fixo. Ele devolve uma chamada de ferramenta no primeiro turno e texto depois que o resultado chega. O papel dele é mostrar onde fica o hospedeiro. Troque o stub por uma chamada real de API no seu ambiente e mantenha a lógica do hospedeiro.

Salve como loop_demo.py:

python
# Local sketch: no provider, no network. The "model" is a fixed stub that
# returns the same shape a provider returns for a tool call.
import json

def get_weather(location: str) -> str:
    return json.dumps({"location": location, "temp_c": 15, "sky": "partly cloudy"})

def delete_file(path: str) -> str:
    return json.dumps({"deleted": path})  # never reached in this demo

# The host decides what may run. The model only names a tool.
REGISTRY = {"get_weather": get_weather}          # delete_file is NOT registered
REQUIRES_APPROVAL = {"delete_file"}

def fake_model(messages):
    # Stub: first turn asks for weather, second turn answers with text.
    last = messages[-1]
    if last["role"] == "user" and isinstance(last["content"], str):
        return {"stop_reason": "tool_use",
                "content": [{"type": "tool_use", "id": "toolu_demo_1",
                             "name": "get_weather", "input": {"location": "Lisboa, PT"}}]}
    return {"stop_reason": "end_turn", "content": [{"type": "text", "text": "done"}]}

messages = [{"role": "user", "content": "Weather in Lisboa?"}]
for step in range(5):  # hard cap on turns
    reply = fake_model(messages)
    messages.append({"role": "assistant", "content": reply["content"]})
    if reply["stop_reason"] != "tool_use":
        print("final:", reply["content"][0]["text"])
        break
    results = []
    for block in reply["content"]:
        if block["type"] != "tool_use":
            continue
        name, args = block["name"], block["input"]
        if name not in REGISTRY:
            out, err = f"unknown tool {name}", True
        elif name in REQUIRES_APPROVAL:
            out, err = "approval required", True
        else:
            out, err = REGISTRY[name](**args), False
        print(f"host ran {name}({args}) -> {out}")
        results.append({"type": "tool_result", "tool_use_id": block["id"],
                        "content": out, "is_error": err})
    messages.append({"role": "user", "content": results})

Rode a partir de uma pasta que contenha só este arquivo:

bash
python -I loop_demo.py

A flag -I inicia o Python em modo isolado. Ela ignora variáveis de ambiente e tira a pasta atual do caminho de importação, um hábito útil para código que você não escreveu.

Em uma máquina Windows 11 com Git Bash e Python 3.12.10, a execução imprimiu:

text
host ran get_weather({'location': 'Lisboa, PT'}) -> {"location": "Lisboa, PT", "temp_c": 15, "sky": "partly cloudy"}
final: done

O stub devolveu um stop reason tool_use e uma chamada a get_weather. O hospedeiro achou o nome no registro, executou a função e gravou um tool_result na conversa. Depois o stub respondeu com texto e o ciclo terminou. delete_file nunca rodou porque o hospedeiro não o registrou. Para ver o ramo de aprovação, faça o stub pedir delete_file e observe o hospedeiro recusar.

Erros comuns

Oferecer todas as ferramentas do código. O modelo usa o que vê, e também qualquer texto que ele lê. Comece com uma lista curta e adicione ferramentas uma de cada vez.

Escrever descrições vagas. "Faz coisas com arquivos" convida a chamadas erradas. Diga o que a ferramenta lê ou escreve e quando não usá-la.

Confiar nos argumentos só porque passaram no esquema. A validação de esquema confere a forma, não o sentido. Um caminho bem formado pode apontar para fora da pasta que você queria liberar.

Confiar no conteúdo devolvido. Um resultado de ferramenta é dado. Colá-lo no próximo prompt como se fosse instrução dá a uma página web o poder de conduzir o seu hospedeiro.

Esquecer o limite de turnos. Um ciclo sem teto pode repetir uma chamada que falha até gastar dinheiro de verdade.

Aprovar a sessão inteira. Aprove chamadas específicas, com os argumentos à vista.

Registrar as coisas erradas. Guarde nomes de ferramentas, decisões e resultados com cuidado com dados pessoais. Prompts e respostas completos nos logs podem guardar dados que você não deveria manter.

Como seguir em frente

Monte um ciclo com uma chave de API real e uma ferramenta somente leitura, como uma função que devolve a hora atual. Inspecione os corpos da requisição e da resposta, encontre o bloco tool_use e o tool_result que você devolve.

Depois adicione uma ferramenta que escreve em um arquivo de rascunho dentro de uma pasta sua, com uma etapa de aprovação antes dela. Peça ao modelo um caminho fora dessa pasta e confira se o hospedeiro interrompe a chamada.

Leia as seções da especificação do MCP sobre ferramentas e segurança, e a entrada da OWASP sobre excesso de agência por inteiro. Associe cada mitigação a uma linha do seu código.

Fontes

Consultadas em 08/11/2026. Páginas de documentação mudam, então confirme a versão atual antes de confiar em um nome de campo.

Fontes

  1. Tool use overview, public API documentation
  2. Function calling guide, public API documentation
  3. OWASP Top 10 for LLM Applications: LLM06:2025 Excessive Agency
  4. Model Context Protocol specification

Mais em Agentes de IA para segurança

Agentes de IA para segurança

O que é MCP? O Model Context Protocol explicado

MCP é o protocolo aberto que liga apps de IA a ferramentas e dados. Veja papéis, primitivas, transportes e segurança com um laboratório isolado.

Intermediário13 min de leitura