Как мы создали open-source кодового агента, работающего с любыми локальными моделями. Agent Loop.. Agent Loop. claude code.. Agent Loop. claude code. cli.. Agent Loop. claude code. cli. ollama.. Agent Loop. claude code. cli. ollama. Open source.. Agent Loop. claude code. cli. ollama. Open source. qwen.. Agent Loop. claude code. cli. ollama. Open source. qwen. vllm.. Agent Loop. claude code. cli. ollama. Open source. qwen. vllm. кодовый агент.. Agent Loop. claude code. cli. ollama. Open source. qwen. vllm. кодовый агент. локальные llm.

PocketCoder: история создания

Стыдно признаться, но я все больше и больше отдаю все на откуп LLM моделям, позволяя им глубже интегрироваться в мои задачи. Внутри постоянный страх, что навык уйдёт – забуду, как писать, как читать, как вообще понимать код. Но с другой стороны, все эти современные инструменты не оставляют другого выбора, как просто брать и интегрировать в свои проекты.

Три недели назад нашей команде из BVM пришёл коллективный счёт на Claude Code в размере $120. Честно говоря, мы платим за подписку уже год, не считая редких API-запросов, которые растворились на фоне такой большой для нас суммы денег.

По совпадению мне подвернулась новость о том, что вышла очередная китайская open-source модель GLM-4.7, смоделированная с фокусом на написание качественного кода. Еще и цены в несколько раз дешевле Anthropic. Точно надо пробовать!

Изображение 1. Новая версия GLM от z.ai

Изображение 1. Новая версия GLM от z.ai

Мы побежали искать какой-то CLI App, но вместо этого нашли обычный inference на их же сайте, где можно пообщаться с ботом ИИ, как с ChatGPT. Как будто это уже прошлый век, ведь так уже практически никто не пишет код, правда?

Так родилась мысль — а есть ли приложение наподобие Claude Code, способное запуститься именно с локальной моделью, если мы её развернём у себя на сервере?

Мы легко ответили на этот вопрос и нашли нескольких кандидатов на тест, но у нас появилась другая идея.

А что, если мы сами напишем своего Code-agent, который будет работать ± качественно с любой, даже самой маленькой моделью LLM? Да ещё и бесплатный, лишь бы люди помогали нам его тестить, а мы, в свою очередь, обязуемся выдавать стабильные апдейты? :)

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

Дисклеймер: Уже как пару месяцев Claude Code даёт возможность пользоваться своим фреймворком и соединять его с локально развёрнутой ollama. Подробнее можете прочитать тут, все очень просто – https://medium.com/data-science-in-your-pocket/run-claude-code-with-local-llms-using-ollama-a97d2c2f2bd1
Об этом мы узнали во время написания статьи, но это нисколько не заставило нас опустить руки, ведь дело не в том, кто первый, а в уникальности решения!


1. Где будет наш «Карманный кодер»?

Поскольку мы привыкли к механике запуска Claude Code в терминале, решили не изобретать велосипед и сделали простое и понятное по дизайну CLI-приложение.

Запускается он довольно просто. Эту фишку мы забрали у наших конкурентов, но о них чуть попозже:

pip install pocketcoder
pocketcoder
Как мы создали open-source кодового агента, работающего с любыми локальными моделями - 2

Ну и всё…

Изображение 2. Приветственное окно Pocketcoder

Изображение 2. Приветственное окно Pocketcoder

2. Структура агента

А вот тут начинается веселье. Навайбкодить дэшборд на HTML или сделать iOS-приложение не составляет труда в наше время. Но сделать с нуля агента, который будет эффективно работать с контекстом, да ещё и выдавать хороший результат, — не самая тривиальная задача :)

Поэтому я предлагаю вам познакомиться с понятием Agent Loop — сердцем любого кодового агента.


3. Что такое Agent Loop?

Agent Loop — это цикл, в котором живёт AI-агент. Отличие от обычного чатбота: чатбот отвечает на вопрос и забывает. Агент — думает, действует, смотрит на результат и решает, что делать дальше. Цикл повторяется, пока задача не выполнена.

Изображение 3. Схема работы Agent Loop LLM

Изображение 3. Схема работы Agent Loop LLM
User: "создай калькулятор"
    │
    ▼
