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

Базовый RAG за 15 минут

1. Введение

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

Наш гайд будет включать следующие шаги:

  1. Загрузка статей и нарезка на chunks
  2. Создание embeddings и загрузка в векторную БД
  3. Реализация базового семантического поиска
  4. Подключение LLM модели
  5. Проверка отсутствия информации у LLM
  6. Общение с LLM по информации из внешних источников

2. Подготовка окружения

Установим и импортируем необходимые библиотеки:

#!pip install langchain-compressa
#!pip install langchain_community
#!pip install requests
#!pip install beautifulsoup4

#!pip install faiss-cpu - если вы запускаете на CPU
#!pip install faiss-gpu - если вы запускаете на GPU с поддержкой CUDA

import os
import requests
from bs4 import BeautifulSoup
from langchain_compressa import CompressaEmbeddings, ChatCompressa
from langchain_core.documents import Document
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_core.prompts import ChatPromptTemplate
from langchain.chains import create_retrieval_chain
from langchain.vectorstores import FAISS
from langchain.chains.combine_documents import create_stuff_documents_chain

Для ячейки ниже вам понадобится API ключ Compressa. Вы можете получить его после регистрации.

os.environ["COMPRESSA_API_KEY"] = "ваш ключ"
# Если вы запускаете локально на Macbook, укажите также следующие переменные в окружении
# os.environ["TOKENIZERS_PARALLELISM"] = "false"
# os.environ["KMP_DUPLICATE_LIB_OK"] = "TRUE"

3. Загрузка веб-страниц и нарезка на chunks

Загрузим информацию о свежих фильмах и сериалах с Wikipedia, а затем нарежем их на chunks (отрывки из статей)

# Для нашего примера возьмем сериал Сёгун и фильм Мастер и Маргарита
urls = [
"https://ru.wikipedia.org/wiki/Мастер_и_Маргарита_(фильм,_2024)",
"https://ru.wikipedia.org/wiki/Сёгун_(телесериал,_2024)"
]

def fetch_wikipedia_content(url):
# Отправляем GET-запрос к указанному URL
response = requests.get(url)

# Создаем объект BeautifulSoup для парсинга HTML-контента
soup = BeautifulSoup(response.content, 'html.parser')

# Находим основной контейнер с содержимым статьи
content = soup.find(id="mw-content-text").find(class_="mw-parser-output")

text = []
# Итерируемся по всем элементам 'p', 'h2' и 'h3' в основном контенте
for element in content.find_all(['p', 'h2', 'h3']):
if element.name == 'p':
# Если это параграф, добавляем его текст как есть
text.append(element.text)
elif element.name in ['h2', 'h3']:
# Если это заголовок, форматируем его с ## и добавляем перенос строки
text.append(f"\n## {element.text.strip()}\n")

# Объединяем все элементы текста в одну строку, разделяя их переносами строк
return '\n'.join(text)

documents = [Document(page_content=fetch_wikipedia_content(url)) for url in urls]

#Проверим, что все загрузилось корректно
print (documents)
# Разбиваем документы на чанки, конкретные настройки приведены для примера

text_splitter = RecursiveCharacterTextSplitter(
chunk_size=500, chunk_overlap=100, add_start_index=True
)
chunks = text_splitter.split_documents(documents)

print(f"Всего создано чанков: {len(chunks)}")
print(f"Пример чанка:\n{chunks[0].page_content[:1000]}...")

4. Создание эмбеддингов

Превращаем полученнные куски текстов в embeddings и загружаем в векторную БД для дальнейшего поиска по ней. Для примера будем использовать Faiss, но можно взять и любую другую векторную БД на выбор (например, ChromaDB или Qdrant)

embeddings = CompressaEmbeddings()

# Создаем и заполняем векторное хранилище
vectorstore = FAISS.from_documents(chunks, embeddings)

print("Векторное хранилище успешно создано")

5. Реализация базового семантического поиска

Настроим простой retriever для семантического поиска:

# Setup the retriever for semantic search
retriever = vectorstore.as_retriever(search_type="similarity", search_kwargs={"k": 3})

# Example usage of retriever
query = "Кто режиссер фильма 'Мастер и Маргарита'?"
retrieved_docs = retriever.invoke(query)
print(f'Найдено {len(retrieved_docs)} релевантных документов')
print(f'Пример найденного документа:\n{retrieved_docs[0].page_content[:200]}...')

6. Подключение LLM модели

Подключим и настроим LLM модель:

llm = ChatCompressa()

# Создадим промпт для обработки запросов

system_template = f"""Ты помощник по вопросам-ответам. Используй следующую контекстную информацию,
чтобы ответить на вопрос. Если в контексте нет ответа, ответь 'Не знаю ответа на вопрос'.
Используй максимум три предложения и будь точным но кратким."""

qa_prompt = ChatPromptTemplate.from_messages([
("system", system_template),
("human", """Контекстная информация:

{context}

Вопрос: {input}
"""),
])

# Создадим цепочку для ответа на вопросы
document_chain = create_stuff_documents_chain(llm, qa_prompt)

7. Проверка отсутствия информации у LLM

Перед использованием RAG, проверим, что у LLM нет информации о наших сериалах:

def check_llm_knowledge(question):
response = llm.invoke(question)
return response.content

questions = [
"Кто режиссер фильма 'Мастер и Маргарита' 2024 года?",
"Кто играет главную роль в сериале 'Сёгун' 2024 года?"
]

print("Проверка знаний LLM без использования RAG:")
for question in questions:
print(f"\nВопрос: {question}")
print(f"Ответ LLM: {check_llm_knowledge(question)}")

8. Сборка полного RAG пайплайна

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

rag_chain = create_retrieval_chain(retriever, document_chain)

def ask_question(question):
response = rag_chain.invoke({"input": question})
return response["answer"]
# Тестирование и демонстрация RAG пайплайна

questions = [
"Кто режиссер фильма 'Мастер и Маргарита' 2024 года?",
"Кто играет главную роль в сериале 'Сёгун' 2024 года?",
"Когда вышел сериал 'Сёгун' 2024 года?",
"Какие актеры играют главные роли в фильме 'Мастер и Маргарита' 2024 года?",
"Сколько серий в сериале 'Сёгун' 2024 года?",
]

print("\nИспользование RAG пайплайна:")
for question in questions:
print(f"\nВопрос: {question}")
print(f"Ответ RAG: {ask_question(question)}")

10. Заключение

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

Например, мы можем улучшить точность поиска с помощью модели CompressaRerank, которая дополнительно приоритизирует найденные отрывки под запрос пользователя. Для этого мы подготовили специальный гайд.

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

Также стоит отметить важность подбора правильных промптов и настроек LLM модели.