С появлением больших языковых моделей (LLM) стало казаться, что они умеют всё: от генерации кода до написания статей в научные журналы. Но, как только дело доходит до фактов, особенно актуальных и узкоспециализированных, начинаются проблемы. LLM — это не поисковики и не базы данных, знания у них статичны: что было в обучающей выборке, то модель и «знает» (да и то не всегда твёрдо). Постоянно дообучать её на актуальных данных — уже вызов. Тут на сцену выходят RAG-системы (Retrieval-Augmented Generation).

Если коротко, RAG — это способ «подкормить» LLM свежими данными: перед генерацией ответа модель получает не только сам вопрос, но и релевантные тексты, найденные внешней поисковой системе или во внутренней базе знаний. Идея звучит просто, но как понять, насколько хорошо это работает? Какие документы действительно помогли модели, а какие запутали её ещё больше? А главное — как сравнить разные RAG-системы между собой по-честному?

Оценка таких систем — нетривиальная задача. С одной стороны, нужно учитывать и качество извлечённых документов, и финальный ответ модели. С другой — важно избегать контаминации: когда модель «угадывает» правильный ответ просто потому, что уже видела его в процессе обучения. Это особенно актуально при использовании статических наборов данных вроде Natural Questions или HotpotQA: они давно «протекли» в открытые датасеты, в том числе для обучения популярных LLM.

Чтобы честно и эффективно сравнивать RAG-системы, нужны динамические бенчмарки — такие, где данные регулярно обновляются и гарантированно не попадают в обучение. В этой статье я расскажу, как мы построили динамический бенчмарк для оценки RAG-систем в новостной сфере, который позволяет бороться с утечками данных с помощью регулярных обновлений тестового датасета, а также использует извлечение графов знаний из текста для более точных операций с фактами.

Источники данных

Для построения динамического бенчмарка нам необходимо использовать данные, которые регулярно обновляются и при этом представляют интерес с точки зрения фактического содержания. В качестве основной области мы выбрали новостные статьи, полученные из открытых источников, таких как lenta.ru, tass.ru, ria.ru и других ведущих российских новостных порталов.

Новости представляют собой удобный и практически неисчерпаемый источник информации, с высокой частотой обновления: новые материалы публикуются ежедневно. Это позволяет нам регулярно генерировать актуальные версии тестового датасета, обеспечивая защиту от утечек в обучающие данные и минимизируя риск контаминации.

Кроме того, новости имеют важное преимущество с точки зрения оценки RAG-систем: тексты, как правило, не требуют от модели специфических знаний в узких предметных областях, таких как программирование, медицина или физика. Это упрощает оценку: мы можем сосредоточиться не на предметной компетентности модели, а на её способности использовать предложенный контекст, извлекать из него релевантные факты и корректно отвечать на вопросы.

Таким образом, выбор новостей в качестве источника данных позволяет создать бенчмарк, в котором акцент смещён в сторону оценки механизма поиска и генерации, снижая влияние внешних факторов вроде доменной специфики или объема знаний модели.

Генерация вопросов

Вопросы генерируем в два этапа. Сначала мы извлекаем граф знаний из новостных текстов с помощью языковой модели, а затем из графа выделяем определённые структуры, по которым языковая модель с помощью специально подготовленного промпта генерирует вопросно-ответные пары. Такой подход позволяет создавать реалистичные и разнообразные вопросы, строго основанные на фактах из новостей, и при этом избегать утечек знаний из обучающей выборки.

Этапы извлечения вопросов из текстов
Этапы извлечения вопросов из текстов

Генерация графов знаний по текстам

Для составления вопросов с привязкой к фактам, содержащимся в новостных статьях, мы сначала извлекали из текстов графы знаний, которые позволяют хранить структурированное представление о фактах. Граф знаний представляет собой направленный реляционный граф, в котором сущности представлены в виде вершин, а отношения между сущностями являются рёбрами графа. Граф знаний хранится в виде набора триплетов (субъект, отношение, объект). Для извлечения структурированной информации из текста использовали подход, предложенный в работе «Prompt Me One More Time», авторы которой используют языковую модель для извлечения триплетов, а затем фильтруют и нормализуют полученный граф.

На первом этапе мы применяли LLM для извлечения «сырых» триплетов из новостных текстов. Модель извлекала факты в виде простых утверждений (например: Россия — нарастила — добыча нефти). Это позволяло нам получить граф, отражающий основные события и взаимосвязи, описанные в материале.

После извлечения триплетов мы нормализовали сущности. Для этого извлечённые субъекты и объекты индексировали, и к каждой сущности мы находили набор похожих кандидатов по семантической близости. Затем LLM получала оригинальный текст, найденную сущность и список возможных нормализованных имён, и выбирала наиболее подходящее имя. Такой подход позволяет бороться с вариативностью упоминаний (например, «РФ», «Российская Федерация» и «Россия» обозначают одну и ту же сущность).

