Формат: туториал + личный опыт. Сложность: средняя. Время чтения: ~9 минут.
Это третья и финальная часть истории про моего личного ИИ-агента. Предыдущие – «Как я переезжал ИИ-агента с OpenClaw на Hermes и собрал все грабли» и «DeepSeek, бесплатная NVIDIA и Codex: как я научил агента думать дёшево и работать, пока я сплю». Сквозную нумерацию граблей продолжаю с того места, где остановился, – с №26. Читать предыдущие не обязательно: рекап будет. Больше частей не планирую – на этой я историю закругляю.
Быстрый рекап, чтобы ты не бегал по прошлым статьям. У меня есть личный ИИ-ассистент – агент на основе Hermes Agent (открытый фреймворк от Nous Research), зовут его Паспарту. Живёт на сервере, отвечает в Telegram, помнит проекты, ходит в интернет, шлёт письма, ведёт календарь. В первой статье я переехал на Hermes с OpenClaw. Во второй – пересадил ассистента на дешёвый DeepSeek и приделал Codex, чтобы сложные задачи доводились до конца сами, без моего участия.
Что в итоге вышло – личный ИИ-ассистент на Hermes, который прямо у тебя на компьютере или на сервере умеет:
-
💬 общаться и помогать – мозг на DeepSeek, умно и дёшево;
-
🧠 помнить – банк памяти с поиском, ничего не теряет между сессиями;
-
🔎 искать в интернете – свежие факты, цены, новости;
-
🖼️ работать с картинками – генерирует и распознаёт: рисует с нуля и понимает, что на присланном фото (с ключом OpenAI);
-
🎙️ понимать голос – расшифровывает голосовые прямо на месте, локально;
-
📄 делать документы – DOCX, PDF и презентации
.pptxсразу на Google Диск; -
📬 жить в Google – почта, календарь, диск, документы, таблицы;
-
✅ вести задачи – трекер с напоминаниями;
-
🎨 красиво отвечать – таблицы, чек-листы, кнопки по-русски, живой стрим.
В прошлых двух частях я вёл рассказ длинной дорогой: как переезжал, как чинил, как учил думать дёшево. В этой я впервые не буду прятать ссылку в конец. Начну прямо с готовой коробки – вот агент, которого можно скачать и поставить одной командой. А сразу за коробкой – короткая история про мышцы, которые я ему докачал, и, что интереснее, свежая пачка граблей, которая вылезла уже после того, как я выложил всё на GitHub. Куда же без них.
Готовое решение из коробки: где взять и как поставить
Паспарту – личный ИИ-ассистент на основе Hermes Agent – живёт в Docker на твоей машине (домашний компьютер или свой VPS – где тебе удобно): ключи и данные остаются у тебя, а общение – обычной перепиской с ботом в Telegram. Полный список возможностей – выше; всё лишнее выключается тумблерами (нет ключа OpenAI (картинки и распознавание) или Perplexity (платный поиск) – эти функции просто отключатся, без ошибок и истерик). При установке выбираешь профиль (minimal / standard / full) – готовый набор функций под твой аппетит.
GitHub: https://github.com/AML1969/Paspartu
Приготовь заранее три вещи: токен бота от @BotFather, свой Telegram ID от @userinfobot и ключ DeepSeek (это «мозг», он дешёвый). Ещё нужен Docker с плагином compose v2, ~4 ГБ RAM и ~15 ГБ диска. По желанию – ключ OpenAI (картинки и распознавание фото) и Perplexity (платный поиск): без них бот работает, эти функции просто выключатся.
⚠️ Для читателей из России: без ключа OpenAI бот полностью работоспособен – отвалится только работа с картинками (генерация и распознавание), всё остальное на месте. А если картинки нужны: к OpenAI API из России напрямую не подключиться (гео-блок, и российской картой не оплатить) – понадобится VPN с зарубежным IP и иностранная карта либо российский сервис-прокси с оплатой в рублях (у бота меняешь baseURL и ключ на их).
🐧 Linux / Ubuntu-сервер – ветка linux:
git clone -b linux https://github.com/AML1969/Paspartu.git && cd Paspartu
./install.sh
🪟 Windows 11 (через WSL2) – ветка main:
git clone https://github.com/AML1969/Paspartu.git && cd Paspartu
./install.sh
Мастер сам задаст вопросы, проверит ключи, соберёт образ и запустит бота – дальше пишешь ему /start в Telegram. Первая сборка идёт несколько минут (качается ~3 ГБ) – это нормально, не пугайся.
📘 Подробная пошаговая инструкция (как с нуля поставить Linux+Docker на Windows, что отвечать мастеру, как проверить и что делать при ошибках) – в репозитории: INSTALL.md.
Вот, собственно, и всё. Дальше – для тех, кому интересно, какой ценой эта одна команда далась и что продолжает ломаться уже после релиза.
Мышцы: память, фон и Docker
Память, которую наконец можно искать
Самая обидная правда про любого ИИ-агента: он помнит ровно до края контекста, а всё, что «запомнил» в файл памяти, – это маленький блокнот, который читается целиком на каждый запрос. У Hermes этот блокнот – примерно 2200 символов. Полтора года жизни туда не влезают при всём желании.
🔴 Грабля №26. Бот клянётся, что помнит, – а помнит 2200 символов. Спрашиваешь про давнее решение по проекту – бот уверенно отвечает мимо. Не потому что глупый, а потому что нужного факта просто нет в блокноте, его вытеснило новым. И агент не скажет «я не знаю» – он достроит правдоподобный ответ. Уговорами это не лечится, лечится архитектурой: внешняя память, в которую он сам кладёт факты и в которой сам ищет, прежде чем начать сочинять.
Так появился hmem – банк памяти. Идея простая: память – это git-репозиторий из markdown-файлов, поверх которого живёт быстрый полнотекстовый поиск (FTS5 bm25 + триграммы, поэтому кириллица и опечатки прощаются). Каждый долговременный факт – отдельный файл-узел. А сверху – онтология в духе Palantir: у любого знания есть тип, и банк разложен на три полки – entities/ (сущности: сервер, бот, проект), events/ (что случилось: деплой, фикс, решение) и documents/ (что описывает: план, отчёт). У каждого узла есть связи на другие, поэтому память – это не свалка заметок, а граф: «этот инцидент случился на этом сервере в рамках этого проекта».
Итог: Паспарту перестал переспрашивать о том, что мы уже решали. Его новый рефлекс – прежде чем сказать «не знаю» или начать сочинять, сходить в банк и поискать. Память стала искомой – после полутора лет «а что мы там решали?» это ощущается почти как суперспособность.
Codex ушёл в фон – и больше не отчитывается
Во второй статье Codex уже умел довести тяжёлую задачу до конца. Но был нюанс: пока он работал, чат был занят. Теперь он работает по-настоящему в фоне: пишешь боту задачу со словом-триггером Codex – мгновенно получаешь квитанцию «✅ Принял задачу №N, ~X минут, можешь давать другие». Чат свободен, короткие вопросы подхватывает дешёвый DeepSeek, тяжёлую задачу молотит Codex. Красота.
И тут – изящное продолжение самой обидной граблы прошлой статьи. Тогда (№25) бот соврал, что запускал Codex, и подсунул мне импровизацию вместо результата.
🔴 Грабля №27. Бот не должен рассказывать о результате, которого у него нет. Если позволить агенту самому «доложить, как там Codex», он снова начнёт сочинять – потому что в момент ответа результата ещё физически нет. Решение архитектурное: за статусы и финал отвечает не бот, а отдельный наблюдатель – системный таймер раз в 3 минуты читает логи запуска и шлёт сообщения в Telegram напрямую, мимо модели. Бот запустил задачу, отчитался квитанцией – и закончил ход. Правда теперь приходит из логов, а не из фантазии агента. Граблю №25 победили не уговорами, а тем, что отобрали у бота возможность врать.
Почему всё это пришлось упаковать в Docker
Заметил общий мотив? Память, русские кнопки, rich-таблицы, фикс стрима – это всё патчи по живому коду, правки прямо в установленный пакет агента.
🔴 Грабля №28. Патчи стираются при каждом обновлении. Любое обновление пакета честно затирает их подчистую. Поэтому рядом жил скрипт, который после каждого апдейта перенакатывал все патчи по порядку (один зависит от другого) и только потом перезапускал бота. Накатываются они по «якорям» – точным строчкам исходника; перепишут авторы этот кусок – якорь не найдётся, перенакат встанет. Я живу с этим техдолгом, но каждое обновление делаю с лёгким холодком под ложечкой.
И вот в этот момент стало ясно: так нельзя ни отдать агента кому-то, ни спокойно спать самому. Всё это надо запечь в образ раз и навсегда, чтобы патчи не выживали чудом после каждого апдейта, а установка была одной командой. Так и появилась та самая коробка из начала статьи – Docker плюс мастер-установщик.
В догонку: грабли, которые вылезли уже после GitHub
Казалось бы, выложил на GitHub – история закончена, ставь точку и иди пить чай. Но агент живёт со мной каждый день, и стоило чуть отойти – как из тех же щелей полезли новые грабли. Причём все одного рода – того самого, с которого началась эта статья: бот уверенно выдаёт то, чего у него на руках нет. Собираю их сюда честно, по горячим следам.
🔴 Грабля №29. «Не помню» – хотя всё помнит. Пишу боту: «мы недавно обсуждали горные курорты рядом с Вильфраншем». Бот честно лезет в поиск по истории разговоров… и отвечает: «не могу найти ту сессию – ни в памяти, ни в истории». А сессия – есть, двумя неделями раньше, слово «Вильфранш» встречается в ней десятки раз. Стал разбирать по базе – и вот причина: полнотекстовый поиск (SQLite FTS5) склеивает все слова запроса через И и не знает русской морфологии. Бот искал фразой «Вильфранш горные курорты горы» – а в той сессии есть «горах» и «горные», но нет ровно «горы». Одно несогласованное слово – и весь запрос честно даёт ноль. Хуже того: получив ноль, бот не говорил «дай уточню», а сразу выпаливал «не помню». Лечение – правило поиска: сначала ищи одним самым характерным словом (лучше именем собственным, ровно в написании собеседника), и только потом добавляй синонимы через OR и окончания через *. И – категорический запрет говорить «не помню», пока не сделан этот первый шаг. «Вильфранш» одним словом находит ту самую сессию мгновенно.
🔴 Грабля №30. Бот «читает» страницу вслепую – и склеивает двух разных людей в одного. Спрашиваю про конкретного человека – бот выдаёт биографию, в которой перемешаны факты двух разных людей с одинаковым ФИО. Стал копать – и обнаружил вещь похуже одной ошибки: инструмент чтения страниц (web_extract) не работал вообще, ни у кого. В конфиге поле провайдера было пустой строкой, движок тихо откатывался на поисковый бэкенд, который умеет искать, но не умеет читать, – и бот, вместо того чтобы открыть страницу, достраивал её содержимое по памяти и обрывкам поисковой выдачи. Отсюда и склейка тёзок: раз текст реально не читается, разные люди с одним именем сливаются в одну биографию. Лечение из двух частей. Во-первых, свой беcключевой ридер страниц (обычный HTTP-запрос + разбор HTML в текст) – теперь чтение не может «умереть» из-за протухшего чужого ключа. Во-вторых, правило «люди и факты – без склеек»: совпадение ФИО не доказывает, что это один человек (различай по году рождения, должности, региону); один факт – один источник; важное – только из первоисточника; не нашёл – так и скажи. Та самая страница, на которой бот раньше лепил чужую биографию, теперь просто честно читается.
🔴 Грабля №31. Ссылки, которые бот не открывал, – и часть из них ведёт в никуда. Классика, на которой я ловил бота не раз: он вставляет в ответ ссылку – на товар, на страницу, на статью, – взятую из поисковой выдачи или из памяти, ни разу её не открыв. Иногда ссылка верная, а иногда – 404 или чужая устаревшая. И упираешься в это на ровном месте: «ссылки не открываются». Мягкое пожелание «проверяй ссылки, не выдумывай URL» в правилах уже висело – но под нагрузкой не держалось. Пришлось делать жёсткий гейт на каждый ответ: перед тем как вставить любую внешнюю ссылку, бот обязан её физически открыть (запросом с проверкой кода ответа – годится только итоговый 200); URL нельзя сочинять, только из результата инструмента; ссылка битая – не слать её вовсе, а дать проверенную альтернативу и честно сказать «прямую страницу подтвердить не смог».
И один мета-урок, общий для всех троих
Заметил, что все три догоняющие грабли – про одно и то же? Это ровно тот враг, с которого началась статья (№26 – «клянётся, что помнит») и с которым мы бились ещё во второй части (№25 – «соврал, что запускал Codex»): в момент ответа у агента нет нужного факта – и он достраивает правдоподобное вместо честного «не знаю».
И лечение каждый раз одно и то же по духу: не уговаривать модель «не ври», а отбирать у неё саму возможность соврать – внешняя искомая память вместо блокнота, живой ридер вместо додумывания, проверка ссылки вместо доверия к выдаче. Отдельным правилом я закрепил в самой памяти «законы точности записи»: один источник статуса; исправил – вычисти везде, а не только в одном месте; помечай непроверенное; датируй изменчивое. Грабли не кончаются – но каждая закрытая делает следующее враньё чуть более невозможным.
Итог
Если первая статья была про «переехать и не сломаться», вторая – про «думать дёшево и работать без меня», то эта – про взросление и раздачу. Сначала агент накачал мышцы: искомую память и фоновые задачи без вранья. Потом я запёк всё это в Docker и выложил на GitHub – чтобы патчи не держались на честном слове, а установка стала одной командой. А когда коробка уже лежала в репозитории – прилетела свежая пачка граблей, и все они оказались про один и тот же вечный грех агента: уверенно говорить то, чего он не знает.
На этом я историю закругляю – это финальная, третья часть. Не потому что грабли кончились (они не кончатся никогда), а потому что круг замкнулся: агент прошёл путь от «переехал и не сломался» до «лежит на GitHub, ставится одной командой». Теперь его можно не пересказывать на словах, а просто дать ссылку – и после полутора лет «это живёт только у меня на одном сервере» это ощущается почти как искомая память: наконец-то можно поделиться. Спасибо, что прошли эти полтора года вместе со мной и котом-космонавтом.
P.S. Кот-космонавт из первых двух частей передаёт привет и сообщает, что теперь он официально упакован – с типом
entity, связями, индексом и собственным Dockerfile. Рисовал, как и прежде, сам Паспарту.
Если зашло – расскажите в комментариях, как вы раздаёте своих агентов: Docker, готовый образ из реестра, облако – или героически объясняете каждому другу про патчи по живому. И где вас ловил на вранье собственный бот – продолжаем народную коллекцию граблей.
Автор: AMLAI


