Привет, Хабр! На связи команда Optimal Cognitive Core (OCC) из AIRI. Развитие языковых моделей в последние годы определяется масштабом: каждое новое поколение вмещает в веса всё больше знаний о мире. Но огромная доля практических задач выигрывает тогда, когда модель демонстрирует не свою энциклопедичность, а способность рассуждать и анализировать предоставленный контекст. Из этого наблюдения и выросло OCC — наше семейство компактных языковых моделей (SLM), которые имеют сильные когнитивные способности, не обладая при этом большим багажом «вызубренной» информации.

В этой статье расскажем о первой модели нашего семейства, OCC‑RAG, которая оптимизирована под задачу контекстного Q&A. Мы выложили два чекпойнта, OCC‑RAG-0.6B и OCC‑RAG-1.7B (плюс ONNX‑ и GGUF‑сборки). При размере 0.6 и 1.7 млрд параметров, соответственно, они отвечают на равных или лучше моделей общего назначения, которые в 2–6 раз больше, а по верности контексту показывают лучший результат среди моделей до 32B (см. рис. 1). Далее — о том, как устроена модель, как мы её обучили и что в итоге получилось.

Рис. 1. Трейд‑офф «качество / размер»: OCC‑RAG-0.6B и 1.7B против Qwen3, Gemma3, SmolLM3.
Рис. 1. Трейд‑офф «качество / размер»: OCC‑RAG-0.6B и 1.7B против Qwen3, Gemma3, SmolLM3.

Контекстный QA, и зачем в нём нужна честность

Контекстный QA (Context QA) — это постановка, в которой модель отвечает на вопрос на основе переданного ей контекста. Контекстом может быть один или несколько документов, инструкция, выписка из базы знаний или финансовый отчёт; словом, всё, что подаётся модели вместе с вопросом.

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

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

Как пример, рассмотрим немного игрушечный сценарий. Представьте, что есть некий источник, где написано утверждение, противоречащее общеизвестным фактам: «в 2022 году Шарль де Голль был избран первым президентом США». Мы подаём модели в контекст этот источник и задаём вопрос «Кто первый президент США?». Здесь возможны три типа поведения. Модель может ответить «де Голль» — для нас это корректное поведение (faithful), она честно следует контексту. Может ответить «Джордж Вашингтон» — формально это правда (truthful), но контекст проигнорирован. А может выдать что‑то третье, чего в источниках нет, — это галлюцинация.

В нашем эксперименте (см. рис. 2) крупная Llama-3.3–70B‑Instruct отвечает по контексту («де Голль»), средняя Llama-3-8B‑Instruct сваливается в память («Джордж Вашингтон»), а маленькая Llama-3.2–1B‑Instruct галлюцинирует («Дональд Трамп»). При этом OCC‑RAG-1.7B, несмотря на свой размер, ведёт себя как большая faithful‑модель и отвечает строго по источнику.

Рис. 2. Faithful / Truthful / Hallucination на примере конфликта «контекст ↔ память».
Рис. 2. Faithful / Truthful / Hallucination на примере конфликта «контекст ↔ память».

Из этого складываются три навыка, которыми обязана обладать сильная Context QA‑модель:

  1. Сильные multi‑hop и commonsense рассуждения: объединение фактов из разных фрагментов контекста и выстраивание логических связей.

  2. Следование контексту: опора только на предоставленные источники, а не на память.

  3. Калиброванный отказ: честно сказать «информации недостаточно», когда контекст не содержит ответа.

Как получить такую модель?

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

  1. Данные: огромный синтетический корпус, который прицельно тренирует multi‑hop рассуждение, верность контексту и отказ.

  2. Формат рассуждения: структура ответа модели, которая делает все три навыка обучаемыми на уровне токенов, а не оставляет их «в уме» у модели.

  3. Mid‑training: непосредственно обучение модели, которое прививает эти навыки исходной модели.

Данные: синтетический корпус

