Grandes Modelos de Linguagem (LLMs), como o GPT-4, o Llama 3, Claude 3 e DeepSeek, são modelos treinados com enormes quantidades de informações. Eles funcionam como “cérebros”, construídos a partir de uma vasta representação do conhecimento humano, capazes de escrever, raciocinar e sintetizar informações em um nível sem precedentes. No entanto, além de apenas gerar texto, um LLM pode identificar que uma ação precisa ser executada e, então, solicitar que essa ação seja realizada por uma função externa.
Um LLM, por padrão, não sabe que horas são. Ele não sabe como está o tempo lá fora. Ele não pode acessar as notícias de hoje, verificar o preço de uma ação ou fazer uma reserva no seu restaurante favorito. Seu conhecimento é estático, congelado no tempo no momento em que seu treinamento foi concluído.
É aqui que entra o Tool Calling (ou Function Calling), uma das capacidades mais transformadoras no campo da IA. Este é o mecanismo que finalmente dá "mãos e olhos" a esses cérebros, permitindo-lhes interagir com o mundo exterior.
A Analogia: O Cérebro na Sala Fechada
Imagine um gênio trancado em uma biblioteca. Ele leu todos os livros, mas não tem janelas, relógio ou telefone. Se você perguntar a ele sobre a história da Roma Antiga, ele lhe dará uma resposta brilhante. Se você perguntar: "Está chovendo em Paris agora?", ele só poderá dizer: "Com base nos padrões climáticos históricos descritos nos livros, Paris frequentemente tem chuvas..."
O Tool Calling é o equivalente a instalar um intercomunicador nessa sala.
Através desse intercomunicador, o gênio (o LLM) não pode sair, mas ele pode pedir a alguém do lado de fora (sua aplicação) para fazer algo por ele. Ele pode dizer: "Por favor, ligue para o serviço de meteorologia e pergunte o tempo em Paris."
Como Funciona o "Tool Calling"?
O processo não é mágico, trata-se de um fluxo de dados elegante e estruturado entre o LLM e o código da sua aplicação. Esse fluxo de chamadas de ferramentas nada mais é do que a execução de funções,classes, métodos, scripts ou serviços externos que expomos para que a IA realize algo em nosso nome.
Outro ponto bastante interessante, que muitas pessoas confundem, é que o LLM não executa diretamente a ferramenta, ele apenas tem conhecimento da sua existência. Quando uma conversa é direcionada para algo que uma ferramenta disponível pode resolver, a IA apenas envia uma solicitação para que essa ferramenta seja executada. Ou seja, quem de fato implementa e realiza a chamada da ferramenta somos nós, por meio do código da aplicação.
Vamos ao mão na massa.
A Definição (O Desenvolvedor Informa as Ferramentas)
Primeiro, nós, como desenvolvedores, temos que dizer ao LLM quais "ferramentas" ele tem à sua disposição.
Para esse primeiro passo, vamos criar a ferramenta para multiplicar dois números de ponto flutuante.
from langchain.chat_models import init_chat_model
from langchain.tools import BaseTool, tool
from langchain_core.messages import (
AIMessage,
BaseMessage,
HumanMessage,
SystemMessage,
ToolMessage,
)
from rich import print
from dotenv import load_dotenv
load_dotenv()
@tool()
def multiply(a: float, b: float) -> float:
"""Multipy a * b and return the result
Args:
a (float): The first number
b (float): The second number
Returns:
float: The resulting foat of the equation a * b"""
return a * b
Depois que decoramos a função com @tool, ela se torna uma ferramenta que pode ser utilizada por LLMs. No entanto, a partir desse momento, ela deixa de ser apenas uma função simples e passa a ser uma instância da classe StructuredTool ou seja, uma ferramenta enriquecida com metadados e descrição, que o LLM pode compreender e utilizar de forma estruturada.Se imprimir a função multiply vamos ter algo como abaixo.
StructuredTool(
name='multiply',
description='Multipy a * b and return the result\n Args:\n a (float): The first number\n b (float):
The second number\n Returns:\n float: The resulting foat of the equation a * b',
args_schema=<class 'langchain_core.utils.pydantic.multiply'>,
func=<function multiply at 0x7532c41da2a0>
)
Vamos agora fazer uma chamada ao modelo GPT-4o-mini: inicialmente sem ferramentas e, em seguida, com elas.
llm = init_chat_model("gpt-4o-mini", temperature=0)
system_message = SystemMessage(
content=""" Você é um assistente de matemática prestativo. Você tem acesso a ferramentas.
Quando o usuário pedir algo, primeiro verifique se você possui uma ferramenta que resolva esse problema."""
)
human_message = HumanMessage("Oi eu sou Goku")
messages: list[BaseMessage] = [
system_message,
human_message,
]
result = llm.invoke(messages)
A saída de result segue abaixo. Na mensagem de retorno AIMessage -> content, temos a resposta do modelo: 'Oi, Goku! Como posso ajudar você hoje? Se precisar de ajuda com matemática ou qualquer outra coisa, é só me avisar!'.
AIMessage(
content='Oi, Goku! Como posso ajudar você hoje? Se precisar de ajuda com matemática ou qualquer outra coisa, é só me avisar!',
additional_kwargs={'refusal': None},
response_metadata={
'token_usage': {
'completion_tokens': 28,
'prompt_tokens': 54,
'total_tokens': 82,
'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0},
'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}
},
'model_provider': 'openai',
'model_name': 'gpt-4o-mini-2024-07-18',
'system_fingerprint': 'fp_560af6e559',
'id': 'chatcmpl-CSuxFiARtuT2nfkJYzXlnQRg656Dl',
'service_tier': 'default',
'finish_reason': 'stop',
'logprobs': None
},
id='lc_run--18e23d3f-ba97-4d4b-8dc4-8bc2a55a4d4b-0',
usage_metadata={'input_tokens': 54, 'output_tokens': 28, 'total_tokens': 82, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}}
)
Vamos agora adicionar uma ferramenta à conversa e observar o resultado. O código completo segue abaixo:
from langchain.chat_models import init_chat_model
from langchain.tools import BaseTool, tool
from langchain_core.messages import (
BaseMessage,
HumanMessage,
SystemMessage,
)
from rich import print
from dotenv import load_dotenv
load_dotenv()
@tool()
def multiply(a: float, b: float) -> float:
"""Multipy a * b and return the result
Args:
a (float): The first number
b (float): The second number
Returns:
float: The resulting foat of the equation a * b"""
return a * b
llm = init_chat_model("gpt-4o-mini", temperature=0)
system_message = SystemMessage(
content=""" Você é um assistente de matemática prestativo. Você tem acesso a ferramentas.
Quando o usuário pedir algo, primeiro verifique se você possui uma ferramenta que resolva esse problema."""
)
human_message = HumanMessage("Oi eu sou Goku, quanto é 12.5 multiplicado por 3.4?")
messages: list[BaseMessage] = [
system_message,
human_message,
]
tools: list[BaseTool] = [
multiply,
]
llm_with_tools = llm.bind_tools(tools)
if __name__ == "__main__":
result = llm_with_tools.invoke(messages)
print(result)
As principais diferenças entre uma chamada sem ferramentas ou seja, uma conversa simples e uma conversa com ferramentas estão na adição da função multiply, que representa nossa ferramenta responsável por executar uma ação específica no caso a multiplicação de dois pontos flutuantes, e na criação da lista de ferramentas disponíveis. Além disso, a mensagem agora é diferente da primeira chamada “Oi, eu sou o Goku! Quanto é 12.5 multiplicado por 3.4?”. Com base nessa solicitação, o LLM identifica a intenção do usuário e sugere a ferramenta mais adequada para resolver a tarefa, neste caso, a função responsável por realizar a multiplicação.
Nesse exemplo, temos apenas uma ferramenta, mas poderíamos incluir várias, cada uma com sua função e finalidade. No entanto, é importante ter cuidado: quanto maior o número de ferramentas disponíveis, maior a complexidade do processo de decisão do modelo. Isso pode levar a comportamentos inesperados ou alucinações, especialmente se as ferramentas tiverem propósitos semelhantes ou se o contexto da solicitação não for claro.
Portanto, ao adicionar múltiplas ferramentas, é recomendável definir descrições claras e propósitos bem delimitados para cada uma delas, garantindo que a LLM saiba quando e como utilizá-las corretamente.
@tool()
def multiply(a: float, b: float) -> float:
"""Multipy a * b and return the result
Args:
a (float): The first number
b (float): The second number
Returns:
float: The resulting foat of the equation a * b"""
return a * b
tools: list[BaseTool] = [
multiply,
]
llm_with_tools = llm.bind_tools(tools)
Após realizar a chamada ao LLM com a ferramenta configurada, observamos a seguinte saída gerada pelo modelo:
AIMessage(
content='',
additional_kwargs={'refusal': None},
response_metadata={
'token_usage': {
'completion_tokens': 21,
'prompt_tokens': 144,
'total_tokens': 165,
'completion_tokens_details': {
'accepted_prediction_tokens': 0,
'audio_tokens': 0,
'reasoning_tokens': 0,
'rejected_prediction_tokens': 0
},
'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}
},
'model_provider': 'openai',
'model_name': 'gpt-4o-mini-2024-07-18',
'system_fingerprint': 'fp_560af6e559',
'id': 'chatcmpl-CTyJBbjWwJJ4ehSSIMqmXzxFIhEXn',
'service_tier': 'default',
'finish_reason': 'tool_calls',
'logprobs': None
},
id='lc_run--961d6734-1a03-4c3a-afe5-2d0a9a34861c-0',
tool_calls=[
{
'name': 'multiply',
'args': {'a': 12.5, 'b': 3.4},
'id': 'call_CezDcbeqTBbMvVMp8WMSNZD8',
'type': 'tool_call'
}
],
usage_metadata={
'input_tokens': 144,
'output_tokens': 21,
'total_tokens': 165,
'input_token_details': {'audio': 0, 'cache_read': 0},
'output_token_details': {'audio': 0, 'reasoning': 0}
}
)
A parte mais importante da resposta é a tag tool_calls, pois é nela que o LLM indica qual ou quais ferramentas são mais adequadas para atender à solicitação do usuário.
Dentro dessa estrutura, encontramos o nome da função selecionada 'name': 'multiply', os argumentos utilizados 'args': {'a': 12.5, 'b': 3.4} e um identificador único da chamada 'id': 'call_CezDcbeqTBbMvVMp8WMSNZD8'.
Esse identificador é útil para rastrear a execução da ferramenta ou correlacionar respostas quando há múltiplas chamadas dentro da mesma interação. Em resumo, o bloco tool_calls representa o momento em que o modelo decide invocar uma ação externa para gerar uma resposta mais precisa.
tool_calls=[
{
'name': 'multiply',
'args': {'a': 12.5, 'b': 3.4},
'id': 'call_CezDcbeqTBbMvVMp8WMSNZD8',
'type': 'tool_call'
}
],
Agora vem o pulo do gato: não é a LLM que executa a função, e sim o seu código. Todas as ferramentas são identificadas a partir do contexto da conversa, e o modelo apenas sugere qual(is) delas devem ser utilizadas.
Cabe ao seu código desenvolvido por você executar efetivamente as funções com os parâmetros indicados pela LLM. Isso permite que várias ferramentas sejam acionadas conforme necessário, enriquecendo a conversa com informações adicionais, sem que o modelo precise realizar cálculos ou ações externas por conta própria.
É bastante interessante entender como isso funciona nos bastidores. Quando você utiliza frameworks como LangGraph, CrewAI, Microsoft Agent Framework, Semantic Kernel, entre outros, a execução dessas funções fica abstraída, facilitando a integração e a automação, sem que você precise lidar com todos os detalhes de implementação manualmente. Vamos ver agora como podemos executar nossa ferramenta.
from langchain.chat_models import init_chat_model
from langchain.tools import BaseTool, tool
from langchain_core.messages import (
BaseMessage,
HumanMessage,
SystemMessage,
AIMessage,
ToolMessage,
)
from rich import print
from dotenv import load_dotenv
load_dotenv()
@tool()
def multiply(a: float, b: float) -> float:
"""Multipy a * b and return the result
Args:
a (float): The first number
b (float): The second number
Returns:
float: The resulting foat of the equation a * b"""
return a * b
llm = init_chat_model("gpt-4o-mini", temperature=0)
system_message = SystemMessage(
content=""" Você é um assistente de matemática prestativo. Você tem acesso a ferramentas.
Quando o usuário pedir algo, primeiro verifique se você possui uma ferramenta que resolva esse problema."""
)
human_message = HumanMessage("Oi eu sou Goku, quanto é 12.5 multiplicado por 3.4?")
messages: list[BaseMessage] = [
system_message,
human_message,
]
tools: list[BaseTool] = [
multiply,
]
tools_by_name = {tool.name: tool for tool in tools}
llm_with_tools = llm.bind_tools(tools)
def run_tool(response):
if isinstance(response, AIMessage) and getattr(response, "tool_calls", None):
call = response.tool_calls[-1]
name, args, id_ = call["name"], call["args"], call["id"]
try:
content = tools_by_name[name].invoke(args)
status = "success"
except (KeyError, IndexError, TypeError, ValueError) as error:
content = f"Erro: Ferramenta '{name}' não encontrada.{error}"
status = "error"
tool_message = ToolMessage(content=content, tool_call_id=id_, status=status)
messages.append(tool_message)
llm_response = llm_with_tools.invoke(messages)
messages.append(llm_response)
print(messages)
if __name__ == "__main__":
llm_response = llm_with_tools.invoke(messages)
messages.append(llm_response)
run_tool(llm_response)
O que tem de novidades nesse código:
- tools_by_name = {tool.name: tool for tool in tools}: Nesse ponto, estamos pegando todos os nomes das ferramentas (funções) que foram passadas para a LLM e criando um dicionário que mapeia cada nome para o respectivo objeto da ferramenta. Isso permite acessar rapidamente qualquer ferramenta pelo seu nome quando a LLM indicar que ela deve ser utilizada.
- run_tool: Executando ferramentas sugeridas pela LLM a função run_tool é responsável por executar de fato a função indicada pela LLM para realizar a ação desejada. Ela recebe a resposta da LLM, verifica se há chamadas de ferramentas (tool_calls) e, se houver, executa a ferramenta correspondente e registra a resposta.
Vamos analisar o trecho de código linha a linha e entender o que ele faz:
def run_tool(response):
if isinstance(response, AIMessage) and getattr(response, "tool_calls", None):
call = response.tool_calls[-1]
name, args, id_ = call["name"], call["args"], call["id"]
try:
content = tools_by_name[name].invoke(args)
status = "success"
except (KeyError, IndexError, TypeError, ValueError) as error:
content = f"Erro: Ferramenta '{name}' não encontrada.{error}"
status = "error"
tool_message = ToolMessage(content=content, tool_call_id=id_, status=status)
messages.append(tool_message)
llm_response = llm_with_tools.invoke(messages)
messages.append(llm_response)
print(messages)
if isinstance(response, AIMessage) and getattr(response, "tool_calls", None):
Verifica duas coisas:
- Se response é uma instância da classe AIMessage.
- Se response possui um atributo chamado tool_calls (usando getattr para evitar erro se não existir).
- Ou seja, só prossegue se a mensagem veio do LLM e contém chamadas para ferramentas.
call = response.tool_calls[-1]
- Pega a última chamada de ferramenta presente na lista tool_calls.
- Cada call é esperado ser um dicionário com informações sobre a ferramenta a ser executada.
name, args, id_ = call["name"], call["args"], call["id"]
Extrai três informações da chamada da ferramenta:
- name: nome da ferramenta a ser chamada.
- args: argumentos que a ferramenta precisa.
- id_: identificador da chamada (usado para rastrear qual mensagem gerou a ação).
try:
content = tools_by_name[name].invoke(args)
status = "success"
Tenta executar a ferramenta:
tools_by_name é um dicionário que mapeia nomes de ferramentas para objetos/funções.
.invoke(args) executa a ferramenta com os argumentos fornecidos.
Se funcionar, define status como "success" e salva a saída em content.
except (KeyError, IndexError, TypeError, ValueError) as error:
content = f"Erro: Ferramenta '{name}' não encontrada.{error}"
status = "error"
Se houver algum erro ao chamar a ferramenta (ex: nome não existe, argumentos incorretos), captura a exceção e:
Define content com uma mensagem de erro.
Marca status como "error".
tool_message = ToolMessage(content=content, tool_call_id=id_, status=status)
Cria uma mensagem do tipo ToolMessage que representa a execução da ferramenta:
content: resultado ou mensagem de erro.
tool_call_id: liga a mensagem ao ID da chamada da ferramenta original.
status: sucesso ou erro.
messages.append(tool_message)
Adiciona a mensagem da ferramenta à lista messages, que é o histórico da conversa.
llm_response = llm_with_tools.invoke(messages)
Envia o histórico atualizado (messages) de volta para o LLM, agora incluindo a resposta da ferramenta.
A saída após a execução da chamada com ferramentas é:
[
SystemMessage(
content=' Você é um assistente de matemática prestativo. Você tem acesso a ferramentas. \n Quando o usuário pedir algo, primeiro verifique se você possui uma ferramenta
que resolva esse problema.',
additional_kwargs={},
response_metadata={}
),
HumanMessage(content='Oi eu sou Goku, quanto é 12.5 multiplicado por 3.4?', additional_kwargs={}, response_metadata={}),
AIMessage(
content='',
additional_kwargs={'refusal': None},
response_metadata={
'token_usage': {
'completion_tokens': 21,
'prompt_tokens': 144,
'total_tokens': 165,
'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0},
'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}
},
'model_provider': 'openai',
'model_name': 'gpt-4o-mini-2024-07-18',
'system_fingerprint': 'fp_560af6e559',
'id': 'chatcmpl-CTzPwOcFISxRtpXMgNEVDTDacTsio',
'service_tier': 'default',
'finish_reason': 'tool_calls',
'logprobs': None
},
id='lc_run--f5058b1e-863d-47af-a444-963f3252a01c-0',
tool_calls=[{'name': 'multiply', 'args': {'a': 12.5, 'b': 3.4}, 'id': 'call_boELMe3XRPYIlgCW5XlJaQzI', 'type': 'tool_call'}],
usage_metadata={
'input_tokens': 144,
'output_tokens': 21,
'total_tokens': 165,
'input_token_details': {'audio': 0, 'cache_read': 0},
'output_token_details': {'audio': 0, 'reasoning': 0}
}
),
ToolMessage(content='42.5', tool_call_id='call_boELMe3XRPYIlgCW5XlJaQzI'),
AIMessage(
content='Oi Goku! O resultado de 12.5 multiplicado por 3.4 é 42.5.',
additional_kwargs={'refusal': None},
response_metadata={
'token_usage': {
'completion_tokens': 25,
'prompt_tokens': 175,
'total_tokens': 200,
'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0},
'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}
},
'model_provider': 'openai',
'model_name': 'gpt-4o-mini-2024-07-18',
'system_fingerprint': 'fp_560af6e559',
'id': 'chatcmpl-CTzPxR7YftzqdNVl3UIKezdPJ6sAz',
'service_tier': 'default',
'finish_reason': 'stop',
'logprobs': None
},
id='lc_run--57a0afb7-aaa3-4a71-986a-c42f167d35d0-0',
usage_metadata={
'input_tokens': 175,
'output_tokens': 25,
'total_tokens': 200,
'input_token_details': {'audio': 0, 'cache_read': 0},
'output_token_details': {'audio': 0, 'reasoning': 0}
}
)
]
O fluxo de mensagens:
a - SystemMessage
SystemMessage(
content='Você é um assistente de matemática prestativo. Você tem acesso a ferramentas. Quando o usuário pedir algo, primeiro verifique se você possui uma ferramenta que resolva esse problema.'
)
- Define o contexto e instruções do assistente.
- Indica que o modelo pode usar ferramentas externas para responder.
- Funciona como a “base” do diálogo, informando como o assistente deve se comportar.
b - HumanMessage
HumanMessage(content='Oi eu sou Goku, quanto é 12.5 multiplicado por 3.4?')
- Representa a pergunta do usuário.
- Nesse caso, o usuário quer calcular uma multiplicação.
c - AIMessage (primeira resposta)
AIMessage(
content='',
tool_calls=[{'name': 'multiply', 'args': {'a': 12.5, 'b': 3.4}, 'id': 'call_boELMe3XRPYIlgCW5XlJaQzI'}],
response_metadata={'finish_reason': 'tool_calls'}
)
- O modelo não respondeu diretamente ao usuário ainda (
content=''). - Identificou que uma ferramenta deve ser usada:
multiply. -
Preencheu
tool_callscom:-
name: nome da ferramenta a ser chamada. -
args: argumentos necessários para a execução. -
id: identificador da chamada.
-
d - ToolMessage
ToolMessage(content='42.5', tool_call_id='call_boELMe3XRPYIlgCW5XlJaQzI')
- Contém o resultado da execução da ferramenta.
- Aqui, a multiplicação foi realizada pelo código (
run_tool) e retornou42.5. - Associado à chamada específica pelo
tool_call_id.
e - AIMessage (resposta final)
AIMessage(
content='Oi Goku! O resultado de 12.5 multiplicado por 3.4 é 42.5.',
response_metadata={'finish_reason': 'stop'}
)
O código completo: Github
Referências:
An introduction to function calling and tool use
Tools & Function Calling in LLMs and AI Agents: A Hands-On Guide


Top comments (0)