跳到正文
DEV Community· Navalny Team·· 3 小時前AI 評分63

Navalny 團隊如何以 AI 建立阿列克謝·納瓦利內數碼檔案館

Алексей Навальный: сохраняем наследие в эпоху ИИ

AI 導讀

Navalny 團隊介紹如何建立阿列克謝·納瓦利內數碼檔案館,並於 2026 年 6 月 4 日上線,當時收錄逾 14,000 份材料。團隊以 Gemini Flash 初步標註主題、人物和地點,並用 GPT-5.4 翻譯,再由編輯核對和修正模型輸出。

正文

Когда-нибудь в России появится музей Алексея Навального. Нам хочется, чтобы туда приходили и те, кто много лет поддерживал Алексея, и те, кто знает о нём совсем немного. Чтобы можно было послушать его выступления, почитать записи в блоге, посмотреть фотографии и узнать, над чем он смеялся, что любил и о чём мечтал.

Трудно сказать, когда это станет возможно, ведь сейчас сторонники Алексея подвергаются преследованию и многим из них пришлось эмигрировать из России под угрозой уголовных дел. Но собирать будущую коллекцию важно уже сейчас. О жизни и работе Алексея сохранилось множество свидетельств: его собственные тексты и записи, воспоминания людей, которые его знали, публикации в прессе, фотографии и видео. Что-то можно найти в интернете, а что-то хранится у людей дома — в старых газетах, фотоальбомах и на кассетах. Нам хочется найти и сберечь как можно больше, пока эти материалы ещё доступны.

Поэтому Юлия Навальная, которая продолжает дело своего мужа, решила создать цифровой архив его памяти на navalny.com. За создание Архива взялась команда Фонда борьбы с коррупцией. Нам хотелось сберечь как можно больше материалов и сделать их доступными на русском и английском языках.

Мы запустили Архив 4 июня 2026 года — в день, когда Алексею исполнилось бы 50 лет. К этому моменту в коллекции было больше 14 тысяч материалов: от коротких постов и агитационных листовок до многочасовых выступлений. Мы находили интервью, которые казались потерянными, сканировали старые газеты и фотографии, переводили тысячи текстов, большинство из которых прежде нельзя было прочитать по-английски. Особенно радостно было находить что-то незнакомое даже тем из нас, кто много лет работал с Алексеем.

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

Как мы искали и сохраняли материалы

К сбору материалов мы приступили больше чем за год до запуска. Начали с того, что помнили сами: собрали в гугл-таблицу самые известные текстовые интервью, видеозаписи и другие материалы с участием Алексей Навального. Постепенно из них складывались разделы — выступления на митингах, последние слова в судах, интервью, — а дальше начались сотни часов поиска. Со стороны эта работа могла выглядеть однообразно. Но когда в неё погружаешься, оторваться трудно, особенно если удаётся найти уникальный материал, который уже считался потерянным.

У сбора материалов были объективные трудности. Многие СМИ, работавшие в нулевые и десятые, закрылись или сменили владельцев и редакционную политику. Российские власти преследуют людей за связь с ФБК, признанным «экстремистской организацией», и под угрозой уголовных дел издания удаляют интервью и комментарии Алексея. Бывает, что заходишь на сайт СМИ, и там есть полный архив всех публикаций, но только статья с участием Алексея таинственно исчезает — так работает цензура.

Например, в архиве радиостанции «Эхо Москвы» не сохранились десятки интервью с Алексеем. Мы искали их на YouTube, во «ВКонтакте», в «Одноклассниках» и других хранилищах и в итоге нашли подавляющее большинство. Этим особенно гордимся, как и материалами, которые теперь можно найти только у нас: видеозаписи судебных заседаний по «Кировлесу-1», за которыми тогда следила вся страна, и архивными фотографиями Алексея.

