Меня зовут Саша, я — старший AI-инженер в Лаборатории искусственного интеллекта «Честного знака». Наша команда развивает «Честного помощника» — мультиагентную LLM-систему для обработки документов, поиска информации по Confluence, Jira, GitLab и генерации текстов. Главная цель команды — повышать эффективность и качество работы сотрудников за счёт расширения числа специализированных агентов в нашей мультиагентной системе.
Но давайте будем честны: мы начинали с решения совсем другой задачи. Терминология на тот момент была ещё сырой и непроверенной, рынок open-source решений оставлял желать лучшего — мы выжимали максимум из того, что было доступно. Поэтому дальнейший рассказ будет полезен широкой аудитории: от тех, кто только начинает разбираться в теме, до руководителей отделов, которые хотят внедрить подобное решение у себя.
Это вторая часть рассказа о том, как мы строили «Честного помощника» — мультиагентную систему на open-source моделях в «Честном знаке».
В первой части мы разобрали базовую терминологию и Naive RAG — подход, с которого начинает большинство команд. Если вы её пропустили, рекомендую начать оттуда: дальнейшее повествование опирается на понятия, которые мы там ввели.
Advanced RAG
После MVP стало понятно: проблема не в модели и не в векторной базе — проблема в том, что мы не умели ни нормально готовить документы к индексации, ни правильно обрабатывать запрос перед поиском, ни отфильтровывать лишнее после него. Advanced RAG — это именно про это.
Если Naive RAG — прямолинейный пайплайн «запрос → поиск → генерация», то Advanced RAG добавляет два дополнительных слоя:
-
Pre-retrieval — всё, что происходит до поиска: переписывание запроса, расширение аббревиатур, построение маршрутов поиска, улучшение качества индексации.
-
Post-retrieval — всё, что происходит после того, как чанки найдены: их перестановка, фильтрация, ранжирование, чтобы в LLM попадало только действительно релевантное.
Мы пошли по обоим направлениям одновременно, начав с того, что болело сильнее всего — с индексации.
Оптимизация индексации
На этапе MVP мы работали с готовым Confluence Loader и стандартным сплиттером. Это давало нам текст, но не давало качества. В Advanced RAG мы переработали весь пайплайн обработки документов.
Small-2-Big — изменили стратегию хранения и поиска чанков. Вместо того чтобы векторизовать и искать по крупным фрагментам, мы стали делать эмбеддинг на уровне отдельных предложений, а при нахождении релевантного предложения — расширять контекст вокруг него до более крупного фрагмента. Это позволяет эмбеддеру точнее попадать в смысл запроса, а LLM получать достаточно контекста для ответа.
Sliding Window — добавили перекрывающиеся чанки: каждый следующий фрагмент частично повторяет конец предыдущего. Это страхует от случаев, когда нужная информация оказывается на границе двух чанков и теряется при разбиении.
Метаданные — к каждому чанку стали добавлять структурированные метаданные: название документа, дата последнего обновления, примеры вопросов и ответов к статье. При поиске это позволяет дополнительно фильтровать результаты, а не опираться только на косинусное расстояние.
Новый пайплайн Chunking
Итоговый пайплайн выглядит так:
Markdown Splitter → RecursiveCharacter Splitter (с модификациями)
Что это даёт: — Специализированный парсинг под Markdown — MarkdownHeaderTextSplitter сначала разбивает документ по заголовкам #, ##, ###, сохраняя структуру страницы. Только потом RecursiveCharacterTextSplitter режет внутри секций по токенам. – Дополнительная обработка таблиц и кода — перед векторизацией таблицы и блоки кода обрабатываются отдельно, чтобы не разрезать их посередине. – Метаданные к чанкам — каждый чанк несёт с собой название статьи из которой он взят, имя автора, пространство Confluence. – Small-2-Big — в LLM передаётся не полный текст статьи, а только релевантный чанк с расширенным контекстом до markdown чанка.
Результаты: что изменилось в метриках
Прежде чем перейти к цифрам — немного о том, как мы вообще получили датасет для оценки.
Процесс разметки
Для каждого Confluence-пространства, которое входит в базу знаний, мы привлекли самих пользователей этого пространства — людей, которые работают с этими документами ежедневно. Задача была простой: придумать реальные запросы, с которыми вы обращаетесь или могли бы обратиться к системе, и для каждого запроса вручную отметить ID статей, которые должны присутствовать в ответе.
В итоге получился датасет, в котором каждая пара «запрос → список релевантных статей» отражает реальное доменное знание и реальные информационные потребности пользователей. Это принципиально важно: именно на таком датасете имеет смысл измерять качество поиска, а не на синтетических вопросах.
Разметка — трудоёмкий процесс, но нам удалось получить около 100 вопросов по каждому из пространств.
Метрики качества поиска
|
k |
Cosine Sim |
Soft Precision |
Soft Recall |
Soft F1 |
|
10 |
0.83 |
0.85 |
0.70 |
0.77 |
|
20 |
0.83 |
0.82 |
0.79 |
0.80 |
|
30 |
0.84 |
0.76 |
0.85 |
0.80 |
|
40 |
0.84 |
0.72 |
0.90 |
0.80 |
Ключевые выводы:
-
Precision остаётся стабильной — колебание в диапазоне 0.72–0.85 говорит о том, что система устойчиво находит релевантные чанки вне зависимости от глубины поиска. Шум с ростом k есть, но он умеренный.
-
Recall растёт с k — при k=10 находим 70% нужной информации, при k=40 уже 90%. Именно recall является основным управляемым параметром при выборе k.
-
F1 выходит на плато при k=20–40 — все три значения дают F1=0.80, что говорит о насыщении: дальше k можно увеличивать ради recall, но без прироста в балансе.
-
Практический выбор k — брать 40 чанков и отдавать все в LLM нецелесообразно: шум в контексте снижает качество ответа. Рабочая схема: извлечь 30–40 чанков, ранжировать через bi-encoder, передать в модель топ-10–15.
Смена модели-эмбеддера
Параллельно с работой над индексацией мы провели масштабное тестирование bi-encoder моделей — в общей сложности проверили десятки кандидатов на нашем доменном датасете. Модели оценивались по качеству поиска на русскоязычных корпоративных текстах: насколько точно они находят релевантные чанки, как ведут себя на специфической терминологии и аббревиатурах.
В результате мы перешли с ai-sage/Giga-Embeddings-instruct, которую использовали в MVP, на intfloat/multilingual-e5-large. Несмотря на меньший размер, multilingual-e5-large показала лучший баланс между качеством поиска, скоростью векторизации и стабильностью на разнородных текстах — именно то, что нужно для production-нагрузки с живым корпусом Confluence.
Влияние на потребление токенов
Переход к чанковому поиску вместо передачи полных статей дал измеримый результат: потребление контекста LLM сократилось в 2 раза — с ~120 000 токенов до ~60 000. Значение 60k — это верхняя граница, установленная нами внутренне из соображений безопасности и стабильности: мы сознательно ограничили максимальный контекст, чтобы не перегружать модель. На практике реальные запросы в production потребляют значительно меньше 60k токенов — в среднем в несколько раз.
Modular RAG
Ранее мы с Вами говорили исключительно про RAG, однако нельзя считать, что наши амбиции останавливаются лишь на нем и дальше мы не захотели бы расширять функционал нашего Честного помощника.
Следующий уровень эволюции нашей системы — переход от монолитного пайплайна к модульной архитектуре, в центре которой стоит понятие агента.
Агент — это «интеллектуальная» система, которая самостоятельно взаимодействует с окружающей средой: принимает решения, вызывает инструменты и формирует ответ, минимально нагружая пользователя уточняющими вопросами. Понятие агента сегодня активно развивается и переосмысляется вместе с ростом возможностей больших языковых моделей.
LLM-агент, или Single Agent, — это языковая модель, наделённая доступом к набору заранее определённых инструментов (tools). Через эти инструменты агент воспринимает окружающую среду и действует в ней. Типичные примеры таких инструментов: поиск в интернете, векторные базы знаний (RAG), интерпретаторы кода и другие внешние сервисы.