В отличие от оригинальной статьи, где авторы использовали Wikidata как источник подтверждения фактов, мы применяли её наоборот: для исключения триплетов, которые уже существуют в Wikidata. Наша гипотеза заключается в том, что если утверждение уже попало в базу знаний вроде Wikidata, то оно, скорее всего, присутствует в обучающих данных LLM. Это означает, что модель может «знать» факт заранее, без опоры на предоставленный контекст. Поэтому такие триплеты исключаем из итогового графа: это позволяет тестировать именно способность модели использовать контекст, а не вспоминать заученные факты.

После извлечения графов по каждой новости все полученные триплеты объединили в один RDF-граф для всего набора новостей. Это позволяет составлять вопросы, которые опираются на несколько независимых текстов, что делает особенно важным решение задач поиска и выбора правильного контекста. Помимо этого набор возможных вопросов расширяется благодаря появлению дополнительных связей в графе.

Генерация вопросов по графу знаний

Когда у нас на руках уже есть очищенный граф знаний, построенный по новостям, следующий шаг — превратить этот граф в набор вопросов. Для этого мы сначала разбиваем большой граф на множество подграфов, каждый из которых представляет собой небольшую связную структуру, по которой можно задать осмысленный вопрос. Мы используем четыре типа подграфов, которые позволяют генерировать разнообразные вопросы: Simple, Set, Conditional, Multi-Hop. Извлекаем подграф с помощью соответствующего SPARQL-запроса, который собирает все наборы отношений, соответствующие ограничениям типа.

Подграфы Simple содержат в себе ровно один триплет из графа и описывают единственный факт, упомянутый в одном или нескольких текстах. В вопросе одна из сущностей, представленных в триплете, используется вместе с отношением, чтобы описать вторую сущность, которая становится ответом. Этот тип вопросов является наиболее простым: при условии, что модели предоставлен правильный текст, она должна легко извлечь из него необходимую информацию.

Схема триплета
Схема триплета

Пример Simple-вопроса

Отношения:
(Морти Смит|озвучен|Кэисукэ Тиба)

Вопрос:
Кто озвучил Морти Смита?

Ответ: Кэисукэ Тиба

SPARQL-запрос Simple

SELECT DISTINCT ?s ?r ?o
WHERE
{
	{
    	SELECT ?s ?r ?o
    	WHERE
    	{
        	?s ?r ?o .
    	}
    	GROUP BY ?s ?r
    	HAVING(count(?o) = 1)
	}
	{
    	SELECT ?s ?r ?o
    	WHERE
    	{
        	?s ?r ?o .
    	}
    	GROUP BY ?o ?r
    	HAVING(count(?s) = 1)
	}
}

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

Схема связанных триплетов
Схема связанных триплетов

Пример Set-вопроса

Отношения:
(Сбербанк|партнер|Национальная система платежных карт (НСПК))
(Сбербанк|партнер|Центр биометрических технологий (ЦБТ))
(Сбербанк|партнер|ЕБС)

Вопрос:
С кем Сбербанк состоит в партнерских отношениях?

Ответ:
Национальная система платежных карт (НСПК)
Центр биометрических технологий (ЦБТ)
ЕБС
SELECT ?s ?r ?o ?len

WHERE
{
	{
    	SELECT ?s ?r (COUNT(?o1) as ?len)
    	(GROUP_CONCAT(DISTINCT(STR(?o1));separator="|") AS ?o)
    	WHERE
    	{
        	?s ?r ?o1 .
    	}
    	GROUP BY ?s ?r
    	HAVING(COUNT(?o1) > 1)
	}
	UNION
	{
    	SELECT ?o ?r (COUNT(?s1) as ?len)
    	(GROUP_CONCAT(DISTINCT(STR(?s1));separator="|") AS ?s)
    	WHERE
    	{
        	?s1 ?r ?o .
    	}
    	GROUP BY ?o ?r
    	HAVING(COUNT(?s1) > 1)
	}
}

Вопросы Conditional являются расширением типа Simple. Здесь добавляется дополнительное связанное отношение, которое накладывает ограничение на возможный ответ. После извлечения цепочек длиной 2, повторяющаяся сущность используется в качестве ответа, а в вопросе содержится её описание через представленные отношения.

Схема триплетов для Conditional вопросов
Схема триплетов для Conditional вопросов

Пример вопроса

Отношения:
(Роман Мирошниченко|выступал в|М-бар)
(Роман мирошниченко|встречался с|Дмитрий Дибров)

Вопрос:
Кто выступал в М-баре и встречался с Дмитрием Дибровым?

