От одного агента с 12 tools к четырём узким — и всё заработало

Gemma 12B с 16K контекстом не справлялась с 12 инструментами одновременно. Разбили на 4 агента через CrewAI — каждый со своими 2-3 tools. Попутно почистили 40% мёртвого кода, запустили Obsidian headless и настроили авто-постинг заметок в Telegram-канал.

Проблема

CrewAI Bot (Deputy Secretary + Garden Manager) работал, но:

  • Garden Manager — один агент с 12 tools в 16K контексте. Gemma 12B путалась, вызывала не те tools, зависала
  • 40% кода мёртво_voice.py, garden_manager agent, create_garden_crew(), 9 SQLite таблиц, ~30 CRUD-функций, ChromaDB global collection
  • Obsidian CLI не работал — Obsidian не запущен на сервере, все garden tools возвращали ошибку
  • Deputy отвечал на каждое сообщение отдельно — батчинг 2 секунды, человек не успевал дописать
  • Дублирование — 2 функции publish, 2 функции search, 2 функции _build_and_deploy, 2 системы global_memory

Решение

4-агентный Garden Crew

Вместо 1 агента с 12 tools — 4 узких через CrewAI sequential process:

АгентToolsmax_iterЗадача
Garden Router01Читает сообщение, определяет intent
Garden Searcher33search, list, show
Garden Writer55create_draft, add_to_draft, show_draft, edit_note, cancel_draft
Garden Publisher33publish_draft, delete_note, update_links

Router → специалист. Каждый агент видит только свои tools — 16K контекста хватает с запасом.

Почему не direct vLLM

Прямой tool-calling через OpenAI API быстрее, но CrewAI даёт оркестрацию. Router определяет intent, передаёт нужному агенту. Скорость не критична — Matrix DM не требует мгновенного ответа. Зато 12B справляется с малым контекстом.

Obsidian headless через systemd

[Service]
Type=simple
Environment=DISPLAY=:99
ExecStart=/home/orca/.local/share/obsidian/app/obsidian --no-sandbox /home/orca/digital-garden
Restart=always

Xvfb на display :99, Obsidian без GUI, CLI socket работает. Все garden_tools (search, list, show, create, edit, publish) функциональны.

Батчинг Deputy: 30 секунд

_BATCH_WAIT = 30.0

Deputy ждёт 30 секунд тишины после последнего сообщения. Все сообщения за это время объединяются в один батч → один ответ. После ответа — ещё 30 секунд ожидания перед следующим.

Сценарий

  1. Человек шлёт “привет” → буфер: [привет]
  2. Через 5 сек: “как дела?” → буфер: [привет, как дела?]
  3. Через 10 сек: “что делаешь?” → буфер: [привет, как дела?, что делаешь?]
  4. 30 секунд тишины → один ответ на всё

Авто-постинг заметок в канал

garden_poster.py — фоновый поток, 3 раза в день (09:00, 15:00, 21:00):

  1. Берёт случайную заметку из сада (без index.md, drafts/)
  2. vLLM генерирует тизер (1-2 предложения на русском)
  3. Постит в Telegram-канал: заголовок + тизер + ссылка на notes.mrgnl.ru
  4. Запоминает последние 10 заметок — без повторов

Чистка мёртвого кода

УдаленоЧто
agents/_voice.pyВесь файл — voice listener никогда не активировался
garden_manager agent12 tools, мёртвый — заменён на Garden Crew
create_garden_crew()Мёртвая фабрика
ChromaDB global collectionСоздавалась, не использовалась
26 CRUD-функцийstate.py — определены, не вызывались
7 SQLite таблицschema оставлена для совместимости

Архитектура после рефакторинга

crewai-bot.service (один процесс)
├── Telegram Deputy Secretary (CrewAI, 1 агент)
│   ├── Батчинг 30 сек — один ответ на все сообщения
│   ├── ChromaDB: per_user, conversations, garden_notes
│   ├── radio_poster — авто-постинг радио в канал
│   └── garden_poster — авто-постинг заметок 3x/день
├── Matrix Garden Manager (CrewAI, 4 агента)
│   ├── Router → Searcher/Writer/Publisher
│   └── garden_pipeline — drafts/ → classify → format → link → publish
└── Obsidian headless (systemd, Xvfb :99)
    └── obsidian-cli socket → garden_tools

Связанные статьи