Это, пожалуй, главный вклад работы. Открытые QA‑датасеты, как правило, маленькие и не таргетируют faithfulness напрямую: модели учатся отвечать, но не обязательно следовать контексту. Поэтому мы построили собственный пайплайн генерации данных.

Простые вопросы

Сначала мы генерируем single‑hop вопросы, где ответ опирается на один факт из одного источника. Это самый дешевый и массовый слой данных, их генерируем прямо из абзацев Википедии, добавляя дистракторы (источники, похожие на тот, в котором содержится нужная информация, но не содержащие её) по графу ссылок и отсеивая неточные пары LLM‑судьёй. Отдельно сразу генерируем примеры, где модель должна отказаться давать ответ: берем исходный пример, урезаем контекст, удаляя корректный источник, и проверяем сильной экстрактивной моделью (DeBERTa, дообученной на SQuAD), — если ответ из урезанного контекста уже не достаётся, значит, критичный факт пропал, и пример помечается как требующий отказа.

Сложные вопросы: генерация по графу знаний

Дальше самое сложное: вопросы, где ответ нужно собрать из нескольких фактов. Чтобы они были «настоящими», мы извлекаем из текстов граф знаний методом Wikontic (про него на Хабре уже выходил отдельный пост от наших коллег). Граф знаний — это набор фактов в виде троек или триплетов: «сущность — отношение — сущность». Например, факт «Морти Смита озвучил Кэйсукэ Тиба» записывается как («Морти Смит» | озвучен | «Кэйсукэ Тиба»). Сущности здесь — узлы графа, а отношения — рёбра между ними; «мульти‑хоп» как раз и означает путь по нескольким таким рёбрам (см. рис. 3).

Рис. 3. Какие триплеты берем и как они соединяются.
Рис. 3. Какие триплеты берем и как они соединяются.

Вопросы мы генерируем по путям в этом графе, структуру каждого вопроса задаёт шаблон обхода по графу: какие триплеты берём и как они соединяются. Для генерации разнообразных по смыслу вопросов мы опираемся на таксономию из работы DRAGOn:

  • Simple: один триплет по отношению и одной сущности спрашиваем вторую. Фактически, это те же самые single‑hop вопросы, о которых шла речь ранее.
    Пример: «Кто озвучил Морти Смита?»

  • Set: несколько триплетов с общими сущностью и отношением; ответ — всё множество объектов, и его приходится собирать из разных источников.
    Пример: «Для каких проектов Райан Оттер написал музыку?»

  • Multi‑Hop: триплеты сцепляются через общую «мостиковую» сущность: назвать её напрямую нельзя, она задаётся описанием из другого факта.
    Пример: «В какой стране находится компания, продавшая 2139 автомобилей в 2023 году?»

  • Conditional: та же цепочка, но «мостиковая» сущность доопределяется дополнительным ограничением — по значению, дате и тому подобное.
    Пример: «В каком городе родился режиссёр фильма, получившего „Оскар“ в год рождения Леонардо Ди Каприо?»

  • Bamboo: три триплета, нанизанных в линейную цепочку через две «мостиковые» сущности.
    Пример: «В каком городе родился режиссёр дебютного фильма актёра, сыгравшего главную роль в „Начале“?»

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

В процессе генерации мы столкнулись с поучительной проблемой. В первых версиях все мульти‑хоп вопросы были single‑context: шагов рассуждения несколько, но все нужные факты лежали в одном источнике, а прочие источники служили лишь дистракторами. На таких данных модель училась хорошо — но почти не генерализовалась на multi‑context, когда факты для цепочки разнесены по разным источникам. А именно этот случай чаще всего и встречается в реальных RAG‑сценариях. В ретроспективе всё очевидно, но на практике это оказалось заметным барьером. Решение — разбивать контекст специальным образом, чтобы соседние шаги цепочки попадали в разные источники, и генерировать отдельный тип multi‑context мульти‑хопов. Добавление их в обучение дало ощутимый скачок качества.

