Подписывайтесь:

Блог AST-SoftPro

RAG-системы: как дать LLM доступ к вашим данным

22.05.2026 10 мин чтения
RAG-системы: как дать LLM доступ к вашим данным

Введение в RAG-системы

RAG (Retrieval-Augmented Generation) — это подход к использованию больших языковых моделей (LLM), при котором модель генерирует ответ на основе не только своих внутренних знаний, но и внешних данных. Вместо того чтобы полагаться исключительно на обучающие данные модели (которые могут устареть или быть ограничены по объёму), RAG позволяет LLM обращаться к актуальным внутренним документам компании: инструкциям, технической документации, базам знаний.

Основные преимущества этого подхода:

  • Точность: ответы основаны на актуальных документах из вашей системы,

  • Контроль над данными: вы не передаёте свои конфиденциальные материалы в публичные модели (например, OpenAI),

  • Обновляемость: можно легко обновлять базы знаний без переобучения LLM.

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

Шаг 1: Подготовка документов для анализа

Перед тем как запускать поиск по документам, их необходимо предварительно обработать:

  • Очистка: удаление лишних символов, HTML-разметки, повторяющихся строк,

  • Нормализация: приведение к единому регистру, замена синонимов (если нужно),

  • Фильтрация: исключение несущественных файлов или разделов.

Пример на Python:

import re
from pathlib import Path

def clean_text(text: str) -> str:
    text = re.sub(r'\s+', ' ', text.strip())  # Удаление лишних пробелов и переносов
    return text.lower()

После очистки документы можно разбивать на логические части — чанки (chunks). Это важно, потому что LLM работает с ограниченным окном контекста (обычно до 32768 токенов).

Шаг 2: Чанкинг документов

Чанкинг — это процесс разбиения текста документа на фрагменты подходящего размера.

Методы чанкинга:

  • По символам: делить текст каждые N символов (например, 512),

  • По предложениям или абзацам,

  • На основе семантики — более продвинутый способ, где границы устанавливаются между смысловыми блоками.

Пример на Python:

from typing import List

def chunk_by_size(text: str, max_chunk_size: int) -> List[str]:
    chunks = []
    current_chunk = ''
    for sentence in text.split('. '):  # Упрощённый разбор по точкам с пробелом
        if len(current_chunk + '. ' + sentence) <= max_chunk_size:
            current_chunk += '. ' + sentence
        else:
            chunks.append(current_chunk.strip())
            current_chunk = sentence + '.'
    if current_chunk:
        chunks.append(current_chunk.strip())
    return chunks

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

Оптимальный размер чанка

  • Слишком маленькие чанки увеличивают количество запросов и снижают эффективность,

  • Слишком большие — могут выходить за пределы контекста LLM (например, 1536 токенов при лимите в 2048).

Рекомендуемый диапазон: 300–700 символов для большинства текстов.

Шаг 3: Генерация эмбеддингов

Эмбеддинг — это числовой вектор (обычно размерность 1536 или 768), представляющий смысл текста. Для каждого чанка генерируется такой вектор, чтобы можно было быстро находить похожие фрагменты при поиске.

Как выбрать модель для эмбеддингов?

  • Мультимодальность: нужна ли поддержка изображений и аудио? Если нет — достаточно текстовых моделей,

  • Язык документов: если документы на русском или других языках — использовать модели, обученные на них (например, intfloat/multilingual-e5-large),

  • Скорость vs точность:

  • Быстрые: all-MiniLM-L6-v2,
  • Точнее: BAAI/bge-large-en-v1.5.

Пример генерации эмбеддингов с помощью Hugging Face Transformers:

from sentence_transformers import SentenceTransformer
model = SentenceTransformer('intfloat/multilingual-e5-large')
embeddings = model.encode(["Данный текст — пример чанка документации..."])

Обратите внимание: не все модели поддерживают русский язык, даже если они «мультиязычные». Проверяйте документацию.

