← Журнал
Jiva Studio

Библиотека внутри ассистента

AICharya открывает корпус Listen to Sadhu любому ИИ-ассистенту — как устроена библиотека, какими двумя способами к ней обращаться и как поиск в две дорожки соединяет смысл с точными словами, чтобы и пересказ, и редкий санскритский термин попадали в нужный фрагмент.

AICharyaEngineeringAIMCPSearch

Попросите языковую модель объяснить стих «Бхагавад-гиты» — и она охотно объяснит, гладко и уверенно, даже если этот стих ей ни разу не встречался. В этом и вся беда, когда речь о писании и памяти модели: уверенный пересказ и точная цитата выглядят одинаково, пока не откроешь источник и не проверишь.

AICharya — наш ответ на это. Это небольшой публичный сервер, который отдаёт ИИ-ассистенту настоящий корпус за Listen to Sadhu: стихи, пураны, расшифровки лекций — с адресами и ссылками. Ассистент читает подлинный текст, а не сочиняет его заново. В названии — тихая игра слов: ачарья — это учитель, который передаёт текст без искажений. Ровно это мы и поручили серверу.

Эта статья — о том, как он устроен: что лежит в библиотеке, какими двумя способами до неё добраться и — самое главное — как поиск на самом деле находит нужный фрагмент.

Что это: один разъём в корпус

AICharya говорит на MCP — Model Context Protocol, стандарте, по которому ассистент обращается к внешним инструментам и данным. На практике это один адрес. Добавьте https://mcp.listentosadhu.app/mcp как пользовательский коннектор в Claude, ChatGPT, Grok, Mistral Le Chat, Perplexity или любой другой MCP-клиент — и вся библиотека станет тем, что ассистент умеет искать и цитировать прямо в обычном разговоре.

Две вещи здесь несущие, и обе выбраны намеренно:

  • Без ключа и без аккаунта. Сервер открытый. Корпус создан, чтобы его читали, поэтому регистрироваться и что-то ставить не нужно.
  • Только чтение, до самого низа. Каждый инструмент помечен как read-only; сервер открывает данные только на чтение и вообще не умеет писать. AICharya покажет библиотеку, но не изменит её.

Ассистент видит компактный набор инструментов — примерно полтора десятка. Каждый возвращает один и тот же аккуратный конверт: либо { ok: true, result }, либо { ok: false, error } с коротким и честным набором кодов ошибок (invalid_argument, not_found, dependency_failed). Ничего не приходится разбирать наугад.

Что лежит в библиотеке

Всё сводится к трём формам — потому что ссылка должна указывать на нужный тип вещи:

  • Стихи. У стиха есть оригинал на деванагари или бенгали, транслитерация IAST и перевод. Адрес — структурный: книга, глава, стих. Поэтому «BG 2.13» называет реальное место, а не удачную фразу.
  • Документы. Проза традиции: пураны и комментарии, прозаические главы, письма. Пурана к стиху — это просто документ-комментарий, привязанный к адресу стиха; получить его — один запрос.
  • Записи. Лекции и беседы в расшифровке. У каждой записи есть метаданные — кто говорит, где, когда, на каком языке, — ссылки на стихи, которые в ней звучат, и расшифровка: её можно читать вокруг любого момента, вплоть до миллисекунды.

Под капотом всё это лежит в двух разных хранилищах — каждое под свою задачу. Стихи и документы держит компактный файл-библиотека; каталог — все книги, авторы и места плюс указатель лекций — лежит в отдельном. Расшифровки лекций, которые нужно искать по смыслу, лежат в Postgres с pgvector — рядом с эмбеддингами. Снаружи этого деления не видно: спрашиваете — отвечает нужное хранилище.

Два пути внутрь: по имени или по вопросу

До нужного фрагмента ведут ровно два пути. Ответам можно доверять именно потому, что пути не смешиваются.

По имени. Знаете адрес — просто назовите его. AICharya понимает канонические ссылки в том виде, в каком их пишет читатель: «BG 2.13», «ŚB 5.5.3», «CC Madhya 8.128» — причём и на латинице, и на кириллице, так что «ШБ 5.5.3» и «SB 5.5.3» ведут к одному стиху. Коды книг распознаются и нестрого: «gita», «бхагаватам», «CC Madhya» — каждый находит свою книгу. Голый номер главы означает всю главу: tokens: "7" — это вся седьмая глава, tokens: "7.1" — только один стих. Этот путь не гадает: ссылка либо ведёт в реальное место, либо нет.

По вопросу. Когда непонятно, где искать — «где Прабхупада говорит, что душа вечна?» — в дело вступает поиск. Вот тут и начинается интересное.

Как работает поиск

