
Можно ли за 72 часа(личный вызов/ограничение) в одиночку спроектировать, собрать и отладить RAG-систему, архитектура которой основана на пяти state-of-the-art научных статьях, опубликованных буквально в последние месяцы? В рамках хакатона AI for Finance Hack 2025 от Changellenge » я решил проверить это на практике. Спойлер: было больно, интересно, а результат превзошел все ожидания.
Задача хакатона была классической, но с подвохом: создать финансового AI-ассистента, который дает точные ответы на вопросы пользователей, основываясь на предоставленной базе знаний. “С подвохом” — потому что среди простых вопросов вроде “что такое ОСАГО” скрывались и сложные, многоэтапные (“multi-hop”) кейсы: “сравни условия по продукту А и продукту Б”, “какой был лимит по вычету в прошлом году и как он изменился сейчас?”.
Стандартный RAG-подход “найди похожие документы -> передай в LLM” на таких задачах быстро ломается. Он либо не находит все части информации, разбросанные по разным документам, либо находит слишком много “шума”, в котором “тонет” языковая модель.
Поэтому, вместо того чтобы строить очередной простой RAG, мы решили пойти по пути “тяжелой артиллерии” — спроектировать полноценного агентского помощника, который умеет планировать, анализировать и итеративно уточнять поиск.
Эта статья — история штурма: от амбициозного плана, собранного из передовых исследований, через жестокую реальность отладки API и библиотек, до финальной, оптимизированной и высокопроизводительной архитектуры. Я покажу, какие именно идеи из научных статей я взял, как адаптировал их под жесткие рамки хакатона и, самое главное, как решал проблемы, которые возникали на каждом шагу.
Дисклеймер: Этот проект был настоящим спринтом. Из команды в четыре человека активным участником оказался только я. Учитывая грандиозность планов и временные рамки в 72 часа(ведь надо оставить время на тестирование и непредвиденные обстоятельства), я активно использовал AI-ассистентов в роли “второго пилота” и “сеньора-ревьюера” на всех этапах — от проектирования архитектуры до написания и отладки кода. Эта статья — история нашего совместного с AI штурма.
Оглавление
Правила и Ограничения Хакатона
Прежде чем мы погрузимся в архитектурные дебри, важно очертить “поле боя”. Хакатон AI for Finance Hack 2025, организованный Changellenge при поддержке экспертов Райффайзен Банка, ставил участников в довольно жесткие, но интересные рамки.
Стек и Модели
Участникм был предоставлен ограниченный, но очень качественный набор инструментов:
-
Генеративные модели: Доступ через прокси-API к четырем моделям — Mistral Small, Llama-3-70B, Gemma-27B и Grok-3 Mini. Это сразу подтолкнуло к идее “Multi-Model” архитектуры, где для каждой задачи используется свой, наиболее подходящий инструмент.
-
Эмбеддинг-модели: Доступ к text-embedding-3-small и text-embedding-ada-002.
Бюджетные и Временные Лимиты
Это были главные вызовы, определившие всю мою стратегию оптимизации:
-
Денежный Лимит:
3$ в день на генеративные модели и 1$ на эмбеддинги. Этот бюджет был общим на команду для всех экспериментов и локальной отладки. Финальный прогон на сервере, к счастью, имел свой, отдельный бюджет.
-
Временной Лимит: 4 часа на полную обработку 500 вопросов на сервере проверки.
Запреты и “Сюрпризы”
-
HuggingFace под запретом: Организаторы ограничили использование HuggingFace для предотвращения запуска тяжелых локальных моделей, что сразу вычеркнуло из моего плана SOTA-реранкеры на базе CrossEncoder.
-
“Монолитный” main.py: Все решение должно было быть собрано в один Python-файл.
-
Реранкер “в догонку”: Уже в середине хакатона, после многочисленных просьб участников, организаторы открыли доступ к API-реранкеру (Qwen3-Reranker-4B). Это было приятным дополнением, но к тому моменту моя архитектура уже была спроектирована так, чтобы показывать высокое качество и без него, полагаясь на синергию гибридного поиска и агентского анализа.
Именно эти ограничения — бюджетные, временные и технологические — стали главным драйвером для всех архитектурных решений, о которых я расскажу дальше.
Часть 1: “Наполеоновские Планы”
Изначальная идея была простой: не пытаться изобрести велосипед, а встать на плечи гигантов. Я решил изучить последние (буквально за последние несколько месяцев) научные статьи, посвященные решению ключевых проблем RAG, и собрать из их идей единую, ультимативную архитектуру.
Научный Фундамент: Собираем “Франкенштейна” из SOTA-Идей
Мой финальный “чертеж” был основан на пяти ключевых концепциях, взятых из пяти блестящих работ:
-
Итеративный Цикл и Анализ Пробелов из FAIR-RAG
-
Проблема: Простой RAG не умеет “дособирать” информацию. Если он с первого раза не нашел все нужные факты, он выдаст неполный ответ.
-
Идея: Превратить RAG в итеративный, агентский процесс. После первого поиска специальный “Агент-Аналитик” (в статье он называется SEA) проверяет, достаточно ли найденной информации для ответа на все части вопроса. Если нет, он определяет “пробелы” в знаниях, и второй “Агент-Уточняющий” генерирует новые, точечные запросы, чтобы закрыть именно эти пробелы. Цикл повторяется.
-
Моё Решение: Это стало сердцем моей архитектуры. Я решил реализовать этот агентский цикл с помощью промпт-инжиниринга, создав двух специализированных агентов.
-
-
Глобальное Планирование из GlobalRAG
-
Проблема: Для сложных вопросов (“сравни А и Б”) простой поиск по всему вопросу неэффективен.
-
Идея: Прежде чем что-либо искать, LLM должна составить “глобальный план” — разбить сложный вопрос на простые, независимые под-вопросы.
-
Моё Решение: Я решил встроить это как альтернативную стратегию (DECOMPOSE). Для явно сравнительных вопросов система сначала вызывает “Агента-Декомпозитора”, который создает план, а уже потом запускает параллельный поиск по всем пунктам этого плана.
-
-
Граф Концептов из Graph-Guided Concept Selection (G2ConS)
-
Проблема: Семантический поиск хорош, но он не видит неявных связей между документами.
-
Идея: Вместо дорогого графа знаний, можно построить дешевый, но эффективный граф “концептов” (ключевых слов). Если “ОСАГО” и “ДТП” часто встречаются в одних и тех же документах, между ними в графе будет сильная связь.
-
Моё Решение: Это стало основой для моей “Головы D” в поисковом движке. Я реализовал создание такого графа с помощью TF-IDF и networkx.
-
-
Иерархический Поиск из LATTICE
-
Проблема: “Плоский” граф из тысяч концептов все еще может быть слишком “шумным”.
-
Идея: Сгруппировать концепты в иерархию (например, “темы” -> “концепты”).
-
Мое Решение: Я не стал строить полное дерево, а адаптировал идею, добавив еще один слой в “Голову D”. Сначала я кластеризую все концепты на “темы” с помощью KMeans, а затем поиск по графу происходит в два этапа: сначала ищем релевантные темы, а потом уже связи внутри них.
-
-
Динамический Выбор Контекста из Dynamic Passage Selector (DPS)
-
Проблема: Жесткий лимит top_k (например, брать всегда 10 документов) неоптимален. Для простого вопроса нужен 1 документ, для сложного — 15.
-
Идея: После грубого поиска заставить LLM выступить в роли “Агента-Селектора”, который сам выберет минимально необходимый набор документов из найденных кандидатов.
-
Моё Решение: Я заложил это в архитектуру как возможное улучшение — еще один агентский шаг после поиска.
-
-
Структурированное Мышление из Chain-of-Conceptual-Thought (CoCT)
-
Проблема: Стандартный “Chain-of-Thought” (“думай шаг за шагом”) слишком неструктурирован. LLM может “уйти в сторону” в своих рассуждениях.
-
Идея: Заставить модель думать “концепциями”. Перед тем как писать текст, она должна сначала сгенерировать тег-план, например: <концепт:ТЕЗИС>, <концепт:ДЕТАЛИЗАЦИЯ>, <концепт:ВЫВОД>.
-
Моё Решение: Это стало основой промпт-инжиниринга для всех моих агентов. Я решил, что каждый “умный” компонент системы — будь то SeaAgent или финальный Generator — должен сначала формировать концептуальный план своих действий, а уже потом выполнять их. Это должно было повысить предсказуемость и качество их работы.
-
“Чертеж на Салфетке”: Финальная Архитектура
Собрав все эти идеи воедино, я получил следующий план — амбициозный, многокомпонентный и, как я надеялся, очень мощный.
Вооружившись этим планом, я приступил к реализации, еще не зная, что почти каждая красивая стрелочка на этой схеме превратится в часы отладки, а API и организаторы хакатона приготовили свои “сюрпризы”.
Часть 2: Жестокая Реальность Отладки
Любой красивый архитектурный план остается лишь теорией до первого запуска. Мой случай не стал исключением. Этап реализации превратился в настоящее детективное расследование, где на каждом шагу меня ждали новые “сюрпризы” от API, библиотек и правил хакатона и собственной невнимательности.
Война с API или как я невнимательно прочёл документацию
Первой и самой болезненной проблемой стали API-вызовы. Архитектурно я решил использовать библиотеку litellm — это блестящий “швейцарский нож”, который предоставляет единый интерфейс для работы с сотнями LLM от разных провайдеров. План был прост: написать один LLMClient и затем легко переключать модели (Llama, Gemma, Mistral) в конфиге, проводя A/B-тесты.
Но моя ошибка была классической: я невнимательно прочитал документацию к этому мощному инструменту, понадеявшись на его “интуитивно понятный” интерфейс. Это стоило мне пары часов отладки.
Симптомы:
Мой LLMClient на litellm начал выдавать загадочные ошибки 401 AuthenticationError.
-
Логи показывали, что запросы к эмбеддинг-модели уходят не на прокси-сервер хакатона, а напрямую на api.openai.com.
-
Запросы к генеративным моделям падали с ошибкой key not allowed, хотя в списке разрешенных эта модель была.
Я попал в “парадокс конфигурации”:
-
Отправляю model = “meta-llama/…” -> litellm ругается, что не указан провайдер.
-
Отправляю model = “openrouter/meta-llama/…” -> Прокси-сервер ругается на “неверный ключ”.
Я перепробовал все: добавлял префикс openai/, менял клиентов, но каждый раз натыкался на новую стену. Казалось, что litellm и прокси-сервер говорят на разных языках.
Решение нашлось, когда я отбросил предположения и вернулся к первоисточнику — документации litellm. Там черным по белому было написано, что для работы с кастомными прокси, имитирующими OpenAI (а именно такой был у организаторов), нужно активировать специальный “главный рубильник”:
# Заставить litellm работать в режиме "прокси"
litellm.use_litellm_proxy = True
Эта одна строка заставила litellm принудительно отправлять любой запрос на указанный api_base, решая проблему с генеративными моделями. А для эмбеддингов, чтобы гарантировать 100% стабильность, я принял “силовое” решение — использовать нативный openai SDK, который корректно работал с base_url из коробки.
Мораль: RTFM (Read The F… Manual). Даже самые мощные и гибкие инструменты, как litellm, имеют свои особенности. 90% “загадочных” проблем с API — это не баги, а невнимательно прочитанная документация. Не доверяйте интуиции, доверяйте первоисточнику. Это сэкономит вам часы (или даже дни) отладки.
“Война с Ограничениями”
Едва я победил API, как столкнулся с последствиями моей собственной неверной интерпретации правил хакатона.
Проблема №1: Запрет на HuggingFace.
В первый день хакатона, организаторы сообщили:
“Принято решение ограничить использование HuggingFace с целью недопущения применения высоконагруженных моделей.”
Это был удар под дых. Мой план включал нейросетевой реранкер (CrossEncoder из sentence-transformers) — “тонкую оптику” для очистки результатов поиска. Этот компонент, загружаемый с HuggingFace, оказался вне закона.
Адаптация: Пришлось отказаться от этой идеи и сделать ставку на повышение качества “грубого” поиска. Я решил компенсировать отсутствие реранкера двумя вещами: внедрением MMR (Maximal Marginal Relevance) для повышения разнообразия результатов и усилением “мозгов” — моих агентских компонентов.
Проблема №2: Неверная Интерпретация “Точки Входа”.
Правила гласили, что решение должно быть представлено в виде zip-архива с main.py, который “включает всю логику”. Я, как и любой инженер, привыкший к модульной разработке, воспринял это как стандартное требование к “точке входа”. Мой план был прост: main.py будет импортировать компоненты из аккуратной структуры папок (rag_components/, utils/), а в zip-архив я положу все эти папки.
Но затем в чате хакатона появилось уточнение:
“предполагается, что весь код для запуска будет находиться в файле main.py“
“…допустимы только 2 файла”
Это полностью разрушило мою стратегию. Сервер проверки ожидал один монолитный файл, а не проект с кучей импортов.
Адаптация: Отказаться от чистой архитектуры в разработке было бы самоубийством. Поэтому я принял единственно верное инженерное решение: “разрабатывай модульно, сдавай монолитно”. Я написал скрипт сборки (build.py), который автоматически “склеивает” все мои 10+ Python-файлов в один гигантский submission_main.py, удаляя локальные импорты и собирая внешние в единый блок. Это позволило мне сохранить чистоту и управляемость в разработке, но при этом сдать решение в том странном формате, который требовали организаторы.
Мораль: Всегда перепроверяй даже самые, казалось бы, очевидные правила. Фраза “точка входа” может означать не то, к чему ты привык. И если требования окружения противоречат здравому смыслу, не ломай свой рабочий процесс — автоматизируй адаптацию к этим требованиям.
Часть 3: Производительность превыше всего
После всех сражений с API, Pylance и правилами, я наконец-то получил работающий MVP. Но первый же полный прогон на 15-20 вопросах показал неутешительную картину: среднее время ответа на один вопрос составляло ~35-40 секунд.
Простая экстраполяция на 500 вопросов давала более 5 часов работы. При лимите хакатона в 4 часа это был гарантированный провал. Стало очевидно, что моя “идеальная” архитектура с 2-3 итерациями, хоть и была умной, оказалась непозволительно медленной.
Настало время безжалостной, но основанной на данных оптимизации. Я использовал собственный ResourceManager как “приборную панель” и начал последовательно “дергать за рычаги”.
Рычаг №1: Радикальное Сокращение Итераций
Логи показали, что 90% времени “съедают” API-вызовы. Самый очевидный способ их сократить — уменьшить количество итераций. Я изменил в config.py всего одну строку:
MAX_ITERATIONS = 1
Это превратило мой сложный FAIR-RAG цикл в более простой, но быстрый пайплайн: Поиск -> Анализ (SEA) -> Генерация. RefinementAgent больше не вызывался. Это сразу же сократило количество LLM-вызовов на один вопрос с 3-4 до двух.
Рычаг №2: Правильный Инструмент для Каждой Задачи (Multi-Model)
Я запустил ab_test_models.py, чтобы проверить, какая комбинация моделей самая эффективная. Результаты были однозначными:
-
Llama-3-70B — идеальный “аналитик”. Она блестяще справлялась с задачами SeaAgent, требующими логики и следования формату JSON.
-
Но в роли “писателя” (Generator) она была медленной и генерировала слишком краткие ответы.
-
Mistral-Small и Gemma оказались гораздо более быстрыми и “разговорчивыми” генераторами.
Это подтвердило мою “Multi-Model” стратегию. Финальный выбор пал на Llama-70B для агентов и Mistral-Small для генерации — лучший баланс “ума”, скорости и качества.
Рычаг №3: Распараллеливание Всего, или Вспоминая про GIL
Даже после всех оптимизаций main.py все еще работал последовательно. Прогноз ~2.5 часа хоть и вписывался в лимит, но оставлял мало “запаса прочности”. Я лег спать с мыслью, что нужно что-то еще.
И, как это часто бывает, решение пришло на следующий день, когда в голове всплыл классический вопрос с собеседований на Python-разработчика: “Расскажите про GIL”. И тут меня осенило.
Global Interpreter Lock (GIL) в Python мешает по-настоящему распараллеливать вычисления на CPU с помощью потоков (threading). Но мои ~20 секунд на вопрос — это не вычисления. Это почти целиком ожидание ответа от сети (I/O-bound задача). А для таких задач threading — идеальный инструмент, потому что пока один поток “спит” в ожидании ответа от API, GIL позволяет другому потоку отправить свой запрос.
Вооружившись этой базовой истиной, я написал еще один тест, test_api_concurrency.py, чтобы измерить, сколько параллельных запросов выдержит API. Результаты показали, что он легко справляется с 8-16 одновременными потоками.
Это был финальный недостающий элемент. Я переписал основной цикл в main.py с использованием concurrent.futures.ThreadPoolExecutor с 8 воркерами. Вместо обработки 500 вопросов один за другим, моя система теперь обрабатывала по 8 за раз.
Мораль: Иногда самые сложные проблемы производительности решаются возвращением к самым базовым принципам Computer Science. И да, ходить на собеседования (или хотя бы вспоминать вопросы с них) — полезно.
Финальный Результат
Комбинация этих оптимизаций дала феноменальный результат. Я запустил финальный полный прогон main.py на всех 500 вопросах. Для максимального качества я использовал конфигурацию с тремя итерациями агентского цикла, рискуя временем выполнения.
Вот финальный отчет ResourceManager после завершения работы:
Давайте проанализируем эти цифры:
-
Время (time_elapsed_sec: 5745.46 s):
-
Общее время составило ~1.6 часа (5745 / 3600).
-
Это означает, что среднее “календарное” время на вопрос, благодаря параллелизму, было всего ~11.5 секунд (5745 / 500).
-
Вывод: Даже самая “тяжелая” и “задумчивая” конфигурация с 3 итерациями уложилась в 4-часовой лимит с более чем двукратным запасом.
-
-
Стоимость (api_spent_usd: $0.98):
-
Мы потратили чуть меньше одного доллара на весь датасет.
-
Вывод: Наш бюджет в $3 был использован всего на ~33%. Это доказывает высочайшую экономическую эффективность “Multi-Model” подхода, где дорогие LLM используются только для коротких, но важных задач.
-
В итоге, благодаря системному подходу к архитектуре и оптимизации, я смог реализовать очень сложный итеративный RAG-пайплайн, который оказался не только качественным, но и поразительно быстрым и дешевым в работе.
Это доказывает главный принцип: сложная архитектура — это не всегда медленная архитектура, если правильно определить и оптимизировать “бутылочные горлышки”.
Часть 4: Заглянем “Под Капот” — Трассировка и Визуализация
Итак, я получили быструю, экономичную и отказоустойчивую систему. Но как именно она “думает”? Чтобы ответить на этот вопрос, я использовал два инструмента для визуализации “внутренностей” моего RAG-агента: Phoenix Tracer для отслеживания логики и Matplotlib для визуализации структуры знаний.
Phoenix Tracer: Разбираем “Мыслительный” Процесс
Чтобы понять, как взаимодействуют все компоненты моего пайплайна, я воспользовался open-source библиотекой arize-phoenix для трассировки. Результат — это наглядная “водопадная” диаграмма, где виден каждый шаг.
Вот как выглядит трейс для стратегии DECOMPOSE. Сначала DecompositionAgent разбивает сложный вопрос на несколько простых под-запросов. Затем наш EnsembleRetriever ищет информацию по каждому из них.
Самое интересное — заглянуть внутрь одного из этих вызовов ParallelEnsembleRetriever. Phoenix позволяет нам “провалиться” и посмотреть, что именно нашла каждая из наших четырех “голов”.
Что мы видим на этом скриншоте:
-
Параллелизм в Действии: Общее время работы ансамбля (1.4с) определяется временем самой медленной “головы” (~945ms), а не их суммой. Это доказывает эффективность нашего ThreadPoolExecutor.
-
Синергия “Голов”: Каждая “голова” находит разные документы. VectorStoreRetriever ищет семантически похожие тексты, TFIDFRetriever — тексты с точными ключевыми словами. Вместе они обеспечивают максимальную полноту поиска.
-
Прозрачность: Мы можем буквально “зайти” в каждый спан и посмотреть, какие документы (output) он вернул. Это превращает наш “черный ящик” в полностью прозрачную систему.
“Мозг” Ретривера: Визуализация Графа Концептов
Одной из самых интересных частей нашего поискового движка является “Голова D” — Иерархический Граф Концептов. Я визуализировал его, чтобы показать семантическую структуру нашей базы знаний.
Что показывает эта “мешанина” графа:
-
Структуру Знаний: Граф имеет четкую структуру “ядро-периферия”. В центре находится плотное ядро из самых общих терминов (деньги, кредит, банк). На периферии — более узкие тематические кластеры (“наследство”, “ОСАГО”).
-
Ограничения TF-IDF: Простота нашего подхода (TF-IDF) приводит к тому, что в ядре много “мусорных” слов. Нейросетевой экстрактор концептов, как в статье LATTICE, сделал бы граф гораздо чище.
-
Почему это Работает: Несмотря на “грязноту”, граф все равно полезен. Он позволяет находить неявные связи между документами, что критически важно для multi-hop вопросов.
Эти инструменты визуализации не только помогли мне в отладке, но и наглядно демонстрируют, как именно работает сложная, многокомпонентная архитектура. В следующей, финальной части, мы посмотрим, как все это отразилось на итоговом качестве ответов.
Часть 5: Финальный Вердикт — В Поисках “Золотой Середины”
Визуализация и скорость — это хорошо, но главный критерий успеха — качество финальных ответов. Финальное решение будет оцениваться на закрытом датасете организаторами, но чтобы объективно сравнить мои архитектурные подходы на этапе разработки, я применил state-of-the-art методику — LLM-as-a-Judge.
Процесс Оценки: Создаем Своего “Экзаменатора”
Я написал скрипт llm_as_judge.py, который для публичного датасета из 500 вопросов (questions.csv) провел автоматическую оценку. В роли “судьи” выступила модель Llama-3-70B-Instruct, которая оценивала каждый ответ по двум критериям: Релевантность и Полнота и Полезность и Структура.
Важное примечание: Оценки, представленные ниже, сгенерированы моим собственным “судьей” и служили для внутреннего сравнительного анализа архитектур. Они могут (и, скорее всего, будут) отличаться от официальных результатов на закрытом тестовом датасете хакатона.
Результаты A/B-тестирования: Битва Стратегий
Я провел четыре полных прогона, тестируя каждую из моих ключевых архитектурных гипотез. Результаты оказались очень интересными и показали, как разные подходы влияют на поведение системы.
Гипотеза №1: “Больше итераций — лучше качество?”
Я протестировал итеративный (ITERATIVE) режим с разным количеством циклов.
|
Итераций |
Общая Оценка (LLM-as-a-Judge) |
Вывод |
|
3 |
8.48 / 10 |
Избыточность. Ответы становятся “водянистыми” и содержат повторения. |
|
2 |
8.85 / 10 |
Хорошо, но все еще страдает от избыточности. |
Вывод: Оказалось, что “больше” — не значит “лучше”. Дополнительные итерации успешно находили больше информации, но модель-генератор, получив слишком обильный и пересекающийся контекст, начинала повторяться. Ответы оставались правильными, но их структура и лаконичность страдали, за что строгий LLM-as-a-Judge и снижал оценки.
Гипотеза №2: “Декомпозиция — ключ к сложным вопросам?”
Я протестировал стратегию DECOMPOSE, которая разбивает вопрос на под-вопросы перед поиском.
|
Стратегия |
Общая Оценка (LLM-as-a-Judge) |
Вывод |
|
DECOMPOSE |
9.42 / 10 |
Отлично. Прекрасный баланс качества и скорости. |
Вывод: Этот подход показал себя блестяще, доказывая, что для сложных вопросов лучше сначала “распланировать” поиск.
Гипотеза №3: “Простота — залог успеха?”
Наконец, я протестировал самую простую “умную” стратегию — ITERATIVE всего с одной итерацией. По сути, это пайплайн: Поиск -> Анализ (SEA) -> Генерация.
|
Стратегия |
Общая Оценка (LLM-as-a-Judge) |
Вывод |
|
ITERATIVE (1 итер.) |
9.66 / 10 |
Лучший результат из всех. |
Финальный Анализ
Результаты тестов привели к важному выводу: для доступных нам моделей (Mistral-Small, Gemma) самая простая из “умных” стратегий оказалась самой эффективной.
Моя гипотеза заключается в том, что наш 4-головый EnsembleRetriever настолько хорош, что одного раунда поиска достаточно для сбора “достаточно хорошего” контекста. Дальнейшие итерации, хоть и добавляют больше деталей, создают для моделей среднего размера (7B, 27B) проблему “утопания в контексте”, что приводит к избыточности в ответах.
Возможно, для более мощных и “умных” моделей (таких как GPT-4o или будущих поколений), способных лучше синтезировать большой и противоречивый контекст, 2-3 итерации действительно дали бы прирост качества. Но в рамках нашего хакатона, простота оказалась залогом надежности и качества.
Таким образом, для финального решения была выбрана именно эта конфигурация: ITERATIVE с MAX_ITERATIONS = 2 — идеальный баланс скорости, стоимости и, как показали внутренние тесты, максимального качества для данного стека технологий.
Примеры работы на одиночных вопросах:
Вопрос: Как безопасно оплатить покупку онлайн?
Decompose:
ОТВЕТ
Как безопасно оплатить покупку онлайн?
1. Используйте проверенные платежные системы
-
Мобильные платежные системы (Apple Pay, Google Pay, Mir Pay, Samsung Pay) — данные карты не сохраняются на сайте, а шифруются и передаются напрямую платежной системе.
-
Платежные шлюзы (Visa Secure, MasterCard SecureCode, Mir Accept) — после ввода реквизитов карты вас должны перенаправить на защищенную страницу банка для ввода кода из СМС.
2. Проверяйте страницу оплаты
-
Убедитесь, что сайт перенаправил вас на страницу платежной системы (не на сам сайт магазина).
-
Логотипы платежных систем должны быть активными ссылками, ведущими на их официальные сайты (а не просто картинками).
3. Не вводите данные карты на подозрительных сайтах
-
Если сайт просит ввести код с обратной стороны карты или код из СМС — это признак мошенничества.
-
Никогда не сохраняйте данные карты на сайте, если не уверены в его надежности.
4. Используйте виртуальные карты или отдельные карты для онлайн-покупок
-
Виртуальные карты (одноразовые или с ограниченным лимитом) — защищают от несанкционированных списаний.
-
Отдельная карта для онлайн-покупок — переводите на нее только нужную сумму.
5. Оплата через Систему быстрых платежей (СБП)
-
QR-коды — безопасный способ оплаты без ввода реквизитов карты.
-
Переводы по номеру телефона — удобно и защищено (лимиты до 100 000 ₽ в месяц без комиссии).
6. Дополнительные меры безопасности
-
Не переходите по ссылкам из мессенджеров — мошенники могут создать фишинговые страницы.
-
Используйте «безопасную сделку» на сервисах объявлений (деньги резервируются до получения товара).
-
Оплачивайте наложенным платежом, если нет возможности передать товар лично.
Вывод: Для безопасных онлайн-покупок выбирайте проверенные платежные системы, избегайте ввода данных карты на подозрительных сайтах и используйте дополнительные инструменты защиты (виртуальные карты, СБП).
КОНЕЦ ОТВЕТА
FAIR с 2 Итерациями:
ОТВЕТ:
Как безопасно оплатить покупку онлайн?
1. Проверка надежности сайта
-
Убедитесь в подлинности сайта:
-
Проверьте, что адрес сайта совпадает с официальным (указанным в реестре регулятора).
-
Убедитесь, что сайт начинается с https и есть значок закрытого замка в адресной строке.
-
Ищите логотипы платежных систем (Visa Secure, MasterCard SecureCode, Mir Accept) — они должны быть активными ссылками.
-
-
Избегайте поддельных сайтов:
-
Мошенники копируют популярные магазины, предлагая заманчивые цены.
-
Не вводите данные карты, если вас не перенаправили на страницу платежной системы.
-
2. Выбор безопасного способа оплаты
-
Используйте системы мобильных платежей:
-
Apple Pay, Google Pay, Mir Pay, Samsung Pay — данные карты не сохраняются на сайте, а шифруются.
-
Продавец не видит реквизиты карты, что снижает риск мошенничества.
-
-
Оплата по QR-коду:
-
Удобна и безопасна, так как не требует ввода данных карты.
-
Убедитесь, что магазин поддерживает платежи через ваш банк или СБП.
-
-
Электронные кошельки:
-
Заведите отдельный кошелек для онлайн-покупок, пополняйте его перед оплатой.
-
Не привязывайте зарплатную или кредитную карту, если не уверены в надежности сайта.
-
3. Дополнительные меры безопасности
-
Не сохраняйте данные карты на сайтах:
-
Если сайт предлагает сохранить реквизиты, откажитесь, если не уверены в его безопасности.
-
-
Используйте отдельную карту для онлайн-покупок:
-
Установите лимит трат и настройте оповещения о списаниях.
-
-
Будьте осторожны с BNPL-сервисами:
-
Проверяйте условия договора, сроки и штрафы за просрочку.
-
Учтите, что такие сервисы не всегда отражаются в кредитной истории.
-
-
Не передавайте коды из СМС:
-
Вводите их только на странице платежной системы, а не на сайте магазина.
-
4. Защита от мошенничества
-
Не переводите деньги до получения товара:
-
Дождитесь подтверждения зачисления средств (уведомление от банка).
-
Используйте услугу «безопасная сделка» на сайтах объявлений.
-
-
Следите за подозрительными операциями:
-
Если перевод не пришел, проверьте, не использовали ли мошенники демоверсию банковского приложения.
-
-
Обновляйте антивирусы и используйте сложные пароли:
-
Включите двухфакторную аутентификацию для защиты аккаунтов.
-
Соблюдая эти правила, вы минимизируете риски при онлайн-покупках.
КОНЕЦ ОТВЕТА
Вопрос: Как меняется роль Банка России в защите интересов клиентов при реорганизации НПФ по сравнению с его мандатом по контролю инфляции?
Decompose:
ОТВЕТ
На основе предоставленного контекста можно сделать следующие выводы о роли Банка России в защите интересов клиентов при реорганизации НПФ по сравнению с его мандатом по контролю инфляции:
1. Роль Банка России в защите клиентов при реорганизации НПФ
-
Регулирование и контроль: Банк России выступает регулятором финансового рынка, включая негосударственные пенсионные фонды (НПФ). В случае проблем у НПФ (например, отзыва лицензии) регулятор может вмешаться, чтобы защитить интересы клиентов.
-
Жалобы и претензии: Клиенты могут обращаться в Банк России с жалобами на НПФ, если их права нарушены. Регулятор рассматривает такие обращения и принимает меры.
-
Информационная поддержка: Банк России предоставляет клиентам информацию о том, как действовать в случае проблем с НПФ, включая возможность обращения в АСВ (Агентство по страхованию вкладов) для разъяснений.
2. Роль Банка России в контроле инфляции
-
Монетарная политика: Основная задача Банка России в этом аспекте — поддержание стабильности национальной валюты и контроль инфляции через регулирование денежной массы, процентных ставок и других инструментов.
-
Финансовая стабильность: Контроль инфляции косвенно влияет на защиту интересов клиентов, так как стабильная экономика снижает риски для сбережений и инвестиций.
3. Различия в мандатах
-
Защита клиентов (НПФ): Банк России действует как надзорный орган, обеспечивая соблюдение законов и защиту прав вкладчиков и инвесторов.
-
Контроль инфляции: Это макроэкономическая задача, направленная на стабилизацию экономики в целом, а не на прямую защиту отдельных клиентов.
Вывод
Банк России выполняет две ключевые функции:
-
Регулирование и защита клиентов (включая НПФ) через надзор, контроль и разъяснение прав.
-
Контроль инфляции через монетарную политику, что косвенно способствует финансовой стабильности.
В случае реорганизации НПФ регулятор действует в рамках защиты прав клиентов, тогда как контроль инфляции — это более общая задача, направленная на устойчивость экономики.
КОНЕЦ ОТВЕТА
FAIR с 2 Итерациями:
ОТВЕТ
Роль Банка России в защите интересов клиентов при реорганизации НПФ
-
Контроль и согласование реорганизации
-
Банк России обязан проверить, сможет ли преобразованный НПФ выполнить обязательства перед клиентами.
-
Если проверка пройдена, регулятор согласовывает реорганизацию; в противном случае она отменяется.
-
-
Защита прав клиентов
-
НПФ обязан уведомить клиентов о реорганизации, включая условия перехода накоплений и возможность отказа.
-
Банк России следит за соблюдением этих требований, обеспечивая прозрачность процесса.
-
-
Финансовая стабильность
-
Регулятор оценивает риски для клиентов, чтобы избежать потерь их пенсионных накоплений.
-
Роль Банка России в контроле инфляции
-
Регулирование денежно-кредитной политики
-
Банк России использует ключевую ставку для поддержания инфляции на уровне 4%, балансируя между сдерживанием роста цен и стимулированием экономики.
-
-
Реакция на экономические кризисы
-
В чрезвычайных ситуациях (например, санкции, падение курса рубля) регулятор корректирует ставку для стабилизации финансового рынка.
-
-
Долгосрочные цели
-
Поддержание низкой инфляции создает условия для устойчивого экономического роста и долгосрочного планирования.
-
Сравнение ролей
-
При реорганизации НПФ Банк России фокусируется на индивидуальной защите клиентов (прозрачность, сохранность накоплений).
-
При контроле инфляции регулятор действует на макроэкономическом уровне, влияя на общие условия экономики через ставки и денежную политику.
Обе роли направлены на стабильность финансового рынка, но реализуются через разные механизмы.
КОНЕЦ ОТВЕТА
Снова обе стратегии справились блестяще. Ответ ITERATIVE режима получился немного более подробным и структурированным, что говорит о пользе второй итерации для таких сложных вопросов. Это идеально демонстрирует компромисс “скорость vs. глубина” между разными архитектурными подходами.
Часть 6: Заключение — Главные Уроки и Будущее Проекта
Заключение: Главные Уроки и Будущее Проекта
Этот 72-часовой спринт стал концентрированным курсом по созданию современных RAG-систем. Вот главные уроки, которые я вынес:
-
Архитектура — это главное. Хорошо продуманная, модульная архитектура (спасибо SOLID!) позволила мне быстро адаптироваться к меняющимся правилам, отлаживать компоненты по отдельности и в итоге собрать сложную систему в одиночку.
-
Телеметрия — это не роскошь, а необходимость. Встроенный ResourceManager для отслеживания времени, стоимости и RAM был моим главным “компасом” при оптимизации. Без объективных данных все решения по производительности были бы просто догадками.
-
“Больше” не всегда значит “Лучше”. Мои эксперименты показали, что для доступных нам моделей одна итерация агентского цикла оказалась качественнее и надежнее, чем три. Это доказывает, что каждая RAG-система — это сложный баланс, и “слепое” наращивание сложности не всегда приводит к лучшим результатам.
-
Научные статьи — это не теория, а руководство к действию. Все ключевые “фишки” моей системы (агентский цикл, граф концептов, CoCT) были взяты из свежих исследований. Умение находить, адаптировать и комбинировать SOTA-идеи — это, возможно, главный навык современного ML-инженера.
Будущие Улучшения (Roadmap)
Этот проект — прочный, высокопроизводительный фундамент. Я вижу несколько ключевых направлений для его дальнейшего развития, превращающих его в полноценную SOTA-платформу для агентского RAG.
Уровень 1: Повышение “Интеллекта” Агентов
-
Адаптивный Маршрутизатор Стратегий:
-
Идея: Вместо ручного переключения STRATEGY в конфиге, создать “Агента-Маршрутизатора”. Этот LLM-агент будет на первом шаге анализировать вопрос и автоматически выбирать, какую из наших стратегий (SIMPLE, DECOMPOSE или ITERATIVE) использовать.
-
Влияние: Превратит фреймворк в полностью автономную, адаптивную систему, которая сама выбирает оптимальный баланс “скорость/качество” для каждого вопроса.
-
-
Гибридный Агентский Пайплайн (Decompose + Iterative):
-
Идея: Создать “супер-стратегию”, которая сначала декомпозирует сложный вопрос на под-запросы (Decomposition), а затем для каждого под-запроса запускает наш итеративный FAIR-RAG цикл (Iterative Refinement).
-
Влияние: Потенциально самый высокий уровень качества и полноты ответов за счет комбинации двух мощных подходов. Основной вызов — оптимизация производительности.
-
-
Динамический Выбор Контекста (Агент-Селектор):
-
Идея: Заменить фиксированный MAX_CONTEXT_DOCS на “Агента-Селектора”. Этот LLM будет анализировать топ-50 найденных документов и генерировать список номеров тех, которые являются минимально необходимыми для ответа (идея из статьи DPS).
-
Влияние: Сделает размер контекста адаптивным и еще более “чистым”.
-
Уровень 2: Улучшение “Оптики” и “Движка”
-
Внедрение Нейросетевого Реранкера:
-
Идея: Добавить дополнительный шаг “тонкой очистки” после EnsembleRetriever. Использовать либо разрешенные API-реранкеры, либо реализовать свой легковесный реранкер на torch (вдохновляясь статьей EBCAR).
-
Влияние: Значительно повысит точность (precision) поиска и качество финального контекста, подаваемого в агенты.
-
-
Обучаемый Граф Концептов:
-
Идея: Вместо TF-IDF использовать более продвинутые методы для построения графа, например, эмбеддинги графов (Graph Embeddings) или обучать веса ребер на основе обратной связи от SeaAgent.
-
Влияние: Сделает “Голову D” еще более мощным инструментом для multi-hop рассуждений.
-
Уровень 3: Инженерное Совершенство
-
Полный Переход на asyncio:
-
Идея: Переписать весь I/O-bound код (API-вызовы, файловые операции) с ThreadPoolExecutor на нативный async/await.
-
Влияние: Потенциальное ускорение параллельной обработки, особенно при масштабировании до десятков и сотен одновременных запросов. Снижение потребления ресурсов по сравнению с потоками.
-
-
Расширение Системы Тестирования:
-
Идея: Реализовать полноценный фреймворк для юнит-тестирования с unittest.mock. Создать “золотой” датасет для регрессионного тестирования качества ответов. Настроить CI/CD для автоматического прогона тестов.
-
Влияние: Повысит стабильность и надежность проекта при дальнейших доработках.
-
Весь код, включая отладочные скрипты, тесты и скрипт сборки, доступен в репозитории на GitHub – https://github.com/Runoi/ai-for-finance-hack-2025.
Заключение: История Одного Архитектурного Спринта
Хакатон — это всегда спринт. Но иногда, в процессе этого забега, ты успеваешь пробежать настоящий марафон идей, гипотез и открытий. Именно таким марафоном и стал для меня AI for Finance Hack 2025.
Вы могли заметить, что эта статья выходит в тот момент, когда первый этап хакатона еще не завершился. Это не случайно. В связи с некоторыми организационно-техническими неполадками у организаторов, сроки были немного сдвинуты, что дало мне неожиданную паузу — момент, чтобы выдохнуть, отрефлексировать и структурировать историю этого архитектурного приключения, пока она еще свежа.
Поэтому эта статья — не отчет о финальном решении и не заявка на победу. Это, скорее, “бортовой журнал” архитектора в реальном времени. Я сознательно сфокусировался на концепциях, идеях и процессе принятия решений, а не на вылизанном до блеска коде. Финальный репозиторий, безусловно, будет опубликован после завершения хакатона, но главную ценность, как мне кажется, представляет именно путь, а не только результат.
Я делюсь этим путем открыто и заранее. Даже если кто-то вдохновится моими идеями, возьмет эту архитектуру и реализует ее лучше — я буду только рад. Я пришел на этот хакатон в первую очередь за опытом, за возможностью отточить навыки в условиях жестких ограничений и, конечно, чтобы узнать новое. Если мой опыт окажется полезен еще кому-то — значит, главная цель достигнута.
Отдельно хочу поблагодарить моего сокомандника Никиту. Хоть он и JS-фронтендер, его свежий взгляд “со стороны” на проблемы и архитектуру был невероятно ценен. Но что еще важнее — просто его присутствие и участие, в отличие от двух других, оказало огромную моральную поддержку, без которой этот “одиночный” штурм был бы гораздо тяжелее.
Наконец, о репозитории. Его история коммитов может показаться немного “грязной” и не всегда последовательной. Причина в том, что одной из моих главных личных целей на этот хакатон было отточить именно командное взаимодействие в Git, с Pull Request’ами, ревью и атомарными коммитами. Когда стало понятно, что большая часть работы ляжет на мои плечи, фокус сместился с идеального Git workflow на скорость итераций и экспериментов. Это тоже ценный урок хакатона: будь готов менять не только архитектуру, но и процессы.
Спасибо, что прошли этот путь со мной. Надеюсь, мой опыт окажется полезным.
Небольшое послесловие: я принял участие в хакатоне в исследовательских целях и для практики, так что результаты не на первом месте. И да, в данной задаче самый лучший – самый простой
Автор: Runoi


