Возможно Вам будет интересно как себя чувствует DeepSeek 70B на ноутбуке, но об этом в конце.

Идея этого проекта пришла мне на допросе, причины допроса оставим за скобками: статья всё-таки техническая. Там я впервые увидел, насколько сложно устроена работа с материалами дела. Со стороны кажется: задал вопросы, записал ответы, подшил бумаги. В реальности перед следователем лежат показания нескольких людей, протоколы, даты, адреса, телефоны, фотографии, аудиозаписи, результаты осмотров и экспертиз. Материалы приходят не по порядку, одни сведения уточняют другие, а иногда прямо им противоречат.

Скорее всего современные средства обработки больших объемов информации используются. Не то, чтобы в любой силовой структуре используют бумагу и ручку, а все тома дел нумеруются карандашом, затем после дырокола сшиваются собственноручно, как это было почти 100 лет назад.

Но прямо на допросе меня осенило: так это же работа для ИИ. Жеглов раскрывал дела через картотеку и чутьё. Картотеку я решил оцифровать, а чутьё заменить гибридным поиском — оно, в отличие от Глеба Егорыча, в 16 ГБ VRAM не помещается.

Так получился проект https://github.com/webzuweb/InvestigationAI.git

InvestigationAI (CaseAI) — автономная рабочая станция для следователя. Она принимает документы, сканы, изображения и аудиозаписи, извлекает текст, распознаёт речь, строит локальный поисковый индекс и позволяет задавать вопросы по материалам. Ответ должен вести не к абстрактному «источнику 2», а к конкретному тому, файлу, странице или таймкоду.

Система работает на Ubuntu без облачных API. В режиме жёсткой изоляции ей вообще не нужен интернет: веб-интерфейс, модели, индексы, аудиообработка и база данных находятся на одной машине и слушают только localhost.

Отдавать машине оценку доказательств, процессуальные решения и ответственность было бы не только глупо, но и опасно. А вот чтение, поиск, первичное сопоставление и подготовка проверяемых ссылок — как раз тот объём механики, который машина может снять с человека.

Тут обнаружилась маленькая проблема размером со всю идею: материалы конфиденциальны. Отправлять их в случайное облако нельзя. Даже обещание провайдера «мы не обучаемся на ваших данных» не превращает передачу охраняемой информации третьей стороне в хорошую идею.

Что я хотел получить — и что получилось

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

Телефон, VIN, госномер и номер документа нельзя искать только по смысловой похожести. Аудио индексируется сегментами с таймкодами и метками говорящих. Длинные операции показывают стадию, текущий файл, таймер, успехи и ошибки. После подготовки станция работает в изолированной сети или вообще без внешнего интерфейса.

Железо должно помещаться в ноутбук, это и будет рабочая станция в режиме полета.

Конфигурацию ниже я называю проверенной, а не «минимальной»: без реальных сравнительных замеров обещать комфорт на более слабом железе было бы нечестно.

 Компонент

 Проверенная конфигурация

 Роль

 CPU

 Intel Core i9-11900

 Парсинг, OCR-обвязка, CPU-слои LLM, embedding и reranker

 RAM

 64 ГБ DDR4

 32B/70B в гибридном режиме, индексация, запас для длительных задач

 GPU

GeForce RTX 3080, 16 ГБ VRAM

 GPU-offload llama.cpp; по очереди — Whisper/pyannote и опциональный vision

 SSD

 1 ТБ

 Веса GGUF, исходные материалы, индексы и промежуточные файлы

 ОС

 Ubuntu 24.04 LTS x86_64

 systemd-сервисы, NVIDIA-драйвер, управляемый air-gap

Важная поправка к красивому маркетингу. Ни DeepSeek-R1-Distill-Qwen-32B Q4_K_M, ни тем более DeepSeek-R1-Distill-Llama-70B Q4_K_M не помещаются в 16 ГБ VRAM целиком. Оба профиля работают гибридно: часть слоёв на GPU, остальное — в RAM/CPU. Fast 32B легче и используется по умолчанию; Deep 70B запускается только для сложных сопоставлений и не обещает серверной скорости.

