Спасибо за глубокий разбор с кодом! Результаты бенчмарков в worst-case сценарии действительно впечатляют.
Но возникает философский вопрос: да, инкрементальный сборщик проигрывает по объему памяти в разы, но в абсолютных цифрах (даже на ваших утяжеленных тестах) речь идет о разнице в пару гигабайт. При этом по latency (особенно p99) инкрементальный алгоритм показывает отличные результаты, минимизируя stop-the-world паузы. Для многих realtime-сервисов (например, в финтехе или геймдеве) плавность работы и предсказуемые задержки критичнее, чем лишние гигабайты оперативной памяти на сервере, которой обычно с запасом.
Не кажется ли вам, что лишение разработчиков выбора (полный откат вместо внедрения флага запуска, как в Go или Java) — это слишком радикальный шаг со стороны core-разработчиков Python? Почему, по вашему мнению, они так упорно отказываются поддерживать два алгоритма параллельно, пусть даже инкрементальный шел бы с пометкой "experimental"?
Никакие :) Весь код проекта от первой до последней строчки написан вручную. Кода там на самом деле не так много, архитектура достаточно компактная, и мне было важно и интересно самому спроектировать взаимодействие через протоколы (typing.Protocol) и логику конвейера. Так что в плане логики тут исключительно чистый ручной крафт!
Единственное — я привлекал LLM, чтобы немного "причесать" структуру и английский язык в README. Но на саму кодовую базу это никак не повлияло.
Спасибо, за вопрос) В статье «BM25 против эмбеддингов» — скорее про почему BM25 появился раньше, а не про то, что эмбеддинги хуже. BM25 и эмбеддинги не противники, а комплементарные сигналы. Сейчас в WhyTrend они просто живут как два независимых Ranker (BM25Ranker / EmbeddingRanker) — в pipeline пока выбирается один, без merge.
Гибрид через RRF как раз хорошо ложится на архитектуру: отдельный HybridRanker, который гоняет оба скорера и склеивает списки. Это логичный следующий шаг по качеству retrieval, рядом с тем, что уже обсуждаем для слоя после ranker (пороги relevance, более умный rerank, меньше ложных корреляций).
По контекстуальному чанкингу — идея сильная для длинных документов, но у WhyTrend evidence чаще уже короткое (title + snippet из API). Там выигрыш меньше, чем в классическом RAG по PDF/базе знаний. Имеет смысл там, где collector тащит длинные тексты (релизы, тикеты, внутренние wiki) — тогда summarize-before-embed как опция к embedding-ветке.
Заголовок, похоже, получился чуть конфликтнее, чем задумывалось 🙂
Спасибо за вопрос — это главная ловушка explainable anomaly detection / RAG.
WhyTrend не претендует на доказательство причинности. Задача сейчас — быстро собрать кандидатов и черновик гипотезы, а не заменить каузальный анализ.
Как снижаем риск ложной корреляции (мягко):
Ограничение контекста. LLM видит только collectors + ranker; промпт запрещает выдумывать факты и просит при слабом evidence снизить confidence. Галлюцинаций меньше, но «дотянуть» историю из совпадения по дате модель всё ещё может.
Прозрачность. В отчёте остаются cited causes (источник, URL, score) и confidence — аналитик видит, на чём стоит вывод, и может отбраковать мусор.
Модульность. Для узких метрик лучше свои collectors (деплои, акции, логи), а не надежда, что Google News + BM25 отделят причину от шума.
Жёсткого фильтра «только совпадение → drop», каузальных тестов и обязательного no_causal_link пока нет: BM25/embeddings ранжируют похожесть текста, не причинность.
В планах — слой после ranker (до explainer): пороги relevance, умнее rerank и явный вердикт (evidence_insufficient / correlation_only / likely_cause). Плюс на стороне ряда в dev уже есть ZScoreDetector, ProphetDetector и RupturesDetector — разные типы аномалий ловим, а вот anti false-correlation для объяснений как раз следующий шаг что добавиться в roadmap. Спасибо, что подсветили это.
1. Изоляция источников. Коллекторы крутятся через asyncio.gather(..., return_exceptions=True). Если Reddit отдаёт 429 или падает с ошибкой, исключение не валит весь pipeline: этот источник просто не добавляет evidence, остальные продолжают работу.
2. Таймауты HTTP. У встроенных collectors из коробки есть timeout (по умолчанию 10 секунд) на httpx-клиент — «зависший» сокет не держит конвейер бесконечно. Значение можно подкрутить в конструкторе, например RedditCollector(timeout=5.0).
Важно: это таймаут сетевых запросов, а не отдельный deadline на весь кастомный collect(). Retry при 429 и circuit breaker пока не встроены — при желании их легко добавить в свой collector или обертку. Идея та же: один капризный источник не должен ронять объяснение аномалии целиком.
Да — на одних только стандартных публичных коллекторах (Wikipedia / HN / общий Google News) узкий B2B-кейс вроде локального магазина автозапчастей обычно не взлетит: в открытом интернете часто просто нет сигнала, а BM25 ранжирует уже собранное и «из воздуха» индустриальный контекст не достанет.
WhyTrend задуман как фреймворк (подключаемые Source / Collector / Ranker / Explainer), а не готовое SaaS «под любую нишу из коробки». Сила в том, что под задачу пишете свой CompetitorPromoCollector или OrdersSQLCollector, цепляете в Pipeline — а ранжирование и суммаризацию WhyTrend берёт на себя. Для тихих бизнес-метрик кастомные (часто внутренние) collectors — нормальный и ожидаемый путь, а не костыль.
Фреймворк модульный и сам по себе «внутренности» компании не видит. Если в Pipeline подключены только внешние источники (Google News, Hacker News и т.п.), а про ваш деплой / падение БД / наплыв ботов в открытом интернете тишина, WhyTrend не сможет надёжно назвать настоящую причину: у него просто нет этих данных.
В лучшем случае, когда внешний контекст пустой или явно слабый, LLM по системному промпту должна сказать, что внешних подтверждений нет, и снизить confidence. Но если внешние новости за ту же дату просто «рядом» по словам, модель всё ещё может ошибочно к ним притянуться — это не железный отказ по низкому BM25 (ranker сейчас ранжирует, а не отбрасывает шум).
Прелесть WhyTrend в другом: пользователь может написать свой InternalSlackCollector или GitLabReleasesCollector на базе BaseCollector, подключить через .add_collector(...) в Pipeline — и тогда фреймворк сможет связать внутренний деплой / инцидент с графиком метрик. Без внутренних источников остаётся честный ответ: «аномалия есть, внешней причины не видно».
Спасибо за отличный вопрос! Вы затыкаете пальцем самую уязвимую зону всех RAG-систем.
В текущей реализации WhyTrend эта проблема решается двумя барьерами:
Жёсткий системный промпт (Prompt Engineering): Мы не просто отдаем модели текст со словами "объясни аномалию". В Explainer зашит системный промпт с правилами:
опираться только на переданный event и evidence;
не выдумывать источники, URL и факты;
если evidence слабый или отсутствует — сказать об этом прямо и снизить confidence.
Современные LLM (вроде тех же Qwen 2.5 или DeepSeek) при таком ограничении работают на удивление честно и вместо фантазий выдают что-то вроде: "Контекст найден, но явной причинно-следственной связи не прослеживается".
Прозрачность для пользователя: Даже если модель немного притянет версию, в Markdown-отчёте WhyTrend видны cited causes: источники, URL и score. Пользователь может открыть ссылки и понять, на чём строился вывод. Если материалы мусорные — человек это быстро заметит. Инструмент не заменяет аналитика, а готовит первый черновик.
В планах — усилить отсев текстового шума ещё до LLM (например, cross-encoder / более умное переранжирование и явный вывод ranked evidence в отчёт), чтобы такие документы реже доходили до модели.
Спасибо за глубокий разбор с кодом! Результаты бенчмарков в worst-case сценарии действительно впечатляют.
Но возникает философский вопрос: да, инкрементальный сборщик проигрывает по объему памяти в разы, но в абсолютных цифрах (даже на ваших утяжеленных тестах) речь идет о разнице в пару гигабайт. При этом по latency (особенно p99) инкрементальный алгоритм показывает отличные результаты, минимизируя stop-the-world паузы. Для многих realtime-сервисов (например, в финтехе или геймдеве) плавность работы и предсказуемые задержки критичнее, чем лишние гигабайты оперативной памяти на сервере, которой обычно с запасом.
Не кажется ли вам, что лишение разработчиков выбора (полный откат вместо внедрения флага запуска, как в Go или Java) — это слишком радикальный шаг со стороны core-разработчиков Python? Почему, по вашему мнению, они так упорно отказываются поддерживать два алгоритма параллельно, пусть даже инкрементальный шел бы с пометкой "experimental"?
Никакие :) Весь код проекта от первой до последней строчки написан вручную. Кода там на самом деле не так много, архитектура достаточно компактная, и мне было важно и интересно самому спроектировать взаимодействие через протоколы (
typing.Protocol) и логику конвейера. Так что в плане логики тут исключительно чистый ручной крафт!Единственное — я привлекал LLM, чтобы немного "причесать" структуру и английский язык в README. Но на саму кодовую базу это никак не повлияло.
Спасибо за вопрос)
Спасибо, за вопрос)
В статье «BM25 против эмбеддингов» — скорее про почему BM25 появился раньше, а не про то, что эмбеддинги хуже. BM25 и эмбеддинги не противники, а комплементарные сигналы. Сейчас в WhyTrend они просто живут как два независимых
Ranker(BM25Ranker/EmbeddingRanker) — в pipeline пока выбирается один, без merge.Гибрид через RRF как раз хорошо ложится на архитектуру: отдельный
HybridRanker, который гоняет оба скорера и склеивает списки. Это логичный следующий шаг по качеству retrieval, рядом с тем, что уже обсуждаем для слоя после ranker (пороги relevance, более умный rerank, меньше ложных корреляций).По контекстуальному чанкингу — идея сильная для длинных документов, но у WhyTrend evidence чаще уже короткое (title + snippet из API). Там выигрыш меньше, чем в классическом RAG по PDF/базе знаний. Имеет смысл там, где collector тащит длинные тексты (релизы, тикеты, внутренние wiki) — тогда summarize-before-embed как опция к embedding-ветке.
Заголовок, похоже, получился чуть конфликтнее, чем задумывалось 🙂
Спасибо за вопрос — это главная ловушка explainable anomaly detection / RAG.
WhyTrend не претендует на доказательство причинности. Задача сейчас — быстро собрать кандидатов и черновик гипотезы, а не заменить каузальный анализ.
Как снижаем риск ложной корреляции (мягко):
Ограничение контекста. LLM видит только collectors + ranker; промпт запрещает выдумывать факты и просит при слабом evidence снизить confidence. Галлюцинаций меньше, но «дотянуть» историю из совпадения по дате модель всё ещё может.
Прозрачность. В отчёте остаются cited causes (источник, URL, score) и confidence — аналитик видит, на чём стоит вывод, и может отбраковать мусор.
Модульность. Для узких метрик лучше свои collectors (деплои, акции, логи), а не надежда, что Google News + BM25 отделят причину от шума.
Жёсткого фильтра «только совпадение → drop», каузальных тестов и обязательного
no_causal_linkпока нет: BM25/embeddings ранжируют похожесть текста, не причинность.В планах — слой после ranker (до explainer): пороги relevance, умнее rerank и явный вердикт (
evidence_insufficient/correlation_only/likely_cause). Плюс на стороне ряда вdevуже естьZScoreDetector,ProphetDetectorиRupturesDetector— разные типы аномалий ловим, а вот anti false-correlation для объяснений как раз следующий шаг что добавиться в roadmap. Спасибо, что подсветили это.Хороший вопрос. Да, это учтено на двух уровнях.
1. Изоляция источников. Коллекторы крутятся через
asyncio.gather(..., return_exceptions=True). Если Reddit отдаёт 429 или падает с ошибкой, исключение не валит весь pipeline: этот источник просто не добавляет evidence, остальные продолжают работу.2. Таймауты HTTP. У встроенных collectors из коробки есть
timeout(по умолчанию 10 секунд) на httpx-клиент — «зависший» сокет не держит конвейер бесконечно. Значение можно подкрутить в конструкторе, напримерRedditCollector(timeout=5.0).Важно: это таймаут сетевых запросов, а не отдельный deadline на весь кастомный
collect(). Retry при 429 и circuit breaker пока не встроены — при желании их легко добавить в свой collector или обертку. Идея та же: один капризный источник не должен ронять объяснение аномалии целиком.Да — на одних только стандартных публичных коллекторах (Wikipedia / HN / общий Google News) узкий B2B-кейс вроде локального магазина автозапчастей обычно не взлетит: в открытом интернете часто просто нет сигнала, а BM25 ранжирует уже собранное и «из воздуха» индустриальный контекст не достанет.
WhyTrend задуман как фреймворк (подключаемые Source / Collector / Ranker / Explainer), а не готовое SaaS «под любую нишу из коробки». Сила в том, что под задачу пишете свой
CompetitorPromoCollectorилиOrdersSQLCollector, цепляете вPipeline— а ранжирование и суммаризацию WhyTrend берёт на себя. Для тихих бизнес-метрик кастомные (часто внутренние) collectors — нормальный и ожидаемый путь, а не костыль.Фреймворк модульный и сам по себе «внутренности» компании не видит. Если в Pipeline подключены только внешние источники (Google News, Hacker News и т.п.), а про ваш деплой / падение БД / наплыв ботов в открытом интернете тишина, WhyTrend не сможет надёжно назвать настоящую причину: у него просто нет этих данных.
В лучшем случае, когда внешний контекст пустой или явно слабый, LLM по системному промпту должна сказать, что внешних подтверждений нет, и снизить confidence. Но если внешние новости за ту же дату просто «рядом» по словам, модель всё ещё может ошибочно к ним притянуться — это не железный отказ по низкому BM25 (ranker сейчас ранжирует, а не отбрасывает шум).
Прелесть WhyTrend в другом: пользователь может написать свой
InternalSlackCollectorилиGitLabReleasesCollectorна базеBaseCollector, подключить через.add_collector(...)в Pipeline — и тогда фреймворк сможет связать внутренний деплой / инцидент с графиком метрик. Без внутренних источников остаётся честный ответ: «аномалия есть, внешней причины не видно».Спасибо за отличный вопрос! Вы затыкаете пальцем самую уязвимую зону всех RAG-систем.
В текущей реализации WhyTrend эта проблема решается двумя барьерами:
Жёсткий системный промпт (Prompt Engineering): Мы не просто отдаем модели текст со словами "объясни аномалию". В
Explainerзашит системный промпт с правилами:опираться только на переданный event и evidence;
не выдумывать источники, URL и факты;
если evidence слабый или отсутствует — сказать об этом прямо и снизить confidence.
Современные LLM (вроде тех же Qwen 2.5 или DeepSeek) при таком ограничении работают на удивление честно и вместо фантазий выдают что-то вроде: "Контекст найден, но явной причинно-следственной связи не прослеживается".
Прозрачность для пользователя: Даже если модель немного притянет версию, в Markdown-отчёте WhyTrend видны cited causes: источники, URL и score. Пользователь может открыть ссылки и понять, на чём строился вывод. Если материалы мусорные — человек это быстро заметит. Инструмент не заменяет аналитика, а готовит первый черновик.
В планах — усилить отсев текстового шума ещё до LLM (например, cross-encoder / более умное переранжирование и явный вывод ranked evidence в отчёт), чтобы такие документы реже доходили до модели.