Набор вопросов для оценки качества
У помощника появилась страница, и теперь легко принять удачный ответ на знакомый вопрос за готовность всего проекта. Для осмысленной оценки нужен постоянный набор задач. Составим несколько вопросов с ожидаемым источником и несколько вопросов, на которые корпус не должен отвечать. Затем определим, что именно сравнивать. Новый результат — редакционный набор вопросов и сценарий отчёта. Сам сценарий при подготовке не запускается; мы описываем его контракт и ожидаемую интерпретацию, сохраняя пользовательское ограничение без тестов.
Состояние lesson-14 содержит questions.json и evaluate.py. Это исходники для самостоятельного изучения, а не опубликованные результаты оценки. В JSON записаны id вопроса, текст, ожидаемый документ и признак refuse. Используем тот же корпус, поэтому сравнение относится к знакомым urls, sitemap, templates и deploy. Добавлены отсутствие цены и вопрос о вымышленной служебной дате. Основная публичная роль во всех случаях — reader.
Что измеряем отдельно
Первое наблюдение — появился ли нужный источник. Если вопрос о постоянном PHP-пути должен опираться на urls, смотрим на doc_id полученных sources. Это не измерение литературного качества ответа и не доказательство каждого утверждения. Источник может присутствовать, а цитата оказаться слишком общей. Зато при его отсутствии сразу видно, что хотя бы один слой потерял нужное основание. Для полного разбора полезно дополнительно сохранять top_k retrieval до упаковки, но краткий сценарий здесь смотрит на источники итогового ответа.
Второе наблюдение — соответствует ли отказ ожидаемому. Для вопроса о тарифе данных нет, поэтому insufficient является правильным результатом. Для вопроса о sitemap отказ нежелателен, если текст явно содержит объяснение. Эти случаи показывают две разные ошибки: необоснованный ответ и лишний отказ. Уменьшение числа ответов само по себе не означает улучшение. Если помощник отказывается на каждый вопрос, он может не раскрывать лишних сведений, но перестаёт выполнять полезную учебную задачу.
Третье свойство — соответствие цитаты источнику. Наша извлекающая версия выбирает строку непосредственно из фрагмента, однако важно сохранять версию и смысловой контекст. При генерации точный источник не доказывает логическую поддержку. Такой слой требует ручной оценки: не появилось ли новое условие, не потерялось ли отрицание, не смешаны ли два правила. Один автоматический столбец expected_source_found не должен превращаться в общий знак «ответ правильный». Простые показатели полезны именно тогда, когда их границы остаются явными.
Как составлять вопросы
Контрольные вопросы желательно писать отдельно от буквальной копии предложения документа. Иначе получится проверка совпадения уже известных термов, а не способности найти нужный смысл. Но наш поиск ещё не понимает морфологию и синонимы, поэтому первые примеры специально сохраняют несколько ключевых слов. Это стартовый диагностический набор, а не независимый benchmark. При расширении добавляйте естественные формулировки, короткие запросы, опечатки и составные вопросы, отмечая, какие особенности нормализации они раскрывают.
Для отсутствующей цены ожидаемый source равен null. Не нужно указывать условный «похожий» документ: это заставит программу вознаграждать релевантность теме вместо наличия ответа. Аналогично вопрос о staff-дате для reader не должен иметь editor-notes как ожидаемое публичное основание. Политика доступа является частью задания. При оценке редакторского режима можно создать отдельную группу вопросов с доверенной ролью, но смешивать её с публичной выдачей в одной средней цифре неудобно и потенциально опасно.
Сохраняйте id вопросов, чтобы узнавать случаи после изменения формулировки. Название missing-price сразу напоминает, что мы оцениваем отсутствие цены, а не определение sitemap. Если исправить корпус и действительно добавить документ о тарифе, прежнее ожидание refuse потребуется пересмотреть. Набор оценки тоже имеет версию и редакционную историю. Иначе улучшение источников будет выглядеть как ошибка помощника только потому, что эталон больше не соответствует разрешённому знанию.
Устройство сценария отчёта
evaluate.py импортирует ask, читает JSON и перебирает вопросы. Для каждого получает result, собирает множество имён документов из id источников и формирует строку отчёта. Поле refusal_matches сравнивает фактический статус insufficient с ожидаемым refuse. Поле expected_source_found проверяет наличие заданного документа либо остаётся null, когда источника быть не должно. Скрипт не создаёт общего процента успеха автоматически: отдельные строки позволяют понять, что именно произошло и какой случай нуждается в разборе.
В текущем примере разделение id по двоеточию допустимо, потому что идентификаторы документов заданы без двоеточий. При более свободном формате лучше передавать doc_id явно. Маленький технический выбор способен незаметно повлиять на оценку: источник с неожиданным именем будет прочитан неправильно, хотя retrieval работал верно. Поэтому сценарий отчёта нужно рассматривать как обычный код с собственными предпосылками, а не независимого непогрешимого судью. Его данные должны соответствовать контракту основной программы.
Версии и режим модели
Для базового сравнения переменная OLLAMA_MODEL должна быть не задана. Тогда получаем детерминированную извлекающую цепочку при одинаковом корпусе и конфигурации. Если включить модель, статус generated_unverified меняет интерпретацию, а текст может различаться между вызовами. Следует записывать модель, digest, версию рантайма, параметры, ревизию индекса и дату. Фраза «использовали локальный AI» не позволяет воспроизвести различие двух ответов. Даже фиксированное имя модели иногда указывает на обновлённые веса, поэтому одного короткого ярлыка недостаточно.
Не сохраняйте реальную историю вопросов посетителей в такой учебный JSON без отдельного согласованного основания. Вопрос может содержать адрес почты, коммерческий план или внутренний текст. Для первоначальной оценки достаточно авторских строк и вымышленных данных. Если позже понадобится анализ настоящих обращений, нужно определить минимизацию, доступ к набору и срок хранения. Показатель качества поиска не отменяет этих решений. Разделение редакционного корпуса и диагностических вопросов также облегчает перенос архива без случайного раскрытия чужих данных.
Полные файлы ниже можно прочитать без выполнения. Для собственной оценки сначала создаётся индекс обычной командой app.py, затем отдельно вызывается evaluate.py. Здесь ни построение базы, ни отчёт не запускались. Ожидайте JSON со строками, а не доказательство семантической корректности каждой фразы. Если источник найден, но ответ плох, проследите путь: термы, top_k, бюджет, цитата, модель. Следующий урок добавит ограниченный кэш и объяснит, почему его ключ зависит от роли, версии данных и политики ответа, а не только от вопроса.
Полный файл questions.json из состояния lesson-14:
[
{
"id": "old-path",
"question": "старого сайта адреса PHP",
"source": "urls",
"refuse": false
},
{
"id": "sitemap",
"question": "sitemap опубликованных страниц",
"source": "sitemap",
"refuse": false
},
{
"id": "pager",
"question": "order предыдущего урока",
"source": "templates",
"refuse": false
},
{
"id": "backup",
"question": "резервная копия перед заменой сайта",
"source": "deploy",
"refuse": false
},
{
"id": "missing-price",
"question": "тариф стоимость рублей",
"source": null,
"refuse": true
},
{
"id": "private-date",
"question": "учебную дату ноября",
"source": null,
"refuse": true
}
]
Полный файл evaluate.py из состояния lesson-14:
import json
from app import ask
cases = json.loads(open("questions.json", encoding="utf-8").read())
report = []
for case in cases:
result = ask(case["question"], "reader")
source_ids = {source["id"].split(":", 1)[0] for source in result["sources"]}
report.append({"id": case["id"], "status": result["status"],
"refusal_matches": (result["status"] == "insufficient") == case["refuse"],
"expected_source_found": case["source"] in source_ids if case["source"] else None})
print(json.dumps(report, ensure_ascii=False, indent=2))
Оглавление серии · Исходники всех шагов · Учебные маршруты
Пример остаётся локальным черновиком. При подготовке он не выполнялся; все описанные эффекты являются ожидаемыми, а не результатами запущенных проверок.