┌─────────────────────────────────┐
│  THINK: Что нужно сделать?      │
│  → Создать файл calc.py         │
│  → Написать функции +,-,*,/     │
│  → Протестировать               │
└─────────────────────────────────┘
    │
    ▼
┌─────────────────────────────────┐
│  ACT: Выполняю действие         │
│  → write_file("calc.py", ...)   │
└─────────────────────────────────┘
    │
    ▼
┌─────────────────────────────────┐
│  OBSERVE: Смотрю результат      │
│  → Файл создан, 45 строк        │
│  → Вижу код, который написал    │
└─────────────────────────────────┘
    │
    ▼
┌─────────────────────────────────┐
│  DECIDE: Задача выполнена?      │
│  → Нет, ещё нужны тесты         │
│  → Повторяю цикл                │
└─────────────────────────────────┘
    │
    ▼
   ... (цикл продолжается)

Но дьявол в деталях. Разные агенты реализуют этот цикл по-разному. Давайте посмотрим, как это делают наши конкуренты и что мы взяли (или не взяли) у каждого из них.


4. Конкуренты: как устроены изнутри

4.1 AIDER

Изображение 4. Экран Aider

Изображение 4. Экран Aider

Aider — консольный инструмент для парного программирования с LLM. Работает в терминале, интегрируется с git.

Когда пользователь отправляет сообщение, Aider собирает контекст для LLM: системный промпт, файлы, добавленные в чат, историю диалога и RepoMap — карту структуры проекта. RepoMap строится через tree-sitter: парсер проходит по всем файлам, извлекает определения классов, функций, методов и ранжирует их через PageRank по связям между файлами. LLM видит скелет проекта — какие классы где определены, какие методы вызывают друг друга — без необходимости читать каждый файл целиком.

LLM в Aider не вызывает инструменты напрямую. Вместо этого модель генерирует обычный текст с маркерами SEARCH/REPLACE. Когда нужно изменить код, ответ выглядит так: блок SEARCH содержит старый код, который нужно найти, блок REPLACE — новый код на замену. Aider парсит текст ответа, находит эти блоки и применяет изменения к файлам. Такой подход работает с любой моделью, даже без поддержки function calling.

<<<<<<< SEARCH
def add(a, b):
    return a + b
=======
def add(a: int, b: int) -> int:
    """Add two numbers."""
    return a + b
>>>>>>> REPLACE
Изображение 5. Схема Agent Loop у Aider

Изображение 5. Схема Agent Loop у Aider

Aider адаптирует формат под возможности модели. Сильные модели вроде GPT-5.2 или Claude получают компактный diff-формат. Модели послабее работают с SEARCH/REPLACE блоками. Совсем слабые локальные модели перезаписывают файл целиком — им сложно точно указать, какой именно фрагмент менять.

После применения изменений Aider автоматически создаёт git commit и запускает линтер. Если линтер находит ошибки, они отправляются обратно в LLM для исправления — до трёх попыток.

Что мы взяли: RepoMap — карту проекта. Очень важная технология, поскольку это, своего рода, навигатор для любой ИИшки. Без этого модель в следующем запросе просто начнет забивать файлы в ненужную директорию.

Что не взяли: После записи файла LLM получает сообщение об успехе, но не видит содержимое того, что записал. Он не может проверить свою работу — только надеяться, что сделал правильно. Мы это исправили.


4.2 CLINE

Изображение 6. Расширение Cline к VS Code

Изображение 6. Расширение Cline к VS Code

Cline — это расширение для VS Code. Интегрируется с редактором, видит открытые файлы, может показать diff визуально.

В отличие от Aider, Cline использует нативные tool calls. LLM возвращает не текст с маркерами, а структурированный объект: какой инструмент вызвать, с какими параметрами. API возвращает готовую структуру — не нужно парсить текст и надеяться, что модель не ошиблась в формате.

Изображение 7. Схема Agent Loop у Cline

Изображение 7. Схема Agent Loop у Cline

Главный цикл Cline — метод recursivelyMakeClineRequests. Он отправляет сообщение в LLM, получает ответ со списком tool calls, выполняет их по очереди, собирает результаты, и если задача не завершена — отправляет следующий запрос.