Архитектурно принято выделять два основных подхода:
-
Одиночный LLM агент (Single-Agent). LLM работает напрямую с набором инструментов (tools) — поиском, Python-интерпретатором, сторонними AI-сервисами. Схема хорошо справляется с задачами умеренной сложности, но при увеличении числа инструментов и усложнении логики агент начинает перегружаться: контекст разрастается, качество рассуждений падает.
-
Мультиагентная LLM система (Multi-Agent). Вместо одного универсального агента задачу решает ансамбль специализированных: Planner, Research, Analyst и другие — каждый отвечает за свой участок работы. Такое разделение ответственности делает систему более устойчивой и масштабируемой: комплексные, многошаговые запросы обрабатываются эффективнее, а отдельные модули можно улучшать независимо друг от друга.
Справка: что такое Tool (инструмент агента)
Скрытый текст
Tool — это функция или внешний сервис, который агент может вызвать самостоятельно в ходе выполнения задачи. С точки зрения LLM инструмент представляет собой формализованный интерфейс: модель получает описание инструмента (название, назначение, параметры), решает, нужно ли его вызвать, формирует аргументы и передаёт управление. После получения результата агент продолжает рассуждение.
Типичные примеры инструментов: поиск в векторной базе знаний, веб-поиск, интерпретатор Python, вызов внешнего API, чтение и запись файлов. Именно инструменты отличают агента от обычной языковой модели: вместо того чтобы «галлюцинировать» ответ, агент получает актуальные данные из внешней среды и строит ответ на их основе.
В мультиагентных системах каждый агент, как правило, наделяется ограниченным набором инструментов, соответствующим его роли. Это снижает когнитивную нагрузку на модель и делает поведение системы предсказуемым.
Далее, мы перешли к мультиагентной архитектуре, в которой каждый агент наделён чёткой зоной ответственности и действует автономно, передавая результат своей работы следующему агенту по цепочке. Вся система работает внутри Честного помощника (ЧП) и поддерживает два режима маршрутизации запросов.
Однако путь к этой архитектуре не был прямым. Нам потребовалось две итерации, чтобы нащупать подход, который действительно работает.
На первой итерации мы воспринимали мультиагентность слишком буквально — как жёстко заданную последовательность шагов, где каждый агент выполняет предписанное действие и передаёт эстафету дальше. По сути, это был детерминированный конвейер, лишь внешне напоминающий агентную систему. Схожая ловушка подстерегала нас и в восприятии источников данных: RAG, веб-поиск и другие внешние сервисы поначалу казались нам самостоятельными «монолитами», каждый из которых требует собственного предопределённого сценария вызова. Это сковывало систему и ограничивало её гибкость.
На второй итерации мы переосмыслили роль языковой модели: из исполнителя инструкций она превратилась в полноценного агента, самостоятельно принимающего решения о том, какой инструмент вызвать, в каком порядке и нужно ли это вообще. Qdrant, Confluence, Jira, Gitlab, веб-поиск и прочие источники данных заняли своё место в едином наборе инструментов — равноправных и взаимозаменяемых по ситуации.
Разработка агентов ведется у нас на базе фреймворка LangGraph, который позволяет строить мультиагентные системы в виде направленных графов.
Справка: что такое LangGraph
Скрытый текст
LangGraph — фреймворк от команды LangChain для построения мультиагентных систем в виде направленных графов. В отличие от линейных цепочек (chains), граф позволяет описывать сложные потоки исполнения: ветвление, циклы, параллельную обработку и возврат к предыдущим шагам.
Три ключевых понятия, которые нужно держать в голове: – Node (нода) — атомарная единица вычисления: функция или агент, выполняющий конкретную задачу (вызов LLM, обращение к инструменту, трансформация данных).- Edge (ребро) — связь между нодами, определяющая порядок передачи управления. Может быть условным (conditional edge): в зависимости от текущего состояния граф выбирает следующую ноду.- State (состояние) — типизированный словарь (TypedDict), который передаётся между нодами и накапливает результаты работы всей системы.
LangGraph хорошо подходит там, где обычных цепочек недостаточно: когда логика нелинейна, агентов несколько, а поток выполнения зависит от промежуточных результатов.
Итерация 1: детерминированный конвейер
Режим «Общий запрос»
В этом режиме пользовательский запрос обрабатывается ансамблем из трёх специализированных агентов, каждый из которых отвечает за собственный тип входных данных:
-
Агент процессинга файлов принимает загруженные пользователем документы (PDF, таблицы, изображения), извлекает из них содержимое и передаёт его в общий контекст.
-
Агент транскрибации самостоятельно обрабатывает видео- и аудиоматериалы, преобразуя их в текстовое представление.
-
Агент генерации ответа получает контекст от других агентов и формирует финальный ответ пользователю — либо опираясь на переданные материалы, либо отвечая напрямую, если входных файлов нет.
Агенты работают параллельно там, где это возможно, и координируются внутри Честного помощника без участия пользователя.
Режим «Поиск по базе знаний»
Когда запрос требует обращения к корпоративной базе знаний, управление передаётся двухагентной цепочке, в которой агенты действуют последовательно: первый улучшает запрос, второй ищет и отвечает.
Метаагент — агент первого уровня, отвечающий за понимание и обогащение запроса. Он включает два автономных инструмента: – Расширение сокращений — агент распознаёт и декодирует доменные аббревиатуры и специфичные термины, уникальные для тематики базы знаний. – Рефлексия и переформулировка — агент критически переосмысливает запрос пользователя и расширяет его, чтобы поиск в векторном пространстве охватил максимально релевантные документы.
RAG-Agent — агент второго уровня, принимающий обогащённый запрос от Метаагента и самостоятельно выполняющий полный цикл работы с базой знаний: – Подготовка контекста — агент извлекает релевантные чанки из векторного хранилища и формирует из них связный контекст. – Реранжирование — агент дополнительно упорядочивает и фильтрует найденные фрагменты, отдавая приоритет наиболее точным. – Генерация ответа — агент формирует финальный ответ, опираясь исключительно на верифицированный контекст из базы знаний.
Такая мультиагентная цепочка позволяет изолировать ответственность каждого агента, независимо дорабатывать отдельные звенья системы и существенно повышать качество ответов по сравнению с монолитным RAG-пайплайном.
Что пошло не так
На бумаге архитектура выглядела стройно. На практике обнаружилась ловушка, о которой предупреждают опытные разработчики агентных систем: когда граф полностью детерминирован, агент перестаёт быть агентом — он превращается в обычный конвейер, просто с LLM внутри каждой ноды.
Любое изменение логики требовало правки топологии графа. Добавить новый источник данных? Значит новая нода, новые рёбра, новые тесты условных переходов. Хочется, чтобы модель обращалась к RAG только при необходимости, а не всегда? Придётся прописывать conditional edges с явными условиями выхода. Система росла, а вместе с ней — хрупкость и стоимость сопровождения.
Именно это противоречие стало отправной точкой для второй итерации.
Итерация 2: агент на основе ReAct
Переосмыслив первую итерацию, мы задались простым, но меняющим всё вопросом: а что если любой источник данных — RAG, транскрибация, внешний API — перестанет быть монолитным блоком и превратится в инструмент (tool), которым агент пользуется по собственному усмотрению? А ещё лучше — оформить их как стандартизированные MCP-протоколы?
Это решение открыло перед нами принципиально иной горизонт. Система перестала быть жёстко заданным конвейером и стала по-настоящему расширяемой: любой новый инструмент подключается без перестройки архитектуры, все наработки предыдущих итераций переиспользуются, а агент сам решает, к чему обратиться и когда.
Стоит признать, что реализовать всё это стало возможным благодаря стечению нескольких факторов одновременно. Во-первых, мы научились доверять open-source моделям — впрочем, не слепо: доверяем, но проверяем, и о том, как именно, расскажем чуть позже. Во-вторых, мы научились выжимать максимум из имеющихся GPU-ресурсов, что критично для production-систем с реальной нагрузкой. И наконец — нельзя не отдать должное команде Qwen, чьи open-source модели стали надёжным фундаментом всей этой архитектуры.
Справка: что такое ReAct
Скрытый текст
ReAct (от Reasoning + Acting) — паттерн построения агентов, при котором языковая модель чередует два типа действий: рассуждение (Thought) и действие (Action), обогащая каждый следующий шаг наблюдением (Observation) из внешней среды.
Цикл работы выглядит так:
-
Thought (Мысль) — модель вербализует рассуждение: анализирует текущее состояние, формулирует, что нужно узнать, и планирует следующий шаг.
-
Action (Действие) — модель вызывает конкретный инструмент: поиск в базе знаний, обращение к API, запрос к веб-поиску.
-
Observation (Наблюдение) — внешняя среда возвращает результат действия (текст статьи, данные из базы), который становится контекстом для следующей мысли.
Цикл повторяется до тех пор, пока модель не решит, что накопленной информации достаточно для финального ответа. При этом рассуждение не просто «предшествует» действию — оно активно направляет его: агент декомпозирует задачу на шаги, отслеживает прогресс, верифицирует данные из нескольких источников и корректирует план при неожиданных результатах — всё это без жёстко заданного сценария.
Ключевое отличие от детерминированного графа: ReAct-агент не следует заранее прописанной топологии. Маршрут строится на ходу, шаг за шагом, в зависимости от того, что удалось выяснить на предыдущем.
Ключевые изменения
Пользовательский запрос теперь попадает напрямую в ReAct-агента. Главный выигрыш здесь не только в упрощении архитектуры, но и в том, что она перестала быть хрупкой. Нам больше не нужно прописывать в графе отдельные ноды для рефлексии, переформулировки и подготовки контекста — модель сама принимает эти решения в цикле Thought → Action → Observation, итеративно уточняя результат.
Поиск по RAG-Agent + реранжирование были обёрнуты в единый инструмент rag_search и переданы агенту. Теперь агент самостоятельно решает: нужно ли вызывать поиск, сколько раз и с какими формулировками — достаточно ли полученной информации для ответа или стоит уточнить запрос и попробовать снова.
В результате мы получили систему, поведение которой определяется не топологией графа, а качеством рассуждения модели. Добавление нового источника данных теперь означает одно действие: зарегистрировать новый tool. Граф не трогаем. Логику переходов не переписываем. Агент сам разберётся, когда и зачем к нему обращаться.
Оценка качества агентных архитектур: метрики RAGAS
Чтобы честно сравнить две итерации архитектуры, нам нужна была не субъективная оценка «стало лучше», а воспроизводимый, численный результат. Для этого мы построили собственный бенчмаркинговый фреймворк поверх библиотеки RAGAS. Объясню, почему мы не стали ждать разметки от пользователей и сразу перешли к синтетической генерации.
Для экспертной разметки нам пришлось бы привлечь десятки сотрудников компании: они составляли бы запросы, отбирали релевантные чанки и писали эталонные ответы. Скажу прямо — это стоит очень дорого, а главное, может ничем хорошим не закончиться. Вы можете возразить: а почему не нанять сторонних разметчиков и не прийти к идеальному ТЗ? Объясняю, почему это не сработает: разметчик вопросов, чанков и ответов обязан обладать доменными знаниями. Насколько ожидания сотрудника компании и будущего пользователю Честного помощника от ответа системы будут соотвествовать ответу от человека, абсолютно не погружённого в бизнес-контекст? Отвечу сам — не очень.
Именно поэтому мы пошли иным путём. А что если развернуть локально SOTA-модель из open source — ту, что не пускаем в прод из-за размера и потребления памяти, — и использовать её исключительно для генерации эталонных примеров? Так и поступили и получили подход для тестирования наших подходов.
Мы стали использовать — Knowledge Graph + RAGAS Testset Generator.
Шаг 1: Граф знаний
Из документов базы знаний (Confluence) мы строим граф (KnowledgeGraph): каждый документ разбивается на чанки, из которых LLM извлекает ключевые фразы (keyphrases) и заголовки (headlines). Чанки связываются рёбрами на основе двух критериев: – keyphrases_overlap — пересечение ключевых фраз (порог зависит от размера корпуса); – summary_similarity — косинусная близость эмбеддингов.
Параметры чанкинга и пороги связей подбираются автоматически через профили датасета (small / medium / large / xlarge) в зависимости от числа документов.
Шаг 2: Синтез вопросов
По графу RAGAS генерирует тестовые вопросы через четыре типа синтезаторов:
|
Тип |
Описание |
Доля |
|
SingleHop / keyphrases |
Вопрос по одному чанку, опирается на ключевые фразы |
30% |
|
SingleHop / headlines |
Вопрос по структуре документа, ссылается на заголовки |
20% |
|
MultiHop / specific |
Вопрос требует объединения информации из нескольких чанков |
20% |
|
MultiHop / abstract |
Обобщающий концептуальный вопрос по нескольким темам |
30% |
Многоходовые вопросы (multi-hop) принципиально важны: они проверяют, умеет ли агент делать несколько поисковых итераций, а не ограничиваться одним вызовом rag_search.
Каждый вопрос генерируется от лица одной из шести персон: новый сотрудник, аналитик данных, DevOps-инженер, разработчик, тестировщик, проджект-менеджер. Это позволяет охватить разные стили формулировок — от конкретно-технических до управленческих. Итоговый testset содержит вопросы с эталонными ответами и списком релевантных документов для каждого.
Прогон агента
AnswerGenerationRunner последовательно прогоняет агента по всем вопросам датасета и для каждого сохраняет: – user_input — вопрос, – reference — эталонный ответ, – response — ответ агента, – retrieved_contexts — текстовое содержимое найденных документов, – retrieved_ids_raw / retrieved_ids_reranked — идентификаторы документов до и после реранжирования.
Два уровня оценки
Мы разделили оценку на два независимых измерения: ретривал и генерацию.
Оценка ретривала
RetrievalEvaluationRunner сравнивает идентификаторы найденных документов с эталонными (relevant_ids) и считает классические IR-метрики при разных значениях top-k:
|
Метрика |
Что измеряет |
|
Precision@k |
Доля релевантных среди первых k результатов |
|
Recall@k |
Доля найденных из всех релевантных |
|
F1@k |
Гармоническое среднее precision и recall |
|
MRR@k |
Средний обратный ранг первого релевантного результата |
|
Hit Rate@k |
Доля вопросов, для которых хотя бы один релевантный документ попал в top-k |
Метрики считаются при k ∈ {5, 15, 30, 40} — это позволяет видеть, как качество поиска меняется в зависимости от глубины выдачи.
Оценка генерации
GenerationEvaluationRunner запускает RAGAS evaluate() с тремя LLM-метриками:
|
Метрика |
Что измеряет |
|
Faithfulness |
Опирается ли ответ только на найденные контексты, без «галлюцинаций» |
|
Answer Relevancy |
Насколько ответ соответствует смыслу вопроса |
|
Answer Correctness |
Насколько ответ совпадает с эталонным по содержанию |
Для оценки используется SOTA open-source LLM.
Метрики
Ниже приведены результаты прогона шести конфигураций на нашем синтетическом датасете. Первые две строки — Итерация 1 (детерминированный RAG-конвейер), остальные — Итерация 2 (ReAct-агент) в различных вариантах моделей. Отмечу, что протестировано было намного больше моделей, также мы пробовали инетгрировать подход SGR – shema guided reasoning, однако он показал наихудшие результаты. В итоговой таблице представлю лишь часть с наилучшими метриками, чтобы не пугать чатателей:).
|
Конфигурация |
Faithfulness |
Answer Relevancy |
Answer Correctness |
Overall |
|
rag 35B gen |
0.838 |
0.555 |
0.522 |
0.638 |
|
rag_react 35B gen |
0.789 |
0.790 |
0.529 |
0.703 |
|
rag_react Qwen3-30B |
0.718 |
0.842 |
0.491 |
0.684 |
|
rag_react gpt-oss-20b + Qwen3-30B |
0.608 |
0.853 |
0.532 |
0.664 |
В Детерминированный конвейер (Итерация 1) показывает высокий Faithfulness — агент строго придерживается найденных контекстов и почти не галлюцинирует. Но Answer Relevancy проваливается до 0.555–0.597: ответы хотя и «честные», но часто мимо сути вопроса. Модель находит документы, но не умеет выбрать, что из них важно для конкретного запроса.
ReAct-агент (Итерация 2) меняет расстановку сил. Answer Relevancy прыгает до 0.790–0.853 — агент итеративно уточняет поиск и отвечает именно на то, о чём спросили. Overall у лучшей конфигурации достигает 0.703 против 0.638 в первой итерации. Прирост ~7 п.п. при том, что мы не меняли ни базу знаний, ни модели генерации — только архитектуру.
Выводы и перспективы
Что мы поняли
Эта статья — честный рассказ о том, как команда проходит путь от «давайте сделаем RAG» до production-системы, которой доверяют реальные пользователи. Мы не пропускали этапы и не перепрыгивали через итерации — и теперь я могу сформулировать несколько выводов, которые кажутся мне универсальными.
RAG — это не коробочное решение. Задача значительно сложнее, чем кажется на старте: нельзя просто взять готовый фреймворк для сборки RAG, развернуть и получить качество. Каждый домен уникален и требует индивидуального подхода. Если вам говорят обратное — они глубоко ошибаются.
GPU-ресурсы — не опция, а необходимость. Нам повезло: у нас есть доступ к собственным GPU, и именно это позволило экспериментировать с open-source моделями без компромиссов. Без нормальных вычислительных мощностей добиться адекватного качества на open-source практически невозможно — и это честно стоит признать до начала проекта.
Следить за трендами недостаточно — нужно разбираться на практике. Мало знать, что существуют ReAct, Knowledge Graph или RAGAS. Важно понимать, как они работают изнутри и когда их применять. Технологии меняются быстро, и отрыв между «слышал» и «применил» стоит очень дорого.
Детерминированный граф — хорошая отправная точка, но плохой финал. Первая итерация научила нас декомпозировать задачу, разделять ответственность агентов и думать об архитектуре системно. Но жёсткая топология графа стала потолком: любое новое требование превращалось в хирургическую операцию над кодом. Если вы начинаете с LangGraph — начинайте с детерминированного пайплайна, но сразу закладывайте путь к его замене.
ReAct-агент решает проблему хрупкости, но требует доверия к модели. Переход от нод к инструментам освободил систему от заранее прописанной логики. Агент сам решает, что и когда вызвать. Но это работает только тогда, когда вы доверяете open-source модели — не слепо, а с измерениями. Без нормальной оценки ReAct превращается в «чёрный ящик», который иногда делает правильные вещи.
Качество нельзя измерить без качественного тестового датасета. Синтетическая генерация через Knowledge Graph оказалась единственным реалистичным способом получить репрезентативный набор вопросов в условиях дефицита разметчиков с доменными знаниями. +7 п.п. к Overall — это цифра, которая не появилась бы без воспроизводимого бенчмарка.
Что осталось за рамками статьи
«Честный помощник» сегодня — это значительно больше, чем RAG по Confluence. В production уже работают:
-
Транскрибатор встреч — автоматическая расшифровка и саммаризация совещаний;
-
Работа со ссылками — агент умеет разбирать контент по URL и встраивать его в контекст;
-
Jira и GitLab — поиск по задачам, комментариям и коду прямо из чата;
-
Саммаризация — краткое изложение длинных документов и переписок.
Каждый из этих инструментов — просто ещё один tool в арсенале ReAct-агента. Это и есть главное преимущество архитектуры второй итерации: она растёт без перестройки.
Что дальше
Переиспользование пользовательской разметки. У нас накоплена история реальных запросов с актуальными списками релевантных статей. Хотим превратить её в эталонный датасет и добавить к синтетическому — это даст более честную картину качества на живых данных, а не только на сгенерированных вопросах.
Доверяй, но проверяй в автоматическом режиме. Планируем встроить бенчмарк в CI/CD: каждое изменение агента или промпта автоматически прогоняется через RAGAS, результаты пишутся в MLflow. Регрессия по метрикам — блокирует деплой.
Автор: chestny_znak


