Проверка на живых данных вскрыла, что панель отладки врала в мою же пользу: показывала 154 046 «документов с эмбеддингом» (87%), тогда как в FAISS реально 93 053 вектора (52.5%). Колонка documents.faiss_id для этого непригодна — отметка остаётся после пересоздания индекса (смена модели: 768 → 1024) и после сбоев worker-gpu. Выборочная проверка: у 8 из 20 «отмеченных» вектора нет. - gpu.index_stats — новая задача, отдаёт реальное содержимое активного векторного бэкенда; API спрашивает её для панели отладки; - панель показывает «векторов в индексе» и отдельно предупреждает о ложных отметках (их 60 993), потому что такие документы молча выпадают из L3: в индексе их нет, а на пересчёт они не попадут — reembed ищет faiss_id IS NULL; - scripts/ops/faiss_reconcile.py — сверяет отметки с индексом и обнуляет ложные, после чего reembed_missing.py отправляет их на пересчёт. Проверен вживую (dry-run на проде: 93 053 в индексе, 60 993 ложных отметок). Документация: зафиксирована реальная глубина корпуса — полный текст только у 27.5% документов, у 79% меньше 500 отпечатков (уровень аннотации). Система ловит списывание из того, что есть целиком; это граница, а не поломка. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
9.4 KiB
Наполнение корпуса — runbook
Текущее состояние (на 2026-08-28)
- 177 147 документов, русский большинство:
ru99 884,en76 936, остальные языки — единицы/десятки. Проблема «не с чем сравнивать русские работы» из более ранней версии этого документа закрыта. - По источникам: CyberLeninka 99 883, OpenAlex 49 276, PMC 14 910, arXiv 13 077,
user_submission(проверенные пользователями работы, не источник для сравнения сами с собой — см. ARCHITECTURE.md §7) 1. - Повторный прогон по уже залитым источникам даёт почти одни дубли (типично
1490 из 1500 на источник): прирост дают только новые публикации. Реальный
рост корпуса — поднятый
limitили новые темы, а не повторный запуск.
Глубина корпуса — чем реально располагает детекция (замер 2026-08-28)
| Что | Значение | Следствие |
|---|---|---|
| Документов | 177 147 | — |
| С полным текстом в MinIO | 48 848 (27.5%) | arXiv 93.7%, КиберЛенинка 27.8%, PMC 17.0%, OpenAlex 12.7% |
| Отпечатков < 500 на документ | 128 246 (79%) | по этим документам L1 сравнивает фактически аннотацию, а не тело статьи |
| Векторов в индексе (L3) | 93 053 (52.5%) | остальные документы семантический поиск не видит |
Ложных отметок faiss_id |
60 993 | вектора нет, но документ считается векторизованным — см. faiss_reconcile.py |
Это не поломка, а честная граница возможностей: система ловит списывание из
того, что у неё есть целиком. Скопированное из тела статьи, от которой в базе
только аннотация, L1 не найдёт. Рычаги углубления: FETCH_FULL_TEXT=true
(докачка OA-PDF, уже включена), приоритет источников с полным текстом (PMC,
arXiv), и добор эмбеддингов для L3.
- OpenAlex при массовом запуске упирается в лимит вежливого пула (10 req/s на mailto, общий для всех воркеров) — пауза между страницами поднята до 1с.
- Добавлен 4-й парсер — PMC (PubMed Central,
scripts/parsers/pmc.py), англоязычные научные статьи открытого доступа. - Массовое расширение по дисциплинам теперь двумя сидерами:
seed_ru_sources.py(русскоязычные, CyberLeninka/OpenAlexlang=ru) иscripts/seed_broad_corpus.py(англоязычные — OpenAlex/arXiv/PMC по широкому списку дисциплин, добавлен позже первой волны). - Операционный риск массовой докачки (
consumer_timeoutRabbitMQ vs долгие таски) закрыт бюджетом времени прогона иworker_prefetch_multiplier=1— подробности и почему префетч тут главный, см. DR-HA.md §6.
Что подготовлено
- Парсер CyberLeninka починен (
scripts/parsers/cyberleninka.py): раньше слал GET на/api/search→ HTTP 405; теперь POST с JSON-телом (mode=articles), authors из списка, чистка<b>/HTML-сущностей. Проверено вживую (5/5 тем) + юнит-тесты (scripts/parsers/tests/), в гейте CI. - Прод-путь готов:
index.run_parser(source_id)уже умеетcyberleninkaиopenalexclang=ru. - Сидер источников:
scripts/seed_ru_sources.py— 30 студенческих дисциплин.
Запуск русской заливки
# 1. (на app-хосте / в контейнере worker-indexer, где есть psycopg2 и прод-.env
# — .env на проде теперь генерируется из Infisical на каждом деплое, см.
# ARCHITECTURE.md §10, руками его не редактировать)
# Посмотреть план:
python scripts/seed_ru_sources.py
# Создать источники в parse_sources (лимит на дисциплину):
python scripts/seed_ru_sources.py --apply --limit 500
# 2. Проверить пару источников на темпе/качестве, затем запустить заливку:
# • Админ-панель → «Источники» → «Запустить» (или «Запустить всё»), ЛИБО
# • Celery: index.run_parser.delay(source_id) по каждому id
Управление заливкой из админки
Всё, что ниже, доступно на странице «Источники» — CLI для этого больше не нужен:
- Шкала загрузки у каждого источника: стадия (выборка → индексация), сколько
получено из скольки, сколько добавлено/дублей/ошибок. Раскрытая строка —
журнал прогона по шагам с таймингами (таблица
parse_runs). - «Запустить всё» — прогон по всем включённым источникам; уже идущие пропускаются. «Остановить всё» и остановка по одному — кооперативная отмена: воркер останавливается сам на ближайшем тике, не обрывая запись в базу.
- Пакетное добавление — один тип источника + список тем (по строке),
опционально с немедленным запуском. Заменяет
seed_*.pyдля разовых расширений. - Загрузка работ в базу — PDF/DOCX/TXT прямо в корпус сравнения
(
index.ingest_upload,source=manual_upload), минуя проверку и отстойник. - Страница «Отладка» — очереди, воркеры, покрытие эмбеддингами, зависшие и упавшие прогоны (см. ARCHITECTURE.md §12).
После большой заливки — свериться с покрытием L3 (панель отладки, «Векторов в индексе»). Порядок именно такой, из двух шагов:
scripts/ops/faiss_reconcile.py(в контейнере worker-gpu) — сверяет отметкиfaiss_idс реальным содержимым индекса и обнуляет ложные. Без этого шага документы, потерявшие вектор при пересоздании индекса, не попадут на пересчёт: они всё ещё «отмечены».scripts/ops/reembed_missing.py(в контейнере worker-indexer) — отправляетgpu.embed_documentsдля всехfaiss_id IS NULL.
Оба по умолчанию dry-run, отправляют/меняют только с --apply.
Прогон со статусом partial — это не ошибка: сработал бюджет времени
(PARSER_TIME_BUDGET_S, 1500с), заливка остановилась раньше consumer_timeout
RabbitMQ. Остаток добирается повторным запуском источника.
Заливка сама: fetch (rate-limit 1 req/s) → add_document (дедуп по ext_id,
fingerprints L1, MinHash L2) → батч-эмбеддинги gpu.embed_documents (L3). ~30 тем ×
500 ≈ 15K русских документов на первый заход.
Проверка результата
SELECT lang, count(*) FROM documents GROUP BY lang ORDER BY 2 DESC; -- должен появиться ru
SELECT source, count(*) FROM documents WHERE source='cyberleninka'; -- > 0
Масштаб (следующий уровень)
- Больше тем + выше
--limit; добавить OpenAlexlang=ru(качество ниже — англ. заголовки с меткой ru). - Для миллионов — bulk (снапшот OpenAlex на S3), а не постраничный API.
- На масштабе обязателен
VECTOR_BACKEND=qdrant(FAISS flat не тянет), а таблицаfingerprints(уже ~113M строк на 177K доков — партиционирование стоит планировать заранее, не постфактум) потребует партиционирования. См. ARCHITECTURE.md и DR-HA.md.