Важное свойство нашего пайплайна: он позволяет генерировать данные из любых неструктурированных документов в практически неограниченном количестве (ограниченном только размером этих источников и вашим бюджетом).

Итоговый корпус для обучения — около 3.25 млн QA‑пар (≈ 8 млрд токенов): ~2.78M single‑hop, 262k multi‑hop в одном контексте, 165k multi‑hop с несколькими контекстами и 43k примеров на отказ (см. рис. 4).

Рис. 4. Бюджет токенов по подмножествам корпуса и их состав (gold / distractor / reasoning).
Рис. 4. Бюджет токенов по подмножествам корпуса и их состав (gold / distractor / reasoning).

Формат рассуждений

Чтобы три целевых навыка были обучаемы на уровне токенов, мы обогащаем каждый пример явными рассуждениями модели в строгом формате. Ответ модели всегда состоит из пяти секций со своими токенами‑разделителями (см. рис. 5):

  • Query Analysis — что именно спрашивает пользователь, какие сущности и отношения участвуют.

  • Source Analysis — какие предоставленные источники релевантны и что каждый даёт; цитирование источников происходит при помощи введения специальных токенов <|source_id|>N.

  • Reasoning — как факты из источников складываются в multi‑hop цепочку до ответа.

  • Status — дискретный вердикт: можно или нельзя ответить на вопрос.

  • Answer — финальный ответ либо отказ.

Секция Status появилась осознанно: мы хотели, чтобы решение «отвечать или отказаться» было явной дискретной меткой, к которой модель приходит до самого ответа, а не извлекалось задним числом из формулировки. А цитаты в ответе — это буквальные фрагменты контекста с привязкой к номеру источника. В сумме получается прозрачность, при этом токенов наша модель тратит примерно в полтора раза меньше, чем при полноценных рассуждениях обычной модели.

Рис. 5. Структура вывода OCC‑RAG: Query Analysis → Source Analysis → Reasoning → Status → Answer.
Рис. 5. Структура вывода OCC‑RAG: Query Analysis → Source Analysis → Reasoning → Status → Answer.

Сами цепочки ответов для обучения мы дистиллируем из более крупной модели и затем фильтруем в несколько ступеней: проверяем формат, сверяем ответ с правильным, перепроверяем LLM‑судьёй и режем слишком переусложнённые рассуждения.

Mid‑training

Мы не обучаем модель с нуля, а стартуем с готовой базовой модели. Из открытых семейств сравнили Qwen3, Gemma3 и SmolLM3 на отложенном QA‑срезе; Qwen3 дал лучший результат при фиксированном бюджете вычислений, поэтому оба наших чекпойнта — это mid‑training поверх Qwen3-0.6B‑Base и Qwen3-1.7B‑Base.

Обучаем через SFT. Промпт — это вопрос плюс контексты в случайном порядке, каждый с числовым id. Для разделения блоков рассуждений вводим дополнительно специальные обучаемые токены.

Оценка

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

  • Multi‑hop reasoning: HotpotQA и MuSiQue (Википедия) и TAT‑QA (табличные финансовые данные). Метрики: In‑Acc (золотой ответ как подстрока предсказания) и F1 для TAT‑QA.

  • Faithfulness: сложный датасет ConFiQA с контрфактическим контекстом. Помимо In‑Acc, считаем Memorization Ratio (MR): как часто модель сваливается в память вопреки контексту. Чем ниже — тем лучше.

  • Refusal: MuSiQue‑Un, версия MuSiQue, где в контексте нет ответа на вопрос. Метрика R‑Acc: доля корректных отказов.