Перед каждым опасным действием — записью файла, выполнением команды — Cline спрашивает разрешения. Появляется диалог: «Cline хочет записать файл main.py. Разрешить?» Это называется human-in-the-loop — человек остаётся в контуре принятия решений.

┌─────────────────────────────────────┐
│  Cline хочет записать main.py       │
│                                     │
│  [Разрешить]  [Отклонить]  [Всегда] │
└─────────────────────────────────────┘

Cline поддерживает MCP — Model Context Protocol. Это стандарт подключения внешних инструментов, придуманный Anthropic.

Что мы взяли: Checkpoints — возможность откатиться к предыдущему состоянию.

Что не взяли: Память между сессиями не сохраняется. Закрыл VS Code — контекст потерян, завтра начинаешь с нуля. Мы это исправили. О системе памяти поговорим позже — реализовали собственную XML структуру.


4.3 OPENCODE

Изображение 8. Экран OpenCode

Изображение 8. Экран OpenCode

OpenCode — CLI-агент нового поколения, построенный на TypeScript с использованием Bun. Сейчас это, пожалуй, самый популярный открытый проект в категории терминальных AI-ассистентов.

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

Агент

Роль

Права

build

Основной для разработки

Все инструменты

explore

Исследование кодовой базы

Только read: grep, glob, read

plan

Планирование

Только текст + планы

general

Субагент для параллельных задач

По контексту

Изображение 9. Схема Agent Loop у OpenCode

Изображение 9. Схема Agent Loop у OpenCode

Агенты в OpenCode — не просто разные промпты. Каждый агент имеет свой набор permissions, определённый через систему правил. Например, explore-агент не может писать файлы, даже если очень захочет — permission denied на уровне системы.

OpenCode использует LSP — Language Server Protocol. Агент может вызвать goToDefinition, findReferences, hover прямо из своего контекста. Это даёт возможности, которых нет у других: понять, что такое функция X, найти все места, где она используется.

Task tool позволяет запускать субагентов параллельно. Основной агент говорит «исследуй authentication, исследуй database, исследуй API» — и три explore-агента бегут параллельно.

Что мы взяли: Идею специализации. Разные режимы для разных задач. Не скажу, что под капотом она активно реализована, но в промты усилить пришлось.

Что не взяли: Всё слишком сложно. Для работы с локальными моделями нужен простой, предсказуемый flow.


4.4 KILOCODE

Как мы создали open-source кодового агента, работающего с любыми локальными моделями - 11

KiloCode — форк Cline с существенными улучшениями. Работает как расширение VS Code.

Главная фишка — система режимов (Modes). Можно переключаться между: code для написания кода, architect для проектирования, ask для вопросов без изменений, debug для отладки.

┌──────────────────────────────────────┐
│  MODE: architect                     │
│  ────────────────                    │
│  Доступно: read, search, plan        │
│  Недоступно: write, execute          │
└──────────────────────────────────────┘
Схема Agent Loop у KILOCODE

Схема Agent Loop у KILOCODE

SwitchModeTool — агент может сам переключать режимы. Если понял, что нужно не кодить, а проектировать — переключится на architect.

ToolRepetitionDetector — защита от зацикливания. Если агент вызывает один и тот же tool с теми же параметрами несколько раз подряд — система прервёт цикл.

NewTaskTool — агент может создавать подзадачи. Основная задача порождает дочерние, каждая со своим контекстом.

Что мы взяли: TodoList как first-class citizen. Агент управляет планом, отмечает выполненное.

Что не взяли: XDual protocol (поддержка и XML, и Native API одновременно) — мы оставили только XML. Один формат для всех моделей — проще поддерживать.


5. Сравнительная таблица

Aider

Cline

OpenCode

KiloCode

PocketCoder

Где работает

Терминал

VS Code

Терминал

VS Code

Терминал

Tool calls

SEARCH/REPLACE

Native API

Native API

XML + Native

XML-теги

Мультиагенты

Нет

Нет

4 агента

Modes

Нет

LSP

Нет

Нет

Да

Нет

Нет

LLM видит результат

Нет

Частично

С truncation

Частично

Да, полностью

Карта проекта

RepoMap

Нет

Нет

Нет

RepoMap (gearbox)

Память между сессиями