Часть коллекции вообще раньше не попадала в интернет. Журналы, газеты и брошюры нам передавали из личных архивов. Один из коллег несколько дней провёл у сканера, переворачивая страницы, чтобы их смогли прочитать посетители сайта. Так у нас появились в том числе редкие районные предвыборные газеты кампании Алексея на выборах мэра Москвы.

Пока редакторы искали и оцифровывали материалы, разработчики делали админку и систему обработки, а дизайнеры работали над тем, как показать материалы посетителям. Для размещения архива мы выбрали собственный сайт Алексея, navalny.com. К сожалению, сайт заблокирован в России, поэтому посетителям необходим VPN, но с этим ограничением пришлось мириться.

Аудитория и алгоритмы

Юлия Навальная хотела, чтобы Архивом могли пользоваться как давние сторонники Алексея, так и те, кто хочет познакомиться с его работой. Также мы считали вероятным, что сайт станет полезным инструментом для тех, кто занимается научными исследованиями.

Эти задачи определяли и содержание, и устройство сайта. Исследователю нужны точная дата, теги, расшифровка и удобный поиск. Посетителю без определённого запроса — возможность сразу открыть что-нибудь интересное. Поэтому мы сделали тематические подборки и блоки с похожими публикациями. Можно начать с Умного голосования, а можно почитать, какие книги и музыку любил Алексей. Рекомендации учитывают тему и дату публикации и помогают переходить от одного материала к другому. На главной ежедневно появляются цитаты Алексея «в этот день», — нам хотелось дать людям повод постоянно возвращаться, чтобы найти для себя что-то новое.

Как показать настолько разные материалы?

За годы работы у проектов Навального накопилось множество визуальных решений, созданных разными командами под разные задачи. Прежде чем собирать из них интерфейс Архива, дизайнеры вернулись к общим правилам: восстановили швейцарский гротеск и фирменный цвет, убрали наиболее заметные противоречия. Компоненты разрабатывали с расчётом на то, что после запуска сайт будет поддерживать небольшая команда. Ей нужно добавлять новые материалы и функции, не пересматривая каждый раз оформление всего проекта.

За время работы дизайн прошёл три большие итерации. Получился 31 экран, каждый минимум в четырёх адаптивных размерах, с общими компонентами и Variables.

В качестве ориентира дизайна мы выбрали YouTube, Netflix и Apple TV: нам нравилось, как эти интерфейсы помогают выбрать нужное из большого каталога. Проблема была в том, что у нас рядом оказывались старый газетный скан, судебный документ, фотогалерея и многочасовое интервью. Для каждого формата требовались свои специфические визуальные решения. Поэтому единообразие мы оставили только на главной, а внутри каждый тип материала выглядел по-своему.

Для каждого типа материала мы подготовили универсальные превью, а почти тысячу обложек к наиболее важным публикациям дизайнеры сделали вручную. Редакторы выбрали 250 экспонатов с тегом «Топ-контент». Они случайным образом появляются на главной и получают повышенный приоритет в поиске, чтобы посетители замечали важные материалы независимо от их давности.

Что мы поручили ИИ

Для каждого материала требовались заголовок, дата и ссылка на источник. Нужно было определить, о каких людях, местах и событиях в нём говорится, подобрать теги и подготовить английскую версию. Для поиска внутри длинных выступлений требовался текст — значит нужно использовать имеющиеся автоматические субтитры YouTube.

Из примерно 60 сотрудников ФБК только около десяти занимались Архивом как основной работой. Для такой команды ручная обработка всей коллекции могла растянуться на годы. Поэтому разработчики построили систему первичной обработки, а в последние два месяца перед запуском к проверке материалов подключились примерно 50 волонтёров.

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

Внедрение искусственного интеллекта позволило небольшой НКО с ограниченным бюджетом обработать тысячи материалов к запуску. Для первичной разметки мы использовали большие языковые модели: Gemini Flash определяла техническую информацию о материале: темы, людей и места, а перевод поручили GPT-5.4. Люди могли сосредоточиться на том, что требовало знания событий и редакторского решения, — например, узнать человека на старой фотографии или разобраться в контексте поста.

