Создайте приватный локальный AI-стек (от начала до конца)
Вы уже видели эти части по отдельности: локальная модель, локальный агентный цикл, инструменты, доступные через MCP и гибридные паттерны Claude+локальная модель. Это завершающая глава — страница, которая соединяет их в один рабочий приватный ассистент на вашей собственной машине: модель с открытыми весами, работающая локально, независимый от модели агентный цикл, способный вызывать инструменты, эти инструменты, доступные через локальный MCP-сервер, ограждение (guardrail) перед опасными из них и — опционально — Claude как подключаемый по выбору «умный слой» для самых сложных 5% шагов. Сквозная идея: всё чувствительное остаётся на устройстве; облако опционально и зарезервировано для сложного меньшинства.
- Увидеть весь стек как одну диаграмму: локальная модель + агентный цикл + локальные MCP-инструменты + ограждение (+ опционально Claude)
- Запустить модель с открытыми весами локально и убедиться, что она умеет вызывать инструменты
- Поднять минимальный агентный цикл, независимый от модели — тот же цикл, меняете endpoint
- Открыть пару инструментов через локальный MCP-сервер и позволить агенту их вызывать
- Добавить одно ограждение: одобрение для разрушительных действий, ограничение цикла/бюджета и обработку недоверенных результатов
- Опционально направлять к Claude только самые сложные рассуждения, оставляя путь по умолчанию полностью локальным
Весь стек на одной картинке
Мысленная модель — это небольшое число блоков, каждый из которых вы уже встречали на соседней странице. Ассистент — это просто эти блоки, соединённые вместе:
Читайте это как цикл. Агент спрашивает локальную модель, что делать дальше. Модель либо отвечает, либо выдаёт вызов инструмента. Каждый вызов инструмента проходит через ограждение, прежде чем достичь локального MCP-сервера, который фактически выполняет работу (читает файл, выполняет команду, ищет в ваших заметках) и возвращает результат. Агент передаёт результат обратно модели и повторяет, пока задача не будет выполнена. Пунктирный путь к Claude подключается по выбору: агент эскалирует только те шаги, с которыми локальная модель не справляется, и только когда вы это разрешаете.
Три свойства делают этот стек стоящим построения:
- Локальный по умолчанию. Модель, цикл, инструменты и ваши данные — всё живёт на вашем оборудовании. Ничто не покидает машину, если только не сработает опциональный путь к Claude — и даже тогда только то, что вы решите отправить.
- Цикл, независимый от модели. Агент общается с chat-endpoint в форме OpenAI. Направьте его на локальный endpoint Ollama сегодня; направьте на другого провайдера завтра, не переписывая цикл.
- Инструменты за одним стандартом. Возможности живут в MCP-сервере, а не зашиты в цикл. Постройте инструмент один раз, и любой клиент, говорящий на MCP (ваш агент, Claude Code, другое приложение), сможет его использовать.
Пошаговое построение
- Установите Ollama и запустите модель, поддерживающую вызов инструментов. ollama run загружает модель при первом использовании и открывает локальный OpenAI-совместимый API на localhost:11434. Это ваш «мозг» по умолчанию — приватный и офлайн. (Полная настройка: страница Run Models Locally.)
- Напишите крошечный цикл: отправьте сообщения + схему инструментов на chat-endpoint, прочитайте ответ, если он содержит tool_calls — выполните их, добавьте результаты и повторяйте, пока модель не вернёт финальный ответ. Цикл ничего не знает о том, с какой моделью он общается — только форму chat OpenAI.
- Поместите ваши реальные возможности (прочитать файл, выполнить команду, поискать в заметках) в локальный MCP-сервер поверх stdio вместо того, чтобы зашивать их. Агент запрашивает список инструментов сервера, отображает их в схему инструментов модели и вызывает их по требованию. Постройте один раз, переиспользуйте между клиентами.
- Прежде чем любой инструмент запустится, поставьте на пути шлагбаум: автоматически разрешайте инструменты только для чтения, требуйте явного одобрения для разрушительных (run_shell, write_file, delete), ограничивайте число итераций цикла и общее число токенов и относитесь к каждому результату инструмента как к недоверенному вводу, который может попытаться направить модель.
- Оставьте локальный путь по умолчанию. Когда шаг действительно сложен — хитрое многошаговое рассуждение, план, который локальная модель всё время портит — позвольте агенту эскалировать именно этот шаг к Claude API, а затем вернуться в локальный цикл. Это идея роутера / draft-then-refine с гибридной страницы, применённая к одному шагу за раз.
1. Локальная модель (ваш мозг по умолчанию)
Запустите модель и убедитесь, что локальный endpoint работает. Выберите модель, которая заявляет о вызове инструментов — агентный цикл зависит от этого.
Запустите локальную модель с поддержкой инструментов + проверьте 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. Агентный цикл, независимый от модели
Цикл намеренно туповат: он перенаправляет сообщения и схему инструментов на chat-endpoint, и всякий раз, когда модель просит вызвать инструмент, он запускает инструмент и передаёт результат обратно. Поскольку он говорит только на форме chat OpenAI, тот же цикл работает как с локальным endpoint сейчас, так и с другим провайдером позже — вы меняете base_url, а не логику.
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 — это список инструментов (в формате вызова функций OpenAI), а dispatch — та единственная функция, которая решает, следует ли и как фактически запустить запрошенный инструмент — именно здесь живут ограждение и MCP-сервер.
3. Инструменты через локальный MCP-сервер
Вместо того чтобы зашивать инструменты внутрь цикла, откройте их через локальный MCP-сервер. MCP — это открытый стандарт для подключения AI-клиента к внешним инструментам; локальный сервер работает как небольшая программа на вашей машине и общается с клиентом через stdio, так что ваши данные и действия остаются на машине. (Почему это правильная граница и как построить сервер, описано в Подключение Claude к локальным инструментам через MCP.)
Минимальный MCP-сервер на Python, открывающий один безопасный инструмент только для чтения:
# 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
Агент подключается к этому серверу, просит его перечислить свои инструменты, преобразует каждый в схему инструментов OpenAI, которую ваш цикл уже понимает, и маршрутизирует вызовы инструментов модели к серверу. Тот же цикл, реальные возможности — и сервер переиспользуем любым клиентом, говорящим на MCP.
4. Ограждение (не пропускайте это)
Это разница между игрушкой и чем-то, чему вы доверили бы работу на вашей собственной машине. Функция dispatch из шага 2 — единственная узкая точка, где каждый вызов инструмента проверяется до того, как он запустится. Три задачи:
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>"
Совместите это с ограничениями цикла/бюджета, уже встроенными в цикл (MAX_STEPS, плюс потолок токенов, который вы отслеживаете на прогон), и у вас есть три контроля, которые имеют значение: человек в цикле для всего разрушительного, жёсткая остановка, чтобы агент не мог вертеться или тратить бесконечно, и привычка относиться к выводу инструмента как к недоверенному тексту.
5. Опционально — Claude как умный слой
По умолчанию никогда не обращайтесь к облаку. Но некоторые шаги действительно выходят за пределы возможностей маленькой локальной модели — заковыристое многошаговое планирование, рефакторинг, который должен быть верным, синтез по длинному контексту. Только для таких шагов агент может эскалировать к Claude API, получить лучший ответ и вернуться в локальный цикл. Это идея роутера / draft-then-refine из Claude + локальные модели, применённая по одному шагу за раз.
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
Два правила держат это честным: облачный путь подключается по выбору (по умолчанию выключен), и вы отправляете только то, что нужно этому единственному шагу — не весь ваш контекст. Локальная модель остаётся рабочей лошадкой; Claude — специалист, которого вы вызываете для сложных 5%. Точные текущие идентификаторы моделей и цены смотрите в примечании о проверке ниже.
- Локальные агенты всё же совершают реальные действия на вашей машине — изолируйте инструменты (sandbox), требуйте одобрения для разрушительных шагов, ограничивайте циклы/бюджет и относитесь к результатам инструментов как к недоверенным (prompt-injection).
Проверьте себя
Проверьте себя
0/4- Приватный ассистент — это четыре блока, соединённые в цикл: локальная модель + агент, независимый от модели + локальные MCP-инструменты + ограждение — с Claude как опциональным пятым блоком
- Локальный — это значение по умолчанию и гарантия приватности: модель, цикл, инструменты и ваши данные остаются на вашей машине, пока ВЫ не подключите облачный путь
- Держите цикл тупым и независимым от модели (форма chat OpenAI) и поместите реальные возможности за локальным MCP-сервером — постройте один раз, переиспользуйте между клиентами
- Ограждение — это часть, которой нельзя пренебречь: одобряйте разрушительные шаги, ограничивайте циклы/бюджет, изолируйте инструменты и относитесь к результатам инструментов как к недоверенным
- Claude — это подключаемый по выбору умный слой для сложных 5% — эскалируйте по одному шагу за раз и отправляйте только то, что нужно этому шагу
- Изменчивые детали (имена моделей, идентификаторы, цены, API SDK) находятся за примечаниями о проверке; архитектура долговечна, а числа — нет
Источники и дополнительное чтение
- Ollama — OpenAI-совместимый API (localhost:11434, параметр tools)
- Ollama — анонс поддержки инструментов
- Библиотека моделей Ollama (текущие модели с поддержкой инструментов)
- Model Context Protocol — введение
- Model Context Protocol — официальные SDK (Python, TypeScript)
- MCP Python SDK (GitHub)
- MCP TypeScript SDK (GitHub)
- Anthropic — модели и цены Claude