Нет

Нет

Частично

Task history

SQLite episodic

State validation

Нет

Нет

Permissions

Repetition

State machine


6. PocketCoder: что мы сделали по-другому

PocketCoder — консольный инструмент, как OpenCode и Aider, но с другой философией.

Задумка была в том, чтобы поймать баланс между длиной контекстного запроса и уровнем качества, выдаваемого ИИшкой. После множества итераций и миллиарда сгоревших токенов на локальных моделях было принято решение создать скелет XML, который должен работать как торт «Наполеон» (уж извините за такую аналогию) — каждая разговорная сессия, от первого запроса до второго, является уникальной выжимкой summary с интересным форматом. Как это работает?

Изображение 10. Схема Agent Loop у Pocketcoder

Изображение 10. Схема Agent Loop у Pocketcoder

6.1 SESSION_CONTEXT: полная структура

Вот пример структуры XML:

<SESSION_CONTEXT>

  <!-- 1. TASK: Цель сессии (формируется один раз при первом запросе) -->
  <task>
    <summary>Создать калькулятор с операциями +, -, *, /</summary>
    <type>CREATE</type>           <!-- CREATE | EDIT | DELETE | RUN | EXPLORE | MULTI -->
    <artifact>calculator</artifact>
    <stack>Python</stack>
    <constraints>без внешних библиотек</constraints>
  </task>

  <!-- 2. PROJECT: Идентификация проекта (определяется автоматически) -->
  <project>
    <name>my_project</name>
    <path>/Users/dmitrij/projects/my_project</path>
    <entry_point>main.py</entry_point>  <!-- определяется по наличию main.py, app.py, index.py -->
  </project>

  <!-- 3. REPO_MAP: Карта проекта (пересоздаётся при изменении файлов) -->
  <repo_map gear="2" files="23">
    core/
      coder.py: class Coder
        def run() → str
        def _execute_tool() → ToolResult
      parser.py: def parse_response() → ParsedResponse
    tools/
      files.py: read_file, write_file, list_files
      agent.py: add_todo, mark_done, attempt_completion
  </repo_map>

  <!-- 4. CONVERSATION_HISTORY: История запросов -->
  <conversation_history>
    <episode id="1" timestamp="2025-01-28T10:30:00">
      <request>создай структуру проекта</request>
      <outcome>created 5 files: main.py, utils.py, config.py, tests/, README.md</outcome>
    </episode>
    <episode id="2" timestamp="2025-01-28T10:35:00">
      <request>добавь функцию сложения</request>
      <outcome>modified main.py: added add() function</outcome>
    </episode>
    <episode id="3" timestamp="2025-01-28T10:40:00">
      <request>запусти тесты</request>
      <outcome>tests passed: 3/3</outcome>
    </episode>
  </conversation_history>

  <!-- 5. FILES: Какие файлы трогали (обновляется после каждого read/write) -->
  <files>
    <modified>
      <file path="main.py" status="created" lines="45">
        def add(a, b): return a + b
        def sub(a, b): return a - b
        ...
      </file>
      <file path="utils.py" status="modified" lines="30">
        Helper functions for validation
      </file>
    </modified>
    <read>
      <file path="config.py">Configuration settings</file>
      <file path="requirements.txt">flask, requests, pytest</file>
    </read>
    <mentioned>
      <file path="TODO.md">mentioned in conversation</file>
    </mentioned>
  </files>

  <!-- 6. TERMINAL: История команд (последние 10, обновляется после execute_command) -->
  <terminal>
    <cmd exit="0">pip install pytest</cmd>
    <cmd exit="0">pytest tests/</cmd>
    <output>3 passed in 0.15s</output>
    <cmd exit="1">
      <command>python main.py</command>
      <error>ModuleNotFoundError: No module named 'utils'</error>
    </cmd>
  </terminal>

  <!-- 7. DECISIONS: Решения и ограничения (опционально, через отдельный LLM-вызов) -->
  <decisions>
    <constraint>без внешних библиотек</constraint>
    <constraint>Python 3.10+</constraint>
    <decision>используем dataclasses</decision>
    <decision>SQLite для хранения</decision>
    <qa q="нужен ли Docker?" a="нет, только локально"/>
  </decisions>

  <!-- 8. KNOWLEDGE_BASE: Документация проекта (определяется по наличию файлов) -->
  <knowledge_base>
    <doc path="README.md" exists="true"/>
    <doc path="docs/API.md" exists="false"/>
    <reference url="https://docs.python.org">Python docs</reference>
  </knowledge_base>

  <!-- 9. TODO: План задач -->
  <todo>
    <item status="done">Создать структуру проекта</item>
    <item status="done">Добавить функцию сложения</item>
    <item status="current">Добавить тесты</item>
    <item status="pending">Написать README</item>
  </todo>

  <!-- 10. CURRENT: Текущее состояние (обновляется после КАЖДОГО tool-вызова) -->
  <current>
    <last_action>write_file: main.py</last_action>
    <last_result>SUCCESS</last_result>
    <last_file type="python" runnable="true">main.py</last_file>
    <pending_task>Добавить тесты</pending_task>
  </current>

  <!-- 11. RELEVANT_HISTORY: Релевантная история (auto-grep по ключевым словам) -->
  <relevant_history query="calculator add" matches="2">
    <match timestamp="2025-01-27T15:20:00">
      <user>как сделать калькулятор?</user>
      <assistant>Создай файл calc.py с функциями add, sub...</assistant>
    </match>
  </relevant_history>

