Джарвис, найди мне всех злодеев с острова Эйнштейна
Джарвис, найди мне всех злодеев с острова Эйнштейна

Привет! У многих разработчиков бывают периоды, когда хочется сделать гениальный пет‑проект на 300кк в наносекунду. Весеннее обострение не обошло меня стороной — захотелось сделать свой Palantir. На старте я ещё не до конца понимал, что именно хочу построить, но было видение и вдохновение — большего, обычно, не нужно, свободное отобранное время находится само собой.

Вернёмся к делу: что мы делаем и, естественно, второстепенный вопрос — зачем? Вдохновившись видео про завайбкоженный палантир, я сформулировал первый тезис: «Хочу получить полную картину об интересующем событии». Это уже даёт некие очертания — какой‑то сервис, который соберёт за меня релевантную информацию и выдаст удобный отчёт.

Итак, что мы имеем:

  1. Нужно собирать информацию

  2. Нужно её очищать и индексировать

  3. Нужно уметь искать по этой прекрасной базе данных

Как не сделать из мухи слона

Начиная пет‑проект, очень легко начать переусложнять архитектуру и раздувать скоуп. Пока у нас отлично получается делать обратное — из «Palantir 2.0 russian edition» мы дошли до двух конкретных функций: сбор информации и её раздача. Осталось не построить космолёт.

Вот простой стек, который я выбрал для проекта:

  • NestJS — много опыта, легко поддерживать, агенты хорошо справляются с DI и модулями, документации навалом.

  • pgvector — дёшево и сердито. Будем хранить тексты и эмбеддинги прямо рядом с метой.

Джарвис, сделай палантир
Джарвис, сделай палантир

Теперь по пунктам: сбор информации. Тут я решил ещё немного сузить скоуп — информации в мире очень много, а треша в ней всё больше и больше. Решение простое: собираем только новости.

Лёгкий старт: многие новостные сайты предоставляют RSS‑ленты, плюс золотая кладезь — Telegram‑каналы. Оба источника зачастую легко поддаются программному скрейпингу и отдают цифровое золото в виде текста.

Для скрейпинга веб‑страниц я использую поэтапный подход — от самого дешёвого к самому дорогому:

  1. Просто скачиваем HTML. Новостные сайты обычно хорошо SEO‑оптимизированы и рендерятся на сервере. Загружаем страницу, ищем нужный блок по XPath — профит.

  2. Не профит: страница рендерится на клиенте или закрыта каким‑нибудь антибот‑решением. Подключаем тяжёлую артиллерию: Playwright + Chromium + puppeteer‑extra‑plugin‑stealth. 99% страниц нормально так открываются — дожидаемся появления элемента по XPath и забираем контент.

  3. Много чего может пойти не так — поэтому на каждый шаг вешаем retry:

    • сайт не ответил;

    • нас заподозрили в ботоводстве (hell nah);

    • сеть моргнула или ответ слишком долго грузился.

Всё это дело нужно ретраить. Поэтому ingest данных состоит из трёх шагов:

  1. Получаем список новых постов, которые мы ещё не проиндексировали, и заводим для каждого таску на обработку.

  2. Каждая таска пытается получить полный текст и сохранить его в базу. В случае ошибки — linear backoff: 60 сек × retryCount.

  3. Если за 5 попыток не получилось — горечь поражения, помечаем статью как failed.

Данные у нас, что дальше?

Отлично, мы умеем добавлять Telegram и RSS‑источники и собирать с них текст. Это уже неплохо, но всё ещё бесполезно. Нужен поиск.

Я выбрал RRF (Reciprocal Rank Fusion) — подход, при котором несколько способов найти нужную статью объединяются в одну итоговую выборку. У нас таких способов два:

BM25 (Best Match 25) — поиск по ключевым словам. Грубо говоря, смотрит на то, как часто ключевое слово встречается в статье и какой у него вес. Частые слова вроде «и» или «но» — почти шум, и в выборке они практически не влияют на результат.