Сравнивались с открытыми семействами Qwen3, Gemma3, SmolLM3 и со специализированной Pleias‑RAG, которая решает ту же задачу, что и мы. Брали все доступные чекпоинты вплоть до 32B (см. таблицу 1). Qwen3 и SmolLM3 также поддерживают thinking‑режим. Результаты с ним отображены в таблице в скобках. Что получилось:

  • Обе OCC‑RAG обходят свои Qwen3-базы по всем датасетам и превосходят Gemma3-1B/4B и SmolLM3-3B на каждом бенчмарке.

  • OCC‑RAG-0.6B обходит Qwen3-1.7B (в 2.8× больше) на 9.5 пункта на ConFiQA и опережает Pleias‑RAG-1.2B на 21.6 пункта на MuSiQue.

    Рис. 6. Сравнение OCC‑RAG-0.6B vs 1–2B и OCC‑RAG-1.7B vs 3–4B
    Рис. 6. Сравнение OCC‑RAG-0.6B vs 1–2B и OCC‑RAG-1.7B vs 3–4B
  • Лучшая faithfulness на всех масштабах до 32B: на ConFiQA обе модели обходят даже Qwen3-32B, а memorization ratio падает с 12.7 (8.3 в thinking) у Qwen3-1.7B до 5.0 у OCC‑RAG-1.7B.

  • По отказам OCC‑RAG-1.7B даёт R‑Acc 87.2 — на уровне моделей 8B+.

    Рис. 5. Сравнение OCC‑RAG-0.6B vs 1–2B и OCC‑RAG-1.7B vs 3–4B
    Таблица 1. Основные результаты: все модели до 32B по трём измерениям (In‑Acc, MR↓, R‑Acc).

Главный вывод из всех цифр простой: хорошего faithfulness’а можно достигнуть и с малыми моделями. Модели 8B+ ещё держат отрыв на чистом multi‑hop reasoning, но он заметно у́же, чем их же отрыв над instruct‑аналогами того же размера, а по верности контексту OCC‑RAG лидирует на всех масштабах.

Как попробовать

Модели лежат на Hugging Face: OCC‑RAG-0.6B и OCC‑RAG-1.7B. Chat‑шаблон принимает аргумент documents= и сам расставляет структурные токены — достаточно передать вопрос обычным текстом, а источники списком словарей. Пример использования:

import re
from transformers import AutoModelForCausalLM, AutoTokenizer
 
MODEL = "occ-ai/OCC-RAG-1.7B"
tokenizer = AutoTokenizer.from_pretrained(MODEL)
model = AutoModelForCausalLM.from_pretrained(MODEL, torch_dtype="auto", device_map="auto")
 
question = "Which country is Alexander Graham Bell buried in?"
documents = [
    {"text": "Alexander Graham Bell was a Scottish-born inventor of the telephone."},
    {"text": "Bell died in 1922 at Beinn Bhreagh, near Baddeck, Nova Scotia, and was buried there."},
    {"text": "Nova Scotia is a province on the east coast of Canada."},
]
 
text = tokenizer.apply_chat_template(
    [{"role": "user", "content": question}],
    documents=documents, tokenize=False,
    add_generation_prompt=True, enable_thinking=False,
)
 
inputs = tokenizer([text], return_tensors="pt").to(model.device)
outputs = model.generate(**inputs, max_new_tokens=2048)
response = tokenizer.decode(outputs[0][inputs.input_ids.shape[1]:], skip_special_tokens=False)
 
m = re.findall(r"<\|answer_start\|>(.*?)(?:<\|answer_end\|>|\Z)", response, re.DOTALL)
print("Answer:", m[-1].strip() if m else "")   # -> Canada

По умолчанию рекомендуем greedy decoding (do_sample=False). Модели легко заводятся на vLLM и SGLang, тот же documents= доступен из OpenAI-совместимого клиента через chat_template_kwargs. Мы также предоставляем ONNX- и GGUF-сборки; благодаря малому числу параметров, обе модели могут спокойно крутится на скромной инфраструктуре, будь то локальный компьютер или телефон.

Что дальше?

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

OCC‑RAG — первая инстанциация идеи «оптимального когнитивного ядра». Сейчас модель отвечает по тому контексту, который ей дали. Следующий шаг — научить систему самостоятельно пользоваться внешними инструментами и находить недостающий контекст, а не полагаться на то, что всё нужное уже лежит во входе. Мы уже работаем над этим — ждите обновлений!

