Все статьи
Статья
10 августа 2026 г.8 мин

RAG на Command A: ответы с цитатами, которым можно верить

Корпоративному ассистенту мало «правильного» ответа — нужна ссылка на источник. Собираем RAG на Command A так, чтобы каждое утверждение было проверяемо.

Елена Рудакова


RAG на Command A: ответы с цитатами, которым можно верить

В корпоративном ассистенте главный вопрос не «ответила ли модель», а «можно ли этому ответу доверять и как проверить». Отсюда требование: каждое утверждение должно опираться на конкретный фрагмент документа, и этот фрагмент должно быть видно. Command A ориентирована ровно под это, и собрать такую систему на ней проще, чем кажется.

Из чего состоит RAG

  1. Индексация. Документы режем на куски (chunks) по 300–800 токенов, каждый превращаем в вектор эмбеддингом и складываем в векторное хранилище.
  2. Поиск. На вопрос пользователя считаем эмбеддинг запроса, достаём топ-N ближайших кусков.
  3. Генерация. Отдаём куски в Command A с жёсткой инструкцией отвечать только по ним и ссылаться на источник.

Первые два шага — про эмбеддинги (об их выборе у нас отдельный материал), третий — про модель.

Индексация фрагментов

Векторы для кусков считаем через /v1/embeddings:

from openai import OpenAI
client = OpenAI(base_url="https://api.yenisei.ru/v1", api_key="YENISEI_KEY")
def embed(texts):
r = client.embeddings.create(model="bge-m3", input=texts)
return [d.embedding for d in r.data]
vectors = embed([chunk.text for chunk in chunks])
# vectors кладём в векторную БД вместе с id и метаданными куска

Генерация ответа с опорой на источники

Ключ — в системном промпте и в том, как оформлены фрагменты. Даём каждому куску явный идентификатор, чтобы модель могла на него сослаться:

context = "\n\n".join(
f"[{c.doc}#{c.id}] {c.text}" for c in retrieved
)
resp = client.chat.completions.create(
model="command-a",
messages=[
{"role": "system", "content": (
"Отвечай строго по фрагментам ниже. "
"После каждого утверждения ставь ссылку в формате [doc#id]. "
"Если ответа в фрагментах нет — напиши: «В документах ответа нет»."
)},
{"role": "user", "content": f"Вопрос: {question}\n\nФрагменты:\n{context}"},
],
)
print(resp.choices[0].message.content)

Почему именно Command A

Дисциплина. Модель, которую не удерживают в узде, на пограничном вопросе дополнит ответ «здравым смыслом» из своих общих знаний — и в корпоративном контексте это баг, а не фича. Command A по своей природе лучше держится приложенных источников и честнее говорит «не знаю», когда ответа в контексте нет. С окном 256K в неё влезает много фрагментов сразу, так что на широких вопросах не приходится жертвовать полнотой.

Что чаще всего ломает RAG

  • Плохие чанки. Порезали документ по 2000 токенов — поиск тащит куски, где нужное разбавлено мусором. Порезали слишком мелко — теряется контекст. 300–800 токенов с небольшим перекрытием обычно золотая середина.
  • Один эмбеддинг на всё. Русскоязычную базу нужно индексировать многоязычной моделью (BGE-M3, Qwen3 Embedding), иначе поиск проседает.
  • Нет запасного «не знаю». Без явной инструкции модель постарается ответить всегда — и на вопросе не по теме выдаст правдоподобную выдумку.
  • Смешение ролей. Инструкция «только по источникам» должна быть в системном сообщении, а фрагменты — в пользовательском; если свалить всё в кучу, дисциплина падает.

Как проверять качество

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