{ "index": 101, "slug": "editorial-2025-03-mechanism-knowledge-retrieval", "title": "Почему высокий vector score не доказывает ответ", "excerpt": "Retrieval находит похожие фрагменты, но не подтверждает их свежесть, доступность и смысл. Разбираем границу между ranking и проверяемым источником.", "contentHtml": "

Проблема. Поиск по инженерной базе возвращает первый фрагмент с высоким vector score. Фрагмент похож на вопрос, поэтому ответ выглядит уверенно. Через несколько дней выясняется, что документ описывает старый контракт, закрыт для автора запроса или не содержит точного правила, на которое ссылается ответ.

\n

Симптом. В результате есть title, excerpt и число вроде 0.98, но нет версии источника, срока действия, access decision и anchor. Инженер меняет код по старой инструкции. Цена ошибки. Команда тратит время на повторное расследование: нужно найти нужную редакцию, выяснить права и отделить цитату от пересказа. Ошибка также подрывает доверие к базе знаний. Следующий хороший фрагмент начинают игнорировать вместе с плохим.

\n

Тезис. Vector score решает одну задачу: упорядочивает похожие candidates в выбранной модели поиска. Он не доказывает истинность claim, право читать источник, актуальность документа и полноту найденных материалов. Поэтому retrieval должен завершаться не выбором top-1, а проверяемым решением: candidate допускается к ответу только после проверок access, freshness, provenance и exact citation.

\n

Где заканчивается score

\n

Nearest-neighbor поиск сравнивает запрос с векторами документов. Его результат помогает сократить очередь чтения. Это полезно, когда корпус велик и человек не может открыть все записи. Но score не знает, кто задаёт вопрос. Он не знает, отозвал ли владелец документ. Он не знает, поддерживает ли найденный абзац именно тот claim, который собирается сделать система.

\n

Порог похожести не исправляет эту границу. Представим учебный корпус из трёх записей. Просроченная инструкция получила score 0.97, закрытое исключение — 0.99, а текущая разрешённая инструкция — 0.91. Фильтр score >= 0.90 оставит все три. Сортировка поставит опасные записи выше нужной. Это не результат реального сервиса, а минимальный пример, который показывает, почему ranking нельзя использовать как authorization или evidence.

\n
Что сообщает каждый сигнал
СигналВопросДействиеЧего он не доказывает
vectorScoreНасколько candidate похож на query?Ставит запись в очередь проверки.Что claim верен, свеж и доступен.
accessLabelsРазрешён ли класс источника requester scope?Отбрасывает закрытый фрагмент.Что выполнена вся реальная policy identity.
publishedAt / expiresAtВходит ли запись в объявленное окно свежести?Отбрасывает будущие и просроченные записи.Что это единственная актуальная редакция.
citationUri#anchorМожно ли открыть точное место в конкретной версии?Делает candidate адресуемым.Что источник поддерживает более широкий вывод.
human verificationСовпадает ли claim с открытым фрагментом?Разрешает сформулировать ответ.Что одна проверка заменяет владельца policy.
\n

Запись индекса должна сохранять происхождение

\n

Chunking часто оставляет в индексе только текст и embedding. Заголовок, версия и срок действия остаются в исходном документе. После retrieval система показывает удобный snippet, но не может ответить на простой вопрос: из какой редакции он взят? Одинаковая фраза может встречаться в новой инструкции, старом RFC и закрытом исключении.

\n

Минимальная запись должна связывать chunk с source record. Source record хранит sourceId, sourceVersion, owner, publishedAt и URI. Chunk хранит sourceId, текстовый фрагмент, citationAnchor, indexedAt, expiresAt и access labels. Retrieval event сохраняет query, момент поиска, список candidates и причины отказа. Система может не передавать все поля в пользовательский интерфейс, но decision должен иметь к ним доступ.

\n

Если у chunk нет стабильной связи с редакцией, он остаётся подсказкой для поиска. Если у него нет anchor, он не становится точной цитатой. Если у него нет declared policy свежести, приложение не должно молча называть его текущим. Эти ограничения лучше показать явно, чем восстанавливать по смыслу из текста.

\n
\"Матрица
Score сокращает очередь проверки. Матрица не объявляет самый похожий фрагмент доказательством: доступ, срок и адресуемая цитата имеют отдельные границы.
\n

Проверяйте источник до составления ответа

\n

Надёжный порядок начинается с evidence, а не с готового текста. Сначала система получает candidates и их metadata. Затем она применяет policy к каждому candidate. Только оставшиеся фрагменты передаются человеку или компоненту, который формулирует ответ. Так нельзя незаметно добавить в draft тезис, которого не было в источнике.

\n
type Candidate = {\n  id: string;\n  score: number;\n  sourceVersion?: string;\n  accessLabels: string[];\n  publishedAt?: string;\n  expiresAt?: string;\n  citationUri?: string;\n  citationAnchor?: string;\n};\n\nfunction admissible(candidate, requesterLabel, retrievalAt) {\n  const allowed = candidate.accessLabels.includes(requesterLabel);\n  const fresh = candidate.publishedAt <= retrievalAt &&\n    (!candidate.expiresAt || retrievalAt < candidate.expiresAt);\n  const citable = Boolean(\n    candidate.sourceVersion &&\n    candidate.citationUri &&\n    candidate.citationAnchor\n  );\n\n  return { allowed, fresh, citable, ok: allowed && fresh && citable };\n}\n\n// Учебный код: локальные значения, без реального corpus и identity provider.
\n

Код показывает контракт, а не готовую систему доступа. Проверка accessLabels.includes не заменяет authentication, authorization policy и аудит. Сравнение дат работает только после фиксации формата и часового пояса. Human verification всё равно нужен: технически доступный anchor может содержать исключение, которое не подтверждает общий claim.

\n

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

\n
Диагностика retrieval без смешения сигналов
СимптомПричинаПроверкаДействие
Top-1 отвечает на вопрос, но описывает старый контракт.Ranking не учитывает срок действия или version policy.Сравнить publishedAt и expiresAt с зафиксированным retrievalAt.Отклонить просроченный candidate и показать актуальную редакцию либо остановить ответ.
Фрагмент релевантен, но его нельзя открыть.Индекс получил текст без согласованной access boundary.Проверить requester scope, labels и решение владельца ресурса.Убрать candidate из answer path; не маскировать отказ новым пересказом.
Ссылка ведёт на страницу, но не на правило.Сохранён URI без версии и anchor.Открыть ссылку на том же snapshot и найти точный фрагмент.Оставить запись кандидатом, пока источник не станет адресуемым.
Ответ звучит шире цитаты.Генератор обобщил локальное исключение.Сопоставить каждое предложение claim с одним или несколькими фрагментами.Сузить формулировку, добавить limitation или отправить вопрос на ручную проверку.
После пустого результата система всё равно отвечает.Fallback подменяет отсутствие evidence вероятным текстом.Проверить negative path: все candidates expired, закрыты или без anchor.Вернуть stop status с причиной и запросить источник или решение владельца.
\n

Отрицательный путь важнее удачного top-1

\n

Проверять нужно не только случай, где нашлась хорошая запись. Система должна остановиться, если все candidates просрочены, закрыты или не имеют точного locator. Пустой результат честнее уверенного ответа без основания. Его можно объяснить и исправить: обновить документ, выдать доступ, добавить anchor или уточнить вопрос.

\n

Не смешивайте причины в один статус вроде relevance_low. access_denied ведёт к владельцу policy. expired_at_retrieval ведёт к владельцу документа. missing_citation_anchor ведёт к ingestion или структуре источника. Такое разделение экономит время и не толкает команду сразу менять embedding model.

\n

Порядок внедрения проверки

\n
  1. Зафиксируйте query, requester scope и retrievalAt. Один и тот же запрос должен иметь воспроизводимый срез времени.
  2. Сохраните для каждого candidate id, score, source version и все причины будущего отказа. Не передавайте дальше только текстовый snippet.
  3. Проверьте access до раскрытия фрагмента в answer path. Техническая доступность записи не равна праву показать её requester.
  4. Примените freshness policy. Запишите, какое поле и какой срок считаются обязательными для operational question.
  5. Проверьте provenance и citation. URI должен вести к нужной редакции, anchor — к месту, где действительно находится утверждение.
  6. Сопоставьте claim с источником. Если источник поддерживает только условие или исключение, сузьте ответ до этой границы.
  7. Если пересечение пусто, остановите ответ, верните понятный reason code и предложите следующий безопасный шаг.
  8. Закрепите проверки на отрицательных примерах: высокий score у expired, закрытый record и candidate без anchor не должны попасть в citations.
\n

Ограничения

\n

Эта схема не обещает, что vector search найдёт полный корпус. Она не сравнивает качество embedding-моделей и не утверждает, что keyword search всегда лучше. Она также не превращает metadata в доказательство смысла. Свежая доступная цитата может быть двусмысленной или слишком узкой для вопроса.

\n

Учебный код использует локальные значения и упрощённые labels. Он не проверяет реальную личность, RBAC, ABAC, SSO, журнал аудита, конкурентное обновление документа или кэш. Применять его как готовую authorization implementation нельзя. В реальной системе policy должен иметь владельца, а source version и правила expiry должны быть частью явного контракта.

\n

Ограничение есть и у даты. HTTP-поле Last-Modified сообщает время, когда origin считает представление изменённым. Оно не доказывает, что инженерное правило всё ещё применимо. Документ может не меняться и устареть из-за миграции. Поэтому freshness policy должна учитывать смысл вопроса, а не только HTTP timestamp.

\n

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

\n

Механизм готов к ограниченному применению, когда для одного зафиксированного query можно показать полный decision trace: candidates с score, source version, access result, freshness result, citation URI с anchor и итоговый human verification. На учебном отрицательном наборе expired, closed и no-anchor записи получают отдельные причины и не попадают в answer.

\n

Дополнительный критерий — повторный запуск с тем же retrievalAt даёт тот же набор допущенных записей, если corpus и policy не менялись. При пустом пересечении система возвращает stop status, а не догадку. Только после этого можно измерять top-k, reranking и стоимость запросов: эти настройки ускоряют поиск кандидатов, но не заменяют границу доказательства.

\n

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

\n" }