Ответ:
Роман мирошниченко
SELECT *
WHERE
{
	{
    	SELECT ?s ?r ?o ?r1 ?o1
    	WHERE
    	{
        	?s ?r ?o .
        	?s ?r1 ?o1 .
        	FILTER(?o != ?o1)
    	}
    	GROUP BY ?o ?o1 ?r ?r1
    	HAVING(COUNT(?s) = 1)
	}
	UNION
	{
    	SELECT ?s ?r ?o ?r1 ?s1
    	WHERE
    	{
        	?s ?r ?o .
        	?s1 ?r1 ?o .
        	FILTER(?s != ?s1)
    	}
    	GROUP BY ?s ?s1 ?r ?r1
    	HAVING(COUNT(?o) = 1)
	}
	UNION
	{
    	SELECT ?s ?r ?o ?r1 ?b
    	WHERE
    	{
        	?b ?r ?o .
        	?s ?r1 ?b .
        	FILTER(?o != ?s)
    	}
    	GROUP BY ?s ?o ?r ?r1
    	HAVING(COUNT(?b) = 1)
	}
	FILTER(?r != ?r1)
}

Вопросы Multi-hop проверяют способность RAG-системы к многоступенчатым рассуждениям. Такой тип вопроса требует от модели сначала разрешить ссылку на некоторую сущность, а затем ответить на вопрос. Для генерации вопросов Multi-hop из графа извлекаются цепочки отношений длиной 2. При составлении вопросно-ответной пары модель может упоминать только те сущности, которые в цепочке не повторяются (одну в ответе, одну в вопросе). Сущность, по которой пересекаются отношения (bridge-сущность), должна быть описана в тексте вопроса без указания самой сущности.

Схема для Multi-hop вопросов
Схема для Multi-hop вопросов
Отношения:
(FAW|страна происхождения|Китай)
(FAW|количество проданных автомобилей в 2023 году|2139)

Вопрос:
В какой стране находится компания, которая
продала 2139 автомобилей в 2023 году?

Ответ:
Китай
SELECT *
WHERE
{
	{
    	SELECT ?s ?r ?o ?r1 ?o1
    	WHERE
    	{
        	?s ?r ?o .
        	?s ?r1 ?o1 .
        	FILTER(?o != ?o1)
    	}
    	GROUP BY ?o ?o1 ?r ?r1
    	HAVING(COUNT(?s) = 1)
	}
	UNION
	{
    	SELECT ?s ?r ?o ?r1 ?s1
    	WHERE
    	{
        	?s ?r ?o .
        	?s1 ?r1 ?o .
        	FILTER(?s != ?s1)
    	}
    	GROUP BY ?s ?s1 ?r ?r1
    	HAVING(COUNT(?o) = 1)
	}
	UNION
	{
    	SELECT ?s ?r ?o ?r1 ?b
    	WHERE
    	{
        	?b ?r ?o .
        	?s ?r1 ?b .
        	FILTER(?o != ?s)
    	}
    	GROUP BY ?s ?o ?r ?r1
    	HAVING(COUNT(?b) = 1)
	}
	FILTER(?r != ?r1)
}

После выделения всех возможных подграфов каждого типа, мы передаём их в языковую модель вместе с промптом, описывающим тип вопроса и структуру графа. Промпт содержит инструкции, которые помогают модели понять контекст: как интерпретировать связи в графе, какого рода вопрос необходимо сформулировать и какой должен быть ответ. Модель на основе этого запроса генерирует вопросно-ответную пару, используя только представленные отношения. Вопросы получаются разнообразными, но в то же время строго опираются на фактические данные, присутствующие в графе. Это делает их пригодными для оценки RAG-систем, сосредотачиваясь именно на способности модели находить и использовать информацию.

Фильтрация вопросов

Автоматически сгенерированные вопросы — это отличная основа для тестового набора, но далеко не все из них получаются качественными. Иногда языковая модель делает грамматические ошибки, иногда — генерирует вопрос, не связанный с графом, или, наоборот, слишком общий и «скучный», не проверяющий способность работать с контекстом. Поэтому перед финальной сборкой датасета мы проводим многоступенчатую фильтрацию, чтобы оставить только те примеры, которые действительно полезны для оценки RAG-систем.

Первый шаг — проверка языковой корректности вопросов. Для этого мы используем модель, обученную на датасете RuCoLA, которая оценивает, насколько формулировка вопроса соответствует требованиям русского языка. Если вопрос звучит неестественно или содержит синтаксические ошибки, то мы его исключаем. Далее мы проверяем, содержит ли вопрос упоминание именованных сущностей, извлечённых из исходной статьи. Мы используем библиотеку Natasha для распознавания сущностей (NER) в оригинальном тексте, а затем сверяем, упоминаются ли эти сущности в вопросе или ответе. Если их нет — значит, модель, скорее всего, сгенерировала слишком общий вопрос, который можно решить без опоры на конкретный контекст, и такой пример мы удаляем.

