Перейти к основному содержанию
VOID

Void APIБаза знанийКак сделать базу знаний с ИИ по своим документам: RAG простыми словами

Как сделать базу знаний с ИИ по своим документам: RAG простыми словами

Модель не знает ваших регламентов, документации продукта и базы ответов поддержки. Дообучать её ради этого обычно не нужно: достаточно в момент вопроса найти подходящие фрагменты документов и передать их модели вместе с вопросом. Этот подход называется RAG — retrieval-augmented generation. Разберём, из чего он состоит и где обычно ломается.

Как работает RAG

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

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

Качество RAG почти всегда ограничено поиском, а не генерацией. Если нужный фрагмент не нашёлся, даже самая сильная модель ответит неправильно или скажет, что информации нет.

Когда RAG нужен, а когда нет

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

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

Стоимость длинного контекста и кеширование

Подготовка документов и нарезка на фрагменты

Извлеките чистый текст: уберите меню, колонтитулы, повторяющиеся шапки. Мусор в документах превращается в мусор в поиске. Таблицы и списки сохраняйте в читаемом виде, например в Markdown.

Режьте по смысловым границам — разделам, заголовкам, абзацам, — а не по фиксированному числу символов посередине предложения. Типичный размер фрагмента — несколько сотен токенов с небольшим перекрытием. К каждому фрагменту сохраните метаданные: название документа, раздел, ссылку, дату обновления. Добавьте заголовок раздела в текст фрагмента: «Отпуск — Перенос отпуска: ...» находится лучше, чем оторванный абзац.

Эмбеддинги и векторная база

Для русского текста выбирайте многоязычную модель эмбеддингов. Её можно запустить локально, например через sentence-transformers, или использовать API, если нужная модель доступна у провайдера. Проверьте список моделей вашего ключа через GET /v1/models: модели эмбеддингов есть не в каждом каталоге.

Для старта не нужна отдельная векторная СУБД: PostgreSQL с расширением pgvector справится с сотнями тысяч фрагментов. Важно использовать одну и ту же модель эмбеддингов для документов и вопросов — векторы разных моделей несовместимы.

Чисто векторный поиск плохо находит точные артикулы, коды ошибок и редкие термины. Гибридный поиск — векторный плюс полнотекстовый — заметно улучшает результат на технической документации.

Индексация и поиск, sentence-transformers + pgvector
from sentence_transformers import SentenceTransformer
import psycopg

model = SentenceTransformer("intfloat/multilingual-e5-base")
db = psycopg.connect("postgresql://localhost/kb")
# CREATE EXTENSION vector;
# CREATE TABLE chunks (id serial, source text, body text, embedding vector(768));

def index(source: str, chunks: list[str]):
    vectors = model.encode([f"passage: {c}" for c in chunks], normalize_embeddings=True)
    with db.cursor() as cur:
        for body, vec in zip(chunks, vectors):
            cur.execute("INSERT INTO chunks (source, body, embedding) VALUES (%s, %s, %s::vector)",
                        (source, body, str(vec.tolist())))
    db.commit()

def search(question: str, k: int = 5):
    vec = model.encode(f"query: {question}", normalize_embeddings=True)
    with db.cursor() as cur:
        cur.execute("SELECT source, body FROM chunks ORDER BY embedding <=> %s::vector LIMIT %s",
                    (str(vec.tolist()), k))
        return cur.fetchall()

Промпт: ответ только по найденным источникам

Передайте найденные фрагменты с номерами и попросите модель ссылаться на них. Явно разрешите ответ «в документах этого нет»: без такого разрешения модель дополнит ответ общими знаниями, и пользователь не отличит регламент компании от догадки.

Генерация ответа через Void API
from kb_search import search  # функция из примера выше
from openai import OpenAI
import os

client = OpenAI(
    api_key=os.environ["VOID_API_KEY"],
    base_url="https://void-api.tech/v1",
)

def answer(question: str) -> str:
    found = search(question)
    context = "\n\n".join(f"[{i + 1}] ({src})\n{body}" for i, (src, body) in enumerate(found))
    response = client.chat.completions.create(
        model="claude-sonnet-5.5",
        messages=[
            {"role": "system", "content": (
                "Отвечай только по фрагментам документов ниже. "
                "После каждого утверждения указывай номер источника в квадратных скобках. "
                "Если ответа во фрагментах нет, так и скажи и не используй общие знания."
            )},
            {"role": "user", "content": f"Фрагменты:\n{context}\n\nВопрос: {question}"},
        ],
    )
    return response.choices[0].message.content

Справочник API

Как проверить качество базы знаний

Составьте 30–50 реальных вопросов с известными ответами и ссылками на документы. Проверяйте два уровня отдельно: нашёл ли поиск нужный фрагмент среди первых k результатов и правильно ли модель сформулировала ответ по нему. Так понятно, что чинить — нарезку и поиск или промпт.

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

Права доступа и безопасность

Если документы имеют разные уровни доступа, фильтруйте фрагменты по правам пользователя до передачи в модель. Инструкция «не показывай секретные документы» в промпте не является защитой.

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

Попробуйте GPT и Claude через Void API

Подключайте GPT и Claude к своим приложениям, ботам и ИИ-агентам через Void API — единый OpenAI-совместимый API. Доступ из России без VPN, пополнение по СБП и оплата по фактическому использованию. Выбирайте модель под задачу, сравнивайте цены и начинайте с одного API-ключа.

Перейти на void-api.techПосмотреть модели и цены

Вопросы и ответы

Чем RAG отличается от дообучения модели?

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

Нужна ли отдельная векторная база данных?

Для старта нет. PostgreSQL с pgvector или даже поиск в памяти подойдут для небольших и средних баз. Специализированные решения нужны при очень больших объёмах и нагрузке.

Какая модель лучше для ответов по документам?

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