{ "index": 102, "slug": "editorial-2025-03-practice-knowledge-retrieval", "title": "Поиск по инженерной базе: как не превратить похожий текст в доказательство", "excerpt": "Практический маршрут для retrieval по инженерной документации: сначала проверить версию, срок, права и точную цитату, а затем решать, можно ли отвечать.", "contentHtml": "

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

Цена такой ошибки складывается из отката, повторного расследования и потери доверия к базе знаний. Пустой результат заметен. Уверенный пересказ устаревшего правила — нет. Поэтому поисковый ответ должен сначала доказать право на использование конкретной записи, её применимость на момент запроса и адресуемость цитаты. Только после этого можно формулировать вывод.

Тезис: score выбирает кандидата, но не подтверждает утверждение

Similarity search решает узкую задачу: он упорядочивает похожие документы или фрагменты. В score нет ответа на вопросы «кто может читать запись», «действует ли правило сейчас» и «подтверждает ли этот абзац конкретный claim». Эти вопросы требуют других данных и других проверок. Если объединить их в одно число, система перестанет объяснять, почему кандидат отклонён.

Рабочая граница выглядит так: query → candidates → access check → freshness check → exact citation → human verification. Retrieval сокращает очередь чтения. Metadata задают условия допуска. Человек сопоставляет утверждение с источником. Если пересечение условий пусто, система останавливается и сообщает причину. Она не заменяет недостающий evidence правдоподобной фразой.

Симптом → причина → проверка → действие

Диагностика результата поиска по инженерной базе
СимптомПричинаПроверкаДействие
Первый кандидат имеет высокий score, но у него нет версииИндекс хранит chunk и embedding без связи с редакцией источникаНайти stable recordId, sourceVersion и URI исходного документаОставить кандидата для поиска, но не использовать как цитату
Фрагмент точно отвечает на вопрос, но expiresAt уже прошёлРанжирование не учитывает локальную политику актуальностиСравнить expiresAt и зафиксированный retrievedAtОтклонить запись и запросить действующую редакцию
Кандидат закрыт для requesterAccess policy проверяется после генерации или не проверяетсяСопоставить accessLabels записи и scope запроса до показа excerptСкрыть закрытый текст, сохранить безопасную причину отказа
Ссылка ведёт на документ, но не на нужный абзацВ индексе нет citationAnchorОткрыть URI и проверить точный раздел, строку или якорьОстановить ответ до появления адресуемого фрагмента
Ответ написан, ссылки добавлены позжеТекст успел включить выводы, которых нет в evidenceСравнить каждый claim с источником до публикацииСначала собрать допущенные citations, затем писать ответ

Механизм: запись индекса должна нести provenance

Голый фрагмент удобен для векторного поиска, но плох для проверки. Минимальная запись связывает chunk с источником и сохраняет границы его использования. Нужны устойчивый recordId, sourceVersion, publishedAt, indexedAt, expiresAt, класс доступа, citationUri и citationAnchor. Поле vectorScore тоже полезно, но только для порядка кандидатов.

publishedAt отвечает на вопрос, существовала ли редакция к моменту поиска. indexedAt показывает задержку между публикацией и попаданием в индекс. expiresAt выражает локальное правило применимости. retrievedAt фиксирует срез, на котором система приняла решение. Ни одна из этих дат сама по себе не доказывает смысл утверждения. Вместе они позволяют воспроизвести проверку.

Такая схема важна после chunking. Когда документ режут на части, title и текст часто сохраняют, а версию, владельца и раздел оставляют в исходном хранилище. Затем в ответ попадает удобный snippet, который нельзя связать с конкретной редакцией. Metadata должны наследоваться каждым chunk или однозначно находиться по stable source id. Иначе retrieval создаёт видимость точности, а не проверяемую provenance.

Схема пути от запроса и retrieval через проверки прав и свежести к точной цитате и human verification; при отсутствии условий ответ останавливается.
Score сокращает список кандидатов. Ответ появляется только после проверки свежего, разрешённого и адресуемого источника.

Пример: фильтр допуска перед формированием ответа

Ниже — учебный пример на фиксированных синтетических записях. Он показывает порядок решения и отрицательный путь. Он не обращается к реальной базе, identity provider, часам, production или сети. Синтетические значения нужны только для проверки контракта обработки.

const retrievedAt = '2025-03-17T10:00:00Z';\nconst requesterLabels = ['engineering-read'];\n\nconst candidates = [\n  {\n    recordId: 'adapter-v1',\n    sourceVersion: 'v1',\n    expiresAt: '2025-03-01T00:00:00Z',\n    accessLabels: ['engineering-read'],\n    citationUri: 'https://docs.example.test/adapter',\n    citationAnchor: '#old-field',\n    vectorScore: 0.97,\n  },\n  {\n    recordId: 'adapter-v3',\n    sourceVersion: 'v3',\n    expiresAt: '2025-04-01T00:00:00Z',\n    accessLabels: ['engineering-read'],\n    citationUri: 'https://docs.example.test/adapter',\n    citationAnchor: '#schema-upgrade',\n    vectorScore: 0.91,\n  },\n];\n\nconst allowed = candidates.filter((item) =>\n  item.expiresAt > retrievedAt &&\n  item.accessLabels.some((label) => requesterLabels.includes(label)) &&\n  item.citationUri && item.citationAnchor,\n);\n\nif (!allowed.length) {\n  throw new Error('stop-no-fresh-authorized-citable-source');\n}\n\n// Учебный результат: v1 имеет больший score, но истёк.\n// v3 остаётся кандидатом для human verification.

Здесь высокий score у adapter-v1 не отменяет истечение срока. adapter-v3 проходит технический фильтр, но это ещё не автоматическое доказательство ответа. Reviewer должен открыть citationUri#schema-upgrade и проверить, что фрагмент действительно говорит о нужной замене. Если у всех записей истёк срок, нет доступа или отсутствует anchor, функция должна вернуть stop condition. Скрытый fallback на старый текст создаёт именно ту ошибку, от которой защищает схема.

Проверка должна идти до генерации текста

Безопасный порядок начинается с вопроса и времени retrieval. Затем система сохраняет candidates вместе с score и metadata. После этого она проверяет права, срок и citation. В ответ проходит только допущенная запись. Generator или автор получают ограниченный набор evidence, а не абстрактное «знание базы». Для каждого отклонённого кандидата остаётся причина: access-label-not-granted, expired-at-retrieval-time или missing-exact-citation-anchor.

Ссылка в конце готового текста не исправляет неверный порядок. Draft уже мог добавить условие, которого нет в документе, или смешать две версии. Citation должна появиться в момент выбора evidence. Тогда reviewer видит claim, sourceVersion, дату и точный fragment, а не пытается восстановить происхождение ответа по памяти.

HTTP-метаданные помогают, но не заменяют внутреннюю политику. В RFC 9110 Last-Modified описывает время, когда origin server считает изменённым выбранное представление. Это полезный сигнал о представлении, но не вся политика актуальности инженерного правила. Документ может иметь собственный срок пересмотра, дату deprecation или область действия. В ответе нужно назвать, какое условие применялось.

Порядок действий

  1. Зафиксируйте точный query, requester scope и retrievedAt. Не меняйте эти значения в середине проверки.
  2. Получите top-k candidates и сохраните для каждого recordId, score, sourceVersion и все поля допуска. Не оставляйте только текстовый snippet.
  3. Проверьте access до показа excerpt. Неподходящий label превращает запись в diagnostic result, а не в материал для ответа.
  4. Сравните publishedAt и expiresAt с retrievedAt по заранее объявленной policy. Не используйте «выглядит свежим» как критерий.
  5. Проверьте citationUri и citationAnchor. Они должны вести к конкретной редакции и месту, которое можно открыть и сопоставить с claim.
  6. Откройте источник и проверьте смысл утверждения. Зафиксируйте короткое подтверждение или точную границу применимости.
  7. Если хотя бы одно обязательное свойство отсутствует у всех candidates, верните stop condition с reject reasons и владельцем следующего действия.

Что показывать читателю

Проверяемый ответ не обязан раскрывать внутренний record целиком. Читателю достаточно названия источника, версии, citation, дат публикации и retrieval, а также краткого ограничения. Конкретные роли, токены и закрытые поля не нужно включать в provenance карточку. Класс доступа можно показать только тогда, когда это разрешает сама policy.

Отдельно храните result decision и human verification. Статус candidate-found означает, что поиск нашёл похожий материал. Статус citation-accepted означает, что запись прошла технические условия. Это всё ещё не равно «утверждение истинно», пока человек не сопоставил claim с фрагментом. Такое разделение делает интерфейс честнее: пользователь видит, что уже проверено, а что ещё нет.

Ограничения и отрицательный путь

Эта схема не доказывает полноту корпуса, качество embeddings, корректность access policy или семантическую истинность ответа. Она не говорит, что vector search лучше keyword search. Она задаёт более узкую границу: score не заменяет version, freshness, authorization и citation. Учебный код не является benchmark и не сообщает production-результаты.

Если источник закрыт, просрочен или не имеет точного anchor, система не должна пересказывать его «для справки». Безопасное действие — показать безопасную причину, сохранить recordId без restricted excerpt и направить вопрос владельцу документа или policy. Если policy неизвестна, нельзя молча выбрать allow или deny как окончательное решение: нужен owner и явное правило. Отрицательный путь входит в контракт наравне с успешным.

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

Один вопрос должен проходить тестовый набор из четырёх случаев: свежая разрешённая запись с anchor, просроченная запись с высоким score, закрытая запись с высоким score и запись без anchor. В первом случае результат содержит citation и требует human verification. В трёх остальных случаях citation не появляется, а decision содержит конкретную причину отказа. Повторный запуск с теми же query, retrievedAt и входными записями даёт тот же decision. Это проверяемый критерий готовности маршрута.

После этого можно отдельно измерять recall, latency и качество ранжирования. Такие метрики улучшают поиск кандидатов, но не отменяют проверку допуска. Если команда не может показать, почему выбран конкретный fragment и почему отклонены остальные, retrieval ещё не стал надёжным источником ответа.

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

" }