Я вроде рассказал, как собрал агентам базу знаний-граф? Если не рассказывал, то там я пришёл к той же идее, что Карпатый описывал как LLM Wiki, — связный самоподдерживающийся граф концептов, где всё ссылается друг на друга в обе стороны. Могу отдельно рассказать про карточки и базу отдельно, если интересно.
Но база бесполезна, если агент не может достать из неё нужное. И вот тут началось самое весёлое. Наивный план был такой: агент формулирует запрос → ищем по тегам → отдаём карточки. Запустил. Агент спрашивает про «работу с базами данных» — находит ноль. Хотя в базе лежит жирный синтез с тегом
databases.Проблема — русский язык. Морфология, будь она неладна
Агент пишет запрос живым языком: «базами данных», «облака в России», «искусственного интеллекта». А теги в базе канонические:
databases, cloud, ai. Точного совпадения нет. Падежи, порядок слов, синонимы — всё это ломает простой поиск.Пришлось строить query expansion — разворачивать запрос перед поиском:
➡️ Синонимы и алиасы. «ИИ» и «субд» — это
ai и databases. Ведётся словарь синонимических групп поверх таксономии: каждый канонический тег знает все свои народные варианты.➡️ Лёгкий RU-стеммер. Отрезаю частое окончание, чтобы «облако», «облака», «облаком» сводились к одному стему. Намеренно тупой стеммер на ~10 правил, но его достаточно и он не тянет зависимостей.
➡️ Фразовый индекс по стемам. Многословный алиас «базы-данных» распознаётся во фразе «работа с базами данных» — по множеству стемов частей, несмотря на падеж и порядок слов. Внутри это
frozenset стемов: пересеклись — попадание.Синоним матчится не только против тегов файла, но и против его домена — это ловит карточки, где тема зашита в домен, а теги пустые.
fact-cards — отдельным потоком
Обычные синтезы ранжируются в общий шортлист. А вот проверяемые факты поднимаются отдельно с уровнем доверия (
confidence) и допуском к публикации (allowed_use). Просроченные по expires из выдачи вырезаются молча, чтобы автор физически не смог процитировать протухшую цифру. Ретривер тут работает как фильтр доверия, а не просто поиск.Playbook на каждый сценарий
Не «агент, посмотри базу». А конкретная карта: для этого типа материала подавай сначала fact-cards и позиционирование, конкурентов по релевантности, а вот эти слои вообще запрещены без явной проверки. Один сценарий — один playbook.
И самое важное — я это померил
Тут очень легко себя обмануть. Погонял поиск руками, пару раз он выдал что нужно и ты такой: «работает». А на деле ты проверил три запроса из тысячи возможных, и на четвёртом всё разваливается. «Вроде находит» — это не факт, это ощущение.
Чтобы перестать врать себе, я сделал скучную, но честную вещь: завёл метрики для поиска. Это просто список запросов, где я заранее, руками, отметил правильный ответ. Типа:
Запрос: «тренды систем управления базами данных»
Правильный ответ: вот эта карточка и вот эта
Таких пар у меня сейчас 15. Ключевой момент: правильный ответ я размечаю по смыслу, честно, а не подсматривая в то, что выдаёт мой же поиск. Иначе экзамен бессмысленный: я бы просто подгонял ответы под то, что система и так находит. Дальше прогоняю все 15 запросов через поиск и смотрю на три цифры.
Метрика 1: hit@5 — «нашёл ли вообще?»
Самый простой вопрос: в первых пяти результатах есть нужная карточка — да или нет? Считаю по всем запросам долю, где ответ «да». Сейчас это 1.0 — то есть по каждому из 15 запросов нужное лежит где-то в топ-5. Пока что промахов нет вообще. Почему именно топ-5, а не топ-1? Потому что агенту я и так отдаю несколько карточек — ему есть из чего выбрать. Мне важно, чтобы нужное попало в стопку, которую он получит.
Метрика 2: MRR — «а насколько высоко?»
Попасть в топ-5 мало. Одно дело — нужная карточка на 1-м месте, другое — на 5-м, за четырьмя лишними. MRR смотрит, на какой строчке стоит нужный ответ, и переводит это в оценку:
— ответ на 1-м месте → 1.0 (идеал)
— на 2-м → 0.5
— на 3-м → 0.33
— на 5-м → 0.2
Потом усредняю по всем запросам. У меня вышло 0.847 — в среднем нужное стоит между первым и вторым местом. То есть поиск не просто «где-то находит», а выносит правильное почти в самый верх. Это уже хорошо.
Метрика 3: precision@5 — «а сколько мусора рядом?»
И вот честная, неприятная цифра. Precision спрашивает: из пяти выданных карточек сколько реально по делу? У меня ~0.21. Грубо: из пяти результатов по-настоящему нужный обычно один, а четыре это «рядом лежало, зацепилось по теме». Выдача грязновата. Казалось бы плохо, но тут важно, для кого этот поиск. Если бы результат читал человек и ему пришлось глазами продираться через мусор — да, беда. А у меня получатель агент (LLM). Он получает стопку из пяти карточек и сам отбрасывает нерелевантное: контекста ему хватает. Поэтому я сознательно терплю низкую precision ради высокого hit@5, лучше отдать лишнее, чем упустить нужное. Но и это принципиально — цифру я вижу. Не прячу, не округляю в уме до «нормально». Если однажды агент начнёт захлёбываться в мусоре, я замечу это по метрике заранее, а не по кривым статьям постфактум.
Главный вывод: RAG на русском ломается не на эмбеддингах, а на морфологии запроса. Пока агент пишет «базами данных», а ты ищешь по
databases — никакая база не поможет. Сначала научи систему понимать, что это одно и то же. А потом обязательно померь, иначе будешь улучшать вслепую.#ai_agents