Гайд · TNWS AI
Как проверить соответствие URL и source_id в RAG-цитировании
Находим конфликт, когда один source_id указывает на разные URL в найденных фрагментах и делает ссылки в ответе ненадёжными.
Задача и граница применения
Не публиковать ответ, в котором один и тот же ID источника ведёт к двум разным страницам. После повторной загрузки документа или объединения индексов одному source_id иногда соответствуют разные URL. Ссылки в ответе тогда становятся неоднозначными: в зависимости от фрагмента пользователь попадёт на разную страницу. Скрипт группирует URL по source_id и отмечает конфликт. Это строгая проверка идентичности; она не решает, являются ли два URL зеркалами. Редактор должен выбрать канонический адрес после проверки авторства и доступности, а не автоматически брать первый. В случае миграции /v1 → /v2 не переписывайте все ссылки только на основании того, что новая строка выглядит свежее. Сначала проверьте, какие редакции документов стоят за адресами и может ли старый регламент оставаться действующим для прошлых заявок. При необходимости сделайте разными source_id или добавьте version_id и дату действия. Затем повторите поиск на вопросах по обеим временным точкам. Такой тест ловит неоднозначность связи, но не принимает юридическое решение о том, какой регламент действует. Этот практический сценарий рассчитан на команду, которая контролирует данные для поискового слоя RAG и может хранить синтетические фикстуры в репозитории. Он не требует отправлять реальные клиентские документы стороннему сервису. Все ID, вопросы и численные результаты ниже сконструированы только для демонстрации. Проверка официальной документации Python и синтаксиса GitHub Actions: 19.09.2026; в собственном окружении перепроверьте версии перед запуском.
Как подготовить фикстуру
Два фрагмента D-1 имеют адреса /v1 и /v2; D-2 имеет только /help. Ожидается конфликт D-1. Сначала отделите эталон, согласованный человеком, от фактической выдачи поисковой системы. Не исправляйте gold по результатам модели: иначе тест перестанет быть независимым. Стабильный идентификатор вопроса позволит повторить сбой без публикации текста закрытой заявки. Если документы меняются, записывайте номер версии индекса и дату действия содержимого, иначе повторная проверка через месяц будет сравнивать разные базы.
Шаблон входа для переноса примера на свою коллекцию: для каждого вопроса заведите поля id, question, expected_doc_ids, retrieved_doc_ids и, где требуется, index_version. Согласуйте с предметным владельцем, что именно считается релевантным, доступным и актуальным источником. Это рабочая схема сбора данных, а не универсальный формат API. В таблице ревью отдельно отметьте спорные случаи, которые нельзя разрешить одной лишь строковой операцией.
Пошаговая проверка
- Создайте файл
scripts/check_retrieval.py; скопируйте Python-шаблон целиком. Использованные названия библиотечных функций сверены с официальными страницами в конце статьи. - Запустите
python scripts/check_retrieval.pyлокально. Сопоставьте консольный вывод с ожидаемым результатом в описании входа; демонстрация обязана проходить без внешнего API и ключей. - Экспортируйте поля source_id и URL до генерации. Запустите скрипт и проверьте, что D-2 не помечен ошибкой. Найдите в каталоге обе версии D-1: адрес /v2 может относиться к новой редакции, тогда нужно развести version_id или убрать старую запись. После согласования обновите индекс и ещё раз проверьте цитаты. Для редиректа отдельно фиксируйте конечный проверенный адрес: равенство строк URL не гарантирует одинаковое содержимое страниц.
- Для регрессии положите тот же файл в репозиторий и добавьте workflow из второго блока в
.github/workflows/rag-retrieval.yml. Проверка запускается наpull_requestс разрешениемcontents: read; не передавайте в неё настоящие секреты и закрытые документы. - Откройте job
retrieval-checkв Actions на конкретном коммите. Сравните исходные ID и обнаруженные нарушения с локальным прогоном. При расхождении проверьте версию фикстур и скрипта, а не меняйте эталон до выяснения причины.
# scripts/check_retrieval.py
from collections import defaultdict
rows=[("C-1","D-1","https://docs.example.org/v1"),
("C-2","D-1","https://docs.example.org/v2"),
("C-3","D-2","https://docs.example.org/help")]
urls=defaultdict(set)
for cid,sid,url in rows:
if not cid or not sid or not url: raise ValueError("missing field")
urls[sid].add(url)
conflicts={sid:sorted(values) for sid,values in urls.items() if len(values)>1}
assert set(conflicts)=={"D-1"}
print("Ambiguous sources:",conflicts)
name: RAG retrieval contract
on:
pull_request:
permissions:
contents: read
jobs:
retrieval-check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: actions/setup-python@v7
with:
python-version: '3.13'
- run: python scripts/check_retrieval.py
Реалистичный пример результата
Вход: Два фрагмента D-1 имеют адреса /v1 и /v2; D-2 имеет только /help. Ожидается конфликт D-1. После запуска ожидается исход, явно проверяемый инструкциями assert в коде. Не публикуйте вымышленное значение как измеренную характеристику вашей поисковой системы. Для рабочего отчёта приложите версию индекса, выбранные фильтры, размер размеченного набора, дату фиксации эталона и список ID провалившихся случаев. По каждому ID откройте первичный документ, прежде чем менять конфигурацию ранжирования: неправильно размеченный источник может выглядеть как технический дефект.
Типовые ошибки и ограничения
Этот тест проверяет однозначность mapping, не доступность страниц и не достоверность их содержания. Разные query-параметры могут быть существенными; не удаляйте их без политики. Если один источник законно представлен несколькими URL, нужна явная таблица алиасов. При расширении теста сначала добавьте отрицательный случай: намеренно испортите один элемент фикстуры и убедитесь, что проверка обнаружила его. Затем верните исходный набор и проверьте положительный случай. Это важнее зелёного CI на данных, где ошибка случайно отсутствует. Не логируйте полный ответ модели, содержимое закрытых документов или персональные сведения только ради диагностики.
Отдельно разделяйте ошибку данных, ошибку ранжирования и ошибку генерации. Если тест выполняется на другом индексе или другой редакции источника, результат нельзя напрямую сравнить с предыдущим. Снимок исходной выдачи, код и эталон должны относиться к одному запросу и одной версии доступных документов. Указанный демонстрационный результат не устанавливает порог качества для бизнеса; порог согласуют по отдельному размеченному набору и цене ошибки.
Чек-лист финальной проверки
- Задача теста и применимость понятны владельцу корпуса.
- Входные ID и эталон не взяты из ответа оцениваемой модели.
- Демо запускается локально и даёт ожидаемый результат.
- Преднамеренно неверный вход обнаруживается проверкой.
- Зафиксированы версия индекса, редакция документов и условия доступа.
- В логах CI нет приватных документов и секретов.
- Разделены ошибка поиска, ошибка эталона и ошибка генерации.
- После исправления проверены соседние пограничные случаи.
FAQ
Почему не выбрать последний URL?
Порядок выдачи не определяет действительность документа.
Это касается нескольких chunk одного документа?
Да, именно между ними удобно заметить конфликт.
Что делать с зеркалами?
Подтвердить их эквивалентность и задать канонический адрес.
Достаточно ли совпадения URL?
Нет, требуется контроль версии и содержания.
Что делать, если тест не прошёл
Сохраните ID вопроса и конкретные ID найденных источников. Повторите запрос на той же версии индекса и проверьте фильтры доступа, затем откройте эталон и убедитесь, что он по-прежнему действует. Если неверен сам эталон, сначала получите предметное подтверждение исправления; если неверен поиск, оставьте проваливающийся тест и только потом меняйте индекс или алгоритм ранжирования. После исправления повторите весь набор, включая отрицательный пример. В публичном отчёте опишите методику и происхождение тестовых данных, а не только красивую среднюю цифру.
Официальные первоисточники
Проверено 19.09.2026: документ 1, документ 2, документ 3, документ 4. Описание и примеры написаны самостоятельно; при переносе на другое окружение перепроверьте названия функций и синтаксис workflow.
Читайте также
Как исключить bias из weight decay через Optax
Optax 0.2.6: маска add_decayed_weights применяет decay к kernel и оставляет bias без изменения.
Как использовать Huber loss в Optax для регрессии с выбросами
Optax 0.2.6: Huber loss квадратичен около нуля и линеен после delta; проверяем ошибки 0,5 и 2.
Как использовать sigmoid focal loss в Optax
Optax 0.2.6: focal loss для logits 0 и labels 0/1 при gamma=2; проверка симметричного базового значения.
Комментарии
Пока тихо. Скажите первое слово