{ "index": 100, "slug": "editorial-2025-03-field-knowledge-retrieval", "title": "Когда поиск по базе знаний должен остановиться", "excerpt": "Высокая похожесть найденного фрагмента не доказывает его актуальность, доступность и пригодность для цитирования. Разбираем stop-условие, проверку источника и безопасный путь для неполного результата.", "contentHtml": "

Поиск по инженерной базе часто ломается не тогда, когда ничего не нашёл. Опаснее другой симптом: система возвращает убедительный фрагмент, а команда принимает его за действующее правило. Документ уже закрыт для текущего пользователя, срок его действия истёк или в нём нет точного места, которое подтверждает вывод. Поиск показывает высокий score, интерфейс показывает ответ, а ошибка обнаруживается позже.

\n

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

\n

Тезис: похожесть не равна доказательству

\n

Retrieval должен отвечать на два разных вопроса. Первый: насколько найденный фрагмент похож на запрос. Второй: можно ли использовать его для конкретного ответа. Векторный или текстовый поиск решает только первый вопрос. Он ранжирует результаты. Он не подтверждает дату, права и точный смысл документа.

\n

Поэтому ответ разрешается продолжить только для записи, которая одновременно проходит три проверки: источник доступен этому запросу, источник свеж по заданному правилу и в нём есть точная цитата с устойчивым anchor. Если хотя бы одно условие не выполнено у всех кандидатов, система возвращает stop. Она не заполняет пробел вероятным пересказом.

\n

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

\n

Механизм проверки

\n

У записи источника должен быть контракт. Минимальный набор полей выглядит так: стабильный идентификатор, версия, URI, anchor, владелец, класс доступа, дата публикации, дата индексации и локальная дата истечения. Поле retrievedAt фиксирует момент, когда поиск увидел запись. Эти даты нельзя сводить к одному timestamp: задержка индекса и срок действия документа описывают разные риски.

\n

Сначала система получает кандидатов. Затем применяет policy-фильтр. Проверка прав происходит до передачи excerpt в контекст ответа. Проверка свежести зависит от типа вопроса. Для операционного вопроса истёкшая инструкция непригодна. Для исторического вопроса она может быть полезна, но ответ должен назвать версию и дату. В обоих случаях правило задают метаданные и владелец политики, а не score.

\n

Положительная ветка возвращает source id, version, URI, anchor, retrievedAt и разрешённый фрагмент. Отрицательная ветка возвращает код причины: access-denied, expired, missing-anchor или unknown-policy. Закрытый excerpt не входит в отрицательный результат. Такой контракт не делает источник истинным автоматически. Он не даёт системе скрыть отсутствие доказательства.

\n
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 здесь не подключены к настоящей базе и не подтверждают реальную авторизацию. Пример показывает границу: положительный результат ещё требует ручного сравнения утверждения с источником, а отрицательный результат останавливает дальнейшую обработку.

\n

Симптомы, причины и действия

\n
Диагностика retrieval-ответа
СимптомПричинаПроверкаДействие
Высокий score, но документ старыйРанжирование не учитывает срок действияСравнить expiresAt и retrievedAtОстановить ответ и направить к владельцу документа
Найден закрытый фрагментПоиск и проверка прав разделены или проверка выполнена поздноПроверить access decision до передачи excerptВернуть безопасную причину без текста источника
Цитата ведёт на страницу без местаВ индексе сохранён URI, но нет устойчивого anchorОткрыть URI и проверить заголовок, номер раздела или якорьОстановить ответ и исправить карточку источника
Разные даты в индексе и документеНе различены публикация, индексация и срок действияСопоставить все даты и владельца каждойИсправить policy или обновить индекс
Система отвечает «ничего нет»Reject reasons потерялись после фильтраПроверить structured result и журнал решенияПоказать safe reason и следующий маршрут
\n

Почему HTTP-дата не решает задачу свежести

\n

HTTP-заголовок Last-Modified сообщает дату, когда origin считает выбранное представление изменённым. Это полезный сигнал для индексации и условных запросов. Но он не доказывает, что инструкция всё ещё действует. Правило могло устареть из-за миграции, смены владельца или изменения контекста, даже если файл с тех пор не менялся.

\n

Практическая политика должна назвать, какая дата управляет ответом. Например, publishedAt отвечает за версию документа, indexedAt показывает задержку конвейера, а expiresAt задаёт допустимый срок для операционных вопросов. Если источник не предоставляет нужные данные, это не повод угадывать. Нужна остановка или явное решение владельца о том, какое ограничение действует.

\n

Права доступа проверяются отдельно

\n

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

\n

Безопасный stop-ответ содержит только то, что разрешено policy: например, внутренний идентификатор записи, общий код причины и группу владельца. Не следует возвращать закрытый заголовок, соседние предложения или признаки, по которым можно восстановить содержание. Уровень подробности диагностического сообщения — тоже часть модели доступа.

\n
\"Схема
Остановка происходит внутри цикла: кандидат проходит проверку доступа, свежести и anchor до формирования ответа.
\n

Порядок действий для одной базы

\n
  1. Разделите вопросы на операционные и исторические. Зафиксируйте разные правила свежести, если они действительно различаются.
  2. Опишите карточку источника. Сохраните id, версию, URI, anchor, владельца, класс доступа и нужные даты.
  3. Определите обязательные условия ответа: доступ, свежесть и точная цитата. Назовите stop-коды для каждого нарушения.
  4. Проверьте отрицательный путь. Подложите кандидата с высоким score, но с истёкшим сроком или запрещённым доступом.
  5. Убедитесь, что excerpt закрытого кандидата не покидает слой проверки. В результате должны остаться только разрешённая причина и маршрут.
  6. Проверьте положительный путь вручную. Откройте exact source, сравните claim с anchor, подтвердите версию и сохраните citation.
  7. Назначьте владельца остаточного риска. Если policy неизвестна, вопрос должен попасть к владельцу policy, а не к слою ответа.
\n

Отрицательный путь важнее красивого ответа

\n

Качество retrieval видно не только по найденным документам. Оно видно по тому, как система ведёт себя при конфликте. Если документ похож на запрос, но истёк, она должна сохранить причину и остановиться. Если фрагмент разрешён, но anchor отсутствует, она не должна ссылаться на всю страницу. Если policy неизвестна, она не должна превращать неизвестность в allow.

\n

Простой тест проверяет именно это поведение: highest-score candidate получает значение expired или access-denied; итоговый объект имеет статус stop; поля claim и citation.excerpt отсутствуют; присутствуют причина и действие владельца. Это проверяет контракт учебного модуля, но не доказывает работу вашей production-базы. Для реальной системы нужны отдельные проверки identity, источника и наблюдаемости.

\n

Ограничения

\n

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

\n

Учебный код не читает настоящую wiki, не обращается к сети и не выполняет реальную authorization decision. Все записи в примере условны. Нельзя переносить его значения дат, score или статусы в production. Переносить стоит только форму результата: accepted либо stop, структурированную причину, citation с anchor и явного владельца следующего действия.

\n

Проверяемый критерий готовности

\n

Работа готова, когда для одного выбранного типа вопроса система может показать полный путь. Разрешённый свежий источник даёт версию, URI, anchor и дату retrieval, после чего человек подтверждает claim. Просроченный, закрытый или нецитируемый кандидат даёт stop без раскрытия запрещённого текста. В обоих случаях есть воспроизводимая проверка и назначенный владелец.

\n

Если тест с высоким score и истёкшим документом всё ещё формирует ответ, проблема находится не в формулировке prompt. Не хватает контракта между индексом, policy и слоем ответа. Сначала добавьте metadata и отрицательную ветку. Только после этого имеет смысл настраивать ranking.

\n

Проверяемые источники

\n" }