Ссылки

P. S. В нашу команду OCC идет активный набор! Мы ищем ML‑инженеров и исследователей, которые хотят вместе с нами строить компактные языковые модели следующего поколения и доводить свои идеи до реальных рабочих продуктов. Открытую позицию и контакты можете найти здесь: вакансия.

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


  1. Front-Den
    19.06.2026 06:11

    Спасибо, Интересно!

    А вы думали тоже самое делать с моделями по крупнее? Qwen 8-14b или MoE 35b?


    1. avgalichin Автор
      19.06.2026 06:11

      Добрый день!

      Спасибо за вопрос.

      Наш подход инвариантен к размеру модели, дообучить можно любую. Помимо 0.6B и 1.7B, тестировали и на 4B, ощутимый прирост качества сохраняется. Поскольку мы изначально ориентировались на модели до 4B, большие аналоги не рассматривали. Но в будущем, если будет потребность, расширим линейку.


  1. FireAndIce
    19.06.2026 06:11

    Уже ведь довольно давно есть google Notebook LLM. Ваша чем-то лучше той?


    1. rimidalvv
      19.06.2026 06:11

      А гугловая работает без интернета на своём оборудовании?


    1. TomskDiver
      19.06.2026 06:11

      Вы конфиденциальные данные кидаете в goole notebook llm? В закрытом контуре, когда нет Интренета вы как хостите у себя goole notebook llm? Ну и вы сравниваете сервис и языковую модель. Это как сравнить Gmail и SMTP.


  1. DzenO
    19.06.2026 06:11

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


  1. kokanov
    19.06.2026 06:11

    Пробовали ли вы использовать для обучения модели концепцию "тренажерных заллов" (gum-based reinforcement learning + RLVR)? Кажется она может матчиться с вашей задачей обучения модели в первую очередь мышлению, а не запоминанию фактов.


  1. maxcat
    19.06.2026 06:11

    Очень круто. Но можно ли все таки уместиться в ещё меньшее количество, типо 0.1-0.3B?

    Поддержка русского есть или планируется?


    1. foss22
      19.06.2026 06:11

      https://huggingface.co/onnx-community/Pleias-RAG-350M-ONNX Fully supported languages include English, French, Spanish, German, Italian, Dutch, Latin and Portuguese.
      Язык - Количество Токенов: Английский 808 Б Французский 266 Б Немецкий 112 Б Испанский 46 Б Латинский 34 Б Голландский 29 Б Итальянская 24 Б Польский 11 Б Греческий 11 Б Португальский 9 Б


    1. avgalichin Автор
      19.06.2026 06:11

      За основу брали базовые модели из семейства Qwen3, в котором 0.6B и 1.7B как раз самые маленькие по размеру модели. Поэтому меньше пока не тестировали.

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


  1. KonstantinTokar
    19.06.2026 06:11

    Про де Голля. А что ответила каждая модель без контекста?


  1. CombaSoft
    19.06.2026 06:11

    Буду ждать обновлений. Пробую делать RAG. В его основе - Qwen3.5_9B_Q6_K, MCP сервер (parent-child+reranking) и промпт, который указывает модели делать дополнительные поисковые запросы если "информации оказалось недостаточно". В модели нравится то, что она достаточно умная, чтобы реализовать поисковый автомат для асинхронных операций (сделали запрос на поиск --> смотрим статус поиска в цикле --> забираем результаты поиска, при необходимости еще раз ходим с запросом в базу знаний, и, наконец, генерим ответ), она в принципе умеет пользоваться MCP серверами и у неё здоровое контекстное окно.

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

    Сейчас для OCC-RAG-1.7B нужны некоторые дополнительные приседания:

    • перевести запрос юзера в поисковый запрос к RAG (ну или передать его as is)

    • вызывать MCP-RAG сервер или еще как-либо отправлять запрос к базе знаний и обрабатывать ход поиска запроса

    • закинуть результат поиска в модель

    • проанализировать ответ модели и, при необходимости, перефразировать запрос и выполнить итерацию заново.

    В таком режиме нужна какая-то умная модель, которая будет решать достаточно ли для ответа набранной информации, не нужно ли делать уточняющие запросы. Т.е. заниматься чем-то вроде оркестрации процесса поиска.

    Как вариант, можно пытаться использовать модель OCC-RAG-1.7B для чего-то типа "однопроходового" контекстного поиска - закидываем в векторную БД запрос от юзера "как есть" и отдаём модели выборку из БД. Можно докрутить сюда парент-чайлд+ переранжирование.


    1. avgalichin Автор
      19.06.2026 06:11

      Добрый день!

      Да, текущую версию OCC-RAG можно рассматривать скорее как одну из компонент RAG системы, которая отвечает за поиск ответа в контексте, который уже был извлечен (например, как вы верно говорите, векторным поиском по базе данных на основе запроса юзера).

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


  1. foss22
    19.06.2026 06:11

    Qwen3.5-27B был выбран среди других генераторов с открытым исходным кодом по результатам обучения на тестовом наборе данных, охватывающем все типы вопросов...

    Как выбор происходил? Среди каких моделек выбирали?

    Дистилляция китайцев по сути этот корпус 3,25 млн пар «вопрос-ответ», поскольку судья тоже Qwen 4-миллиардный. Наследует предвзятость выбора данных корпорацией Алибаба. Не прозрачно, ведь они не поделились своими датасетами.

    Как Вы думаете, отсекает какие возможности такой однобокий выбор модели генератора? Пробовали ли оценить метриками отсечённые возможности (bias)?


    1. avgalichin Автор
      19.06.2026 06:11

      Сравнивали с другими семействами, сопоставимыми по размеру (gemma-3-27b, qwen3-32b, llama-3.1-70b). Qwen3.5 показал себя лучше как по качественному, так и по количественному анализу ответов.

      Явно bias не сравнивали, но на тех же данных мы пробовали учить и gemma-3-1b - прирост по качеству был сравнимым с результатами, представленными в посте для qwen3.


  1. Neko1313
    19.06.2026 06:11

    Я считаю, что результатов, которых вы добились, более чем достаточно. Такая модель хорошо вписывается в концепцию мультиагентных систем, где одна модель выступает в роли оркестратора и распределяет задачи между более узкоспециализированными моделями.


  1. vagon333
    19.06.2026 06:11

    Прошу прощения за дилетантский вопрос, какой максимальный размер контекстного окна?

    У меня простая задача разбивки документов на секции с требованиями.
    Разбить документ на куски нельзя, так как в документе могут быть перекрёстные ссылки, которые определяют тип секции.

    В конечном итоге на вход подается документ, а на выходе массив секций с индикатором "требование" или "вспомогательная информация".

    Желательно: обрабатывать локально.
    И обязательно: без девиаций при повторных прогонах.


    1. avgalichin Автор
      19.06.2026 06:11

      Обучали на входных последовательностях с максимальной длиной до 5.120 токенов, или порядка 20.000 символов. Но сам Qwen изначально поддерживает на порядок больше, поэтому должен быть перенос и на более длинный по длине входной контекст.


  1. Mavito
    19.06.2026 06:11

    Спасибо, очень интересное исследование. Несколько вопросов:

    • Для этой модели не нужен реранкер “documents=” т.к. при обучении “Обучаем через SFT. Промпт — это вопрос плюс контексты в случайном порядке” и качество ответа не должно сильно зависеть от положения релевантной информации в “documents=”?

    • Могут быть ли документами поисковые сниппеты, содержащие обрывочные html-теги, css, Markdown или тренировка осуществлялась только на очищенных текстах?

    • Сколько документов(сниппетов) и какого размера оптимально подавать в “documents=” (возможно при тренировке использовали некоторые базовые количество/размер) или имеет значение только проблема забывания контекста при его увеличении, т.е. для Qwen/Qwen3-1.7B-Base чем ближе к максимальной длине контекста Context Length: 32,768, тем хуже?

    • Способ подачи документов через “documents=” защищает от опасностей содержания в них prompt-injection?

    • Время ответов этой модели не постоянно? Т.е. режим рассуждений отключить нельзя (в примерах уже стоит enable_thinking=False,), и учитывая размер модели в <= 1.7B она может быть склонна к зацикливанию, но маленький max_length=512 разумных размеров тоже сделать не получится из-за недерминированного размера “блока рассуждений” в выводе, иначе может конечный ответ обрезаться.

    • Сохранила ли модель кроссязыковые свойства Qwen3, когда контекст на одном языке, а вопрос на другом и можно ли подавать в “documents=” смешанный набор документов из en и ru?


    1. avgalichin Автор
      19.06.2026 06:11

      Спасибо!

      1. Во время обучения мы подаем контексты в случайном порядке, чтобы избежать bias'а, при котором модель бы неявно выбирала источники по их порядковому номеру.

      2. Могут, но вероятно на таких документах качество будет чуть ниже, так как в выборке их было меньше.

      3. Обучали, подавая на вход случайное кол-во источников, от 1 до 10. Соответственно оптимально - любое количество в этом диапазоне. Но даже если подадите больше - модель должна адаптироваться.

      4. Явно на это мы не обучали.

      5. У нас специальный формат рассуждений, который всегда будет использоваться моделью. Длина рассуждений обычно находится в диапазоне 300-700 токенов, поэтому лучше ставить max_length=1024. Модели семейства Qwen склонны иногда зацикливаться, и это свойство, к сожалению, актуально и для OCC-RAG. Сейчас боремся с этой проблемой.

      6. OCC-RAG поддерживает входные запросы и документы на русском и английском языке. Единственное - в случае русского, рассуждения будут на английском, а финальный ответ - на русском.


      1. Mavito
        19.06.2026 06:11

        Спасибо за ответы. Если позволите, то мое предположение/гипотеза по борьбе с зацикливанием (помимо обязательного repeat_penalty>=1.15) с учетом специфики решаемой вами задачи. Т.к.

        1) Вы тренируете модель отвечать "into five sections";

        2) Модель предназначена не для вывода для людей, а для вывода для другого кода, который требует четкость, стабильность и надежность;

        3) На данный момент в различном ПО для инференса LLM уже хорошо/надежно поддерживается Structured Output в JSON;

        Возможно ли не использовать спецтокены, а обучать модель выводу используемых вами 5 секций в виде JSON, по строгой JSON схеме с полями эквивалентными используемыми вами "special tokens", а затем, при эксплуатации модели, всегда пользоваться Structured Output для гарантирования формата вывода с целью защиты от зацикливаний, галлюцинаций и, заодно, т.к. ответ в JSON поле, то re.findall(r"<|answer_start|>...)" будет не нужен)? Тогда использование модели будет иметь примерный вид:

        import json
        import requests
        
        from pydantic import BaseModel, Field, TypeAdapter
        
        class RAGResponseModel(BaseModel):
            # ... query_analysis, source_analysis - не понял тип поля, массив ли это и его min,max 
            # при помощи max_length ограничил "бюджет" полей reasoning и ответа, что также не даст зациклится в этих полях до предела max_model_len
            reasoning: str = Field(..., max_length=700)
            final_answer: str = Field(..., max_length=10)
        so_schema = TypeAdapter(RAGResponseModel).json_schema()
        print(so_schema) # Схема в JSON, которую можно использовать без pydantic - {'properties': {'reasoning': {'maxLength': 700, 'title': 'Reasoning', 'type': 'string'}, 'final_answer': {'maxLength': 10, 'title': 'Final Answer', 'type': 'string'}}, 'required': ['reasoning', 'final_answer'], 'title': 'RAGResponseModel', 'type': 'object'}
        
        import json
        import requests
        
        import json
        import requests
        prompt = '''Поможет ли Structured Output (SO) побороть зацикливания?'''
        
        url = 'http://127.0.0.1:8080/v1/chat/completions'
        headers = { 'Content-Type': 'application/json' }
        
        q = {
            "messages": [
                {
                    "role": "system",
                    "content": f"Your output should be JSON object with schema {json.dumps(so_schema)}"
                },
                {
                    "role": "user",
                    "content": prompt
                }
            ],
            'response_format': {
                'type': 'json_schema', 
                'json_schema': {
                    'schema': so_schema, 
                    'strict': True,
                    "name": "RAGResponseModel",
                }
            },
            "chat_template_kwargs": {"enable_thinking": False}, 
            "max_tokens":None,
            "stream":False,
            "repeat_penalty": 1.1,
            "temperature": 0, 
            "top_p": 0.90, 
            "top_k": 64,
            "min_p": 0.001,
        }
        
        response = requests.post(url, json=q, headers=headers)
        assert(response.status_code == 200)
        response_content = response.json()["choices"][0]["message"]['content']
        print(response_content) # или сразу поле final_answer

        и последующий код всегда будет получать или строгий ожидаемый валидный json (SO из ПО инференса должно это гарантировать) или сможет при ошибке парсинга .json() сделать повтор запроса с немного измененным содержимым (например, for retry in range(5): documents = shuffle(documents) ... break;

        Это просто моя гипотеза на ваше усмотрение, на практике я так не пробовал (и нет подтверждения её реальности на моем практическом опыте тренировки ИИ, однако, сам SO работает все надежнее и ПО инференса охотно принимают issue и чинят его если с ним что-то не так случилось при простом запросе) и точно не знаю возможно ли так сделать и (насколько) это сработает (но, показалось, что с учетом размеров моделей OCC это не очень ресурсозатратно проверить).

        Кроме отмеченной в публикации проблемы информационного дрейфа (несовпадения претрейна модели с актуальной информацией из контекста), кажется, что в RAG существует проблема AI alignment, когда ИИ напрочь отказывается или описывает некорректность контекста вместо ответа - интересно, существуют ли уже для этого бенчмарки/лидерборды и метрика в плане RAG и устраняет ли эту проблему OCC-файнтюн, ведь alignment это немного другое чем "считай данные из контекста актуальными" и, если более менее известно, какие данные из претрейна модели устарели в рамках домена, где применяется RAG (и модель можно дообучить на актуальных данных или задать в промпте, что актуально), то заранее совсем неизвестно на что авторы модели сделали alignment (имеется ввиду, что пользователь RAG совершенно не имеет цели узнать или получить от ИИ модели, что-то выходящее за границы дозволенного, а просто задает обычный условный вопрос "новости науки", "новости про остров" и в контекст попадают обычные публичные новости из разных СМИ, среди которых есть те, на которые срабатывает alignment авторов модели - про достижения в разделах науки; про альтернативные точки зрения по каким-то вопросам и т.п. - например, если qwen3 спросить "новости про остров", подав в контекст новости из СМИ этого острова).

        Еще раз спасибо за публикацию этого исследования, вопрос того, чтобы ИИ модель (особенно дешевая, маленькая и локальная) не отклонялась от контекста, похоже, имеет место быть не только в области в RAG и метод его решения может пригодиться, например, даже при проверке орфографии  https://habr.com/ru/companies/sberdevices/articles/806897/ где ИИ, например, может начать всегда исправлять в актуальных текстах из СМИ корректный год, имена должностных лиц и т.п. на те, что он знал при претрейне (т.е. текущий год или ФИО нового должностного считать за орфографическую ошибку и предлагать замену 2026->2024; или полностью всей текущей ФИО->ФИО прежнего должностного лица...).


  1. morginalium8
    19.06.2026 06:11

    А что на счет данных? Будете синтетику выкладывать?


    1. avgalichin Автор
      19.06.2026 06:11

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