# Локальная Gemma внутри Codex: эксперимент, который удвоил расход вместо экономии
Каноническая страница: https://plainews.ru/posts/gemma-codex-mcp-local-llm-tokens
Опубликовано: 2026-09-19T11:01:19.886Z

Разбор эксперимента: MCP-сервер с Gemma внутри Codex не сэкономил, а удвоил облачный input. Как устроена схема, где ломается и когда локальная модель всё-таки окупается.
Идея выглядела разумно: отдавать простые задачи локальной модели, а дорогой облачный Codex беречь для сложного. Эксперимент с MCP-сервером и gemma3:4b показал обратное — маршрут через локальную модель потребовал вдвое больше облачного input на той же задаче. Вот что произошло и как это повторить без сюрпризов.

## Зачем вообще это пробовать

Если у вас уже стоит Ollama с небольшой моделью, соблазн очевиден: зачем тратить лимит Codex на разбор короткого traceback, сводку лога или генерацию commit message? Пусть Codex делегирует рутину локально, а сам занимается архитектурой и ревью.

Для делегирования есть готовый механизм — MCP (Model Context Protocol). Codex поддерживает локальные STDIO MCP-серверы: процесс запускается на машине, читает JSON-RPC из stdin и возвращает результат через stdout. Конфигурация хранится в ~/.codex/config.toml.

## Как собрать MCP-сервер для Ollama

Сервер получился компактным, без внешних Python-зависимостей. У него один инструмент — local_llm. Входящий запрос выглядит так:

```json
{
  "prompt": "Что означает эта ошибка?",
  "context": "SMTPAuthenticationError: 535 authentication failed",
  "mode": "classify",
  "max_tokens": 160,
  "temperature": 0
}
```

В ответ приходят не только слова модели, но и метрики:

```json
{
  "answer": "Incorrect username or password for the email server",
  "model": "gemma3:4b",
  "wall_time_ms": 6098,
  "prompt_tokens": 71,
  "output_tokens": 44,
  "tokens_per_second": 9.52
}
```

> **К сведению.** В журнал пишутся только SHA-256 промпта, длина контекста, модель, время и число токенов — не сам код и не промпты. Иначе «приватный локальный помощник» незаметно превращается в ещё одно хранилище исходников.

Конфигурация в ~/.codex/config.toml:

```plain
[mcp_servers.ollama-worker]
command = "codex-ollama-worker"
default_tools_approval_mode = "approve"

[mcp_servers.ollama-worker.tools.local_llm]
approval_mode = "approve"
output_token_limit = 1200
```

Без явного approval_mode = "approve" первый же вызов падает с ошибкой:

```plain
MCP tool call requires approval, but approval policy is never
```

Это не баг — неинтерактивный процесс не может показать диалог подтверждения, поэтому Codex блокирует новый инструмент по умолчанию.

## Что умеют gemma3:4b и gemma2:2b на реальных задачах

Восемь задач из обычного проекта: TLS hostname mismatch, SMTP 535, извлечение портов из Docker Compose, path traversal, логика записей в БД, защита от повторной рассылки, добавление UNIQUE с дублями, commit message. Все запуски с temperature=0.

| Модель | Время 8 задач | Медиана | Скорость | Сгенерировано |
| --- | --- | --- | --- | --- |
| gemma3:4b | 147,1 с | 9,4 с | 9,40 ток/с | 1202 токена |
| gemma2:2b | 109,1 с | 13,8 с | 14,19 ток/с | 1328 токенов |

Обе модели работали на CPU — MacBook Pro 2019 с Radeon Pro 5300M (4 ГБ VRAM), но Ollama показывала 100% CPU. GPU не задействовался.

На механических задачах всё нормально: порты из Compose извлечены верно, Redis назван зависимостью, SMTP 535 объяснён, commit message написан.

На security review начались проблемы. Фрагмент с path traversal:

```python
target = Path('/srv/uploads') / upload.filename
with target.open('wb') as output:
    output.write(await upload.read())
```

gemma3:4b упомянула directory traversal, но главным исправлением предложила заменить wb на w. Опасное имя файла осталось опасным, а загрузка бинарных файлов дополнительно сломалась. gemma2:2b правильно указала на upload.filename, но посоветовала Path.joinpath() — функция соединяет пути, но не обезвреживает ../../etc/passwd.

На задаче с логикой записей 4B-модель предложила перевести русское сообщение об ошибке на английский, проигнорировав настоящую проблему: запрос учитывает любую старую запись, и после первого занятия пользователь теряет возможность записываться навсегда.