Semantic Search — поиск по смыслу. Для каждого текста считаем эмбеддинги — векторное представление, в котором закодирован смысл. То же самое делаем для поисковой фразы: считаем вектор и ищем документы, максимально близкие к нему по дистанции.

Интуитивно: представьте градусник. Одна ось — температура. Кладём его на стол — появляется вторая ось: левее или правее. Теперь представьте, что таких осей 1536 — это и есть наши «смысловые координаты», они же эмбеддинги.

Для этого я использую text‑embedding-3-small от OpenAI с размерностью 1536 — дёшево и сердито, и в моих масштабах куда проще, чем поднимать локальную модель.

Прорезюмируем, как работает поиск:

  1. Пользователь отправляет поисковую фразу, например: «санкции против России».

  2. Считаем для неё эмбеддинги через text‑embedding-3-small.

  3. Делаем два запроса в pgvector (postgres с поддержкой векторных полей):

    1. найди статьи, которые содержат слова «санкц», «против», «росс» — это BM25;

    2. найди статьи, которые по смыслу похожи на «санкции против России» — это Semantic Search.

  4. Объединяем результаты через RRF. Для каждой статьи считаем score по формуле:

score = 1 / (rank + k)

где rank — позиция статьи в выдаче отдельного поиска, k — константа (обычно 60, потом тюнится). Складываем score из обоих поисков. В лучшем случае в топ залетают статьи, которые совпадают и по смыслу, и по ключевым словам. Если совпадение только по ключевым словам, а по смыслу — мимо, статья окажется ниже в выдаче.

Как этим пользоваться и для чего?

Смотрите, я сделал
Смотрите, я сделал

Прототип собран! Добавили кучу источников, данные собираются, поиск работает... но как этим вообще пользоваться? Ответ пришёл сам собой — я попросил своего верного товарища Claude Code исправить баг в поиске и понял: пусть он сам ищет нужные новости, агрегирует и обобщает их. 2026 год на дворе, в конце концов!

Решение напрашивалось: реализовать MCP-сервер, с которым любой ИИшке будет легко взаимодействовать.

По сути, MCP-сервер — это обёртка над уже существующим API, поэтому в подробности вдаваться не буду. Сервер живёт по адресу: https://api.vestly.ru/mcp

Локально его подключить можно так:

{
  "mcpServers": {
    "vestly": {
      "type": "http",
      "url": "https://api.vestly.ru/mcp"
    }
}

Рабочий пример в claude

Используя vestly составь мне подробный таймлайн блокировки телеграм в России - со всеми несостыковками из разных источников и официальной повестки. Сделай вывод - каков шанс полной блокировки, какой сентимент у граждан РФ

Part 1
Part 1
Part 2
Part 2
Resume
Resume

Мне кажется, получилось очень интересно: подробный отчёт вкупе с наглядной визуализацией от Claude — и в тему можно вникнуть за считанные минуты.

Его слова, не мои
Его слова, не мои
Каждый шаг в таймлайне кликабельный
Каждый шаг в таймлайне кликабельный

Резюмируя: да, это самопиар — но, кажется, полезный. В нашей неспокойной реальности, где всё нужно перепроверять и сравнивать источники, такой инструмент явно не лишний.

Сервис бесплатный. Если будет спрос и из этого вырастет полноценный продукт — сделаю, но не обещаю, что скоро.

Пара оговорок: исторических данных пока немного — индексация идёт примерно с 25 марта. Если руки дойдут и спрос будет, реализую исторический сбор тоже. На сейчас проиндексировано ~70 000 постов из 260 источников.

И бонусом — небольшой Telegram‑бот: можно подписаться на интересующий топик и получать новости из разных источников.

Пример использования
Пример использования

Все ссылки:

С удовольствием отвечу на ваши вопросы в комментариях и обработаю запросы на новые фичи или обратную связь.

Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.
Читаете новости?
33.33%Да, регулярно2
0%Да, подборки0
66.67%Если зацепит превью/тайтл4
0%Нет, игнорирую новости0
Проголосовали 6 пользователей. Воздержавшихся нет.