</SESSION_CONTEXT>

6.2 Как формируется каждый блок

<task> — формируется один раз при первом запросе. Когда ты пишешь «создай калькулятор», система отправляет это в LLM и просит вытащить суть: что делаем, какой тип задачи, на каком стеке, есть ли ограничения. Сохраняется и больше не меняется до сброса сессии. Если LLM недоступен — работают эвристики по ключевым словам.

<project> — определяется автоматом при запуске. Берёт имя текущей папки, абсолютный путь и ищет точку входа (main.py, app.py, index.py). Ты ничего не делаешь — система сама понимает, где находится.

<repo_map> — карта проекта. Сканирует все файлы и строит структуру. На маленьком проекте покажет полные сигнатуры классов и методов. На среднем — структуру папок и ключевые функции. На большом — только папки и точки входа, чтобы не забить весь контекст. Кэшируется, пересобирается, когда файлы меняются.

<conversation_history> — летопись запросов. Каждый твой запрос = одна запись: что спросил, что получилось, какие файлы создали. Показывает последние 20. Можно искать по ключевым словам — «что я делал с калькулятором?».

<files> — следит, какие файлы трогали. Три кучки: создали/изменили, прочитали, упомянули в разговоре. Обновляется автоматом после каждого read_file или write_file. Хранит путь, статус, сколько строк, превью содержимого.

<terminal> — история команд. Последние 20 штук с кодом выхода. Если команда упала — сохраняет stderr. Дубликаты подряд игнорирует, чтобы не засорять.

<decisions> — решения и ограничения. Опциональный блок, формируется отдельным LLM-вызовом. Вытаскивает из разговора: какие ограничения ставил юзер, какие решения приняли, какие вопросы обсуждали. Обычно вызывается при завершении сессии.

<knowledge_base> — что есть в проекте из документации. Проверяет наличие README.md, API.md, docs/. Показывает, что есть, чего нет.

<todo> — план с валидацией. Не просто список, а машина состояний. Когда LLM говорит «задача выполнена» — система проверяет: а файл реально появился? Если нет — ошибка. Ловит галлюцинации, когда модель говорит «готово», хотя ничего не сделала.

<current> — что только что произошло. Обновляется после каждого действия автоматом. Последняя операция, результат, какой файл трогали, можно ли его запустить. Нужно, чтобы работали фолловапы типа «запусти» — система знает, что запускать.

<relevant_history> — поиск по прошлому. Вытаскивает ключевые слова из текущего запроса и грепает по истории. Нашёл похожее — покажет контекст. Помогает, когда возвращаешься к старой задаче.


7. Хранение: .pocketcoder/ как .git/

Вся магия живёт в папке .pocketcoder/ — она создаётся в корне проекта, как .git/. Идею с ней взяли у Claude Code, хоть там и немного (или совсем) другая логика закладывается. Обычно её там используют под относительно новый Skills, но я её переосмыслил немного :) Лучше всего в ней как раз таки вести документацию, и Claude лучше её распознаёт, куда ему обращаться в случае чего, если ему не хватает данных. Проверено на собственном опыте. Вот и у меня пришла мысль: почему бы лучшие практики работы с код-агентом не внедрить уже в наш продукт?

