Как стать автором
Поиск
Написать публикацию
Обновить
79.67
Amvera
Amvera — облако для хостинга IT-приложений

Свой ChatGPT на документах: делаем RAG с нуля

Уровень сложностиСредний
Время на прочтение6 мин
Количество просмотров15K

Всем привет! Наверняка у вас были ситуации, когда нужно быстро найти что-то в длинном документе-договоре, инструкции или отчёте. Сегодня я покажу, как сделать своего помощника, который будет отвечать на такие вопросы автоматически. Соберем RAG с нуля: загрузим документы, "нарежем" их на куски, проиндексируем в Qdrant и подключим LLaMA через Amvera Inference.

Если вы увидели незнакомые слова — не пугайтесь, далее я расскажу об этом подробно.

Самое главное, что всё это работает самостоятельно, без зависимости от OpenAI.

RAG - что это?

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

Обычно LLM не знает, что происходит в ваших документах, не знает контекст вопроса, если этого нет в её базе знаний, которую провайдеры (OpenAI и подобные) внедряют при обучении. Она отвечает "из головы". Но с RAG:

  • Вы загружаете документы в векторную базу (например, в нашем случае, Qdrant).

  • При каждом запросе делается поиск по смыслу.

  • Модель подставляет найденные куски текста в промт.

  • LLM формирует ответ уже с учетом этих данных.

Пример: у вас есть договор на 40 страниц. Вместо того чтобы пересказывать его в промпте, мы грузим его в RAG. А дальше просто спрашиваем: "Какие у нас сроки?" и LLM ответит, потому что увидит кусок загруженного договора, где это написано.

В этом вся магия. И именно ее мы рассмотрим в данной статье, создадим RAG с нуля.

Наши зависимости

Предварительно нам понадобится поднять несколько сервисов и получить токен LLM Inference API — все это сделаем в Amvera.

Amvera LLM Inference

Первое и самое важное — доступ к LLM. В нашем случае мы будем работать через наш инференс LLaMA. Это удобно: вам не нужно запускать модель вручную, возиться с зависимостями, подбирать железо, всё это за вас уже сделал Amvera, просто отправляете запрос и получаете ответ от LLaMA через готовый API. А главное, это не требует иностранной карты и у тарифов есть ограничения, которые не позволят уйти в минус при ошибке.

Для получения доступа вам необходимо:

  1. Зарегистрироваться в Amvera по ссылке.

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

  3. На странице проектов уже доступны LLM - переходим туда и покупаем необходимый нам пакет токенов. Я уже взял тариф на 200 тыс. токенов за 490 рублей от LLaMA 3.3 70B.

Переходим в раздел LLM
Переходим в раздел LLM
Овормляем нужную нам подписку на токены LLM. Все в рублях.
Овормляем нужную нам подписку на токены LLM. Все в рублях.
  1. Во вкладке "Инфо" можем выпустить новый токен для доступа к API.

Мы получили токен для доступа и можем сделать тестовый запрос в Inference API.

Подробнее можно почитать на странице документации Amvera и Swagger.

Проверяем работу API LLM
Проверяем работу API LLM

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

Qdrant

Следующая важная часть нашей системы — векторная база данных. Мы будем использовать Qdrant, который идеально подходит для RAG.

Проще говоря, Qdrant - это хранилище "смысла". Поиск идет не по ключевым словам, а по значению кусков текста. Именно поэтому мы используем данную СУБД, чтобы передать LLM только нужную информацию из большого массива данных. Помимо прочего, это сэкономит нам расход токенов на промт для моделей.

Мы выпустили Qdrant как преднастроенный сервис — с WEB UI, API и всем необходимым. Всё это подробно описано в нашей отдельной новости на Хабре: «Векторная база данных Qdrant стала доступна как сервис в Amvera Cloud».

Qdrant готов к работе сразу после запуска — нет необходимости настраивать его и окружение самостоятельно. В этом особенность Amvera — запуска кода и любых сервисов "одной кнопкой".

Подробности — в документации Qdrant от Amvera.

Для запуска Qdrant вам необходимо лишь нажать на выделенную кнопку на скриншоте ниже. Эта кнопка доступна на странице проектов.

Запуск Qdrant
Запуск Qdrant

Далее мы подключим библиотеку qdrant-client и начнём добавлять туда данные из наших документов.

Практика с Qdrant

Начнём с самого простого — подключение к нашему экземпляру Qdrant через Python. В Amvera он уже настроен, так что подключиться можно буквально в пару строк:

from qdrant_client import QdrantClient

client = QdrantClient(
    url="http://amvera-pushmaster-run-qdrant-test:6333",  # укажите внутреннее доменное имя
    api_key="your_secret_api_key_here"  # если используется авторизация
)

Учтите, что все взаимодействия с Qdrant через клиент qdrant-client будет приведено на примере взаимодействия из проектов развернутых в Amvera.

Вот как можно создать коллекцию и добавить в неё данные вручную:

from qdrant_client.models import VectorParams, Distance, PointStruct

# Создание коллекции
client.recreate_collection(
    collection_name="my_documents",
    vectors_config=VectorParams(size=384, distance=Distance.COSINE)
)

