iniciante35 minutosLição 7 de 10

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:

python
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:

ComponenteDescriçãoFonte
NomeO nome da função (ex.: get_weather)Gerado do nome da função
DescriçãoDocstring explicando quando usarDe """docstring"""
ParâmetrosArgumentos da função com tipagemDa assinatura da função
CorpoA lógica de implementaçãoO código da função
📌Important

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:

python
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:

  1. Responder normalmente com texto se nenhuma ferramenta for necessária
  2. Solicitar uma chamada de ferramenta retornando um objeto tool_calls estruturado
python
# 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': '...'}]
ℹ️Note

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:

python
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

python
# 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:

python
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]}
Success

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:

python
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]}
💡Tip

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

python
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)
⚠️Warning

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:

python
# 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

python
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]}
ℹ️Note

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

AspectoDecorador @toolFunção Simples
Geração de schemaAutomática a partir da assinaturaManual
Descoberta pelo LLMVia bind_tools()Não visível ao LLM
Tratamento de erroPode ser configuradoManual
Caso de usoLLM precisa chamá-laLógica interna do nó

Perguntas de Prática

Practice Question

O que o decorador @tool faz?

Practice Question

Qual método torna um LLM ciente das ferramentas disponíveis?

Practice Question

O LLM executa ferramentas diretamente?

Practice Question

Qual tipo de mensagem envolve resultados de execução de ferramenta para o LLM?

Practice Question

O que ToolExecutor faz?

Practice Question

Qual parte de uma função @tool é mais importante para a compreensão do LLM?

Practice Question

O que acontece em um loop de agente ReAct?

Practice Question

Como você deve lidar com erros de execução de ferramenta?

Practice Question

Um LLM pode solicitar múltiplas chamadas de ferramenta em uma resposta?

Practice Question

Qual propriedade na resposta do LLM contém as requisições de chamada de ferramenta?


Success

Principais Conclusões

  • @tool decora uma função com metadados que o LLM usa para decidir quando chamá-la
  • llm.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
Progresso70%