Собственную модель для классификации мы не обучали: в начале проекта не было достаточного набора размеченных данных. Подготовить его означало потратить много времени. Вместо этого мы составили подробные инструкции и около десяти-двадцати эталонных примеров; несколько из них включали в запрос к готовой модели.

Как обрабатывается материал

Все эти операции нужно было связать в систему, которая готовит публикацию поэтапно и позволяет выполнять независимые задачи одновременно. Стек выбирали из того, что команда уже хорошо знала: Python 3.13, Django, Pydantic для DTO, Django Q2 для асинхронных задач, Postgres, Redis и Elasticsearch для хранения и поиска, GCS для файлов, OpenTelemetry для наблюдаемости и GitLab для CI/CD. Django Q2 — форк Django Q.

Django помог быстро собрать прототип и дал готовую админку. По мере роста проекта обработка становилась многоэтапной, а мы стремились отделять бизнес-логику от работы с данными, следуя Clean Architecture. Совместить этот подход с Active Record в Django оказалось трудоёмко: пришлось выносить логику в репозитории, дублировать модели в Pydantic и в итоге переписать встроенную админку с нуля. За быстрый старт мы позже заплатили временем и большим количеством шаблонного кода. С учётом этого опыта следующий проект, скорее всего, будем писать на FastAPI + SQLAlchemy.

Для каждого материала система готовит текст для поиска и обработки, версию с разметкой для показа на сайте, метаданные и связи с темами, людьми и местами. Часть сведений приходит вместе с источником, остальные извлекаются при обработке.

Например, у видеоролика сначала извлекаются субтитры. Полученный текст делится на секции, после чего одновременно запускаются четыре задачи: подготовить краткое описание, подобрать теги, определить упомянутые места и людей. Собственный раннер управляет последовательностью операций и максимально распараллеливает те, которые не зависят друг от друга. Это сокращает время подготовки материала к проверке редактором.

Где ИИ ошибался и что мы меняли

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

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

Следующая трудность — определить, насколько тема важна для конкретного текста. Упоминание чиновника в одном предложении и расследование о нём не должны давать одинаковый результат в виде тега. Поэтому мы составили шкалу, по которой модель оценивает кандидатов.

И попросили модель применить её для оценки наиболее подходящих сущностей на основе текстовой транскрипции материала:

[
    {
        "reasoning": "The investigation is entirely centered around Dmitry Medvedev ('Dimon') and his hidden wealth, luxury real estate, and assets, making him and this theme the core subject of the material.",
        "tag": "dont-call-him-dimon",
        "score": 1.0
    },
    {
        "reasoning": "As the foundational focus of the film and Navalny's anti-corruption foundation, corruption is deeply analyzed throughout the entire investigation with extensive documentation.",
        "tag": "corruption",
        "score": 0.8
    },
    {
        "reasoning": "The investigation was produced, published, and driven by the Anti-Corruption Foundation (FBK), which conducted the research and released the film.",
        "tag": "fbk",
        "score": 0.8
    },
    {
        "reasoning": "One of the most famous specific properties exposed in the investigation is the elaborate estate colloquially referred to as the 'duck house' on the Milovka estate grounds.",
        "tag": "duck-house",
        "score": 0.5
    },
    {
        "reasoning": "The entire documentary is structured as a detailed, multi-part investigative report uncovering hidden ownership structures and slush funds.",
        "tag": "investigation",
        "score": 0.5
    },
    ...
]

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

В структурированный ответ к каждой оценке добавили поле reasoning с коротким объяснением. Пользователю оно не показывается и после обработки удаляется. По наблюдениям команды, с этим полем модель оценивала сущности последовательнее. Примеры разметки в запросе показывали, как применять шкалу к реальным текстам — подход multi-shot prompting.

