Ferramentas Básicas
Aprenda como definir ferramentas com @tool, vinculá-las a LLMs e executar ferramentas dentro de nós LangGraph.
Ferramentas Básicas
Ferramentas dão aos LLMs a capacidade de interagir com o mundo exterior — pesquisar na web, executar cálculos, consultar bancos de dados e mais. Esta lição cobre como definir, vincular e executar ferramentas em LangGraph.
Definindo Ferramentas com @tool
O decorador @tool do LangChain converte uma função Python em uma ferramenta que LLMs podem usar:
from langchain_core.tools import tool
@tool
def get_weather(location: str) -> str:
"""Get the current weather for a location."""
# Em produção, chamar uma API de clima
return f"The weather in {location} is sunny, 72°F."
@tool
def calculator(expression: str) -> str:
"""Evaluate a mathematical expression. Use Python syntax."""
try:
return str(eval(expression, {"__builtins__": {}}, {}))
except Exception as e:
return f"Error: {e}"Estrutura da Ferramenta
Cada ferramenta tem:
| Componente | Descrição | Fonte |
|---|---|---|
| Nome | O nome da função (ex.: get_weather) | Gerado do nome da função |
| Descrição | Docstring explicando quando usar | De """docstring""" |
| Parâmetros | Argumentos da função com tipagem | Da assinatura da função |
| Corpo | A lógica de implementação | O código da função |
A docstring é crítica. O LLM a lê para decidir quando e como chamar a ferramenta. Seja descritivo e inclua exemplos de quando a ferramenta é apropriada.
Vinculação de Ferramentas
Vincule ferramentas a um LLM para que ele saiba que elas existem:
from langchain_openai import ChatOpenAI
llm = ChatOpenAI(model="gpt-4o")
# Vincular ferramentas ao LLM
llm_with_tools = llm.bind_tools([get_weather, calculator])Como a Vinculação Funciona
bind_tools() envia os schemas das ferramentas (nome, descrição, parâmetros) para o LLM como parte da chamada de API. O LLM pode então decidir:
- Responder normalmente com texto se nenhuma ferramenta for necessária
- Solicitar uma chamada de ferramenta retornando um objeto
tool_callsestruturado
# LLM pode responder sem ferramenta
response = llm_with_tools.invoke("Hello!")
print(response.content) # "Hi! How can I help you?"
# LLM pode solicitar uma ferramenta
response = llm_with_tools.invoke("What is 2+2?")
print(response.tool_calls)
# [{'name': 'calculator', 'args': {'expression': '2+2'}, 'id': '...'}]O LLM não executa a ferramenta. Ele apenas gera uma requisição de chamada de ferramenta. Sua função de nó deve lidar com a execução.
Verificando Chamadas de Ferramenta
Após invocar um LLM habilitado para ferramentas, verifique se ele quer usar uma ferramenta:
response = llm_with_tools.invoke(messages)
if response.tool_calls:
# LLM quer chamar ferramentas
for tool_call in response.tool_calls:
tool_name = tool_call["name"]
tool_args = tool_call["args"]
tool_id = tool_call["id"]
print(f"Tool requested: {tool_name}({tool_args})")
else:
# LLM respondeu com texto
print(f"Response: {response.content}")Estrutura da Chamada de Ferramenta
# Cada tool_call é um dict com:
{
"name": "calculator", # Nome da ferramenta
"args": {"expression": "2+2"}, # Dict de argumentos
"id": "call_abc123", # ID único da chamada
"type": "tool_call" # Sempre "tool_call"
}Execução de Ferramenta em um Nó
O padrão padrão: invocar LLM, verificar chamadas de ferramenta, executar ferramentas, retornar resultados:
from langchain_core.messages import ToolMessage
class AgentState(TypedDict):
messages: list
tool_results: dict
def agent_node(state: AgentState) -> dict:
# 1. Chamar LLM com ferramentas
response = llm_with_tools.invoke(state["messages"])
# 2. Verificar se LLM quer usar ferramentas
if response.tool_calls:
results = {}
new_messages = state["messages"] + [response]
for tool_call in response.tool_calls:
# 3. Executar a ferramenta
tool_name = tool_call["name"]
tool_args = tool_call["args"]
if tool_name == "calculator":
result = calculator.invoke(tool_args)
elif tool_name == "get_weather":
result = get_weather.invoke(tool_args)
else:
result = f"Unknown tool: {tool_name}"
# 4. Armazenar resultado
tool_call_id = tool_call["id"]
results[tool_call_id] = result
# 5. Adicionar ToolMessage à conversa
new_messages.append(
ToolMessage(content=result, tool_call_id=tool_call_id)
)
return {"messages": new_messages, "tool_results": results}
# 6. Sem chamadas de ferramenta — retornar resposta LLM
return {"messages": state["messages"] + [response]}O padrão é: LLM decide → analisar chamadas de ferramenta → executar ferramentas → anexar resultados como ToolMessages → continuar.
Execução Simplificada com ToolExecutor
LangGraph fornece ToolExecutor para execução mais limpa de ferramentas:
from langgraph.prebuilt import ToolExecutor
# Criar executor a partir de suas ferramentas
tools = [get_weather, calculator]
tool_executor = ToolExecutor(tools)
def agent_node(state: AgentState) -> dict:
response = llm_with_tools.invoke(state["messages"])
if response.tool_calls:
new_messages = [response]
for tool_call in response.tool_calls:
# ToolExecutor lida com roteamento e invocação
result = tool_executor.invoke(tool_call)
new_messages.append(
ToolMessage(content=str(result), tool_call_id=tool_call["id"])
)
return {"messages": state["messages"] + new_messages}
return {"messages": state["messages"] + [response]}ToolExecutor automaticamente roteia chamadas de ferramenta para a função correta baseada no nome da ferramenta. Ele lida com o dicionário de mapeamento internamente.
Agente ReAct Completo com Ferramentas
from langchain_openai import ChatOpenAI
from langchain_core.tools import tool
from langchain_core.messages import HumanMessage, ToolMessage
from langgraph.graph import StateGraph, START, END
from langgraph.prebuilt import ToolExecutor
from typing_extensions import TypedDict, List, Dict, Any
# 1. Definir ferramentas
@tool
def search(query: str) -> str:
"""Search the web. Use for general knowledge questions."""
return f"Search results for '{query}': LangGraph is a framework..."
@tool
def calculator(expression: str) -> str:
"""Evaluate math expressions. Use for calculations."""
return str(eval(expression, {"__builtins__": {}}, {}))
# 2. Configuração
tools = [search, calculator]
llm = ChatOpenAI(model="gpt-4o")
llm_with_tools = llm.bind_tools(tools)
tool_executor = ToolExecutor(tools)
# 3. Estado
class AgentState(TypedDict):
messages: List[Any]
# 4. Nó
def agent(state: AgentState) -> dict:
response = llm_with_tools.invoke(state["messages"])
if response.tool_calls:
new_messages = [response]
for tc in response.tool_calls:
result = tool_executor.invoke(tc)
new_messages.append(ToolMessage(content=str(result), tool_call_id=tc["id"]))
return {"messages": state["messages"] + new_messages}
return {"messages": state["messages"] + [response]}
# 5. Roteador
def should_continue(state: AgentState) -> str:
last_message = state["messages"][-1]
if hasattr(last_message, "tool_calls") and last_message.tool_calls:
return "continue"
return "end"
# 6. Grafo
builder = StateGraph(AgentState)
builder.add_node("agent", agent)
builder.add_edge(START, "agent")
builder.add_conditional_edges("agent", should_continue, {
"continue": "agent", # Loop de volta — resultados de ferramentas alimentam de volta ao LLM
"end": END
})
app = builder.compile()
# 7. Executar
result = app.invoke({
"messages": [HumanMessage("What is 15 * 7 and search for LangGraph?")]
})
print(result["messages"][-1].content)Quando o roteador retorna "continue", o grafo faz loop de volta ao nó agente. O agente recebe os ToolMessages (resultados das ferramentas) e pode decidir se chama mais ferramentas ou responde. Sempre defina um recursion_limit para prevenir loops infinitos.
Múltiplas Chamadas de Ferramenta
O LLM pode solicitar múltiplas chamadas de ferramenta em uma única resposta:
# Exemplo: Resposta LLM com duas chamadas de ferramenta
response = llm_with_tools.invoke(
"What's the weather in Paris and calculate 2^10?"
)
print(len(response.tool_calls)) # 2
# Executar todas as chamadas de ferramenta
for tc in response.tool_calls:
result = tool_executor.invoke(tc)
print(f"{tc['name']} → {result}")Todas as chamadas de ferramenta de uma resposta LLM são executadas e seus resultados são retornados como ToolMessages para o LLM para processamento final.
Tratamento de Erro em Ferramentas
def safe_agent_node(state: AgentState) -> dict:
try:
response = llm_with_tools.invoke(state["messages"])
except Exception as e:
print(f"LLM call failed: {e}")
return {"messages": state["messages"] + [
AIMessage(content=f"I encountered an error: {str(e)}")
]}
if response.tool_calls:
new_messages = [response]
for tc in response.tool_calls:
try:
result = tool_executor.invoke(tc)
except Exception as e:
result = f"Tool execution error: {e}"
new_messages.append(
ToolMessage(content=str(result), tool_call_id=tc["id"])
)
return {"messages": state["messages"] + new_messages}
return {"messages": state["messages"] + [response]}Sempre envolva a execução de ferramenta em try/except. Uma ferramenta com falha não deve quebrar o grafo inteiro. Retorne uma mensagem de erro como resultado da ferramenta para que o LLM possa lidar com isso graciosamente.
Ferramentas vs Funções Embutidas
| Aspecto | Decorador @tool | Função Simples |
|---|---|---|
| Geração de schema | Automática a partir da assinatura | Manual |
| Descoberta pelo LLM | Via bind_tools() | Não visível ao LLM |
| Tratamento de erro | Pode ser configurado | Manual |
| Caso de uso | LLM precisa chamá-la | Lógica interna do nó |
Perguntas de Prática
O que o decorador @tool faz?
Qual método torna um LLM ciente das ferramentas disponíveis?
O LLM executa ferramentas diretamente?
Qual tipo de mensagem envolve resultados de execução de ferramenta para o LLM?
O que ToolExecutor faz?
Qual parte de uma função @tool é mais importante para a compreensão do LLM?
O que acontece em um loop de agente ReAct?
Como você deve lidar com erros de execução de ferramenta?
Um LLM pode solicitar múltiplas chamadas de ferramenta em uma resposta?
Qual propriedade na resposta do LLM contém as requisições de chamada de ferramenta?
Principais Conclusões
@tooldecora uma função com metadados que o LLM usa para decidir quando chamá-lallm.bind_tools([...])torna o LLM ciente das ferramentas disponíveis- LLMs apenas solicitam chamadas de ferramenta — seu código de nó as executa
- Resultados de ferramentas vão em ToolMessages vinculados por tool_call_id
- ToolExecutor simplifica o roteamento e execução de múltiplas ferramentas
- O padrão ReAct faz loop: LLM → chamadas de ferramenta → executar → ToolMessages → LLM novamente
- Sempre trate erros de ferramenta com try/except
- Uma docstring clara é a parte mais importante de uma definição de ferramenta