Команда загрузила в индекс Google Drive целиком. Там оказались пять копий регламента, протоколы встреч, выгрузки Jira и PDF без извлечённого текста. На вопрос о лимите система возвращала четыре разных числа. Увеличение top_k только покупало больше мусора за токены.
Поисковая система начинается не с vector database, а с правил корпуса. У документа должны быть владелец, канонический идентификатор, версия, статус публикации и срок пересмотра. Индексация без этих полей превращает поиск в склад, где новый файл лежит рядом со старым и оба считаются правдой.
Конвейер качества
source -> parse -> normalize -> deduplicate -> classify
-> validate metadata -> publish version -> chunk/embed -> indexПубликацию полезно делать атомарно: новая версия полностью подготовлена, затем становится активной; старая перестаёт участвовать в retrieval. Content hash убирает побайтовые копии, но смысловые дубли приходится находить отдельно.
import hashlib
def canonical_hash(text: str) -> str:
normalized = " ".join(text.split()).casefold()
return hashlib.sha256(normalized.encode("utf-8")).hexdigest()
REQUIRED = {"document_id", "version", "owner", "effective_at", "status"}
def validate_document(doc):
missing = REQUIRED - doc.metadata.keys()
if missing:
raise ValueError(f"missing metadata: {sorted(missing)}")
if len(doc.text.strip()) < 100:
raise ValueError("parser produced suspiciously little text")Наблюдаемые признаки помойки
Доля запросов без клика на источник растёт; один документ занимает большую часть top-k; старые версии появляются рядом с новыми; parser выдаёт пустые страницы; у документов нет владельца; индекс расходится с каталогом. Это метрики данных, не модели.
Failure modes тоже лежат в данных. OCR путает цифры. Таблица теряет заголовки. Confluence отдаёт вложения без ACL. Инкрементальная загрузка создаёт дубликат после переименования. Удаление источника не доходит до индекса. Смена embedding-модели оставляет половину корпуса в старом пространстве.
Для эксплуатации нужен quarantine: подозрительный документ сохраняется с причиной отказа, но не участвует в поиске. Иначе загрузчик либо молча теряет данные, либо публикует всё, что смог прожевать.
Правило главы
Индекс является производной от управляемого каталога. Его можно перестроить. Если источник, версия и владелец неизвестны, фрагмент нельзя выдавать как свидетельство.
Практикум
Возьмите сто документов из настоящего источника. Посчитайте дубли, пустые результаты парсинга, документы без владельца и одновременно активные версии. Добавьте quarantine и атомарную публикацию. Затем удалите один документ, переименуйте другой и загрузите третью версию регламента. Проверьте каталог, индекс, citations и кэш.