Chamadas encadeadas
Na semana passada, o agente usava uma ferramenta por vez. Mas e quando a pergunta precisa de duas ou mais?
Exemplo: "Quanto é 234 × 987 + 100?"
Thought: Preciso multiplicar 234 × 987, depois somar 100 Action: calculate(234, 987, "multiply") → 230958 Action: calculate(230958, 100, "add") → 231058 Answer: "O resultado é 231.058."
O create_agent faz isso automaticamente. Ele repete o ciclo Thought→Action→Observation até ter a resposta final.
from langchain.agents import create_agent agente = create_agent(llm, [calculate, get_time, get_word_length]) resultado = agente.invoke({"messages": "Quanto é 234 vezes 987 mais 100?"}) print(resultado["messages"][-1].content) # "O resultado de 234 × 987 + 100 é 231.058."
O agente decide sozinho: preciso de duas chamadas a calculate? Então faço duas.
O problema das descrições vagas
Se duas ferramentas parecem fazer a mesma coisa, o modelo confunde.
Exemplo de conflito:
@tool def search_wiki(query: str) -> str: """Search for information.""" ... @tool def search_web(query: str) -> str: """Search for information.""" ...
Duas ferramentas com a mesma descrição genérica. O LLM não sabe qual usar.
Solução: descrições específicas
@tool def search_wiki(query: str) -> str: """Search Wikipedia for factual, historical, and encyclopedic information. Use for established facts about people, places, events, and concepts. """ ... @tool def search_web(query: str) -> str: """Search the web for current information like news, prices, and recent events. Use for real-time data that changes frequently. """ ...
Agora o modelo distingue: fato histórico → search_wiki, notícia atual → search_web.
Regra prática: cada ferramenta precisa de uma descrição exclusiva que explique quando usar.
Debug de ferramentas
O LLM nem sempre acerta. Eis os problemas mais comuns e como resolver.
Problema 1: LLM chama a ferramenta errada
Pergunta: "Que horas são?"
LLM chama: calculate em vez de get_time
Causa: descrição de calculate muito vaga ("Faz cálculos") ou descrição de get_time não menciona "que horas são".
Solução: melhorar a descrição da ferramenta correta.
# ❌ Vaga @tool def get_time() -> str: """Returns current date and time.""" # ✅ Específica @tool def get_time() -> str: """Returns the current date and time. Use when the user asks 'what time is it', 'what date is today', or any question about the current moment. """
Problema 2: argumentos errados
LLM manda: calculate(a=234, b=987, operation"234 vezes 987")=
Esperado: operation deve ser "multiply", não "234 vezes 987"
Causa: descrição do parâmetro operation não lista os valores possíveis claramente.
Solução: documentar valores permitidos no docstring.
@tool def calculate(a: float, b: float, operation: str) -> float: """Performs a mathematical operation between two numbers. Use when you need to perform numeric calculations. Args: a: First number b: Second number operation: One of 'add', 'subtract', 'multiply', 'divide' """
Problema 3: ValidationError
Se o LLM manda um argumento com tipo errado (ex: passar texto onde espera número), Pydantic gera um ValidationError com contexto claro.
# ValidationError: Input should be a valid number, # unable to parse string as a number (field: a)
Isso é bom — o agente recebe o erro e pode corrigir. Mas se acontece muito, a descrição do parâmetro pode estar confusa.
Ferramentas externas via MCP
Até agora, você escreveu as ferramentas. Mas e se outra pessoa escreveu a ferramenta e você só quer usar?
O MCP (Model Context Protocol) é um protocolo padrão para ferramentas. Pense nele como o HTTP das ferramentas de IA — assim como HTTP padronizou a web, MCP padroniza como ferramentas se conectam a agentes.
Conceito
Model Context Protocol (criado pela Anthropic, open-source):
- Um servidor MCP expõe ferramentas
- Um cliente MCP consome essas ferramentas
- Protocolo padrão — qualquer LLM pode se conectar a qualquer servidor MCP
Na S06, você escreveu as ferramentas com @tool. Na S07, você consome ferramentas que alguém escreveu, via MCP.
Servidor MCP com FastMCP
Vamos criar um servidor MCP simples — depois vamos consumi-lo como cliente.
# math_server.py — Servidor MCP com duas ferramentas from mcp.server.fastmcp import FastMCP mcp = FastMCP("Math") @mcp.tool() def add(a: int, b: int) -> int: """Add two numbers together.""" return a + b @mcp.tool() def multiply(a: int, b: int) -> int: """Multiply two numbers together.""" return a * b if __name__ == "__main__": mcp.run(transport="stdio")
Repare: é quase idêntico ao @tool do LangChain, mas com @mcp.tool() e FastMCP em vez de langchain_core.tools.
O servidor usa transport="stdio" — roda no mesmo processo, sem servidor externo.
Cliente: consumindo MCP no LangChain
Instale: pip install langchain-mcp-adapters
from mcp import ClientSession, StdioServerParameters from mcp.client.stdio import stdio_client from langchain_mcp_adapters.tools import load_mcp_tools from langchain.agents import create_agent server_params = StdioServerParameters( command="python", args=["math_server.py"], ) async with stdio_client(server_params) as (read, write): async with ClientSession(read, write) as session: await session.initialize() tools = await load_mcp_tools(session) # tools agora são @tool do LangChain — usa igual! agente = create_agent(llm, tools) resultado = await agente.ainvoke( {"messages": "Quanto é 3 + 5 multiplicado por 2?"} ) print(resultado["messages"][-1].content)
O que aconteceu:
stdio_clientiniciou o servidormath_server.pyem backgroundClientSessionfez o handshake MCPload_mcp_toolsconverteu as ferramentas MCP em@tooldo LangChaincreate_agentusou as ferramentas normalmente — igual à S06
Servidor MCP remoto (HTTP)
O exemplo acima usa transport="stdio" — o cliente spawn o servidor localmente. Mas o MCP também suporta transporte HTTP, permitindo conectar a servidores remotos.
Para rodar o mesmo servidor como HTTP:
# math_server_http.py — Mesmo servidor, mas acessível via HTTP from mcp.server.fastmcp import FastMCP mcp = FastMCP("Math") @mcp.tool() def add(a: int, b: int) -> int: """Add two numbers together.""" return a + b @mcp.tool() def multiply(a: int, b: int) -> int: """Multiply two numbers together.""" return a * b # Roda em http://localhost:8000 (ou qualquer host remoto) mcp.run(transport="streamable-http")
O código do servidor é idêntico — só muda a última linha. O servidor fica disponível em http://localhost:8000/mcp e pode ser acessado de qualquer máquina com conectividade.
Cliente remoto (HTTP)
No cliente, em vez de stdio_client, usamos streamablehttp_client com uma URL:
from mcp import ClientSession from mcp.client.streamable_http import streamablehttp_client from langchain_mcp_adapters.tools import load_mcp_tools from langchain.agents import create_agent # Aponta pra URL — pode ser localhost ou um servidor remoto async with streamablehttp_client( "http://localhost:8000/mcp" ) as (read, write): async with ClientSession(read, write) as session: await session.initialize() tools = await load_mcp_tools(session) agente = create_agent(llm, tools) # API idêntica — só muda como conecta
A API do agente é idêntica. A única diferença é como o cliente se conecta ao servidor: stdio spawn um processo local, streamable-http conecta a uma URL. Servidores MCP públicos (Slack, GitHub, Notion) funcionam assim — rodam remotamente e você só precisa da URL.
S06 vs S07 — resumo
| Semana | Ferramentas | Origem | Código |
|---|---|---|---|
| S06 | @tool — você escreve |
Seu código Python @tool + bind_tools + create_agent |
|
| S07 | MCP — alguém escreveu | Servidor externo load_mcp_tools + create_agent |
O agente não sabe a diferença. Pra ele, ferramenta é ferramenta.
Múltiplos servidores MCP
Você pode conectar vários servidores MCP ao mesmo agente:
from langchain_mcp_adapters.client import MultiServerMCPClient client = MultiServerMCPClient({ "math": { "command": "python", "args": ["math_server.py"], "transport": "stdio", }, "weather": { "url": "http://localhost:8000/mcp", "transport": "http", } }) tools = await client.get_tools() agente = create_agent(llm, tools) # Agora o agente tem ferramentas de math E weather
Isso é poderoso: você monta um agente combinando ferramentas de fontes diferentes sem escrever código de integração.
Referências Importantes
Paper: Gorilla
Título: "Gorilla: Large Language Model Connected to Massive APIs" Autores: Shishir Patil, Tianjun Zhang, Xin Wang, Joseph E. Gonzalez Ano: 2023 Link: arXiv:2305.15334
Paper: ReAct
Título: "ReAct: Synergistic Reasoning and Acting in Language Models" Autores: Shunyu Yao, et al. Ano: 2022 Link: arXiv:2210.03629
MCP — Model Context Protocol
Site: modelcontextprotocol.io
Biblioteca: langchain-mcp-adapters v0.2.2 (MIT, Mar 2026)
GitHub: langchain-ai/langchain-mcp-adapters
Exercícios Práticos
Exercício 1: Descrições em Conflito
Crie duas ferramentas com descrições parecidas e teste o que acontece. Depois, corrija as descrições e veja a diferença.
📝 Gabarito
from langchain_core.tools import tool # ❌ Descrições vagas — LLM confunde @tool def find_word_meaning(word: str) -> str: """Search for information about a word.""" # (implementação simplificada) return f"Definition of {word}: ..." @tool def find_word_rhymes(word: str) -> str: """Search for information about a word.""" # (implementação simplificada) return f"Rhymes of {word}: ..." # Teste: "What does 'serendipity' mean?" # Possível erro: LLM escolhe find_word_rhymes em vez de find_word_meaning # ✅ Descrições específicas @tool def find_word_meaning(word: str) -> str: """Look up the definition and meaning of a word in a dictionary. Use when the user asks 'what does X mean' or 'definition of X'. """ return f"Definition of {word}: ..." @tool def find_word_rhymes(word: str) -> str: """Find words that rhyme with a given word. Use when the user asks for rhymes, similar-sounding words, or poetry help. """ return f"Rhymes of {word}: ..."
Exercício 2: Debug de Descrição
O agente abaixo chama a ferramenta errada. Corrija a descrição.
@tool def calculate(a: float, b: float, operation: str) -> float: """Do math.""" ops = {'add': a+b, 'subtract': a-b, 'multiply': a*b, 'divide': a/b} return ops[operation] @tool def get_time() -> str: """Gives info.""" from datetime import datetime return datetime.now().strftime("%d/%m/%Y %H:%M:%S") # Teste: "Que horas são?" # Resultado: LLM chama calculate em vez de get_time # Por quê? "Gives info" e "Do math" são vagas demais
📝 Gabarito
@tool def calculate(a: float, b: float, operation: str) -> float: """Performs a mathematical operation between two numbers. Use when you need to perform numeric calculations like addition, subtraction, multiplication, or division. Args: a: First number b: Second number operation: One of 'add', 'subtract', 'multiply', 'divide' """ ops = {'add': a+b, 'subtract': a-b, 'multiply': a*b, 'divide': a/b} return ops[operation] @tool def get_time() -> str: """Returns the current date and time. Use when the user asks about the current time, today's date, or 'what time is it'. """ from datetime import datetime return datetime.now().strftime("%d/%m/%Y %H:%M:%S")
Exercício 3: Servidor MCP
Crie um servidor MCP com uma ferramenta reverse_string que inverte uma string, depois escreva um cliente que a consome.
📝 Gabarito
Servidor (reverse_server.py):
from mcp.server.fastmcp import FastMCP mcp = FastMCP("Reverse") @mcp.tool() def reverse_string(text: str) -> str: """Reverses a string. Use when the user wants to flip or reverse text.""" return text[::-1] if __name__ == "__main__": mcp.run(transport="stdio")
Cliente (Colab):
from mcp import ClientSession, StdioServerParameters from mcp.client.stdio import stdio_client from langchain_mcp_adapters.tools import load_mcp_tools from langchain.agents import create_agent from langchain_openai import ChatOpenAI llm = ChatOpenAI( model="lfm2.5:8b", base_url="http://localhost:11434/v1", api_key="nao_precisa", temperature=0, ) server_params = StdioServerParameters( command="python", args=["reverse_server.py"], ) async with stdio_client(server_params) as (read, write): async with ClientSession(read, write) as session: await session.initialize() tools = await load_mcp_tools(session) agente = create_agent(llm, tools) resultado = await agente.ainvoke( {"messages": "Reverse the word 'Guilda'"} ) print(resultado["messages"][-1].content) # "The reverse of 'Guilda' is 'adliuG'."
Exercício 4: Agente com Ferramentas Mistas
Combine duas ferramentas @tool (que você escreveu) com ferramentas MCP de um servidor externo em um único agente.
📝 Gabarito
from langchain_core.tools import tool from langchain.agents import create_agent from langchain_mcp_adapters.client import MultiServerMCPClient # Suas ferramentas @tool @tool def calculate(a: float, b: float, operation: str) -> float: """Performs a mathematical operation between two numbers. Args: a: First number b: Second number operation: One of 'add', 'subtract', 'multiply', 'divide' """ ops = {'add': a+b, 'subtract': a-b, 'multiply': a*b, 'divide': a/b} return ops[operation] # Ferramentas MCP client = MultiServerMCPClient({ "math": { "command": "python", "args": ["math_server.py"], "transport": "stdio", }, }) mcp_tools = await client.get_tools() # Combina tudo all_tools = [calculate] + mcp_tools agente = create_agent(llm, all_tools) resultado = await agente.ainvoke( {"messages": "Quanto é 10 + 5 multiplicado por 3?"} ) print(resultado["messages"][-1].content)