LLM вероятностна. Если она ответила правильно 5 раз подряд, это не значит, что на 6-й раз она не улетит в галлюцинации. Без продуманного процесса непрерывной оценки вы строите крайне хрупкую конструкцию.
Доброго времени суток!
Хочется поговорить об одной из самых «больных» тем в современной AI-разработке — как проверить, что система работает правильно. :-)
Удивительно, но текущий хайп вокруг LLM привел к довольно значительной деградации инженерной культуры в этой области («в среднем по больнице»). В эпоху первых трансформеров (да и более ранние эпохи) ни у кого не возникало сомнений: нужен «Golden Set», ручная разметка и жесткий контроль метрик. NLP был уделом специалистов по машинному обучению.
С приходом LLM порог входа упал. Теперь любой может написать промпт и получить ответ. Проверка качества превратилась в «vibe-check»: разработчик задает три вопроса, видит, что агент ответил «вроде нормально», и считает задачу решенной.
Но тут есть проблема: LLM вероятностна. Если она ответила правильно 5 раз подряд, это не значит, что на 6-й раз она не улетит в галлюцинации. Без продуманного процесса непрерывной оценки вы строите крайне хрупкую конструкцию.
Первое, что разумно ввести в ваши практики — это вспомнить про Golden Set.
Golden Set — это эталонный набор данных «вопрос-ответ», на котором вы гоняете свою систему. Но для агентов старый формат пар «запрос-текст» уже не всегда достаточен.
Агент — это система, которая совершает действия. Поэтому современный Golden Set должен содержать траектории:
Эталонные рассуждения (Chain-of-Thought).
Эталонные вызовы инструментов (Function Calling): какие функции и с какими аргументами должны быть вызваны.
Эталонные данные (Ground Truth): не просто текст ответа, а факты, которые должны в нем присутствовать.
В идеальном случае к GoldenSet-у можно предъявить следующие требования:
РепрезентативностьGoldenSet должен отражать реальные сценарии, а не только «идеальные» кейсы. Если ваша система работает с клиентами, в GoldenSet должны попасть:
Типичные запросы — то, что 80% пользователей спрашивают в норме
Граничные случаи — опечатки, неполные фразы, многосмысленные запросы
Региональные особенности — если аудитория из разных стран, добавьте запросы на разных языках и с культурным контекстом
Сложные сценарии — многошаговые запросы, где нужно вызвать 3+ инструмента
БалансПример: если вы делаете агента для поддержки клиентов, добавьте в датасет не только «Как вернуть товар?», но и «Я хочу вернуть свой товар, который я купил у вас три дня назад, но он сломался, и я не помню номер заказа».
GoldenSet не должен состоять из однотипных примеров. Распределяйте образцы по:
Сложности — от простых (один инструмент) до сложных (цепочка из 5+ вызовов)
Тематике — если агент работает в нескольких доменах, каждый домен должен быть представлен пропорционально реальной нагрузке
Длине ввода — короткие запросы («Погода») и длинные («Мне нужно забронировать переговорку на завтра утром, она должна вмещать 10 человек и иметь проектор»)
КачествоПример: если 70% обращений — это вопросы о товарах, а 30% — о доставке, то и в датасете должно быть примерно такое же соотношение. Иначе метрики будут врать.
Лучше 50 тщательно проверенных образцов, чем 500 сгенерированных без проверки. Плохой GoldenSet — это хуже, чем его отсутствие.
Ручная проверка — каждый образец должен быть проверен человеком
Непротиворечивость — ожидаемый ответ должен быть однозначным или иметь чёткие критерии оценки
Консистентность — все образцы должны быть оформлены по одному шаблону
ВерсионированиеПример: если вы оцениваете вызовы инструментов, эталонные вызовы должны быть не просто «правильными», но и соответствовать тому, что действительно вызвала бы идеальная система. Не «document_search» и «document_retrieve» вразнобой, а конкретная последовательность с конкретными аргументами.
GoldenSet — такой же код. Его нужно версионировать, фиксировать изменения и отслеживать, какие версии датасета использовались для каких экспериментов.
Фиксируйте версию — храните датасет в Git или используйте MLflow/DVC
Логгируйте изменения — кто, когда и зачем добавил/удалил образец
Привязывайте к экспериментам — в метаданных эксперимента всегда указывайте версию GoldenSet-а
Где взять данные?Пример: если вы обновили GoldenSet и получили скачок метрик на 15%, нужно понимать: это улучшение системы или вы просто добавили лёгкие образцы? Без версионирования это невозможно узнать.
Собирать такие данные вручную — это долго, дорого и больно (именно поэтому об этом так любят забывать :-)). Но у некоторых типов ИИ-систем, скажем самой популярной агентской топологии RAG, есть «читерское» преимущество: ваши документы сами по себе — идеальный источник данных для тестов.
Генерация GoldenSet для RAG системыОдин из самых эффективных способов получить Golden Set для RAG — при помощи LLM построить Граф Знаний (Knowledge Graph) на основе ваших же документов.
Есть отличная реализация в рамках популярной библиотеки оценки ИИ-систем RAGAS. Используя её и Конституцию России, взятую в качестве примера PDF-документа, давайте рассмотрим, как это работает.
Процесс выглядит так:Построение графа (Knowledge Graph): Мы разбиваем документы на иерархические узлы (Document -> Section -> Chunk). К каждому узлу LLM добавляет метаданные:
Summary: Краткое резюме контента.
Entities: Имена, даты, специфические термины.
Связи (Relationships): Узлы связываются на основе структуры (следующий/предыдущий) или семантической близости извлеченных сущностей.
Синтез вопросов: На основе структуры графа мы запускаем «синтезаторы», которые обходят получившийся граф и создают вопросы разной сложности.
Ragas предлагает довольно гибкую систему синтезаторов, позволяющую проверить RAG под разными углами:
Simple (Single-Hop): Проверяет базовый поиск. Вопрос касается одного конкретного факта в одном документе. Пример: «В каком году была принята текущая Конституция РФ?»
Multi-Hop: Самый важный тест. Требует сопоставления фактов из разных частей документа или даже разных файлов. Это проверяет способность ретривера собирать разрозненный контекст. Пример: «Какие ограничения накладываются на президента, если он одновременно является главой совета безопасности?»
Comparative: Заставляет модель сравнивать сущности. Пример: «Чем полномочия Государственной Думы отличаются от полномочий Совета Федерации в вопросе принятия федеральных законов?»
Specific vs Abstract: Мы можем генерировать как очень конкретные вопросы (фактология), так и абстрактные (обобщение темы).
Давайте рассмотрим, как выглядит в коде:
Я буду использовать в качестве LLM qwen/qwen3.6-35b-a3b, а в качестве модели эмбеддинга text-embedding-qwen3-embedding-0.6b. И одна и вторая запущенна локально в LM_Studio.
import argparse
import asyncio
import logging
import os
import instructor
from langchain_community.document_loaders import PyMuPDFLoader
from langchain_openai import OpenAIEmbeddings
from openai import AsyncOpenAI
from ragas.embeddings.base import LangchainEmbeddingsWrapper
from ragas.llms.base import InstructorLLM, InstructorModelArgs
from ragas.testset import TestsetGenerator
from ragas.testset.graph import KnowledgeGraph
from ragas.testset.synthesizers.multi_hop.specific import MultiHopSpecificQuerySynthesizer
from ragas.testset.synthesizers.single_hop.specific import SingleHopSpecificQuerySynthesizer
# Настройка логирования
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)
async def main():
parser = argparse.ArgumentParser()
parser.add_argument("--pdf", type=str, default="constitution.pdf", help="Путь к PDF")
parser.add_argument("--num-pages", type=int, default=5, help="Сколько страниц использовать (по умолчанию 5, -1 — все)")
parser.add_argument("--output", type=str, default="golden_set.csv", help="Путь для сохранения Golden Set")
args = parser.parse_args()
# 1. Конфигурация из переменных окружения
base_url = os.getenv("LLM_BASE_URL", "http://127.0.0.1:1234/v1")
api_key = os.getenv("LLM_API_KEY", "lm-studio")
model = os.getenv("LLM_MODEL", "qwen/qwen3.6-35b-a3b")
emb_model = os.getenv("LLM_EMBEDDING_MODEL", "text-embedding-qwen3-embedding-0.6b")
# Путь к PDF
pdf_path = args.pdf
if not os.path.exists(pdf_path):
logger.error(f"Файл {pdf_path} не найден. Положите constitution.pdf рядом со скриптом.")
return
# 2. Загрузка страниц
logger.info(f"Загрузка PDF: {pdf_path}")
loader = PyMuPDFLoader(pdf_path)
all_documents = loader.load()
if args.num_pages != -1:
documents = all_documents[:args.num_pages]
logger.info(f"Ограничение: используем первые {args.num_pages} страниц из {len(all_documents)}")
else:
documents = all_documents
logger.info(f"Используем все страницы: {len(documents)}")
# 3. Сооружаем обертки которые ожидает Ragas, заодно чиня ряд проблем LM_Studio :)
client = AsyncOpenAI(base_url=base_url, api_key=api_key)
# Используем MD_JSON для корректной работы структурного вывода с моделью, запущенной в LM_Studio
patched_client = instructor.from_openai(client, mode=instructor.Mode.MD_JSON)
# Провайдер "custom" предотвращает некоторые проблемы библиотеки с попыткой ходить не локально, а в облачный OpenAI
ragas_llm = InstructorLLM(
client=patched_client,
model=model,
provider="custom",
model_args=InstructorModelArgs(temperature=0.2)
)
# Эмбеддинги через LangChain-обертку, снова чиним особенности LM_Studio :)
emb_lc = OpenAIEmbeddings(
base_url=base_url,
api_key=api_key,
model=emb_model,
check_embedding_ctx_length=False
)
ragas_emb = LangchainEmbeddingsWrapper(emb_lc)
# 4. Генерация Knowledge Graph и Golden Set
logger.info(f"--- Запуск генерации Knowledge Graph (страниц: {len(documents)}) ---")
try:
kg = KnowledgeGraph()
generator = TestsetGenerator(llm=ragas_llm, embedding_model=ragas_emb, knowledge_graph=kg)
# Настройка синтезаторов вопросов
distribution = [
(SingleHopSpecificQuerySynthesizer(llm=ragas_llm), 0.5),
(MultiHopSpecificQuerySynthesizer(llm=ragas_llm), 0.5),
]
# Адаптация промптов под русский язык...
# Это не исключит генерации на английском на 100%, но минимизирует такие случаи
for query, _ in distribution:
prompts = await query.adapt_prompts("russian", llm=ragas_llm, adapt_instruction=True)
query.set_prompts(**prompts)
# Генерируем 10 вопросов, настоящий GoldenSet обычно содержит больше 100...
testset = generator.generate_with_langchain_docs(
documents,
testset_size=10,
query_distribution=distribution
)
df = testset.to_pandas()
logger.info("Генерация успешно завершена!")
# Сохранение в файл
output_file = args.output
df.to_csv(output_file, index=False)
logger.info(f"Golden Set сохранен в: {output_file}")
except Exception as e:
logger.error(f"Ошибка при генерации: {e}")
if __name__ == "__main__":
asyncio.run(main())
Обратите внимание на конструкцию query.adapt_prompts(…). Библиотека оперирует для генерации англоязычными промптами, что может приводить к тому, что генерируемые вопросы и ответы, будут на английском, это предусмотренный авторами способ минимизировать такие случаи (хотя иногда она все равно будет писать транслитом) :)
В реальных кейсах, довольно часто используют, логику деления на документы таким же способом, что и при индексации, условно используете RecursiveCharacterTextSplitter, его же чанки и используйте для построения графа документов и вопросов по ним.
Благодарю за внимание!
В следующей, части статьи поговорим, что вообще разумно измерять в агенсткой ИИ системе, а затем перейдем к Е-Е как совместить такой Golden Set с процессом непрерывной проверки.
| # | Наименование новости | Тональность | Информативность | Дата публикации |
|---|---|---|---|---|
| 1 | LLM-судье нельзя верить на слово: как построить надёжный гейт и проверить сами тесты | 0 | 12.53 | 28-07-2026 |
| 2 | Инженерия качества: Как перестать надеяться на удачу и начать измерять своих ИИ-агентов [Часть 3] | 0 | 10.04 | 18-05-2026 |
| 3 | Держите LLM подальше от тестов чат-бота | 0 | 10.76 | 15-06-2026 |
| 4 | Как думает LLM | 0 | 6.2 | 30-07-2026 |
| 5 | Скрытый риск масштабирования ИИ: дрейф решений | 0 | 10 | 20-07-2026 |
| 6 | The foundational elements of AI architecture that IT leaders need to scale | 0 | 7 | 07-07-2026 |
| 7 | Почему трансформацию инженерии в эпоху ИИ будут определять организационные аспекты | 0 | 8.4 | 22-07-2026 |
| 8 | Агентный SDLC на внутренней LLM: инженерия вместо промптов | 0 | 10.59 | 28-07-2026 |
| 9 | Для эффективного использования ИИ промышленникам нужно преодолеть недоверие | 0 | 5 | 09-07-2026 |
| 10 | Один скилл, четыре модели — что может пойти не так | 0 | 7.18 | 11-04-2026 |