Чтобы убедиться, что вопрос действительно требует размышлений и не является тривиальным, мы прогоняем его через маленькие языковые модели (например, Qwen 2.5 7B  Instruct и LLaMA 3 8B) без контекста. Если модель без подсказки легко угадывает правильный ответ — скорее всего, вопрос слишком простой или основан на общих знаниях. Такие случаи мы исключаем, чтобы наш бенчмарк оставался сложным даже для продвинутых моделей.

Следующий фильтр — проверка соответствия вопроса исходному подграфу, по которому он был сгенерирован. Мы проверяем, содержат ли вопрос и ответ хотя бы одну сущность из этого подграфа. Для этого ищем ближайшее совпадение по расстоянию Левенштейна между словами из вопроса и именами сущностей в графе. Если ни одна из сущностей не упоминается, или, наоборот, встречаются «лишние» — это значит, что вопрос сгенерирован некорректно или не соответствует заданному типу подграфа.

На последнем этапе мы применяем подход LLM-as-a-Judge, то есть просим языковую модель оценить качество генерации. Мы используем специализированного «судью» — модель POLLUX 7B, обученную для тонкой оценки генеративных ответов на русском языке. Она оценивает каждую пару вопрос-ответ по восьми критериям, включая формулировку, связность, обоснованность и зависимость от контекста. Если модель считает, что пример недостаточно хорош, то он не попадает в финальный датасет.

После всех этапов фильтрации мы отбираем по 150 лучших вопросов на каждый тип подграфа. Так формируем итоговый тестовый набор, в котором каждый пример проходит проверку на качество, смысловую связность и сложность.

Структура бенчмарка

Картинку, кстати, рисовал на https://d2lang.com/. Используйте в своей работе (рисовать документацию, в статьи вставлять и т.д.), все диаграммы описываются декларативно (текстом), очень удобно. Это аналог Mermaid диаграм, но чуть покрасивее на мой вкус (Mermaid зато часто работает в markdown формате, например, в вашем Readme на GitHub).

Данные, на которых основан бенчмарк, состоят из нескольких частей:

  • Public Texts — очищенные новостные документы с анонимизированными ID;

  • Public Questions — только вопросы, связанные с этими текстами (без ответов);

  • Private QA — правильные ответы (используются только для оценки);

  • Private Texts Mapping — сопоставление публичных и внутренних ID (тоже только для оценки).

Настроенные скрипты периодически (сейчас — раз в месяц) автоматически генерируют эти датасеты и выгружают на Hugging Face. Первые два датасета являются открытыми, а последние два, содержащие правильные ответы и соответствия между публичными и приватными номерами (поля  Id), — закрытыми.

Мы выбрали такую схему, чтобы иметь возможность принять решение от пользователя с последующей его оценкой, а также размещать такие решения в публичном лидерборде.

Лидерборд мы сейчас наполняем и дорабатываем, первую его версию можно посмотреть здесь. Идеи и предложения приветствуются.

Исторические датасеты

Кроме этого мы решили выкладывать все генерируемые датасеты с отставанием в одну версию, чтобы у всех исследователей была возможность иметь актуальные QA-датасеты на русском языке.

Найти их можно на Hugging Face:

  • Public Texts — ai-forever/rag-bench-public-texts;

  • Public Questions — ai-forever/rag-bench-public-questions;

  • Private QA — ai-forever/hist-rag-bench-private-qa;

  • Private Texts Mapping — ai-forever/rag-bench-private-texts.

Мы версионируем датасеты, то есть все предыдущие и будущие версии можно будет скачать под этими именами, а отсчёт версий ведётся от августа предыдущего года с интервалом в месяц. Например, так можно скачать данные за август 2024 года:

from datasets import load_dataset

ds = load_dataset('ai-forever/hist-rag-bench-private-qa', revision="1.0.0")

А так — за январь 2025-го:

ds = load_dataset('ai-forever/hist-rag-bench-private-qa', revision="1.6.0")

Последняя версия на сегодня — 1.10.0, за июнь.

Выглядят датасеты примерно так:

Механизмы фильтрации можно еще улучшить, но мне кажется, что на таком датасете уже можно замерять свои RAG-системы.

Напишите, что думаете по этому поводу, и как вообще лучше замерять такого рода системы.

Ссылки

Репозиторий с кодом

Статья на arXiv

// Статью написали вместе с коллегой Федей Черногорским. А над бенчмарком поработала наша небольшая кросс-команда — Федя, Завен Мартиросян, Лиля Кудралеева, Маша Тихонова, Валя Малых, Алена Феногенова и Сережа Аверкиев.

Комментарии (1)


  1. Andriljo
    25.07.2025 15:41

    Подбираются ли для эмбеддеров префиксы, чтобы использовать всю их мощь в RAG? Замеряется ли качество извлечения подсказок на основе retrieval метрик? Или только везде судья?