Вот как эта махина выглядит:

.pocketcoder/                      # Создаётся в корне проекта (как .git/)
  │
  ├── project_context.json           # Состояние сессии
  │   ├── task: {summary, type, artifact, stack, constraints}
  │   ├── identity: {name, path, entry_point}
  │   ├── files: {modified: {...}, read: {...}, mentioned: {...}}
  │   ├── terminal: [{command, exit_code, error, output, timestamp}, ...]
  │   ├── todo: [{text, status, created_at, completed_at}, ...]
  │   ├── current: {last_action, last_result, last_file, pending_task}
  │   └── session_id: "20250128_103000_a1b2c3d4"
  │
  ├── episodes.jsonl                 # История запросов (append-only, как git log)
  │   line 1: {"id": 1, "user_input": "создай калькулятор", "outcome": "Created calc.py", "files_created": ["calc.py"], ...}
  │   line 2: {"id": 2, "user_input": "добавь тесты", "outcome": "Created test_calc.py", ...}
  │   line 3: {"id": 3, "user_input": "запусти", "outcome": "Tests passed: 3/3", "commands_run": ["pytest"], ...}
  │
  ├── memory.json                    # Факты (долгосрочная память)
  │   ├── version: "1.0"
  │   ├── facts: {
  │   │     "project_structure": {"value": "list_files .", "fact_type": "pointer", "category": "project"},
  │   │     "working_project": {"value": "/projects/calc", "fact_type": "value", "category": "context"},
  │   │     "last_created_file": {"value": "calc.py", "fact_type": "value", "category": "context"},
  │   │     "file_read:config.py": {"value": "Configuration with DB settings", "fact_type": "value", "ttl_days": 1},
  │   │     "docs_api": {"value": "https://api.example.com|API documentation", "fact_type": "ref"}
  │   │   }
  │   └── meta: {created, last_cleanup, requests_since_cleanup}
  │
  ├── current_session                # ID текущей сессии (plain text)
  │   → "20250128_103000_a1b2c3d4"
  │
  └── sessions/                      # Архив сессий
      └── 20250128_103000_a1b2c3d4/
          ├── raw.jsonl              # Сырые сообщения (для grep)
          │   line 1: {"role": "user", "content": "создай калькулятор", "timestamp": "..."}
          │   line 2: {"role": "assistant", "content": "Создаю файл calc.py...", "timestamp": "..."}
          │
          └── summary.json           # Итог сессии (генерируется при /quit или /session save)
              ├── task: {...}
              ├── files: {...}
              ├── decisions: {constraints: [...], decisions: [...], qa: [...]}
              └── todo_final: [...]

project_context.json — это мозг сессии. Тут лежит всё: какую задачу решаем, какие файлы трогали, что запускали в терминале, план работ. Когда ты закрываешь терминал и открываешь снова — оно загружается обратно. Поэтому можно продолжить с того места.

episodes.jsonl — история запросов. Каждая твоя фраза = одна строка. Append-only, как git log. «Создай калькулятор» → записали, что спросил, что сделали, какие файлы создали. Следующий запрос — следующая строка. Потом можно искать: «Что я делал с калькулятором?» — грепнули файл, нашли.

memory.json — долгосрочная память. Факты трёх типов. POINTER — команда, чтобы узнать данные («хочешь структуру проекта? выполни list_files .»). VALUE — конкретное значение («последний созданный файл = main.py»). REF — ссылка с описанием. У фактов есть TTL: контекстные живут день, проектные — месяц, явно запомненные — вечно.

sessions/ — архив. Каждая сессия в своей папке. Внутри raw.jsonl с сырыми сообщениями (для grep) и summary.json с итогом.

Ух, надеюсь не намудрили…


8. Live Demo: «Создай калькулятор»

