🔧 Como Funciona
Tool calling permite que o LLM "chame" funções no seu código. Você envia a mensagem do usuário junto com uma lista de ferramentas disponíveis (schemas JSON). O modelo analisa a intenção e responde com uma chamada estruturada indicando qual ferramenta usar e com quais argumentos.
Tool Calling com Ollama
response = chat(
model='qwen3:8b',
messages=[{'role': 'user', 'content': 'Read /tmp/hello.txt'}],
tools=[{
'type': 'function',
'function': {
'name': 'read_file',
'description': 'Read a file from disk',
'parameters': {
'type': 'object',
'properties': {'path': {'type': 'string'}},
'required': ['path']
}
}
}],
stream=False,
options={'num_ctx': 65536}
)
# response.message.tool_calls contém as chamadas
💡 Sempre stream=False para Tools
Quando enviando ferramentas, use stream=False. O modelo precisa gerar o JSON completo da tool call antes que você possa processá-lo. Streaming parcial de JSON é problemático e pode causar erros de parsing.
📋 Formato dos Schemas
Cada ferramenta precisa de um schema JSON que descreve seu nome, propósito e parâmetros. O formato segue o padrão OpenAI function calling — o que significa que schemas escritos para GPT-4 funcionam com Qwen3 no Ollama sem alteração.
Schemas Completos — 4 Ferramentas
TOOLS = [
{
'type': 'function',
'function': {
'name': 'read_file',
'description': 'Read the full contents of a file given its path',
'parameters': {
'type': 'object',
'properties': {
'path': {'type': 'string', 'description': 'Absolute file path'}
},
'required': ['path']
}
}
},
{
'type': 'function',
'function': {
'name': 'write_file',
'description': 'Write content to a file, creating it if needed',
'parameters': {
'type': 'object',
'properties': {
'path': {'type': 'string', 'description': 'File path to write'},
'content': {'type': 'string', 'description': 'Content to write'}
},
'required': ['path', 'content']
}
}
},
{
'type': 'function',
'function': {
'name': 'run_shell',
'description': 'Execute a shell command and return stdout+stderr',
'parameters': {
'type': 'object',
'properties': {
'command': {'type': 'string', 'description': 'Shell command'}
},
'required': ['command']
}
}
},
{
'type': 'function',
'function': {
'name': 'search_in_files',
'description': 'Search for a pattern in files using grep',
'parameters': {
'type': 'object',
'properties': {
'pattern': {'type': 'string', 'description': 'Regex pattern'},
'path': {'type': 'string', 'description': 'Directory to search'}
},
'required': ['pattern', 'path']
}
}
}
]
📊 Compatibilidade OpenAI
Formato idêntico: o schema type: function é o mesmo usado pela OpenAI e Anthropic
Migração fácil: trocar de Ollama local para API cloud requer mudar apenas o client, não os schemas
JSON Schema: os parâmetros seguem JSON Schema draft-07 — suporta types, enums, descriptions e required
🔄 O Fluxo Completo
Tool calling não é uma chamada única — é um loop. Você envia a mensagem com tools, o modelo responde com tool_calls, você executa as ferramentas, envia os resultados de volta, e o modelo gera a resposta final (ou pede mais ferramentas). Este ciclo repete até o modelo responder sem tool calls.
Loop de Tool Calling
# 1. Enviar com tools
response = chat(model, messages, tools=TOOLS)
# 2. Checar tool_calls
if response.message.tool_calls:
for tc in response.message.tool_calls:
result = TOOL_MAP[tc.function.name](**tc.function.arguments)
messages.append({
'role': 'tool',
'content': result,
'name': tc.function.name
})
# 3. Repetir
Enviar mensagem + schemas das tools
O modelo recebe o contexto e sabe quais ferramentas existem
Modelo responde com tool_calls (JSON)
Indica qual ferramenta usar e com quais argumentos
Executar ferramenta e enviar resultado
O resultado vai como mensagem role: "tool" de volta ao modelo
Modelo gera resposta final (ou pede mais tools)
O loop continua até o modelo responder sem tool_calls
🧠 Thinking vs Non-Thinking
Modelos Qwen3 suportam dois modos de raciocínio: non-thinking (rápido, direto) e thinking (com cadeia de pensamento em blocos <think>). Para tool calling, non-thinking é geralmente melhor — mais rápido e menos propenso a gerar JSON malformado.
Modos de Raciocínio
# Non-thinking (rápido, para tools):
# Basta enviar normalmente — Qwen3 usa non-thinking por default com tools
# Thinking (para planejamento):
# Adicione instrução no system prompt: "Think step by step"
# O modelo emitirá <think>...</think> blocks
# IMPORTANTE: Strip <think> do histórico!
import re
clean = re.sub(r'<think>.*?</think>', '', content, flags=re.DOTALL).strip()
💡 Parâmetros Recomendados para Qwen3
Use temperature=0.6, top_p=0.95, top_k=20 como baseline. Esses valores foram otimizados pelo time da Alibaba para balancear criatividade e consistência no tool calling.
🌊 Streaming com Ferramentas
Streaming e tool calling têm uma tensão natural: streaming quer enviar tokens incrementalmente, mas tool calls precisam do JSON completo para serem executadas. A solução é uma abordagem híbrida — stream para texto, não-stream para tools.
Abordagem Híbrida
# LIMITAÇÃO: stream=True + tools não é incremental
# O modelo gera JSON completo antes do stream terminar
# SOLUÇÃO: Abordagem híbrida
# - Tool turns: stream=False (precisa do JSON completo)
# - Text turns: stream=True (UX melhor)
Fazer
-
✓
Usar
stream=Falsepara chamadas com tools -
✓
Usar
stream=Truepara respostas de texto ao usuário - ✓ Mostrar indicador de progresso durante tool execution
Evitar
- ✗ Tentar parsear JSON incremental de tool calls em stream
-
✗
Usar
stream=Truesempre — tool calls ficam quebradas - ✗ Bloquear a UI inteira durante tool execution sem feedback
⚖️ Local vs API
A decisão entre rodar localmente (Ollama) ou usar uma API cloud (Claude, OpenAI) não é binária. Cada abordagem tem trade-offs claros em custo, latência, qualidade e privacidade. Muitos projetos de produção usam ambos — local para desenvolvimento, API para deploy.
Comparativo: Local vs API
| Aspecto | Local (Ollama) | API (Claude/OpenAI) |
|---|---|---|
| Custo | $0 | $$/token |
| Latência | 1-5s/turn | 0.5-2s/turn |
| Contexto | 8-128K | 128K-1M |
| Tool calling | Bom (Qwen3+) | Excelente |
| Privacidade | Total | Dados vão pro cloud |
| Qualidade | 35-70% SWE | 72-77% SWE |
💡 Quando Usar Cada Um
Local: desenvolvimento, testes, projetos pessoais, dados sensíveis, prototipagem rápida sem custo. API: produção com usuários finais, tarefas que exigem contexto longo (>128K), cenários onde qualidade máxima é crítica. Ambos: desenvolver com local, validar com API antes de deploy.
📋 Resumo do Módulo
Próximo Módulo:
6.3 - Construindo o Agent Loop Local