# Добавление данных
client.upsert(
    collection_name="my_documents",
    points=[
        PointStruct(
            id=1,
            vector=[0.1] * 384,  # пример вектора
            payload={"text": "Срок поставки составляет 30 дней."}
        )
    ]
)

На практике, конечно, мы не генерируем векторы вручную. Для этого используют модели эмбеддингов. Один из самых простых вариантов - sentence-transformers:

from sentence_transformers import SentenceTransformer

model = SentenceTransformer("intfloat/e5-small")

texts = [
    "Срок составляет 30 дней.",
    "Оплата производится по факту доставки материалов."
]

vectors = model.encode(texts).tolist()

Теперь мы можем загрузить эти вектора в Qdrant вместе с текстами:

points = [
    PointStruct(id=i, vector=vectors[i], payload={"text": texts[i]})
    for i in range(len(texts))
]

client.upsert(
    collection_name="my_documents",
    points=points
)

На этом этапе Qdrant уже содержит документы в векторном виде и готов к поиску по смыслу. Следующим шагом будет использование этого поиска вместе с LLaMA, чтобы отвечать на вопросы пользователя на основе этих текстов.

Но сначала рассмотрим, что это вообще за штуки такие: "вектор", "эмбеддинг", "поиск по смыслу"?

Что такое векторы и зачем они нужны

Когда мы говорим, что сохраняем текст в Qdrant, на самом деле мы сохраняем его представление в виде чисел — векторов. Это такие списки из 384 (или 768, или 1024) чисел, которые описывают смысл текста. Чем ближе два вектора, тем ближе тексты по смыслу.

Это называется эмбеддингом, и создаётся с помощью специальной модели, например intfloat/e5-small. Именно она превращает фразу вроде:

"Срок составляет 30 дней"

в вектор из 384 чисел вроде:

[0.12, -0.03, 0.85, ...]

Сами числа нам не важны, главное, что такие представления можно сравнивать между собой.

Вот зачем нам Qdrant: это база данных, в которой можно делать поиск по близости векторов, то есть по смыслу. Поэтому когда мы получим от пользователя вопрос, мы сначала найдём в Qdrant те куски документа, которые по смыслу ближе всего к этому вопросу, и уже их покажем LLaMA.

Теперь, когда векторная база готова, переходим к LLM.


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

from sentence_transformers import SentenceTransformer
from qdrant_client import QdrantClient
from qdrant_client.models import VectorParams, Distance, PointStruct, Filter, SearchParams

import requests
import os

# Подключение к Qdrant
client = QdrantClient(url="http://amvera-user-run-project:6333") # не забудьте заменить

# Загрузка документа
with open("/data/data.txt", "r", encoding="utf-8") as f:
    raw_text = f.read()

lines = [line.strip() for line in raw_text.split("\n") if line.strip()]

# Эмбеддинги
model = SentenceTransformer("intfloat/e5-small")
vectors = model.encode(lines).tolist()

collection_name = "my_documents"

if client.collection_exists(collection_name):
    client.delete_collection(collection_name)

client.create_collection(
    collection_name=collection_name,
    vectors_config=VectorParams(size=384, distance=Distance.COSINE)
)

points = [
    PointStruct(id=i, vector=vectors[i], payload={"text": lines[i]})
    for i in range(len(lines))
]
client.upsert(collection_name=collection_name, points=points)

# Запрос
query = "Какие сроки указаны в документе? И где об этом написано?"
query_vector = model.encode(query).tolist()

search_result = client.search(
    collection_name=collection_name,
    query_vector=query_vector,
    limit=3
)


context = "\n".join(hit.payload["text"] for hit in search_result if "text" in hit.payload)

# Запрос в Inference API
api_url = "https://kong-proxy.yc.amvera.ru/api/v1/models/llama"
payload = {
    "model": "llama70b",
    "messages": [
        {
            "role": "user",
            "text": f"Контекст:\n{context}\n\nВопрос: {query}"
        }
    ]
}
headers = {
    "Content-Type": "application/json",
    "X-Auth-Token": "Bearer <ваш-токен>" # Токен лучше всего хранить в переменных окружения!
}

response = requests.post(api_url, json=payload, headers=headers)
response.raise_for_status()
print("Ответ:", response.json().get("result"))

Конкретно в этом примере используется предзагруженный документ data.txt, где содержится информация про некоторые сроки. При запуске выводится ответ, где LLM отвечает корректно по заданному вопросу и контексту документа.

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

Итог

Вот и всё! Мы собрали свою RAG систему, которая умеет отвечать на вопросы по документам. Просто загружаем файл, разбиваем его на куски, ищем нужные фрагменты через Qdrant — и передаём их модели LLaMA от Amvera, чтобы получить ответ.

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

Попробуйте сами — и, возможно, это станет вашим новым любимым инструментом!

Теги:
Хабы:
+6
Комментарии17

Публикации

Информация

Сайт
amvera.ru
Дата регистрации
Численность
11–30 человек
Местоположение
Россия
Представитель
Кирилл Косолапов