AI-помощник по внутренней базе знаний (RAG): бот в Telegram с уровнями доступа
Бот отвечает на вопросы по внутренним чатам, письмам, голосовым и документам — с цитатами на источник. Поиск переписан с индекса в оперативной памяти на SQLite FTS5: память упала в 20 раз, и бот уехал с ноутбука подрядчика на сервер, где отвечает круглосуточно.
Вся фактура бизнеса жила в переписке
Бренд товаров для активного отдыха из США, два магазина на Shopify, распределённая команда: владелец бизнеса, операционисты, специалист по рекламе, подрядчики. Ни одной системы, где хранились бы решения. Всё — в рабочих чатах: десятки тысяч сообщений, голосовые вместо документов, пересланные PDF и скриншоты, плюс почтовый корпус в более чем 100 тыс. писем.
Ответ на вопрос вида «что мы решили по этому две недели назад» стоил получаса раскопок: пролистать чат, вспомнить, в каком именно, найти голосовое, послушать его целиком. Часть решений просто терялась — их переспрашивали и принимали заново.
Требование владельца было прямое: «важно, чтобы в базе было как можно больше данных». Но в тех же чатах лежала личная переписка и персональные данные — то, что команде показывать нельзя. Задача сразу стала не «сделать поиск по своим данным», а «сделать его так, чтобы полнота не превратилась в утечку».
Полный поиск по своим данным — приватность решается до индекса
Контур односторонний: сырьё собирается, приводится к тексту, чистится от секретов, попадает в индекс с правами доступа, и только потом модель собирает ответ. Приватность решается на входе, а не фильтром на выходе — так её нельзя обойти удачной формулировкой вопроса.
Выгрузка чатов по потокам со слиянием по номеру сообщения, архив почты, пересланные документы
whisper расшифровывает голосовые, OCR распознаёт скриншоты — всё становится текстом со ссылкой на источник
Номера карт, коды и пароли вырезаются до записи в индекс, а не при выдаче
SQLite FTS5 хранит индекс, ранжирование своё, поверх источников — уровни доступа
Модель собирает ответ и обязательно приводит цитаты; доставка в Telegram
Сбор: слияние по id, а не rsync
Чаты снимаются лейнами и сливаются по id сообщения. Это не педантизм: простое копирование поверх стёрло бы историю при первом же неполном съёме — источник отдаёт последние N сообщений, а не всё. Голосовые расшифровываются через whisper, скриншоты — через OCR, пересланные документы попадают в индекс как отдельные источники. На выходе всё — текст, у каждого чанка есть ссылка обратно.
Уровни доступа задаются в одном месте
Три уровня: владелец — команда — общий хаб. Права заданы на уровне источников, а не на уровне ответов. Дальше самое важное: над базой работает не только бот, но и выжимка фактов, и её фильтр — отдельный код, который легко разойдётся с правами доступа бота.
Поэтому обе стороны ходят в одну функцию
is_private_source,
а на дрейф между ними стоит гард-тест: если кто-то заведёт вторую копию логики приватности,
падает CI, а не приватность.
Поиск: BM25 в памяти → SQLite FTS5
Первая версия держала весь индекс в памяти процесса. Работало, пока бот жил на ноутбуке.
Переписал хранение на SQLite FTS5, но с нюансом: FTS5
используется только как хранилище индекса (через
fts5vocab),
а ранжирование остаётся своим.
Причина конкретная: встроенная
bm25()
в FTS5 отдаёт по документу одно число и не показывает вклад каждого слова — а он нужен,
чтобы понять, насколько документ покрывает слова запроса, а не просто набрал вес
на одном частом слове). Отдать ранжирование движку значило бы ухудшить выдачу.
Миграция сделана без риска: движок переключается одной переменной окружения, старый и новый прогоняются на одних запросах, выдача сверена ранг-в-ранг. Отличий нет — изменилась только цена.
Что надстроено сверху базы
База знаний оказалась платформой, а не отдельной функцией. Поверх неё живут:
- Дистилляция фактов — переписка сворачивается в структурированные факты, а не пересказ
- Утренние сводки и доски в Telegram-хабе: бизнес-пульс, пульс кампаний, инфра-пульс, дайджест трекера
- Около двух десятков сторожей по принципу «упало — напиши в личку и в Discord»: живость CRM, зависшие заказы, срок на оспаривание платежа, важное сообщение владельца, устаревание индекса
- MCP-сервер над базой — личные модели владельца видят ту же базу знаний, без второй такой же системы
Никакой векторной базы — SQLite и собственный ранкер
Ядро: сбор, индекс, ранжирование, доставка — один язык на весь контур
Хранит индекс; ранжирование остаётся своим — чтобы учитывать полноту покрытия запроса
Вес каждого слова и полнота покрытия запроса; движок переключается одной настройкой
Ответ собирается только из найденных фрагментов, цитаты обязательны
Голосовые сообщения и скриншоты становятся полноценными источниками, а не чёрными ящиками
Бот в личных сообщениях и форум с отдельными ветками под сводки и оповещения
Та же база знаний доступна личным моделям владельца
Службы, таймеры и ~20 сторожевых процессов с оповещениями в канал
70 тестовых файлов, ~845 тестов — включая проверку, что права доступа не «поплыли»
153 модуля на Python, около 36,8 тыс. строк, 318 коммитов. Ключевые части: движок индекса (~1300 строк), бот (~1500), вычистка секретов (~560), выжимка фактов (~430), доставка в Telegram на стандартной библиотеке, MCP-сервер, сборщик потоков и ~20 сторожевых модулей.
Одна и та же выдача за другие деньги
в 20 раз меньше — при идентичной выдаче
выдача сверена построчно, отличий нет
замер на живом индексе, до и после правки прав доступа
Бот съехал с ноутбука
Падение памяти в 20 раз — не цифра ради цифры. Именно оно позволило унести бота с ноутбука подрядчика на сервер, где всего 961 МБ RAM. Хроническая болезнь «ноутбук уснул — бот молчит ночью» закрыта в корне: бот отвечает круглосуточно, а не пока у кого-то открыта крышка.
Приватность: замерили, а не понадеялись
Права доступа можно долго обсуждать на словах. Вместо этого сделан замер на живом индексе: сколько приватных фрагментов реально видит уровень «команда». Ответ был неприятный — 1327.
После правки прав доступный этому уровню объём сжался с 6139 до 4560 фрагментов, приватных среди них — 0, при этом рабочих данных не потеряно. Отдельно поднят лог обращений: проверка подтвердила, что реального доступа к этим фрагментам никто не успел получить.
Что изменилось в работе
Вопрос «что мы решили по этому» теперь задаётся боту и возвращается ответом с цитатами на конкретные сообщения — включая те, что были голосовыми. Утренние сводки приходят сами, сторожа пишут раньше, чем проблему заметит клиент, а личные модели владельца через MCP работают с той же базой — без второй такой же системы и второй копии данных.
Где ещё ложится та же методология
Этот кейс — не «бот для чатов». Это типовая задача «знания компании живут в неструктурированной переписке, и часть из них показывать нельзя». Она есть почти везде, где команда старше года:
- → Поддержка и продажи — история переписок с клиентами как база ответов, при этом персональные данные не уходят в общий доступ
- → Агентства и студии — созвоны, брифы и голосовые от клиента в поиске с цитатой на источник вместо «кажется, договаривались так»
- → Производство и сервис — заявки, фото с объектов, переписка прорабов; ответ бота с цитатой заменяет обзвон
- → Ввод новых сотрудников в курс дела — уровень доступа «команда» даёт рабочий контекст, не открывая личную переписку руководства
- → Компании с ограничением на облако — весь контур, кроме синтеза, работает на своей машине: SQLite-файл, а не внешний векторный сервис
- Сбор данных со слиянием по номеру сообщения — история не затирается, если источник выгрузился не полностью
- Вычистка секретов до попадания в индекс и единое место, где заданы правила приватности, с тестом на их «расползание»
- Схема «FTS5 хранит индекс, ранжирование своё»: экономия памяти без потери качества выдачи
- Переключение поискового движка одной настройкой и построчная сверка выдачи — переход проверяется, а не принимается на веру
- Сторожевые процессы и утренние сводки поверх базы — знания начинают приходить сами, а не по запросу
Если знания компании лежат в чатах и почте и их страшно открывать всем — это решается
Начинаем с одного источника и одного уровня доступа — рабочий бот с цитатами появляется раньше, чем полная база. Дальше подключаются почта, голосовые, документы и сторожа.
Разбор по теме:Персональные данные и нейросети: 152-ФЗ, права доступа и чек-лист на 20 минут
Похожие кейсы
Автономные боты и проверка на истории: инженерия надёжности
Десяток сервисов, принимающих решения по потоку внешних событий. WebSocket вместо постоянных опросов, честная…
Избирательная маршрутизация трафика через собственный сервер
Статические маршруты по AS-диапазонам вместо неработающего механизма авто-маршрутов, точечные DNS-привязки…
Конвертер выписок Федерального Казначейства в 1С
XML-протокол V3 + V4 → формат 1CClientBankExchange, Windows-1251.
Аудит за 5 000 ₽ — с конкретным отчётом и сметой
Расскажу что внедрить в вашем бизнесе в первую очередь, какая будет окупаемость, и нужен ли вообще AI для вашей задачи (иногда — нет).
Или просто напишите свой вопрос — отвечу в течение 2 часов