Embedding и reranker в стабильном релизе намеренно работают на CPU (--n-gpu-layers 0), чтобы не конкурировать с LLM за VRAM. Whisper освобождается перед запуском диаризации. На одной видеокарте диспетчеризация важнее попытки одновременно запустить всё сразу.

Архитектура: несколько сервисов вместо одного магического процесса

Первую версию хотелось сложить в один Python-процесс. Удобно — до первого зависшего распознавания аудио или смены модели. В стабильной версии компоненты разделены на systemd-сервисы, каждый можно перезапустить и диагностировать отдельно.

Все HTTP-службы привязаны к 127.0.0.1

 Сервис

 Порт

 Роль

 CaseAI Web / FastAPI

 8080

 Интерфейс, API, дела, тома, импорт, журнал, очередь

 Text LLM

 8081

 llama.cpp с текущим профилем Fast или Deep

 Embedding

 8082

 Qwen3 Embedding 0.6B Q8_0, CPU

 Vision

 8083

 Опциональная Qwen3-VL 8B для изображений

 Reranker

 8086

 Qwen3 Reranker 0.6B Q8_0, CPU

 Model Manager

 8087

 Выгрузка, запуск и проверка готовности LLM

 Audio worker

 8090

 faster-whisper large-v3 + pyannote Community-1

На одном из этапов я добавил ещё harness и прокси — хотелось «умнее управлять рассуждением». Взамен получил лишний процесс, новые таймауты и ещё один журнал, который надо читать ночью. Production-путь снова стал коротким: CaseAI → Model Manager → llama.cpp. Локальная система не обязана копировать облачную архитектуру, самым важным было улучшение производительности без потери качества обработки информации.

Как устроен RAG: смысл, точность и происхождение

RAG здесь это конвейер, который обязан сохранить происхождение фрагмента и сочетать несколько типов поиска. Материал копируется в хранилище дела, получает стабильный идентификатор и SHA-256.

Из документа извлекается текст; для сканов запускается OCR, для аудио — транскрибация и диаризация. Текст режется на фрагменты до 3200 символов с перекрытием 450; рядом сохраняются том, файл, страница или таймкод.

Qwen3 Embedding строит нормализованные векторы для FAISS IndexFlatIP.

Параллельно SQLite FTS5 даёт лексический поиск, а телефоны/VIN/номера попадают в точный индекс. Извлекается до 40 кандидатов; Qwen3 Reranker переоценивает их; в контекст попадает до восьми фрагментов в общем бюджете 18 000 символов (подобран эмпирическим путем).

LLM получает вопрос, текущие фрагменты и инструкцию ссылаться на карту источников. Для явного точного поиска LLM вообще не запускается.

combined = 0.30 base_score + 0.70 normalized_reranker

if exact_identifier_match:

    combined += 1.2

retrieval_candidates = 40

answer_sources = 8

context_budget_chars = 18000

Эти коэффициенты — текущая инженерная эвристика, а не научно доказанный оптимум. До появления размеченного синтетического корпуса и ablation-теста относиться к ним надо именно так. Я оставляю числа в статье, чтобы решение было воспроизводимым, но не выдаю конфигурацию за объективную истину.

Почему нельзя оставить один vector search? Телефоны +7 999 123-45-67 и +7 999 123-45-68 почти близнецы для embedding, но для дела это разные сущности. И наоборот: вопрос «кто менял время своего прибытия?» может быть сформулирован в документах десятком способов — здесь FTS уже недостаточно. Поэтому точный, лексический и семантический маршруты работают вместе.

Аудио — это не приложение к делу

Часовую запись бесполезно транскрибировать одним полотном и сослаться на имя файла. faster-whisper large-v3 разбивает речь на сегменты, pyannote Community-1 назначает метки SPEAKER_XX. В результате источник ведёт не просто к записи, а к диапазону 00:12:31–00:12:47. Пользователь нажимает ссылку и попадает на нужную секунду.