Наивный подход — превратить и запрос, и корпус в векторы и взять ближайших соседей по косинусной близости. Так находится смысл: фразу «душа никогда не умирает» поиск свяжет с отрывком о вечности души, даже если ни одного общего слова нет. Но к точным строкам такой поиск слеп — а писание полно точных строк, и они важны. Санскритский термин вроде sanātana, имя, каноническая формула: смысловой вектор преспокойно округлит их до чего-то просто похожего.

Поэтому AICharya ведёт сразу две дорожки и сводит их вместе.

flowchart TD
  Q["Запрос на естественном языке"] --> E["Превращаем запрос в вектор"]
  E --> V["Смысловая дорожка · pgvector HNSW · косинусные соседи"]
  Q --> L["Дорожка точных строк · Postgres full-text плюс триграммы"]
  V --> F["Reciprocal Rank Fusion · k равно 60"]
  L --> F
  F --> R["Ранжированные фрагменты · у каждого id для полной записи"]

Смысловая дорожка превращает запрос в вектор на 1536 измерений и просит у pgvector ближайшие фрагменты по косинусному расстоянию, проходя по индексу HNSW. Она настроена искать чуть упорнее, чем по умолчанию: шире ef_search и мягче итеративный обход — чтобы даже запрос с фильтрами возвращал полный список, а не почти пустой.

Дорожка точных строк — это полнотекстовый поиск Postgres, и он намеренно работает в двух конфигурациях. Первая — russian, со стеммингом Snowball: русские запросы совпадают в любой словоформе. Вторая — simple, вообще без стемминга, — и она незаметно, но критически важна: транслитерация IAST остаётся как есть, поэтому sanātana-dharma не превращается в кашу до того, как успеет совпасть. Рядом идёт триграммный индекс — ловит канонические адреса и почти-совпадения написаний.

Дальше два ранжированных списка сводит Reciprocal Rank Fusion. RRF — приятно скучный алгоритм: каждая дорожка добавляет фрагменту очки, обратные рангу в этой дорожке, 1/(k+rank)1 / (k + \text{rank}), где k=60k = 60; очки складываются, список сортируется, а ничьи для повторяемости разводятся по чистому косинусу. Хорош этот приём тем, чего ему не нужно: дорожки дают очки в совершенно разных шкалах — косинусную близость и текстовый ранг, — и напрямую эти очки RRF никогда не сравнивает. Он сравнивает только ранги. Фрагмент, который понравился обеим дорожкам, поднимается наверх; фрагмент, который нашла лишь одна, тоже получает слово. Смысл и точное совпадение — и ни одно не глушит другое.

На выходе — ранжированный список, где нужный фрагмент находят и пересказ обычными словами, и редкий санскритский термин. У каждого попадания есть min_score: поднимите его — и слабые совпадения отсеются. Поиск, который честно признаёт, что нашёл мало, стоит больше, чем тот, что раздувает выдачу.

От попадания — к источнику

Результат поиска — не конец, а указатель. В каждом попадании лежит ровно тот id, по которому ассистент достаёт полную запись. Правила везде одинаковы:

  • verse_id идёт в verse_get — за оригиналом, транслитерацией и переводом;
  • document_id — в document_get, за полным комментарием или письмом;
  • track_id вместе с start_ms и end_ms идёт в transcript_window — тот читает расшифровку вокруг этого момента, то есть соседние фразы, а не голый обрывок;
  • media_id — в media_get.

Последние два ведут к чему-то приятнее текста. Если ответ — это лекция, ассистент показывает прямо в диалоге аудиоплеер, настроенный на нужный отрывок: источник звучит вслух, а сам отрывок нарезается по запросу. Если ответ — снятый момент, воспоминание ученика или запись беседы, — видеоплеер проигрывает его тут же, в разговоре. Ссылка здесь не сноска, за которой надо куда-то идти, — это сама запись, в одно касание.

Почему только чтение — это принцип

Легко было бы разрешить AICharya больше — помечать, ставить закладки, писать обратно. Мы сознательно этого не сделали. Открытый корпус писания хочется отдать так, чтобы его читали многие ассистенты и никто не правил. Только чтение — не ограничение, за которое неловко; это гарантия, ради которой одну и ту же библиотеку можно спокойно отдать сразу всем.

Вот, собственно, и вся картина. Listen to Sadhu уже отвечает на такие вопросы прямо в приложении — в чате с опорой на источники. AICharya берёт слой под ним — корпус с адресами, поиском и ссылками — и открывает его так, что и ваш ассистент может его читать. Работа ачарьи, сведённая к разъёму: дать модели настоящий текст — и пусть цитирует источник, а не своё воспоминание о нём.


Часть проекта

AICharya

Открыть проект