Function Calling: Como Fazer LLMs Executarem Ações
Tutorial completo para conectar LLMs ao mundo real
•15 min de leitura
Function Calling é o que transforma um chatbot em um agente útil. Permite que LLMs chamem suas APIs, busquem dados e executem ações reais.
O que é Function Calling?
Sem Function Calling:
User: "Qual o tempo em São Paulo?"
LLM: "Não tenho acesso a dados em tempo real..."
Com Function Calling:
User: "Qual o tempo em São Paulo?"
LLM: [Decide chamar get_weather("São Paulo")]
Sistema: [Executa função, retorna dados]
LLM: "Em São Paulo está 25°C e ensolarado!"
O LLM DECIDE quando chamar funções!Como Funciona
Fluxo:
1. Você define funções disponíveis
2. Envia pro LLM junto com a mensagem
3. LLM decide se precisa chamar alguma função
4. Se sim, retorna nome da função + argumentos
5. Você executa a função
6. Retorna resultado pro LLM
7. LLM gera resposta final
┌───────┐ 1. Msg + Tools ┌───────┐
│ Você │──────────────────▶│ LLM │
└───────┘ └───┬───┘
▲ │
│ 2. tool_call: │
│ get_weather("SP") │
│◀───────────────────────────┘
│
│ 3. Executar função
▼
┌───────┐
│ API │ → "25°C, sol"
└───────┘
│
│ 4. Resultado
▼
┌───────┐ 5. Resultado ┌───────┐
│ Você │──────────────────▶│ LLM │
└───────┘ └───┬───┘
▲ │
│ 6. "Está 25°C..." │
│◀───────────────────────────┘Implementação Básica
1. Definir Ferramentas
from openai import OpenAI
import json
client = OpenAI()
# Definir ferramentas no formato esperado pela API
tools = [
{
"type": "function",
"function": {
"name": "get_weather",
"description": "Busca a previsão do tempo atual para uma cidade",
"parameters": {
"type": "object",
"properties": {
"city": {
"type": "string",
"description": "Nome da cidade, ex: São Paulo"
},
"unit": {
"type": "string",
"enum": ["celsius", "fahrenheit"],
"description": "Unidade de temperatura"
}
},
"required": ["city"]
}
}
},
{
"type": "function",
"function": {
"name": "search_products",
"description": "Busca produtos no catálogo da loja",
"parameters": {
"type": "object",
"properties": {
"query": {
"type": "string",
"description": "Termo de busca"
},
"max_price": {
"type": "number",
"description": "Preço máximo em reais"
},
"category": {
"type": "string",
"enum": ["eletrônicos", "roupas", "casa"],
"description": "Categoria do produto"
}
},
"required": ["query"]
}
}
}
]2. Implementar Funções
import requests
def get_weather(city: str, unit: str = "celsius") -> str:
"""Implementação real da função."""
# Integração com API de clima (exemplo)
# api_key = os.getenv("WEATHER_API_KEY")
# response = requests.get(f"https://api.weather.com/...")
# Para exemplo, retorno mockado:
return json.dumps({
"city": city,
"temperature": 25,
"unit": unit,
"condition": "ensolarado"
})
def search_products(query: str, max_price: float = None, category: str = None) -> str:
"""Busca produtos no banco de dados."""
# Implementação real buscaria no banco
results = [
{"name": f"{query} Premium", "price": 199.90},
{"name": f"{query} Básico", "price": 99.90}
]
if max_price:
results = [p for p in results if p["price"] <= max_price]
return json.dumps(results)
# Mapear nome da função para implementação
AVAILABLE_FUNCTIONS = {
"get_weather": get_weather,
"search_products": search_products
}3. Loop de Chamada
def chat_with_functions(user_message: str) -> str:
"""Chat que pode usar funções."""
messages = [
{
"role": "system",
"content": "Você é um assistente útil. Use as ferramentas disponíveis."
},
{"role": "user", "content": user_message}
]
# Primeira chamada
response = client.chat.completions.create(
model="gpt-4",
messages=messages,
tools=tools,
tool_choice="auto" # LLM decide se usa ou não
)
message = response.choices[0].message
# Verificar se quer chamar função
while message.tool_calls:
# Adicionar resposta do modelo às mensagens
messages.append(message)
# Executar cada função chamada
for tool_call in message.tool_calls:
function_name = tool_call.function.name
function_args = json.loads(tool_call.function.arguments)
print(f"🔧 Chamando {function_name} com {function_args}")
# Executar função
function_response = AVAILABLE_FUNCTIONS[function_name](**function_args)
# Adicionar resultado às mensagens
messages.append({
"role": "tool",
"tool_call_id": tool_call.id,
"content": function_response
})
# Nova chamada com resultados
response = client.chat.completions.create(
model="gpt-4",
messages=messages,
tools=tools
)
message = response.choices[0].message
return message.content
# Testar
print(chat_with_functions("Como está o tempo em São Paulo?"))
print(chat_with_functions("Busque tênis até R$150"))Forçar ou Desabilitar Funções
# tool_choice controla comportamento
# AUTO: LLM decide (padrão)
tool_choice="auto"
# NONE: Nunca usa funções
tool_choice="none"
# FORÇAR função específica
tool_choice={
"type": "function",
"function": {"name": "get_weather"}
}
# REQUIRED: Deve usar alguma função
tool_choice="required"Parallel Function Calls
# GPT-4 pode chamar múltiplas funções de uma vez!
User: "Compare o tempo em São Paulo e Rio"
LLM retorna:
tool_calls = [
{"function": {"name": "get_weather", "arguments": '{"city":"São Paulo"}'}},
{"function": {"name": "get_weather", "arguments": '{"city":"Rio de Janeiro"}'}}
]
# Execute em paralelo para melhor performance:
import asyncio
async def execute_parallel(tool_calls):
tasks = []
for call in tool_calls:
func = AVAILABLE_FUNCTIONS[call.function.name]
args = json.loads(call.function.arguments)
tasks.append(asyncio.to_thread(func, **args))
return await asyncio.gather(*tasks)Boas Práticas
1. DESCRIÇÕES CLARAS
# O LLM decide baseado na descrição!
❌ "description": "Pega dados"
✅ "description": "Busca a previsão do tempo atual
para uma cidade brasileira"
2. VALIDAR ARGUMENTOS
def get_weather(city: str):
if not city or len(city) < 2:
return "Cidade inválida"
# ...
3. TRATAR ERROS
try:
result = function(**args)
except Exception as e:
result = f"Erro: {str(e)}"
4. LIMITAR FUNÇÕES DISPONÍVEIS
# Não exponha funções perigosas
# Valide permissões do usuário
5. TIMEOUT
# Funções externas podem demorar
import timeout_decorator
@timeout_decorator.timeout(10)
def call_external_api(): ...Segurança
⚠️ Function Calling pode ser perigoso!
RISCOS:
- LLM pode chamar funções destrutivas
- Injection via argumentos
- Vazamento de dados
MITIGAÇÕES:
1. NUNCA exponha funções destrutivas
❌ delete_database()
❌ send_money()
2. VALIDE todos os argumentos
def search(query: str):
# Sanitizar input
query = sanitize(query)
# Validar formato
if not is_valid_query(query):
raise ValueError()
3. USE ALLOWLIST de funções
SAFE_FUNCTIONS = ["get_weather", "search"]
if function_name not in SAFE_FUNCTIONS:
raise SecurityError()
4. RATE LIMIT por função
# Evitar abuse
5. LOG todas as chamadas
logger.info(f"Function call: {name}({args})")Exemplos de Ferramentas Úteis
# Ferramentas comuns para agentes:
tools = [
# Busca
{"name": "search_web", "description": "Busca na internet"},
{"name": "search_database", "description": "Busca no banco de dados"},
# Comunicação
{"name": "send_email", "description": "Envia email"},
{"name": "send_slack", "description": "Envia mensagem no Slack"},
# Dados
{"name": "get_user_info", "description": "Busca dados do usuário"},
{"name": "get_order_status", "description": "Status de pedido"},
# Ações
{"name": "create_ticket", "description": "Cria ticket de suporte"},
{"name": "schedule_meeting", "description": "Agenda reunião"},
# Cálculos
{"name": "calculator", "description": "Faz cálculos"},
{"name": "convert_currency", "description": "Converte moedas"}
]Quer Construir Agentes de IA?
Nosso curso ensina Function Calling e Agentes na prática.
Conhecer o Curso