Как я создал аналог NotebookLM: эффективное обучение с JuliaLM

Как я создал аналог NotebookLM: эффективное обучение с JuliaLM

Устав от проблем с доступом к зарубежным сервисам, я решил создать собственный аналог NotebookLM — доступный инструмент для работы с исследовательскими материалами, который подойдёт не только жителям России, но и всем, кто ищет альтернативу импортным решениям.

Что такое NotebookLM и зачем его заменять?

NotebookLM — это сервис, позволяющий загружать научные статьи, PDF, видео и другие материалы, а затем задавать по ним вопросы на естественном языке. Система анализирует контент, строит базу знаний, генерирует выжимки и даже помогает готовиться к экзаменам через флешкарточки.

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

Зачем делать своё, если есть ChatGPT или RAG?

Да, ChatGPT умеет работать с PDF. Но только пока речь о паре файлов. Когда нужно проанализировать 15 статей для курсовой, модель начинает путать авторов, приписывать одни выводы другим. Не потому что тупая — просто это чат. Закрыл вкладку — и всё забыто.

Можно собрать RAG самостоятельно. Есть фреймворки: LlamaIndex, Haystack. Они режут текст на чанки, векторизуют, ищут. На чистом PDF — красиво. В реальности: скан учебника превращается в мусор, YouTube-лекции не парсятся, а поиск возвращает одни и те же фрагменты из 40 источников.

Я решил сделать систему, где источники загружаются один раз и остаются в блокноте навсегда. Система сама решает, сколько контекста взять из каждого документа. Плюс — инсайты, заметки, флешкарточки. Всего этого нет в стандартных RAG-решениях.

Как это выглядит для пользователя

Работа строится вокруг блокнотов — проектов вроде «Курсовая по нейробиологии» или «Подготовка к ЕГЭ». Внутри — источники, чат, заметки и карточки.

Загрузил материалы один раз — они остаются. Через месяц добавил новую статью к 20 старым — задал вопрос. JuliaLM видит всё сразу. Не нужно начинать с нуля.

Источники — это PDF, ссылки, YouTube-видео, сканы, текст из буфера. JuliaLM сама извлекает текст, делает саммари и готовит фрагменты для поиска.

Чат — задаёшь вопрос, получаешь ответ с привязкой к источникам. Без галлюцинаций. Только по тому, что загружено.

Архитектура

Фронтенд — Nuxt.js, бэкенд — FastAPI, основная база — SurrealDB. Всё в Docker Compose.

SurrealDB — неочевидный выбор. Это мультимодальная база: реляционные таблицы, графовые связи и встроенный векторный поиск — всё в одном. Можно спорить о зрелости, но для моего случая это заменило Postgres + Redis + Pinecone. Один сервис вместо трёх — проще деплоить и дешевле поддерживать.

Что происходит при загрузке источника

Пользователь добавляет источник: ссылку, файл или текст. Запускается многоступенчатый пайплайн.

Обработка очереди

Обработку нельзя делать синхронно. PDF на 200 страниц может парситься 30–40 секунд. Держать HTTP-соединение — плохая идея: таймауты, обрывы, спиннер без прогресса.

Решение: два режима. Для лёгких источников — синхронная обработка (5–10 сек). Для тяжёлых — асинхронная: API сразу возвращает command_id, статус «Обработка…», фронтенд опрашивает прогресс.

Очередь задач — своя реализация на SurrealDB. Можно было взять Celery или RQ, но это ещё один Redis. Наш вариант хранит задачи в базе: с ретраями (до 5 попыток), экспоненциальным бэкоффом и отслеживанием прогресса. Не для масштаба, но для нас — работает.

Извлечение текста: каскад фолбэков

Тип источника определяет путь извлечения. Для каждого — не один метод, а цепочка с резервными вариантами.

PDF и документы: основной парсер — Docling (через обёртку content_core). Он сохраняет структуру: заголовки, таблицы, списки — в чистый Markdown. Для PPTX или XLSX — Gotenberg (headless LibreOffice), который конвертирует почти всё.

YouTube: три уровня:

  • youtube-transcript-api — официальные субтитры. Приоритет: русские → английские → испанские/португальские (для академического контента).
  • pytubefix с флагом ANDROID — притворяется мобильным приложением, реже блокируется.
  • Firecrawl или Jina — крайние случаи, когда всё сломалось.

Каждые пару месяцев YouTube что-то ломает. Каскад спасает: пользователь не замечает сбоев.

Веб-страницы: основной — Playwright (headless Chromium) с полным рендерингом JS. Рандомизируем User-Agent, viewport, navigator — иначе капча или пустая страница. Текст извлекаем через readability (алгоритм из Firefox). Если не сработало — Jina Reader, потом простой HTTP. Опять каскад.

Изображения: сканы и фото отправляются в vision-модель как base64. Медленно, но хорошо работает с рукописным текстом и плохими сканами.