Давайте посмотрим, как всё работает на реальном примере. Но перед тем как начнём, всё же договоримся на берегу — наши амбиции крутить мелкими и не очень моделями в нужное направление — это очень хорошо. Но будем реалистами: ИИшки на 7b и меньше всё же будут выдавать бред или иногда не вызывать нужные нам инструменты. Думаю, что начиная с моделей 20b качество может сильно поменяться, а если ещё взять reasoning-истории или же MoE, то так вообще, качество и коммуникация с агентом взлетит до небес!

Чтож, начнем.
Первым делом выбираем метод работы с моделями. Я выберу Ollama, ведь все из-за нее и затевалось в конце концов. Но тесты также проводились и на облачной ChatGPT nano. Да, мы добавили модуль vLLM и LM Studio — бек автоматом ищет запущенные процессы от этих фреймов и предлагает их прикрепить в файл .config для запросов в ИИ. Но основное тестирование все же было на Ollama, так что там могут быть баги.

Изображение 11. Выбор модели перед началом работы

Изображение 11. Выбор модели перед началом работы
Изображение 12. Выбрали Ollama - подтягиваются все доступные модели

Изображение 12. Выбрали Ollama – подтягиваются все доступные модели

Отлично, модель выбрали. В качестве теста развернули qwen2.5-coder-7b. Ошеломляющие результаты не ожидаем, но давайте работать с тем что есть и на что хватает денег :)

Нажав на /, мы видим доступные нам команды. Сори, но мы первой командой добавили /donate, вдруг кто-то захочет нас поддержать :)

Изображение 13. Пример всплывающих команд.

Изображение 13. Пример всплывающих команд.

Пора делать первый запрос – будем делать калькулятор, а то попросить сделать hello_world совсем уж как-то тривиально.

Изображение 14. Первый запрос в Pocketcoder (с богом)

Изображение 14. Первый запрос в Pocketcoder (с богом)

Первый запрос формирует ToDo лист, вместе с самым первым summary в XML формате. Если модели что-то непонятно или ей кажется, что запрос слишком обобщенный (как например этот), LLM задает наводящие вопросы.

Изображение 15. Мыслительный процесс нашего агент-кодера

Изображение 15. Мыслительный процесс нашего агент-кодера

Заранее говоря, , я очень хочу услышать ваш фидбек — стоит ли скрывать эту XML-схему от пользователей или нет. Честно говоря, session_context должен был показываться только в режиме --debug, но я просто забыл это скрыть :) Исправить легко, но хочу сначала узнать ваше мнение.

А вот и первые мыслительные процессы и вопросы от ИИшки, как и сказал выше. Поможем ей разобраться, что нужно сделать.

Изображение 16. Thinking мод у ИИшки и наводящие вопросы по задаче

Изображение 16. Thinking мод у ИИшки и наводящие вопросы по задаче

Шустрая qwen-coder-7b уже сообразила на целый файл. Одобряем ее выбор.

Изображение 17. Инструмент write_file для создания скрипта калькулятора

Изображение 17. Инструмент write_file для создания скрипта калькулятора
Изображение 18. Инструмент write_file: одобрение записи файла

Изображение 18. Инструмент write_file: одобрение записи файла

Задача на этом не заканчивается, ведь мы только написали первый файл. Не то, что мы запрашивали полноценный проект создавать с разветвленной модульной логикой, но все же есть одна фишка, которую мы постарались внедрить, взяв у Claude. Модель обязательно проверит синтаксис и работоспособность скрипта, написав его. Параллельно проверит, реально ли задача выполнилась и если да, то что делаем дальше. Самоличный reasoning, если проще.

Изображение 19. Осмысление сделанной задачи и добавление новых задач, если необходимо

Изображение 19. Осмысление сделанной задачи и добавление новых задач, если необходимо

Видим, что модель хочет добавить еще модули базовой арифметики в наш скрипт. Кстати да, он сразу начал создавать именно приложение в Tkinter на Python, не спросив нас, но кто мы такие для 7b модели, чтобы спрашивать? Дай бог, выдаст нормальный результат)

Собственно, сейчас и увидим главный минус работы с маленькими моделями с любыми агентами, особенно кодовыми. Вместо вызова инструмента apply_diff — редактирование текущего файла и удаление/добавление новых строк — он перезаписывает текущий файл, вызывая write_file. Мы долго боролись с базовым промтом, чтобы такого не происходило, но решили не пилить костыли и оставили как есть. Проверьте, на моделях >=20b такой проблемы уже нет.