Шаг 4: Выбор векторной базы данных (Vector Database)

Векторная база хранит эмбеддинги и позволяет быстро находить самые близкие по смыслу фрагменты. Основные параметры при выборе:

  • Скорость поиска — чем меньше документов, тем важнее скорость,

  • Масштабируемость — нужна ли поддержка миллионов записей? Т.е. POC vs production-системы.

Популярные векторные БД:

База Поддержка Python Масштаб Особенности
Chroma ✅ Да (простая) До 10k+ Легко для старта, встроенная в LangChain
Weaviate ✅ API + SDK До млн+ Поддержка GraphQL, графы знаний
Pinecone ✅ REST/SDK Масштабируемо Облачная, высокая доступность
Milvus ⚠️ Сложнее (Go/C++) Очень масштабно Для enterprise-решений

Для прототипа или малого проекта подойдёт Chroma. Пример инициализации:

from chromadb import Client
chroma_client = Client('http://localhost:8000')
collection = chroma_client.create_collection(name='docs_embeddings')

Шаг 5: Загрузка данных в векторную БД

После получения эмбеддингов каждый чанк с метаданными (ID, имя файла, дата) сохраняется в коллекцию.

Пример структуры элемента:

on
{
  "id": "doc_001_chunk_3",
  "embedding": [0.25, -0.89, ...],
  "metadata": {
    "source_file": "manual_v4.pdf",
    "file_id": "abc123",
    "chunk_index": 3,
    "page_number": 7
  }
}

Загрузка в Chroma:

collection.add(
    embeddings=[embeddings],
    documents=['текст чанка...'],
    metadatas=metadata_list,
    ids=['doc_001_chunk_3']
)

Шаг 6: Поиск ближайших документов (Retrieval)

Когда пользователь задаёт вопрос, система:

  1. Генерирует эмбеддинг запроса LLM или через ту же модель,

  2. Находит топ-K самых близких чанков в векторной базе,

  3. Возвращает их модели для генерации ответа.

Пример поиска 5 ближайших документов:

results = collection.query(
    query_vector=query_embedding,   # эмбеддинг запроса
    n_results=5                      # количество результатов
)

Результаты содержат:

  • ids — уникальные идентификаторы чанков,

  • distances — расстояние (cosine similarity),

  • documents, metadatas — сами тексты и метаданные.

Шаг 7: Ранжирование результатов поиска

Даже после векторного поиска могут быть найдены не самые релевантные фрагменты. Поэтому важно добавить ранжирование:

  • Удаление дубликатов,

  • Оценка по длине совпадения (чем больше — тем лучше),

  • Проверка на ключевые слова из запроса.

Пример простого ранжирования в Python:

from collections import defaultdict
import re

def score_matches(documents: list, query_text: str) -> dict:
    scores = defaultdict(float)
    for doc in documents:
        text_lower = doc.lower()
        query_lower = query_text.lower()
        # Подсчёт частичного совпадения слов и фраз
        words_matched = len(set(re.findall(r'\b\w+\b', query_lower)) & set(re.findall(r'\b\w+\b', text_lower)))
        phrase_count = sum(1 for w in [query_lower] if w in doc.lower())  # Фразы
        scores[doc['id']] += words_matched * 0.6 + phrase_count * 0.4
    return dict(scores)

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

Заключение: основные принципы построения RAG-системы

  1. Подготовка данных — очищайте и структурируйте документы до чанкинга,

  2. Чанкинг — выбирайте метод в зависимости от типа контента (абзацы, предложения или символы),

  3. Эмбеддинги — используйте подходящие модели для языка ваших документов,

  4. Векторная БД — начинайте с Chroma, масштабируйтесь на Weaviate/Pinecone при росте объёма данных,

  5. Поиск и ранжирование — не полагайтесь только на векторное расстояние; добавляйте семантическое/ключевое ранжирование.

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

AI-Помощник