Грабли, которые я собрал

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

1. 70B не помещается в 16 ГБ VRAM. Кто бы мог подумать

Попытка загрузить максимум слоёв приводила либо к CUDA error, либо к тому, что система съедала память и переставала быть управляемой. Решение — два профиля и один резидентный LLM-процесс. Model Manager останавливает предыдущую модель, ждёт освобождения ресурсов, запускает новую и проверяет health endpoint. llama.cpp закреплён на конкретной ревизии: воспроизводимость важнее пары процентов из сегодняшнего nightly.

2. Векторный поиск уверенно нашёл не тот телефон

На фразах смысловой поиск выглядел волшебством. На идентификаторах — опасным фокусником. Я добавил нормализацию сущностей, отдельный точный индекс, SQLite FTS5/BM25 и бонус exact match. Если запрос явно про телефон, VIN или номер документа, система показывает совпадения напрямую и тратит ноль токенов LLM.

3. Ссылка «[ИСТОЧНИК 2]» ничего не доказывала

Ранний интерфейс честно нумеровал источники. Формально цитирование было. Практически пользователь понятия не имел, откуда взялась фраза. Теперь источник — структура: дело → том → материал → страница/таймкод → SHA-256. Номер удобен внутри ответа, но проверяемость появляется только после привязки к конкретному материалу.

[ИСТОЧНИК 2]

Том 4 → Протокол осмотра телефона

стр. 83

SHA-256: …

Для аудио:

Том 7 → Звонок 17 февраля

00:12:31–00:12:47 • SPEAKER_01

4. Потом пришёл database is locked

Небольшие тесты проходили. Я импортировал пачку файлов — и большинство операций закончилось знакомством с database is locked. «Хьюстон, у нас проблема». И, как в «Аполлоне-13», спасли не дополнительные мощности, а правильный порядок действий. Причины оказались не нейросетевыми: вложенная запись из кэша, аудит и статус после каждого файла, плюс перестроение FAISS на каждом шаге.

Исправление: SQLite WAL, единый сериализованный writer, запрет вложенных записывающих транзакций, одна активная партия импорта и перестроение FAISS один раз после партии. busy_timeout=300000 остался страховкой, а не основным способом «лечения». Ошибка одного файла теперь попадает в отчёт, но не откатывает остальные.

Самая заметная оптимизация RAG в итоге оказалась оптимизацией порядка транзакций. Нейросети снова остались без главной роли.

5. Флешка с пробелом в имени победила автоматику

Ubuntu вернула точку монтирования как /media/computer/UBUNTU\x2024_0, а реальный каталог назывался /media/computer/UBUNTU 24_0. Программа приняла \x20 буквально. Когда я это исправил, desktop automount уже смонтировал флешку, а агент каждые две секунды пытался сделать то же самое ещё раз.

Теперь агент сначала ищет существующую точку монтирования, декодирует \x20 и старый вариант \040, а собственный mount создаёт только при необходимости. Мелочь — пока не импортируешь пятьдесят файлов с носителя, который «не существует».

6. JSON.parse скрывал настоящую ошибку

Backend вернул обычный текст Internal Server Error, а фронтенд без проверки попытался разобрать его как JSON. Пользователь увидел ошибку парсера вместо PermissionError. Клиент теперь проверяет статус и Content-Type; backend отвечает структурированно. Обработчик ошибок не должен уничтожать исходную ошибку — неожиданно полезный урок и без всякого ИИ.

7. Длинная операция выглядела как зависание

OCR, аудио и индексация занимают минуты. Спиннер без объяснений провоцирует нажать кнопку ещё раз, затем появляется вторая партия, блокировка базы и тот самый «крайний случай». Интерфейс показывает текущий файл, стадию, таймер и счётчик вроде «7 из 24 • готово 6 • ошибок 1». Ход обработки показывает retrieval, rerank и подготовку модели, но не публикует скрытую chain-of-thought как будто это проверенный анализ.

