Void APIБаза знанийКак сделать базу знаний с ИИ по своим документам: RAG простыми словами
Как сделать базу знаний с ИИ по своим документам: RAG простыми словами
Модель не знает ваших регламентов, документации продукта и базы ответов поддержки. Дообучать её ради этого обычно не нужно: достаточно в момент вопроса найти подходящие фрагменты документов и передать их модели вместе с вопросом. Этот подход называется RAG — retrieval-augmented generation. Разберём, из чего он состоит и где обычно ломается.
Как работает RAG
Подготовка: документы делятся на фрагменты, для каждого вычисляется эмбеддинг — числовой вектор, отражающий смысл текста. Векторы сохраняются в базу с поиском по близости.
Ответ: вопрос пользователя тоже превращается в вектор, база находит несколько самых близких фрагментов, и они вместе с вопросом отправляются в языковую модель. Модель формулирует ответ по найденному тексту и указывает источники.
Качество RAG почти всегда ограничено поиском, а не генерацией. Если нужный фрагмент не нашёлся, даже самая сильная модель ответит неправильно или скажет, что информации нет.
Когда RAG нужен, а когда нет
RAG подходит для больших и меняющихся наборов документов: внутренняя вики, документация продукта, договоры, ответы поддержки. Документы обновляются без переобучения модели, а ответ можно проверить по ссылке на источник.
Если документов немного — десятки страниц, — проще передать их целиком в контекст модели с большим окном и использовать кеширование промпта. Это надёжнее поиска и быстрее в разработке. RAG оправдан, когда документы не помещаются в контекст или передавать их целиком слишком дорого.
Подготовка документов и нарезка на фрагменты
Извлеките чистый текст: уберите меню, колонтитулы, повторяющиеся шапки. Мусор в документах превращается в мусор в поиске. Таблицы и списки сохраняйте в читаемом виде, например в Markdown.
Режьте по смысловым границам — разделам, заголовкам, абзацам, — а не по фиксированному числу символов посередине предложения. Типичный размер фрагмента — несколько сотен токенов с небольшим перекрытием. К каждому фрагменту сохраните метаданные: название документа, раздел, ссылку, дату обновления. Добавьте заголовок раздела в текст фрагмента: «Отпуск — Перенос отпуска: ...» находится лучше, чем оторванный абзац.
Эмбеддинги и векторная база
Для русского текста выбирайте многоязычную модель эмбеддингов. Её можно запустить локально, например через sentence-transformers, или использовать API, если нужная модель доступна у провайдера. Проверьте список моделей вашего ключа через GET /v1/models: модели эмбеддингов есть не в каждом каталоге.
Для старта не нужна отдельная векторная СУБД: PostgreSQL с расширением 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()Промпт: ответ только по найденным источникам
Передайте найденные фрагменты с номерами и попросите модель ссылаться на них. Явно разрешите ответ «в документах этого нет»: без такого разрешения модель дополнит ответ общими знаниями, и пользователь не отличит регламент компании от догадки.
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Как проверить качество базы знаний
Составьте 30–50 реальных вопросов с известными ответами и ссылками на документы. Проверяйте два уровня отдельно: нашёл ли поиск нужный фрагмент среди первых k результатов и правильно ли модель сформулировала ответ по нему. Так понятно, что чинить — нарезку и поиск или промпт.
Типичные проблемы: устаревшие версии документов в индексе, слишком мелкие фрагменты без контекста, вопросы с синонимами, которых нет в тексте, и противоречащие друг другу документы. Добавьте дату в метаданные и удаляйте старые версии при переиндексации.
Права доступа и безопасность
Если документы имеют разные уровни доступа, фильтруйте фрагменты по правам пользователя до передачи в модель. Инструкция «не показывай секретные документы» в промпте не является защитой.
Текст документов может содержать инструкции для модели — намеренно или случайно. Считайте найденные фрагменты данными, а не командами, и не давайте RAG-ассистенту инструментов, которые выполняют действия, без отдельной проверки.
Попробуйте GPT и Claude через Void API
Подключайте GPT и Claude к своим приложениям, ботам и ИИ-агентам через Void API — единый OpenAI-совместимый API. Доступ из России без VPN, пополнение по СБП и оплата по фактическому использованию. Выбирайте модель под задачу, сравнивайте цены и начинайте с одного API-ключа.
Вопросы и ответы
Чем RAG отличается от дообучения модели?
Дообучение меняет поведение и стиль модели, но плохо подходит для хранения фактов, которые часто обновляются. RAG передаёт актуальные документы в момент вопроса и позволяет указать источник ответа.
Нужна ли отдельная векторная база данных?
Для старта нет. PostgreSQL с pgvector или даже поиск в памяти подойдут для небольших и средних баз. Специализированные решения нужны при очень больших объёмах и нагрузке.
Какая модель лучше для ответов по документам?
Модель должна точно следовать инструкции отвечать только по источникам. Сравните несколько моделей на своём наборе вопросов и учитывайте стоимость: каждый запрос содержит найденные фрагменты.