Monte uma Stack de IA Privada e Local (De Ponta a Ponta)
Você já viu as peças separadamente: um modelo local, um loop de agente local, ferramentas expostas via MCP e os padrões híbridos Claude+local. Esta é a conclusão — a página que liga tudo em um assistente privado funcionando na sua própria máquina: um modelo de peso aberto rodando localmente, um loop de agente agnóstico ao modelo que pode chamar ferramentas, essas ferramentas expostas através de um servidor MCP local, uma barreira de proteção (guardrail) na frente das perigosas e — opcionalmente — o Claude como uma "camada inteligente" opcional para os 5% de passos mais difíceis. O fio condutor: tudo que é sensível permanece no dispositivo; a nuvem é opcional e reservada para a minoria difícil.
- Ver a stack inteira como um diagrama: modelo local + loop de agente + ferramentas MCP locais + barreira de proteção (+ Claude opcional)
- Rodar um modelo de peso aberto localmente e confirmar que ele consegue fazer chamadas de ferramentas (tool calling)
- Montar um loop de agente mínimo que é agnóstico ao modelo — mesmo loop, troca-se o endpoint
- Expor algumas ferramentas através de um servidor MCP local e deixar o agente chamá-las
- Adicionar uma barreira de proteção: aprovação para ações destrutivas, um limite de loop/orçamento e tratamento de resultados não confiáveis
- Opcionalmente rotear apenas o raciocínio mais difícil para o Claude, mantendo o caminho padrão totalmente local
A stack inteira, em uma imagem
O modelo mental é um pequeno número de caixas, cada uma das quais você já conheceu em uma página irmã. O assistente é apenas essas caixas ligadas entre si:
Leia isso como um loop. O agente pergunta ao modelo local o que fazer em seguida. O modelo ou responde, ou emite uma chamada de ferramenta. Toda chamada de ferramenta passa por uma barreira de proteção antes de chegar ao servidor MCP local, que efetivamente faz o trabalho (lê um arquivo, roda um comando, busca nas suas notas) e retorna um resultado. O agente devolve o resultado ao modelo e repete até a tarefa estar concluída. O caminho pontilhado para o Claude é opcional: o agente escala apenas os passos que o modelo local não consegue lidar, e só quando você permite.
Três propriedades fazem esta stack valer a pena de construir:
- Local por padrão. O modelo, o loop, as ferramentas e seus dados vivem todos no seu hardware. Nada sai da máquina a menos que o caminho opcional do Claude dispare — e mesmo então, apenas o que você escolher enviar.
- Loop agnóstico ao modelo. O agente fala com um endpoint de chat no formato OpenAI. Aponte-o para o endpoint local do Ollama hoje; aponte-o para um provedor diferente amanhã sem reescrever o loop.
- Ferramentas atrás de um único padrão. As capacidades vivem em um servidor MCP, não codificadas rigidamente dentro do loop. Construa uma ferramenta uma vez e qualquer cliente que fale MCP (seu agente, o Claude Code, outro app) pode usá-la.
Construção passo a passo
- Instale o Ollama e inicie um modelo que suporte tool calling. O ollama run faz o download no primeiro uso e expõe uma API local compatível com OpenAI em localhost:11434. Este é seu 'cérebro' padrão — privado e offline. (Configuração completa: a página Rodar Modelos Localmente.)
- Escreva um loop minúsculo: envie mensagens + um schema de ferramentas ao endpoint de chat, leia a resposta, se ela contiver tool_calls execute-as, anexe os resultados e itere até o modelo retornar uma resposta final. O loop não sabe nada sobre com qual modelo ele fala — apenas o formato de chat da OpenAI.
- Coloque suas capacidades reais (ler um arquivo, rodar um comando, buscar notas) em um servidor MCP local via stdio em vez de codificá-las rigidamente. O agente lista as ferramentas do servidor, mapeia-as no schema de ferramentas do modelo e as chama sob demanda. Construa uma vez, reutilize entre clientes.
- Antes de qualquer ferramenta rodar, controle-a: autorize automaticamente ferramentas somente-leitura, exija aprovação explícita para as destrutivas (run_shell, write_file, delete), limite o número de iterações do loop e o total de tokens, e trate todo resultado de ferramenta como entrada não confiável que pode tentar direcionar o modelo.
- Mantenha o caminho local como padrão. Quando um passo é genuinamente difícil — raciocínio complicado de múltiplos passos, um plano que o modelo local insiste em errar — deixe o agente escalar apenas aquele passo para a API do Claude, e então volte ao loop local. Esta é a ideia de roteador / rascunhar-depois-refinar da página híbrida, aplicada um passo de cada vez.
1. O modelo local (seu cérebro padrão)
Inicie o modelo e confirme que o endpoint local está ativo. Escolha um modelo que anuncie tool calling — o loop de agente depende disso.
Rode um modelo local capaz de chamar ferramentas + confirme a API
# Start a model that supports tool/function calling
ollama run llama3.1
# In another terminal, confirm the local OpenAI-compatible endpoint is live.
# Ollama serves it at http://localhost:11434/v1 — no internet required.
curl http://localhost:11434/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "llama3.1",
"messages": [{"role": "user", "content": "Reply with the single word: ready"}]
}'2. O loop de agente agnóstico ao modelo
O loop é deliberadamente burro: ele encaminha mensagens e um schema de ferramentas ao endpoint de chat, e sempre que o modelo pede para chamar uma ferramenta, ele roda a ferramenta e devolve o resultado. Como ele só fala o formato de chat da OpenAI, o mesmo loop funciona contra o endpoint local agora e um provedor diferente depois — você muda uma base_url, não a lógica.
from openai import OpenAI
# Point at the LOCAL model. Swap base_url/api_key later to change providers —
# the loop below does not change. That is what "model-agnostic" means here.
client = OpenAI(base_url="http://localhost:11434/v1", api_key="ollama")
MODEL = "llama3.1"
MAX_STEPS = 8 # hard cap on loop iterations (a guardrail — see step 4)
def run_agent(user_goal, tool_schemas, dispatch):
messages = [
{"role": "system", "content": "You are a local assistant. Use tools when needed."},
{"role": "user", "content": user_goal},
]
for _ in range(MAX_STEPS):
resp = client.chat.completions.create(
model=MODEL, messages=messages, tools=tool_schemas,
)
msg = resp.choices[0].message
if not msg.tool_calls:
return msg.content # model gave a final answer
messages.append(msg)
for call in msg.tool_calls:
result = dispatch(call) # runs through the guardrail + MCP server
messages.append({
"role": "tool",
"tool_call_id": call.id,
"content": result,
})
return "Stopped: hit the step cap." # never loop forever
tool_schemas é a lista de ferramentas (no formato de chamada de função da OpenAI), e dispatch é a única função que decide se e como efetivamente rodar uma ferramenta solicitada — é aí que vivem a barreira de proteção e o servidor MCP.
3. Ferramentas via um servidor MCP local
Em vez de codificar rigidamente as ferramentas dentro do loop, exponha-as através de um servidor MCP local. O MCP é um padrão aberto para conectar um cliente de IA a ferramentas externas; um servidor local roda como um pequeno programa na sua máquina e fala com o cliente via stdio, de modo que seus dados e ações permanecem na máquina. (Por que essa é a fronteira correta, e como construir um servidor, é abordado em Conecte o Claude a Ferramentas Locais com MCP.)
Um servidor MCP mínimo em Python que expõe uma ferramenta segura, somente-leitura:
# server.py — a tiny local MCP server exposing one read-only tool.
# Run it over stdio; an MCP client (your agent, Claude Code, ...) connects to it.
from mcp.server.fastmcp import FastMCP
mcp = FastMCP("local-tools")
@mcp.tool()
def search_notes(query: str) -> str:
"""Search the user's local notes folder and return matching snippets."""
# ... read from a LOCAL directory only; never reach outside it ...
return f"(stub) matches for: {query}"
if __name__ == "__main__":
mcp.run() # stdio transport by default — local, no network
O agente se conecta a este servidor, pede a ele para listar suas ferramentas, converte cada uma no schema de ferramentas da OpenAI que seu loop já entende, e roteia as chamadas de ferramenta do modelo para o servidor. Mesmo loop, capacidades reais — e o servidor é reutilizável por qualquer cliente que fale MCP.
4. A barreira de proteção (não pule esta parte)
Esta é a diferença entre um brinquedo e algo em que você confiaria na sua própria máquina. A função dispatch do passo 2 é o único ponto de estrangulamento onde toda chamada de ferramenta é inspecionada antes de rodar. Três tarefas:
READ_ONLY = {"search_notes", "read_file", "list_dir"}
def dispatch(call):
name = call.function.name
args = call.function.arguments
# 1) APPROVAL: read-only tools auto-run; everything else asks a human first.
if name not in READ_ONLY:
if not human_approves(name, args): # destructive => require consent
return "DENIED by user."
# 2) The MCP server does the actual work (it, too, is sandboxed to safe paths).
result = call_mcp_tool(name, args)
# 3) UNTRUSTED RESULT: a tool result is data, not instructions. Do not let it
# silently become a new command to the model (prompt-injection defense).
return f"<tool_result name={name}>\n{result}\n</tool_result>"
Combine isso com os limites de loop/orçamento já presentes no loop (MAX_STEPS, mais um teto de tokens que você rastreia por execução) e você tem os três controles que importam: um humano no loop para qualquer coisa destrutiva, uma parada rígida para que o agente não possa girar ou gastar para sempre, e o hábito de tratar a saída da ferramenta como texto não confiável.
5. Opcional — o Claude como a camada inteligente
Por padrão, nunca chame a nuvem. Mas alguns passos estão genuinamente além de um modelo local pequeno — planejamento complicado de múltiplos passos, uma refatoração que precisa estar correta, uma síntese em contexto longo. Para apenas esses passos, o agente pode escalar para a API do Claude, obter uma resposta melhor e voltar ao loop local. Esta é a ideia de roteador / rascunhar-depois-refinar de Claude + Modelos Locais, aplicada um passo de cada vez.
import anthropic
cloud = anthropic.Anthropic() # reads ANTHROPIC_API_KEY from env
def hard_step(prompt, allow_cloud=False):
"""Escalate ONE hard step to Claude — only when explicitly allowed."""
if not allow_cloud:
return None # default: stay fully local, send nothing off-device
msg = cloud.messages.create(
model="claude-sonnet-4-5", # check current model ids before pinning
max_tokens=1024,
messages=[{"role": "user", "content": prompt}],
)
return msg.content[0].text
Duas regras mantêm isso honesto: o caminho da nuvem é opcional (desligado por padrão), e você envia apenas o que aquele único passo precisa — não todo o seu contexto. O modelo local continua sendo o burro de carga; o Claude é o especialista que você chama para os 5% difíceis. Para os IDs de modelo atuais exatos e preços, veja a nota de verificação abaixo.
- Agentes locais ainda executam ações reais na sua máquina — coloque as ferramentas em sandbox, exija aprovação para passos destrutivos, limite loops/orçamento e trate os resultados de ferramentas como não confiáveis (prompt injection).
Teste você mesmo
Teste você mesmo
0/4- Um assistente privado são quatro caixas ligadas em um loop: modelo local + agente agnóstico ao modelo + ferramentas MCP locais + uma barreira de proteção — com o Claude como uma quinta caixa opcional
- Local é o padrão e a garantia de privacidade: o modelo, o loop, as ferramentas e seus dados permanecem todos na sua máquina a menos que VOCÊ opte pelo caminho da nuvem
- Mantenha o loop burro e agnóstico ao modelo (formato de chat da OpenAI) e coloque as capacidades reais atrás de um servidor MCP local — construa uma vez, reutilize entre clientes
- A barreira de proteção é a parte que você não pode pular: aprove passos destrutivos, limite loops/orçamento, coloque as ferramentas em sandbox e trate os resultados de ferramentas como não confiáveis
- O Claude é a camada inteligente opcional para os 5% difíceis — escale um passo de cada vez e envie apenas o que aquele passo precisa
- Especificidades voláteis (nomes de modelos, IDs, preços, APIs de SDK) ficam atrás de notas de verificação; a arquitetura é durável, os números não
Fontes e leitura adicional
- Ollama — API compatível com OpenAI (localhost:11434, parâmetro tools)
- Ollama — anúncio de suporte a ferramentas
- Biblioteca de modelos do Ollama (modelos atuais capazes de chamar ferramentas)
- Model Context Protocol — introdução
- Model Context Protocol — SDKs oficiais (Python, TypeScript)
- MCP Python SDK (GitHub)
- MCP TypeScript SDK (GitHub)
- Anthropic — modelos e preços do Claude