Изображение 20. Вызов write_file вместо apply_diff на модели qwen-7b-coder

Изображение 20. Вызов write_file вместо apply_diff на модели qwen-7b-coder

Проблема номер 2: чет qwen2.5-7b:coder не хочет заканчивать задачу и создает снова новый файл, хотя буквально 5 секунд назад он уже все выполнил. Удобно будет показать, что мы на такой случай приготовили.

Да, в общем-то, ничего особенного. Просто нажимаешь на n и тебе выводятся 4 варианта ответа на выбор, чтобы пояснить модели, что с его решением не так. Я сразу нажал на 4 чтобы остановить цикл задачи.

Изображение 21. Отменяем решение записи файла и закрывает цикл выполнения

Изображение 21. Отменяем решение записи файла и закрывает цикл выполнения

Давайте укажем ему, что он уже создал все необходимое и надо редактировать текущий файл, а не переписывать существующий. Вроде ничего страшного, но что, если этот файл не на 50 строк, а на 5к+?

Заодно давайте его попросим запустить наш калькулятор! Хочется уже увидеть результат его творений.

Изображение 22. Просим ИИ запустить программу

Изображение 22. Просим ИИ запустить программу

В этот момент формируется второй подблок XML файла со всем необходимым – саммари, прошлые сессии и сырые запросы. Но тоже в своих пределах – внутри стоит 3 уровня ограничения вывода контекста в XML, от всего файла/запроса до его основных частей и функций.

Изображение 23. ИИ просит запустить калькулятор (cmd execute tool)

Изображение 23. ИИ просит запустить калькулятор (cmd execute tool)
Изображение 24. Наш первый MVP калькулятора

Изображение 24. Наш первый MVP калькулятора

Ухты, неплохо для первой итерации! Сделали сразу калькулятор с клавишами ввода чисел и базовой арифметики. Если честно, даже я не ожидал, что на демо так быстро все заработает и сформируется :)

После запуска, он решил добавить еще функций на расчет арифметики, перезаписывая сразу весь файл. Ничего не могу с ним поделать, но давайте соглашаться.

Изображение 25. Предыдущая проблема вызова write_file вместо apply_diff на малых моделях

Изображение 25. Предыдущая проблема вызова write_file вместо apply_diff на малых моделях

Наверное, хватит его мучать и пора выдать в production, чтобы тесты проводили уже вы….


9. Выводы и куда хотим пойти дальше?

Что ж, пока на этом всё. Мне не жалко потраченного времени, хотя прекрасно понимаю, что люди, кому будет интересно это протестировать, получат достаточное количество багов. Но с другой стороны — разве бывают идеально вылизанные продукты, особенно на момент релиза? Вспомним тот же Cyberpunk, который только спустя два года стал полноценно стабильной игрой. Да тот же Claude Code до сих пор глючит, когда работаешь с длинным контекстом.

Но вообще, я не это хотел сказать. Этим продуктом буду пользоваться лично я. Уж очень мне нравится вся эта тема с кодовыми агентами. А PocketCoder ещё и подстёгивает запускать локально разные модели — новые и старые — и смотреть, кто на что способен. Мы слишком зациклились на облачных ChatGPT и Deepseek, но в мире выходят новые модели буквально каждый час!

Давайте не будем отставать от всего мира и активно внедрять различные решения на таких готовых фреймворках, как наш PocketCoder!

Наши планы:

  • WebSearch – ну тут база

  • MCP-интеграция – Пока не знаю, как это внедрять внутрь контекста, но без этого уже никуда

  • Больше облачных провайдеров (например, только 1% населения Земли сможет запустить у себя GLM 4.7, это тоже надо понимать :)) . Правда, есть :cloud режим в Ollama.

  • Web UI (но это не точно, и пока не вижу перспектив)


PocketCoder — это про open source.

GitHub: github.com/Chashchin-Dmitry/pocketcoder
PyPI: pypi.org/project/pocketcoder


Буду рад за лайк и подписку на канал :) https://t.me/notes_from_cto

Наш сайт: https://bvmax.ru/ai


Автор: Dmitrii-Chashchin

Источник

Rambler's Top100