Один package.json — 15 тысяч токенов: локальный LLM-агент на RX 580
Как я запустил локального LLM-агента на старом Xeon и RX 580, подключил его к Kilo CLI и увеличил рабочий контекст с 24k до 64k.
Я хотел проверить довольно простую идею: можно ли превратить старый компьютер с Xeon и RX 580 в полноценного локального coding-агента.
Не просто открыть чат с небольшой LLM и попросить её написать функцию, а дать модели доступ к проекту: читать файлы, выполнять команды, анализировать результат, вносить изменения и последовательно решать задачу в несколько шагов.
Самым неожиданным ограничением оказалась даже не RX 580.
На просьбу прочитать практически пустой однострочный package.json Kilo отправил модели 14 962 входных токена. Сам файл занимал ничтожную часть запроса — остальное съели системные инструкции, описание инструментов и окружение агента.
При окне в 16–24 тысячи токенов этого уже достаточно, чтобы простая работа с проектом довольно быстро закончилась компакцией или ошибкой контекста.
Так начался эксперимент, в котором пришлось разобраться сразу с несколькими уровнями: llama.cpp, видеопамятью RX 580, GGUF-моделями, модельным роутером, OpenCode, Kilo CLI и наконец контекстом в 65 536 токенов.
Спойлер: на моём железе всё это заработало. Но с некоторыми оговорками.
Что получилось в итоге
Конфигурация, на которой остановился эксперимент:
- CPU: Xeon E5-2640 v3
- GPU: RX 580 2048SP, 8 ГБ VRAM
- RAM: 16 ГБ
- Сервер:
llama.cppb11173 с Vulkan - Агент: Kilo CLI 7.7.9
- Основная модель: HauhauCS Qwen3.5 9B Q4_K_M
- API: OpenAI-совместимый сервер на
127.0.0.1:8080 - Контекст основной модели: 65 536 токенов
- KV-кэш:
q8_0 - Flash Attention: включён
- GPU offload: все слои модели
- Одновременно загруженных моделей: максимум одна
Самый длинный фактически проверенный запрос содержал 27 519 входных токенов.
Модель успешно прочитала большой тестовый файл и нашла уникальную контрольную метку в самом его конце.
Без компакции.
Холодная обработка такого запроса заняла около 17 минут, а короткий ответ после обработки генерировался примерно со скоростью 10–15 токенов в секунду.
Полные 64 тысячи токенов я не прогонял. Поэтому правильнее говорить не «RX 580 уверенно работает с 64k», а:
Конфигурация с окном 64k успешно загрузилась и позволила обработать как минимум 27,5k входных токенов там, где прежнее окно 20–24k уже заканчивалось.
Какие модели я использовал
Это не рейтинг моделей и не сравнение их качества. Ниже просто список профилей, которые появились у меня в llama.cpp во время эксперимента.
Размер указан для локального GGUF-файла в GiB.
У Gemma 4 дополнительно используется mmproj для изображений — его размер в таблицу не включён.
| Модель | Квантование и размер | Что проверено |
|---|---|---|
| HauhauCS Qwen3.5 9B | Q4_K_M · 5,24 GiB | Основная модель, тест на 27,5k входных токенов |
| HauhauCS Qwen3.5 4B | Q6_K · 3,23 GiB | Загрузка и короткий ответ |
| HauhauCS Qwen3 4B Instruct 2507 | Q4_K_M · 2,33 GiB | Короткий ответ; при 64k на GPU оставлено 30 слоёв |
| HauhauCS Qwen3.5 2B | Q4_K_M · 1,18 GiB | Загрузка и короткий ответ |
| Gemma 3n E4B | Q4_K_M · 3,95 GiB | Добавлена в каталог, длинный тест не проводился |
| Gemma 4 E2B | Q4_K_M · 3,19 GiB | Добавлена в каталог, длинный тест не проводился |
| Llama 3.2 3B Instruct | Q4_K_M · 1,88 GiB | Добавлена в каталог, длинный тест не проводился |
| Qwen3 4B Thinking 2507 | Q4_K_M · 2,33 GiB | Добавлена в каталог, длинный тест не проводился |
Контекст 64k я в итоге настроил для четырёх моделей HauhauCS. Остальным четырём профилям оставил 24 576 токенов.
Мне нужен был не локальный чат, а агент
Запустить локальную LLM и поговорить с ней относительно просто.
С coding-агентом всё интереснее.
Мне хотелось, чтобы модель могла:
- открыть файл проекта;
- понять его содержимое;
- выполнить команду;
- посмотреть результат;
- изменить другой файл;
- продолжить исходную задачу, не забыв предыдущие действия.
То есть выполнить нормальную последовательность действий, а не отвечать на каждый запрос как на независимое сообщение.
Для инференса я установил Windows-сборку llama.cpp с Vulkan. Для старой AMD-карты это оказался удобный вариант.
Каждую модель сначала проверял отдельно от агентов: загружается ли GGUF, отвечает ли сервер на простой запрос и сколько после загрузки остаётся памяти.
Это важный этап.
Если модель сама по себе не может нормально загрузиться в llama-server, подключение поверх неё OpenCode, Kilo или другого агента только добавляет ещё один уровень проблем.
Совет: сначала добейся стабильной работы модели через прямой API и только потом подключай агентный клиент.
Главная проблема RX 580 — не только размер GGUF
Основной GGUF с Qwen3.5 9B Q4_K_M занимает около 5,24 GiB.
На первый взгляд кажется, что в 8 ГБ VRAM он должен помещаться без особых проблем.
Но размер файла модели — далеко не вся память, которая понадобится во время работы.
Кроме весов модели видеопамять занимают:
- KV-кэш;
- вычислительные буферы;
- контекст;
- временные данные Vulkan.
Чем больше окно контекста, тем заметнее становится эта разница.
Поэтому все 8 ГБ RX 580 нельзя мысленно считать свободным местом под GGUF.
В итоге 9B Q4_K_M стала основной моделью, а более лёгкие 4B и 2B я оставил как запасные варианты.
От BAT-файлов к единому роутеру моделей
Сначала каждая модель запускалась отдельно.
Для тестирования это даже удобно: указал конкретный GGUF, запустил llama-server, задал вопрос и посмотрел результат.
Потом появились отдельные BAT-файлы.
Потом моделей стало много.
И в какой-то момент ручное переключение между серверами начало раздражать сильнее, чем сама настройка моделей.
У llama.cpp есть режим модельного роутера. В INI-файле можно описать несколько именованных профилей, а наружу предоставить один API.
Важно: это не означает, что RX 580 одновременно держит в памяти восемь LLM.
У меня используется:
| |
То есть одновременно загружена максимум одна модель.
Для переключения сервер предоставляет /models/load и /models/unload.
Фрагмент моего итогового models.ini выглядит примерно так:
| |
Пути здесь условные — вместо них нужно указать расположение своих GGUF.
n-gpu-layers = 99 в данном случае не означает наличие у модели 99 слоёв. Это способ попросить llama.cpp выгрузить на GPU максимально возможное количество слоёв.
Qwen3 4B Instruct с контекстом 64k полностью в видеопамять уже не помещалась, поэтому для неё я оставил:
| |
Запуск роутера:
| |
Проверить, какие модели видит сервер, можно без веб-интерфейса:
| |
Сначала проверяем API, потом агента
Прежде чем подключать очередной CLI, полезно убедиться, что OpenAI-совместимый API вообще отвечает нормально.
Для короткой проверки я использовал Qwen3 4B Instruct:
| |
Если /models не работает или /v1/chat/completions не возвращает нормальный ответ, искать проблему в агенте ещё рано.
Почему OpenCode мне не подошёл
Первым полноценным агентным клиентом в этом эксперименте стал OpenCode.
Несколько локальных моделей я подключил без особых проблем. Короткий диалог тоже работал.
Но короткий диалог вообще оказался плохим тестом для coding-агента.
Проблемы начинались, когда модель должна была прочитать файл, выполнить следующее действие и продолжить работу.
При окне 16–24k довольно быстро запускалась компакция. После неё OpenCode пытался снова прочитать файл, получал ошибку и опять возвращался к сжатию.
Получался цикл:
| |
Вместо продолжения работы агент фактически застревал.
Я пробовал менять конфигурацию, но стабильного сценария добиться не получилось.
Aider тоже попробовал, но ненадолго.
В итоге перешёл на Kilo CLI.
И уже там стало окончательно понятно: проблема не только в конкретном агенте. Окна в 16–24 тысячи токенов для такого сценария мне действительно мало.
Как я связал Kilo CLI с локальным llama.cpp
Kilo позволяет явно описывать локальные модели через OpenAI-совместимый provider.
Есть один важный нюанс.
То, что llama.cpp показывает восемь моделей через /models, ещё не означает, что Kilo автоматически предложит выбрать одну из них.
ID моделей нужно добавить в provider.openai-compatible.models внутри kilo.jsonc.
Это соответствует документации Kilo по кастомным моделям.
После этого я сделал небольшой лаунчер.
Он:
- получает список моделей от
llama.cpp; - показывает меню;
- предлагает выбрать модель;
- проверяет состояние роутера;
- выгружает предыдущую модель;
- загружает выбранную;
- ждёт статуса
loaded; - запускает Kilo с нужным ID.
То есть модель выбирается до начала сессии Kilo.
Пример:
| |
Основная часть PowerShell-лаунчера:
| |
Мой полный вариант дополнительно проверяет порт, умеет сам запускать llama-server и повторно использовать уже работающий одиночный запуск 9B.
Совет: если Kilo внезапно видит не тот конфиг, полезно проверить
kilo debug configиGet-Command kilo -All. Несколько установок CLI легко приводят к ситуации, когда редактируешь один конфиг, а запускается совершенно другой экземпляр.
Откуда взялись 15 тысяч токенов на одном package.json
После перехода на Kilo я вернулся к проблеме, с которой всё и началось.
В безопасном тесте я попросил агента прочитать однострочный package.json.
Первый запрос к 9B содержал:
14 962 входных токена.
Очевидно, дело было не в содержимом файла.
В запрос уже входили инструкции агента, описания доступных инструментов, информация об окружении и другие служебные данные.
В одной из проблемных сессий Kilo показывал:
| |
А затем:
| |
То есть окно 20–24k для моего сценария начинало заканчиваться ещё до серьёзной работы с проектом.
Именно в этот момент я решил попробовать 64k.
Почему я поднял контекст сразу до 64k
Можно было оставить 24k и просто чаще начинать новую сессию.
Для обычного чата это вполне нормальный вариант.
Для агента — уже хуже.
Он читает файлы, получает результаты команд, вызывает инструменты, сохраняет промежуточные данные. Сессия растёт значительно быстрее обычного диалога.
Поэтому я решил проверить, сможет ли текущая машина вообще поднять окно 65 536.
Решение было неочевидным.
На машине всего:
| |
А увеличение контекста означает дополнительную память под KV-кэш и сильно увеличивает время обработки длинного входа.
Лимит пришлось изменить сразу в двух местах.
В профиле llama.cpp:
| |
И в Kilo:
| |
Также увеличил таймаут до 30 минут:
| |
И включил раннюю компакцию на 80% окна с очисткой старых результатов инструментов.
Итоговый фрагмент kilo.jsonc:
| |
Документация Kilo отдельно описывает лимиты кастомных моделей и механизм context condensing.
Совет: прежде чем просто увеличивать окно, посмотри реальный размер первого запроса агента. В моём случае почти 15 тысяч токенов появлялись ещё до серьёзной работы с кодовой базой.
64k упёрлись в видеопамять
Разумеется, заменить 24576 на 65536 оказалось недостаточно.
После увеличения контекста 9B начала периодически отказываться загружаться.
Пришлось экспериментировать с Flash Attention, типом KV-кэша, размером batch и количеством слоёв на GPU.
| Попытка | Что произошло | Решение |
|---|---|---|
64k, Flash Attention выключен, K/V q8_0 | quantized V cache requires flash_attn to be enabled | Включил Flash Attention |
64k, Flash Attention выключен, только K q8_0, все слои на GPU | Vulkan выдал ErrorOutOfDeviceMemory при создании вычислительного буфера | Уменьшал GPU offload и batch |
64k, Flash Attention включён, K/V q8_0, 1024/256 | 9B успешно загрузилась целиком на GPU | Эту конфигурацию оставил для основной модели |
| Qwen3 4B Instruct, 64k, все слои на GPU | Снова закончилась память Vulkan | Оставил 30 слоёв и 512/128 |
Для 9B в итоге сработало:
| |
Сжатый KV-кэш и изменение размера пакетов позволяют заметно влиять на использование памяти.
Но это не универсальная конфигурация.
На другой видеокарте, другом драйвере или другой версии llama.cpp оптимальное сочетание параметров может оказаться совершенно другим.
Странный поток из слешей
На одном из простых вопросов 9B вместо нормального reasoning внезапно начала генерировать длинную последовательность:
| |
Сначала я заподозрил Flash Attention.
Временно выключил его, затем позже вернул вместе с новым профилем KV-кэша и изменёнными пакетами.
После перенастройки прямой запрос, tool calling и длинный тест снова работали нормально.
Но здесь важно не придумывать объяснение задним числом.
Точную причину этого эпизода я не установил.
Одного наблюдения недостаточно, чтобы утверждать, что проблема была именно во Flash Attention.
Совет: при таких сбоях меняй параметры по одному. После каждого изменения отдельно проверяй загрузку модели, прямой запрос к API и выполнение инструментов агентом.
Когда проблема вообще не в LLM
После всех экспериментов с контекстом всплыла гораздо более прозаичная проблема.
Агент попытался выполнить:
| |
Но окружением был Windows PowerShell 5.1.
Операторы && и || появились только в PowerShell 7.
Для PowerShell 5.1 команды пришлось разделить:
| |
В инструкции Kilo я явно указал, что используется Windows PowerShell 5.1 и нельзя применять && или ||.
После этого агент нормально выполнял команды последовательно. Например, отдельно вызывал:
| |
и больше не падал на синтаксисе оболочки.
Хорошее напоминание о том, что далеко не каждая ошибка «AI-агента» связана непосредственно с моделью.
Memory и Code Indexing не решили бы проблему
По ходу эксперимента возникли ещё две очевидные гипотезы.
Первая: контекст съедает Memory.
Проверка показала, что файлы Memory моего проекта занимали меньше 2 КБ. Они явно не могли объяснить стартовый запрос почти на 15 тысяч токенов.
Memory я оставил включённой.
Вторая идея — Code Indexing.
Code Indexing в Kilo действительно полезен: он позволяет искать релевантный код по смыслу вместо тупого чтения всего проекта.
Но ему нужны embedding-модель и векторное хранилище.
И главное — он не увеличивает контекстное окно самой LLM.
Поэтому воспринимать Code Indexing как лекарство от:
| |
не стоит.
Это другой инструмент для другой задачи.
Проверка: сможет ли агент реально выйти за прежние 24k
Оставалось проверить самое главное.
Не просто загрузится ли llama.cpp с параметром:
| |
а сможет ли модель реально обработать запрос, который раньше уже не помещался в окно.
Я создал большой файл с повторяющимися строками и добавил уникальную контрольную метку в самый конец:
| |
Затем попросил агента найти её.
В логе получилось:
| |
Ответ содержал правильную метку:
| |
Компакция во время этого запроса не запускалась.
То есть агент фактически обработал 27,5 тысячи входных токенов — больше прежнего рабочего потолка в 20–24k.
Работает. Но быстро ли?
Вот здесь начинается обратная сторона большого контекста на старом железе.
Холодная обработка тестовых 27,5 тысячи токенов заняла примерно:
17 минут.
После обработки входа короткий ответ генерировался со скоростью около:
10 токенов/с.
В других коротких запросах скорость могла находиться примерно в диапазоне 10–15 токенов/с.
И здесь важно не путать две вещи.
Скорость генерации следующих токенов — это не скорость первоначальной обработки огромного контекста.
Модель может довольно бодро печатать ответ после того, как закончила долгий prompt processing.
Поэтому результат эксперимента я бы сформулировал так:
RX 580 действительно может работать с локальным 9B coding-агентом и контекстом больше 24k, но длинные запросы на таком железе становятся дорогими прежде всего по времени.
Полностью забивать все 65 536 токенов я уже не стал.
После 17 минут на 27,5k примерно понятно, в какую сторону будет двигаться комфорт использования.
Что в итоге
Главный вывод эксперимента для меня оказался немного неожиданным.
Сам по себе запуск 9B GGUF на RX 580 — не самая сложная часть.
Настоящие проблемы начинаются, когда поверх модели появляется агент.
Он приносит с собой системные инструкции, tool calling, результаты выполнения команд, историю действий и собственную логику управления контекстом.
В моём случае даже просьба прочитать однострочный package.json превращалась почти в 15 тысяч входных токенов.
После перехода на Kilo, увеличения окна до 64k, настройки Flash Attention, KV-кэша и модельного роутера удалось получить вполне рабочую конфигурацию:
| |
Проверенный предел пока составляет 27 519 входных токенов.
Для ежедневной работы это всё ещё компромисс: облачные модели несравнимо быстрее и удобнее на больших проектах.
Но как эксперимент результат оказался гораздо интереснее, чем я ожидал.
Старый Xeon и RX 580 способны не только запустить локальную LLM, но и обслуживать полноценного агента, который читает проект, вызывает инструменты и сохраняет достаточно большой рабочий контекст.
А главное — теперь я хотя бы точно знаю, куда исчезают эти чертовы 15 тысяч токенов ещё до того, как агент толком начал работать.