Даже после этих изменений оставались ошибки. Например, пост Алексея «Мои страх и ненависть» о предательстве со стороны постсоветских реформаторов получил тег «Лайфстайл». Тюремные записи часто попадали в юмор: Алексей действительно шутил, но за шуткой мог стоять очень грустный рассказ, который посетитель вряд ли ожидал найти в такой подборке. А под тегом «пытки» оказывались тексты о задержании на митинге или полицейской слежке. Редакторы разбирали эти случаи с менеджером, после чего разработчики уточняли инструкции для ИИ.

Особенно много внимания требовали посты в Instagram. Чтобы ограничить расходы, мы не использовали распознавание изображений, а лишь по одной подписи часто невозможно понять, что происходит на фотографии. Редактору приходилось тщательно перепроверять присвоенные теги. С публикациями 2011–2012 годов особенно помогали коллеги, которые помнили события: молодые сотрудники и волонтёры могли просто не узнать человека или ситуацию на снимке.

С описаниями трудность была другой: модель регулярно возвращалась к оборотам вроде «Автор считает…» и «В материале рассматривается вопрос…». Исправить тон одними инструкциями не удалось. Поэтому сгенерированные описания стали черновиками, которые редакторы правят перед публикацией, убирая канцелярские обороты и уточняя формулировки.

Перевод: как мы показали миру слова Алексея

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

Чтобы получить этот результат, мы сделали собственный сервис на базе GPT-5.4. Он позволяет управлять обработкой текста и форматом ответа, а при необходимости переводить и с английского на русский. Главная сложность возникала с длинными документами: модель могла добавлять детали, которых не было в оригинале.

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

Как найти нужное среди тысяч материалов

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

Elasticsearch позволил собрать полнотекстовый поиск по материалам, фильтры и векторный поиск в одном движке. BM25 учитывает совпадения слов, а kNN по эмбеддингам OpenAI text-embedding-3-small добавляет близкие по смыслу результаты. Это нужно, например, чтобы по запросу «коррупция на госзакупках» находились материалы антикоррупционного проекта РосПил, даже если в таком материале нет такой формулировки, но есть близкие, например «распил бюджета на тендерах».

Модель эмбеддингов выбрали за поддержку разных языков и удобство интеграции: OpenAI уже использовался в конвейере. Она представляет текст вектором из 1536 чисел, по которому можно сравнивать близость материалов и запросов. По оценке разработчиков, расходы на их вычисление невелики. По их расчётам, создание эмбеддингов для всего корпуса стоит центы, а для одного поискового запроса — порядка 5·10⁻⁷ доллара. Эмбеддинги запросов кешируются в Redis на неделю, поэтому повторный запрос не требует нового обращения к модели.

У выдачи есть фильтры по источнику, типу, формату и датам. Для буквального совпадения можно ввести фразу в кавычках. Слова обрабатываются с учётом русского и английского языка, чтобы изменение формы слова не мешало поиску.

В используемой нами конфигурации Elasticsearch без платной лицензии был недоступен RRF — механизм объединения результатов по их позициям в разных выдачах. Поэтому оценки полнотекстового и векторного поиска складываются без согласования шкал. Ранжирование получается приблизительным, но на нашей коллекции, по наблюдениям команды, работает уверенно.

Почему поиск пришлось ускорять

Найти подходящие публикации через поиск было половиной задачи: посетитель должен быстро получить выдачу и увидеть, где именно в найденном документе встречается его запрос. Когда мы начали нагрузочные тесты с k6, выяснилось, что сам поиск в Elasticsearch занимает миллисекунды, а задержки возникают на этапе fetch — получения найденных документов и подсветки совпадений.

В _source каждого документа лежало около 49 КБ векторов, ненужных при формировании ответа: kNN использовал отдельный индекс. Удалив эти данные из _source, мы уменьшили размер документа примерно с 75 до 26 КБ.