8. Кнопка «Остановить» останавливала только настроение пользователя

AbortController закрывал запрос браузера, но llama.cpp продолжал считать. Получался HAL 9000: «Прости, Дэйв, боюсь, я не могу прервать генерацию». После перехода на streaming сервер при отмене закрывает поток к llama.cpp, освобождает слот и не сохраняет незавершённый ответ. На локальной машине один забытый запрос к 70B способен надолго занять весь аттракцион.

9. Ответ обрывался примерно на 1000 output токенах

Это выглядело как слабость модели, а оказалось жёстким max_tokens. Теперь предел настраивается, usage сохраняется, а finish_reason=length показывается явно. Для DeepSeek-R1 бюджет completion расходуется и на reasoning, и на видимый текст. При этом миллионы токенов, которые у фронтир моделей норма, на локальной машине будут несбыточной мечтой.

Чтобы понять локальную LLM, нужно стать локальной LLM

Даже если вы никогда не работали с материалами дела, в проекте есть несколько общих уроков для локального RAG и утилит на одной рабочей станции:

Точные идентификаторы требуют отдельного маршрута; embedding не обязан различать одну цифру.

Provenance надо хранить на уровне каждого chunk, а не придумывать ссылку после генерации.

Память диалога и доказательная выборка — разные контуры.

Один writer и пакетное перестроение индекса часто полезнее очередной «AI-оптимизации».

Отмена должна доходить до вычислительного backend, иначе она декоративна.

На одной машине меньше слоёв обычно означает больше надёжности.

Наблюдаемость длительной операции — часть корректности, а не косметика интерфейса.

А нельзя было взять готовый локальный RAG?

Можно — если нужен универсальный чат с базой знаний. Open WebUI и AnythingLLM гораздо зрелее как общие платформы, PrivateGPT даёт сильный API-фундамент. Я не объявляю InvestigationAI «лучше». Отличие — узкая предметная модель и другой набор приоритетов.

 Система

 Сильная сторона

 Почему я не остановился на ней

 Open WebUI

 Зрелая self-hosted AI-платформа, знания, плагины, multi-user

 Нет модели дело → том → материал → проверяемый источник

 AnythingLLM

 Удобный on-device ассистент и document knowledge

 Не ориентирован на доказательное происхождение и такой workflow

 PrivateGPT

 Сильная private-AI основа и API

 Основа для разработки, а не готовое рабочее место с томами, USB и аудитом

 InvestigationAI

 Вертикальный hybrid RAG, exact ID, аудио, USB, карта источников

 Я тут!

Коммерческие юридические продукты уровня CoCounsel живут в другой экосистеме: лицензированные правовые массивы, облачные workflows и многолетняя экспертная валидация. Сравнивать их по числу кнопок с проектом одного разработчика было бы странно.

Установка

Целевая система — обычная Ubuntu 24.04 LTS x86_64 с NVIDIA GPU, не WSL и не Docker-контейнер. Интернет требуется на этапе установки и загрузки моделей.

sudo apt update

sudo apt install -y git

git clone https://github.com/webzuweb/InvestigationAI.git

cd InvestigationAI

sudo bash install.sh

sudo bash scripts/download_models.sh

sudo bash scripts/build_deepseek_32b.sh

sudo bash scripts/download_audio_models.sh

sudo bash scripts/build_deepseek_70b.sh

sudo bash scripts/optimize_runtime.sh --all

sudo bash scripts/verify_install.sh

Для staging и сборки Fast-профиля проект требует около 180 ГБ свободного места; для Fast + Deep с промежуточными файлами рекомендуется примерно 420 ГБ. Это не размер готового Q4-файла, а запас на загрузку исходных весов, конвертацию и квантизацию.

После проверки интерфейс открыт только локально: http://127.0.0.1:8080. Жёсткую изоляцию включаем последней, иначе получаем очень безопасную машину, которая не успела скачать модели:

sudo bash scripts/generate_sbom.sh

sudo bash scripts/lockdown_offline.sh --confirm