Векторизация: нарезка, эмбеддинги и грабли

После извлечения текста — подготовка к семантическому поиску. Это фоновая задача vectorize_source.

Нарезка на чанки

Фрагменты по ~500 токенов с перекрытием 15%. Сплиттер использует приоритеты: сначала двойные переносы (абзацы), потом одинарные, точки, запятые, пробелы — чтобы не рвать предложения.

Почему 500 токенов? Маленькие чанки (100–200) — точный поиск, но теряется контекст. Большие (1000+) — наоборот. 500 — эмпирическая золотая середина для научных статей и конспектов.

Четыре стратегии поиска

При вызове search_sources запускаются четыре стратегии параллельно:

  • Vector search — семантический поиск по эмбеддингам. Находит по смыслу: «проблемы со сном» → «нарушения циркадных ритмов».
  • Text search — классический BM25. Точные совпадения ключевых слов.
  • Title search — поиск по заголовкам. Быстро, если знаешь, откуда информация.
  • Insight search — поиск по саммари и инсайтам. Полезно, когда ключевые тезисы уже выделены.

Результаты дедуплицируются: если три стратегии нашли один источник — это сильный сигнал. Скор увеличивается на +0.05 за каждое дополнительное попадание. Возвращаются топ-5.

Почему четыре? Ни одна стратегия не работает везде. Комбинация покрывает больше случаев.

Бюджет контекста: как не взорвать окно модели

Проблема: модель нашла 5 релевантных источников, но каждый — по 100 страниц. Отдать всё? Не влезет. По одному чанку? Потеряем контекст.

Решение — система бюджетов. Общий лимит на запрос — 300 000 символов (~75K токенов). На каждый источник — минимум 5 000, максимум 40 000 символов.

Три сценария:

  • Короткий источник — отдаём целиком.
  • Длинный, запрос известен — инсайты + релевантные чанки. Запускаем vector search внутри документа (порог ≥ 0.15).
  • Длинный, запрос неизвестен — инсайты + начало текста. Лучше, чем ничего.

Так можно работать с книгами на сотни страниц, не перегружая контекст.

Флешкарточки и интервальное повторение

Отдельная фишка: генерация флешкарточек с интервальным повторением.

Пользователь выбирает источники, указывает количество карточек — модель генерирует пары «вопрос-ответ». Один факт — одна карточка. Формат — строгий JSON, с валидацией.

После генерации — FSRS (Free Spaced Repetition Scheduler). Тот же алгоритм, что в Anki, но современная реализация. Каждая карточка хранит стабильность, сложность и состояние (новая, изучается, повторяется, переучивается). При оценке 1–4 алгоритм рассчитывает следующий показ.

Зачем не интегрировались с Anki? Чтобы убрать шаги. Читаешь в JuliaLM — генерируешь карточки — учишь. Без экспорта, импорта, переключений.

Поиск по базе знаний

Кроме RAG в чате — отдельный поиск: BM25 (полнотекстовый) и vector search (семантический).

Есть и третий режим — «Спросить базу знаний». Это LangGraph-граф:

  • Модель анализирует вопрос и генерирует стратегию: до 5 поисковых запросов с инструкциями.
  • Запросы выполняются параллельно, каждый возвращает до 10 результатов.
  • Для каждого — промежуточный ответ.
  • Финальная модель синтезирует всё в один связный ответ.

Звучит как overkill, но для сложных вопросов («в чём авторы расходятся по теме X?») работает заметно лучше, чем одиночный поиск.

Планы и квоты

Тарификация — сложный инженерный вызов. Три плана:

  • Free — 0₽, 20 промптов за всю жизнь.
  • Pro — 799₽/мес, 800 промптов в 5-часовом скользящем окне.
  • Business — 1199₽/мес, 2000 промптов.

Скользящее 5-часовое окно вместо дневного или месячного — компромисс для студентов. Типичный паттерн: 100 запросов за вечер, потом неделя без активности. Месячный лимит — щедрый, дневной — жёсткий. Пятичасовое окно — баланс: можно бустить, но нельзя утилизировать всё за час.

Перед каждым вызовом LLM проверяется квота. При истечении подписки — автоматический даунгрейд на Free. Без сюрпризов, без скрытых списаний.

Полный путь: от PDF до ответа

От загрузки PDF до ответа с цитатами — 15–30 секунд на обработку + 5–10 секунд на генерацию.

Итог

JuliaLM — не обёртка над ChatGPT. Это система с собственным пайплайном, четырёхстратегийным RAG, бюджетированием контекста и интервальным повторением.

Не всё идеально: YouTube периодически ломает транскрипцию, SurrealDB пока не дотягивает до Postgres по зрелости. Но для задачи «загрузил 30 статей, задал вопрос, получил ответ с цитатами» — работает.

Есть бесплатный план на 20 запросов. Сервис в бета-версии — возможны баги.

Читать оригинал