С подсветкой пришлось разбираться дольше. На наших текстах она занимала около 400 мс на страницу выдачи. Перебор движков заметного выигрыша не дал, а fvh под нагрузкой тратил много CPU на построение структуры термов даже без совпадений. В нашей конфигурации на каждую ноду приходилось семь поисковых потоков; под нагрузкой fetch с подсветкой занимал около секунды, и две ноды упирались примерно в 13 запросов в секунду.

Мы перенесли подсветку на бэкенд, который можно масштабировать горизонтально. Это позволило добавлять мощности, но время самой операции почти не изменилось.

Первая версия проходила по всем словам документа, приводила их к основе с помощью стеммера и сравнивала с запросом. Ради нескольких совпадений приходилось обрабатывать тысячи слов. Замена Python-реализации стеммера на реализацию на C заметного выигрыша не дала.

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

Стеммер сохранили, потому что один префикс захватывает лишние слова: например, «коррупц…» найдёт и «коррупцию», и «коррупционеров». Дополнительная проверка отсекает неподходящие совпадения. Пришлось учесть и языковые особенности: английский стеммер превращает country в countri, поэтому для отбора нужен общий префикс, а русские «е» и «ё» следует обрабатывать одинаково.

В тестах время подсветки сократилось примерно с 400 до 30 мс. На проверочном корпусе объёмом больше миллиона символов команда не обнаружила расхождений с нативной версией. Эти измерения относятся к самой подсветке; время ответа всего поискового сервиса мы оценивали отдельно.

Как мы управляем нагрузкой

После облегчения fetch следующим ограничением стал бэкенд. В отдельном тесте без подсветки система обрабатывала около 107 запросов в секунду при p95 в 1,8 секунды — 95% запросов укладывались в это время. Ответ с примерно двадцатью карточками весил около 66 КБ: в него входили полные описания и списки тегов, людей и мест. Сокращение этого объёма стало следующей задачей.

Настройки, с которыми приходилось экспериментировать, вынесли в админку. Там можно включить семантический поиск, изменить его вес и порог близости, выбрать движок подсветки и режим сниппетов: без них, по описанию или по полному тексту. Там же задаются кеширование ответов и срок жизни кеша. Настройки хранятся в БД и читаются через 30-секундный кеш, поэтому для изменений не нужен деплой. Позже этот подход применили и к адаптивному троттлингу. Параметры устройства индекса, например число шардов, по-прежнему задаются при его создании.

Для пиков нагрузки добавили кеш ответов в Redis и адаптивный троттлинг. Он следит за p95 времени ответа и при перегрузке отклоняет часть запросов с HTTP 429. Переиндексация тоже не требует останавливать сайт: новый индекс собирается рядом с действующим, после проверки на него переключается алиас.

Архив продолжает расти

Сайт Архива мы делали с расчётом на то, что коллекция будет расти и после запуска.

Каждый месяц мы добавляем примерно 100–200 материалов: видео, интервью, юридические документы и другие находки. Они проходят тот же путь, что и первые публикации: от найденного файла или бумажного оригинала через обработку и редакторскую проверку — к материалу на сайте, который можно открыть, прочитать и найти в поиске.

Часть материалов присылают сторонники. Люди находят у себя записи и фотографии, делятся ими с нами, и мы продолжаем узнавать что-то новое об Алексее. Нам ещё многое предстоит найти и оцифровать, поэтому такая помощь очень важна. Если у вас есть подходящие материалы, напишите нам на archive@navalny.com.

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

Продолжать эту работу нам помогает ваша поддержка. Пожертвования оплачивают хранение коллекции, работу сайта и подготовку новых публикаций. Поддержите команду ФБК, чтобы мы могли сохранять больше свидетельств жизни Алексея и открывать их людям по всему миру — тем, кто его помнит, и тем, кто только знакомится с ним.

來源:DEV Community · dev.to