sudo bash scripts/test_no_internet.sh

Безопасность, лицензия и честные ограничения

Локальность убирает риск передачи данных внешнему LLM-провайдеру, но не делает систему автоматически защищённой. Для реального внедрения потребуются модель угроз, шифрование накопителя, контроль USB, резервное копирование, роли, подписанные обновления, аудит, физическая защита и выполнение требований конкретного контура.

Текущий релиз — single-user станция без формальной сертификации и полноценного RBAC. LLM может ошибаться и убедительно связывать несвязанные факты. RAG уменьшает риск, но не отменяет проверку: если ссылка не подтверждает фразу, фразу нельзя использовать.

Что в итоге?

Я начинал с наивной идеи: большая локальная модель прочитает материалы и найдёт противоречия. В процессе выяснилось, что сама модель — едва ли половина работы.

Нужно правильно импортировать файлы, не поссориться с автомонтированием Ubuntu, не заблокировать SQLite, не потерять происхождение фрагмента, различать смысловую похожесть и точный номер, управлять общей памятью, останавливать генерацию по-настоящему и объяснять пользователю, чем машина занята прямо сейчас.

Именно эти скучные части превращают демонстрацию RAG в инструмент. Не «DeepSeek 70B» в заголовке, а возможность нажать на ссылку, открыть правильный том и проверить страницу или секунду записи. Облако здесь — синяя таблетка: удобно и не думаешь, где оказались данные. Локальная станция — красная: сложнее, зато сам видишь, насколько глубока кроличья нора и где лежит каждый файл.

InvestigationAI не является электронным следователем и не должен принимать процессуальные решения. Но он может снять часть механического поиска и первичного сопоставления — при условии, что человек проверяет каждый существенный вывод по первоисточнику.

Репозиторий и инструкции: github.com/webzuweb/InvestigationAI

Если вы работаете с локальным RAG, OCR, аудио или защищёнными контурами — мне полезна практическая обратная связь. Особенно по методике оценки retrieval, безопасной работе с синтетическими корпусами и операциям, которые автоматизировать нельзя.

Как себя чувствует DeepSeek 70B на ноутбуке?

Ответ с input 10.000 токенов и output 1.500 токенов требует примерно 30 минут, на модели 32B примерно 5минут. Это конечно вызывает неудобство, но за неимением альтернатив это убирает рутинную работу, а за счет очередности задач, можно задать хоть 20 вопрос сразу и подойти к машине через несколько часов.

Если Вы работаете в силовых структурах и будете использовать данное решение и оно принесет пользу радости моей не будет предела.

Спасибо за внимание!

Комментарии (6)


  1. ya_gaeo
    26.08.2026 07:05

    Хорошо, что здесь источником считается не абстрактный «фрагмент № 2», а том, файл, страница или таймкод. Но SHA-256 доказывает неизменность исходного файла, а не правильность OCR, чанкинга и ответа конкретной версии модели. Для реальной проверяемости придётся версионировать весь конвейер: OCR, модели, промпт, параметры поиска и карту фрагментов. Иначе ссылка на доказательство есть, а повторить путь от доказательства к выводу уже нельзя.


    1. webzuweb Автор
      26.08.2026 07:05

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


  1. Bardakan
    26.08.2026 07:05

    почему модель должна быть именно на ноутбуке? Почему например не использовать связку мощный пк+любой ноутбук?


    1. webzuweb Автор
      26.08.2026 07:05

      тогда будет сеть и будет контур, а это уже вопросы безопасности


      1. Bardakan
        26.08.2026 07:05

        ноутбук вы используете внутри своей организации? Тогда почему не стационарный пк?

        Если выносите его куда-то за пределы, то получаются те же самые вопросы с безопасностью.

        Или я вас неправильно понял?


        1. webzuweb Автор
          26.08.2026 07:05

          Согласен, ноутбук или ПК. Я имею ввиду, что изначально исключал связку (сервер+клиент)мощный пк+любой ноутбук