14 KiB
AI Update по ТЗ
Дата: 2026-04-18
Что просмотрено
doc/ТЗ_на_хакатон_Индексация_и_поиск_по_сообщениям.pdfREADME.mddocker-compose.ymlindex/main.py,search/main.pyindex/Dockerfile,search/Dockerfileindex/Makefile,search/Makefiledata/Go Nova.json
По примеру данных:
- всего сообщений:
25 - с
parts:14 - с цитатами:
5 - с пересланными сообщениями:
2 - с
mentions:4 - с
file_snippets:1 - системных сообщений:
1
Это важно, потому что в текущем коде часть этих сигналов либо не используется вообще, либо теряет смысл при индексации.
Что уже соответствует ТЗ
- В репозитории есть оба требуемых сервиса:
indexиsearch. - Обязательные endpoints реализованы:
GET /health,POST /index,POST /sparse_embeddingвindex/main.pyGET /health,POST /searchвsearch/main.py
- Контракты request/response по основным endpoint'ам не менялись и в целом совпадают с шаблоном и ТЗ.
searchиспользуетQdrant, dense endpoint и reranker через HTTP.- В обоих Dockerfile sparse-модель предзагружается внутрь образа, что соответствует оффлайн-ограничению контейнеров.
HOSTиPORTчитаются из env, как требует ТЗ.
Что отсутствует или реализовано частично
1. Обогащенный вопрос из ТЗ почти не используется
В search/main.py:90-100 описаны поля:
search_textvariantshydekeywordsentitiesdate_mentionsdate_rangeasker
Но в реальном поиске используется только question.text:
search/main.py:307-316
Это главный недобор относительно ТЗ. Само ТЗ явно дает эти поля как сигналы для retrieval, а код их сейчас просто игнорирует.
2. Метаданные чанков объявлены, но не участвуют в поиске
В README.md:52-58 отдельно сказано, что в metadata чанка сохраняются:
participantsmentionscontains_forwardcontains_quote
В search/main.py:132-145 есть модель ChunkMetadata, но дальше она никак не используется в query_points. Поиск не делает:
- фильтрацию по
mentions - фильтрацию по
participants - учет
thread_sn - учет временного диапазона через
start/end - отдельную обработку quote/forward чанков
То есть сильный канал улучшения качества уже предусмотрен схемой, но сейчас не задействован.
3. Реранк отбрасывает часть кандидатов
Сейчас:
- retrieval берет до
20чанков:search/main.py:174-177 - rerank берет только первые
10:search/main.py:278-296 - после rerank возвращаются только эти
10, а хвост11-20теряется:search/main.py:321-328
Это не нарушение контракта, но это реальная потеря recall.
4. Выдача не дедуплицируется и не ограничивается по полезному top-K
Сейчас message_ids просто конкатенируются:
search/main.py:323-328
Проблемы:
- дубликаты message id не удаляются
- результаты не агрегируются по лучшему score сообщения
- нет явного ограничения на топ полезных
50, хотя именноK=50участвует в метрике из ТЗ
Если один и тот же message_id попал в несколько чанков, он тратит место в выдаче.
5. Индексация пока очень базовая: фиксированные символьные чанки
В index/main.py:118-190 чанки строятся просто по длине строки:
CHUNK_SIZE = 512OVERLAP_SIZE = 256- разбиение идет по символам, а не по сообщениям, тайм-гепам, тредам или смысловым блокам
Из-за этого:
- длинные пересланные сообщения и цитаты могут резаться в неудобных местах
- один и тот же смысловой блок может быть разнесен по чанкам неестественно
- overlap строится по хвосту текста, а не по границе сообщений
6. page_content, dense_content и sparse_content сейчас одинаковые
См. index/main.py:180-186.
ТЗ прямо оставляет это место как точку оптимизации качества, но пока этот резерв не используется.
7. Индексация берет только text и parts[*].text, остальное почти теряется
См. index/main.py:99-115.
Сейчас не используются как поисковые сигналы:
sender_idmentionsfile_snippetsmember_eventthread_snis_hiddenis_system- явное различение
quoteиforward
Особенно важные пробелы:
member_eventу системных сообщений сейчас фактически пропадает, если обычного текста нетfile_snippetsне разбирается, хотя там могут быть имена файлов, URL и служебные поля- запросы вида "кто писал", "кого упоминали", "какой файл/документ кидали" сейчас поддержаны слабо
8. Смысл quote и forward не маркируется
В index/main.py:105-113 текст из parts просто подшивается в общий текст без явных маркеров вида:
- "цитата:"
- "пересланное сообщение:"
- "автор цитаты:"
В итоге dense/sparse видят просто общий текстовый комок. Для поиска по обсуждениям это ощутимая потеря контекста.
9. Есть расхождение между локальной инфраструктурой и ТЗ
По ТЗ для search ожидается API_KEY.
В коде это поддержано:
search/main.py:21-30search/main.py:42-47search/Makefile:10-15
Но локальный docker-compose.yml:57-60 требует OPEN_API_LOGIN и OPEN_API_PASSWORD.
Итог:
- сам сервис гибче ТЗ
- локальный compose не повторяет боевую схему из ТЗ один в один
Это не ломает контракт, но может запутать при локальной отладке.
10. Есть еще одна инфраструктурная несостыковка со сдачей
В doc/upload_to_docker.md:45-48 явно сказано собирать образы с --platform linux/amd64.
Но index/Makefile:16-18 и search/Makefile:26-28 собирают без --platform linux/amd64.
На x86 это может пройти незаметно, а на ARM-машине дать неправильный образ для отправки.
11. Есть неоднозначность между README и PDF по sparse в search
README.md:211говорит, что sparse-модель дляsearchдолжна быть локально внутри образа- PDF в формулировке требований к
Search Serviceделает акцент, что обращения к dense/sparse/rerank идут через проверяющую систему
Текущий код следует логике README/example: sparse считается локально в search/main.py:148-151 и search/main.py:198-207.
Я бы это не считал блокером, но как минимум это место стоит держать в голове как неоднозначное требование.
Что улучшать в первую очередь
Приоритет 1. Начать использовать все поля question
Минимально стоит задействовать:
search_textкак основной нормализованный запросvariantsкак дополнительные формулировкиhydeкак дополнительные dense-запросыkeywordsкак основу для sparseentitiesдля фильтров и lexical boostdate_rangeиdate_mentionsдля ограничения по времениaskerкак сигнал по людям и email
Самый логичный путь без смены стэка: несколько dense/sparse запросов + fusion в Qdrant.
Приоритет 2. Перестроить chunking под структуру чата, а не под символы
Нужны чанки по:
- окнам сообщений
- временным разрывам
- границам thread/forward/quote
- ограничению на размер по сообщениям, а не только по символам
Для чатов это обычно дает больше пользы, чем любые косметические тюнинги rerank.
Приоритет 3. Развести page_content, dense_content, sparse_content
Хорошая схема:
page_content: читабельный исходный текст чанкаdense_content: нормализованный текст с ролями, автором, маркерами quote/forwardsparse_content: keyword-heavy версия с email, mentions, именами файлов, ссылками, документами, леммами
Сейчас эта возможность не используется вообще.
Приоритет 4. Нормально собирать финальную выдачу
Нужно:
- не терять кандидатов после rerank
- удалять дубликаты
message_id - агрегировать по лучшему score сообщения или чанка
- отдавать осмысленный top-50
Это прямой выигрыш по Recall@50 и nDCG@50.
Приоритет 5. Начать использовать metadata в Qdrant
Особенно полезно для:
mentionsparticipantscontains_quotecontains_forwardthread_snstart/end
Для многих вопросов это позволит не просто "лучше ранжировать", а сразу отрезать нерелевантный шум.
Приоритет 6. Превратить скрытые сигналы в индексируемый текст
Стоит отдельно материализовать:
member_eventв текст вида "пользователь X добавил Y"file_snippetsв текст вида "файл: NAME, url: ..."- автора сообщения
- список упомянутых пользователей
Сейчас эти сигналы либо не попадают в индекс, либо попадают слишком слабо.
Что можно добавить по Python-библиотекам, не меняя стек
Стек Qdrant менять не нужно. Самые полезные добавки я бы смотрел такие:
pymorphy3для лемматизации русских слов при подготовкеsparse_contentrazdelдля аккуратной токенизации русского текстаrapidfuzzдля точного lexical match по именам, email, документам, ссылкам и названиямpython-dateutilилиdateparserдля нормализации дат, если захотите усиливать работу сdate_mentionstenacityдля аккуратных retry/timeout-оберток вокруг dense/rerank HTTP вызовов
Что не нужно делать:
- менять
Qdrant - тащить внешние LLM/API
- усложнять архитектуру ради "модности", пока не использованы базовые сигналы из самого ТЗ
Короткий вывод
Сейчас репозиторий соответствует ТЗ как рабочий базовый шаблон, но почти не использует те сигналы, ради которых это ТЗ вообще интересно:
- обогащение вопроса
- metadata чанков
- структуру chat messages
- сигналы автора, упоминаний, файлов, системных событий, цитат и пересылок
Самый большой потенциал улучшения здесь не в замене базы или модели, а в трех вещах:
- умный chunking
- multi-query hybrid retrieval
- использование metadata и нормальной сборки финального top-50