Вывод по качеству: маленькие модели справляются с механической обработкой текста, но не с ревью безопасности и предметной логикой.

## Куда исчезла экономия: разбор токенов

Одна и та же задача — объяснить SMTP 535 и назвать первую проверку — прогнана тремя маршрутами:

| Маршрут | Время | Облачный input | Облачный output | Локальные токены |
| --- | --- | --- | --- | --- |
| Ollama напрямую | 6,1 с | 0 | 0 | 71 + 44 |
| Codex напрямую | 8,8 с | 14 309 | 49 | 0 |
| Codex → MCP → Ollama → Codex | 21,4 с | 29 301 | 384 | 71 + 44 |

Маршрут через MCP использовал примерно вдвое больше облачного input, чем прямой Codex. Причина структурная: локальная модель не заменяет облачный проход, а добавляет к нему дополнительные. Codex сначала читает запрос и решает вызвать инструмент — это один полный inference со всей историей. Потом читает ответ Gemma и формирует итог — ещё один. Каждый такой «поворот» тянет за собой весь накопленный контекст.

Это подтверждается известной проблемой Codex: каждый вызов инструмента возвращает управление модели и запускает новую полную инференцию с полной историей. В одном из публичных тредов 28 из 34 вызовов инструментов оказались связаны с ожиданием и опросом статуса. Промпт-кэширование Codex CLI чувствительно к порядку инструментов: стоит поменять конфигурацию или добавить новый MCP-сервер — кэш сбрасывается и стоимость растёт.

## Где это ломается

**Кэш слетает при любом изменении конфигурации.** Добавили инструмент, поменяли порядок в config.toml — Codex пересчитывает всё с нуля. Экономия на кэше исчезает.

**Polling убивает бюджет.** Если задача требует нескольких обращений к инструменту (проверка статуса, итерация), каждый вызов — отдельный полный inference. На длинных цепочках расход токенов растёт нелинейно.

**Маленькие модели галлюцинируют на безопасности.** Результат выглядит убедительно, но содержит ошибки, которые сложно заметить без глубокого знания темы. Использовать вывод gemma3:4b как окончательный ревью — риск.

**GPU может не подключиться.** Ollama не всегда подхватывает дискретную видеокарту автоматически. Проверьте ollama ps — если видите 100% CPU при наличии GPU, нужна ручная настройка.

## Что попробовать дальше

Схема имеет смысл для строго механических задач без итераций: извлечение структурированных данных из текста, форматирование, классификация с заранее известными категориями, генерация commit message по diff. Там один вызов — один ответ, без polling.

Если хочется реально сократить расход — стоит смотреть в сторону промпт-кэширования внутри самого Codex и минимизации числа tool call в сессии, а не в сторону делегирования через MCP.

---

## FAQ

### Почему MCP-маршрут дороже прямого Codex?

Каждый вызов инструмента через MCP — это отдельный полный inference Codex со всей историей диалога. Вместо одного прохода получается минимум два: решение вызвать инструмент и обработка его ответа. На коротких задачах накладные расходы превышают любую экономию от локального inference.

### Можно ли заставить Ollama использовать GPU на Mac?

Ollama поддерживает Metal на Apple Silicon, но на Intel Mac с дискретной Radeon автоматического подключения GPU может не быть. Проверьте командой ollama ps — в колонке Processor должно быть GPU или GPU+CPU, а не 100% CPU.

```bash
ollama ps
```

### Для каких задач локальная модель через MCP всё-таки окупается?

Для однократных механических преобразований без итераций: извлечение портов из конфига, классификация ошибки по коду, генерация commit message. Главное условие — один вызов инструмента на задачу, без polling и без цепочек.

### Как разрешить вызов MCP-инструмента без диалога подтверждения?

В ~/.codex/config.toml нужно явно указать approval_mode = "approve" для конкретного инструмента. Без этого Codex заблокирует вызов с ошибкой approval policy is never.

### Влияет ли порядок MCP-серверов в конфиге на расход токенов?

Да. Codex CLI использует промпт-кэширование, которое чувствительно к изменениям конфигурации. Если поменять порядок инструментов или добавить новый сервер, кэш сбрасывается и стоимость inference растёт до следующего «прогрева».

## Источники

- Habr AI: Подключил локальную Gemma к Codex, чтобы экономить лимит. На простой задаче расход вырос вдвое
