{ "index": 100, "slug": "editorial-2025-03-field-knowledge-retrieval", "title": "Когда поиск по базе знаний должен остановиться", "excerpt": "Высокая похожесть найденного фрагмента не доказывает его актуальность, доступность и пригодность для цитирования. Разбираем stop-условие, проверку источника и безопасный путь для неполного результата.", "contentHtml": "
Поиск по инженерной базе часто ломается не тогда, когда ничего не нашёл. Опаснее другой симптом: система возвращает убедительный фрагмент, а команда принимает его за действующее правило. Документ уже закрыт для текущего пользователя, срок его действия истёк или в нём нет точного места, которое подтверждает вывод. Поиск показывает высокий score, интерфейс показывает ответ, а ошибка обнаруживается позже.
\nЦена такой ошибки измеряется не длиной задержки. Старая инструкция может привести к неверной миграции. Закрытый фрагмент может попасть в ответ человеку без права доступа. Неточная цитата превращает предположение в решение на ревью. Исправлять последствия дороже, чем остановить ответ на несколько минут и передать вопрос владельцу источника.
\nRetrieval должен отвечать на два разных вопроса. Первый: насколько найденный фрагмент похож на запрос. Второй: можно ли использовать его для конкретного ответа. Векторный или текстовый поиск решает только первый вопрос. Он ранжирует результаты. Он не подтверждает дату, права и точный смысл документа.
\nПоэтому ответ разрешается продолжить только для записи, которая одновременно проходит три проверки: источник доступен этому запросу, источник свеж по заданному правилу и в нём есть точная цитата с устойчивым anchor. Если хотя бы одно условие не выполнено у всех кандидатов, система возвращает stop. Она не заполняет пробел вероятным пересказом.
\nStop не означает «в базе ничего нет». Он означает «найденное нельзя безопасно использовать». Это важное различие для диагностики. Пользователь должен увидеть безопасную причину и следующий маршрут, но не закрытый текст. Владелец документа должен понять, что обновить или разрешить. Так система сохраняет и полезность, и границу доказательств.
\nУ записи источника должен быть контракт. Минимальный набор полей выглядит так: стабильный идентификатор, версия, URI, anchor, владелец, класс доступа, дата публикации, дата индексации и локальная дата истечения. Поле retrievedAt фиксирует момент, когда поиск увидел запись. Эти даты нельзя сводить к одному timestamp: задержка индекса и срок действия документа описывают разные риски.
Сначала система получает кандидатов. Затем применяет policy-фильтр. Проверка прав происходит до передачи excerpt в контекст ответа. Проверка свежести зависит от типа вопроса. Для операционного вопроса истёкшая инструкция непригодна. Для исторического вопроса она может быть полезна, но ответ должен назвать версию и дату. В обоих случаях правило задают метаданные и владелец политики, а не score.
\nПоложительная ветка возвращает source id, version, URI, anchor, retrievedAt и разрешённый фрагмент. Отрицательная ветка возвращает код причины: access-denied, expired, missing-anchor или unknown-policy. Закрытый excerpt не входит в отрицательный результат. Такой контракт не делает источник истинным автоматически. Он не даёт системе скрыть отсутствие доказательства.
const result = retrieve(query, records, policy);\n\nif (!result.accepted) {\n return {\n status: 'stop',\n code: result.reason,\n sourceId: result.sourceId,\n next: result.ownerAction,\n };\n}\n\nreturn {\n status: 'review',\n claim: draftClaim(query, result.excerpt),\n citation: {\n uri: result.uri,\n version: result.version,\n anchor: result.anchor,\n retrievedAt: result.retrievedAt,\n },\n};\nЭто учебный пример. Функции retrieve и draftClaim здесь не подключены к настоящей базе и не подтверждают реальную авторизацию. Пример показывает границу: положительный результат ещё требует ручного сравнения утверждения с источником, а отрицательный результат останавливает дальнейшую обработку.
| Симптом | Причина | Проверка | Действие |
|---|---|---|---|
| Высокий score, но документ старый | Ранжирование не учитывает срок действия | Сравнить expiresAt и retrievedAt | Остановить ответ и направить к владельцу документа |
| Найден закрытый фрагмент | Поиск и проверка прав разделены или проверка выполнена поздно | Проверить access decision до передачи excerpt | Вернуть безопасную причину без текста источника |
| Цитата ведёт на страницу без места | В индексе сохранён URI, но нет устойчивого anchor | Открыть URI и проверить заголовок, номер раздела или якорь | Остановить ответ и исправить карточку источника |
| Разные даты в индексе и документе | Не различены публикация, индексация и срок действия | Сопоставить все даты и владельца каждой | Исправить policy или обновить индекс |
| Система отвечает «ничего нет» | Reject reasons потерялись после фильтра | Проверить structured result и журнал решения | Показать safe reason и следующий маршрут |
HTTP-заголовок Last-Modified сообщает дату, когда origin считает выбранное представление изменённым. Это полезный сигнал для индексации и условных запросов. Но он не доказывает, что инструкция всё ещё действует. Правило могло устареть из-за миграции, смены владельца или изменения контекста, даже если файл с тех пор не менялся.
Практическая политика должна назвать, какая дата управляет ответом. Например, publishedAt отвечает за версию документа, indexedAt показывает задержку конвейера, а expiresAt задаёт допустимый срок для операционных вопросов. Если источник не предоставляет нужные данные, это не повод угадывать. Нужна остановка или явное решение владельца о том, какое ограничение действует.
Доступ к документу нельзя выводить из факта, что поисковый сервис смог его прочитать. Индекс может работать с расширенными правами, а запрос пользователя — с ограниченными. Проверка должна учитывать субъекта, ресурс и цель обращения. Deny должен произойти до того, как закрытый фрагмент попадёт в контекст следующего шага.
\nБезопасный stop-ответ содержит только то, что разрешено policy: например, внутренний идентификатор записи, общий код причины и группу владельца. Не следует возвращать закрытый заголовок, соседние предложения или признаки, по которым можно восстановить содержание. Уровень подробности диагностического сообщения — тоже часть модели доступа.
\nКачество retrieval видно не только по найденным документам. Оно видно по тому, как система ведёт себя при конфликте. Если документ похож на запрос, но истёк, она должна сохранить причину и остановиться. Если фрагмент разрешён, но anchor отсутствует, она не должна ссылаться на всю страницу. Если policy неизвестна, она не должна превращать неизвестность в allow.
\nПростой тест проверяет именно это поведение: highest-score candidate получает значение expired или access-denied; итоговый объект имеет статус stop; поля claim и citation.excerpt отсутствуют; присутствуют причина и действие владельца. Это проверяет контракт учебного модуля, но не доказывает работу вашей production-базы. Для реальной системы нужны отдельные проверки identity, источника и наблюдаемости.
Эта модель не выбирает лучший алгоритм поиска и не обещает, что keyword search, embeddings или reranking дадут конкретную точность. Она не заменяет классификацию документов, аудит прав и управление версиями. Score остаётся полезным для порядка кандидатов, но не становится доказательством. На качество ответа также влияет полнота корпуса: если нужного документа нет, фильтр не создаст его.
\nУчебный код не читает настоящую wiki, не обращается к сети и не выполняет реальную authorization decision. Все записи в примере условны. Нельзя переносить его значения дат, score или статусы в production. Переносить стоит только форму результата: accepted либо stop, структурированную причину, citation с anchor и явного владельца следующего действия.
\nРабота готова, когда для одного выбранного типа вопроса система может показать полный путь. Разрешённый свежий источник даёт версию, URI, anchor и дату retrieval, после чего человек подтверждает claim. Просроченный, закрытый или нецитируемый кандидат даёт stop без раскрытия запрещённого текста. В обоих случаях есть воспроизводимая проверка и назначенный владелец.
\nЕсли тест с высоким score и истёкшим документом всё ещё формирует ответ, проблема находится не в формулировке prompt. Не хватает контракта между индексом, policy и слоем ответа. Сначала добавьте metadata и отрицательную ветку. Только после этого имеет смысл настраивать ranking.
\n