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

RAG — важнейший компонент современной инфраструктуры на базе LLM. Предоставляя моделям ИИ доступ к внешним данным, эта технология помогает решать такие проблемы, как галлюцинации и неполнота обучающих данных. Кроме того, она позволяет компаниям использовать внутренние базы знаний, содержащие конфиденциальную или проприетарную информацию.
Реализация RAG может быть очень сложной. Однако базовую RAG-систему построить на удивление просто. Создание простой архитектуры поможет начинающим понять, как RAG работает на практике, и станет отправной точкой для дальнейших экспериментов.
В этой статье мы опишем базовую реализацию RAG, рассмотрим лучшие практики, а затем построим работающую систему с помощью Python, ChromaDB и двух моделей Perplexity — для создания эмбеддингов и генерации ответов.
Процесс базовой реализации RAG: общий обзор
Базовая RAG-система состоит из трех основных компонентов: оркестратора, источника информации и LLM. В типичном рабочем процессе пользователь вводит промпт, а оркестратор преобразует его для поиска по всем подключенным источникам информации. Затем найденная информация добавляется к исходному входному контексту и отправляется в LLM для генерации ответа.
При создании реализации RAG необходимо учесть следующие этапы:
- Индексирование и создание эмбеддингов: на этом этапе ваши документы преобразуются в источник информации, по которому оркестратор может выполнять поиск. Один из самых распространенных способов — построить векторный индекс с возможностью поиска. Мы воспользуемся специальной моделью Perplexity, чтобы преобразовать фрагменты документов в эмбеддинги. Затем мы сохраним эмбеддинги в ChromaDB — популярной векторной базе данных с открытым исходным кодом.
- Оркестрация: это приложение, которое находится между внешним источником информации (в нашем случае — векторной базой данных) и LLM. В качестве оркестратора мы создадим простой скрипт на Python.
- Извлечение и генерация: когда пользователь задает вопрос через чат-интерфейс, оркестратор принимает его, выполняет векторный поиск и передает расширенные входные данные в LLM. Вместо создания отдельного фронтенда мы будем взаимодействовать с оркестратором на Python через командную строку.
Лучшие практики: что делает реализацию RAG эффективной?
Существует несколько типов архитектуры RAG, и для разных сценариев использования требуются разные подходы. Тем не менее ряд лучших практик применим во всех случаях и позволяет создавать точные, экономичные, масштабируемые и удобные в сопровождении системы.
Следующие лучшие практики применимы ко всем типам реализации RAG:
- Обеспечьте чистый конвейер загрузки данных: перед индексированием исходные данные должны быть максимально очищены. Кроме того, используйте модель эмбеддингов, подходящую для вашего контента.
- Используйте гибридный поиск: оптимальные методы извлечения — будь то поиск по векторной базе данных, веб-поиск через API, обход графа знаний, поиск по ключевым словам или другие методы — зависят от сценария использования. Правильно подобранное сочетание методов важно для минимизации затрат.
- Оценивайте систему: после внедрения систему следует оценить по трем ключевым показателям: точности, релевантности и задержке. Точность означает фактическую корректность, релевантность — применимость найденной информации к запросам пользователей, а задержка — скорость извлечения информации.
- Обеспечьте безопасность информации: если ваша RAG-система использует закрытую внутреннюю информацию, убедитесь, что она надежно хранится, а доступ к интерфейсу системы контролируется. Никогда не передавайте ключи API другим лицам.
Базовая реализация RAG: руководство из 7 шагов
В этом руководстве мы создадим приложение командной строки, которое отвечает на вопросы по одному документу. Мы будем использовать Python, ChromaDB и Perplexity API.
Цель — не построить готовую к промышленной эксплуатации RAG-систему. Это руководство для начинающих, которое наглядно показывает, как простая архитектура RAG работает на практике.
Шаг 1: установка программного обеспечения
Убедитесь, что на вашем компьютере установлено следующее ПО и у вас есть аккаунт Perplexity API:
- Python: язык программирования, на котором мы создадим оркестратор, координирующий поток данных между моделью эмбеддингов, ChromaDB и Perplexity.
- Visual Studio: в качестве редактора кода мы будем использовать Visual Studio. После установки откройте панель Extensions и загрузите расширение Python.
- Аккаунт Perplexity API: Perplexity предоставит как модель эмбеддингов (которая преобразует ваши данные в векторные эмбеддинги), так и LLM, которая будет обрабатывать расширенный промпт. Платный аккаунт для этого не нужен, хотя придется потратить немного кредитов.
Шаг 2: создание папки проекта
Для начала создайте папку с именем rag-project и откройте ее в VS Code, выбрав File > Open Folder. Затем откройте встроенный терминал через Terminal > New Terminal.
Введите следующую команду, чтобы создать виртуальное окружение Python:
py -3.12 -m venv .venvТеперь необходимо установить четыре библиотеки Python: perplexityai, chromadb, python-dotenv и numpy. Perplexity SDK позволит подключаться к моделям Perplexity через API. ChromaDB создает векторную базу данных и будет работать локально. Python-dotenv безопасно загружает ваши ключи API из локального файла, а NumPy нужна в процессе загрузки данных.
Установите их с помощью следующей команды:
.\.venv\Scripts\python.exe -m pip install perplexityai chromadb python-dotenv numpyШаг 3: настройка файлов
Добавьте следующие файлы в rag-project (корень проекта), выбрав New File… на боковой панели Explorer:
- index.py: приложение, которое будет индексировать ваш документ. Его можно запустить повторно, чтобы добавить новый текст.
- rag.py: приложение на Python, которое будет выполнять роль оркестратора.
- document.txt: этот файл будет содержать документацию, которую вы преобразуете в векторную базу данных.
- .env: здесь будет храниться ваш ключ Perplexity API.
В файл .env введите следующее:
PERPLEXITY_API_KEY=your_api_key_hereЗамените your_api_key_here своим реальным ключом. Для этого перейдите на главную панель управления Perplexity API Console и выберите Create an API key. Храните этот ключ API в тайне.
Мы будем использовать две модели: pplx-embed-v1-0.6b от Perplexity — для создания эмбеддингов документации перед загрузкой в ChromaDB, и GPT-5.6-Luna (с доступом через Perplexity API) — для обработки найденной информации.
В document.txt добавьте текст, который будет извлекать ваш RAG-конвейер. Если подходящего документа под рукой нет, используйте для этого руководства сборник Уильяма Блейка Songs of Innocence and of Experience («Песни невинности и опыта») из Project Gutenberg. Это короткий и понятный текст, перешедший в общественное достояние.
Шаг 4: написание программы индексирования
Скопируйте приведенный ниже код и вставьте его в index.py. Каждый подзаголовок кратко объясняет, что делает следующий за ним фрагмент кода. Эта программа преобразует исходные материалы в векторные эмбеддинги и сохраняет их в базе данных ChromaDB с помощью модели эмбеддингов Perplexity pplx-embed-v1-0.6b и библиотеки NumPy.
Вот что делает программа:
- Открывает document.txt.
- Разбивает текст на перекрывающиеся фрагменты.
- Отправляет эти фрагменты в модель эмбеддингов Perplexity pplx-embed-v1-0.6b.
- Декодирует и нормализует полученные векторы с помощью NumPy — библиотеки Python с вычислительными функциями.
- Сохраняет векторы и исходные фрагменты в базе данных ChromaDB (она появится в каталоге вашего проекта).
Программа настроена так, чтобы не переиндексировать документ при каждом запуске. Если вы дополните информацию в document.txt, она создаст эмбеддинги только для нового текста.
from pathlib import Path
from collections import defaultdict, deque
from hashlib import sha256
import base64
import numpy as np
import chromadb
from dotenv import load_dotenv
from perplexity import Perplexity
# 1. Connect to Perplexity and ChromaDB
load_dotenv()
ai = Perplexity()
db = chromadb.PersistentClient(path="./rag_database_perplexity")
collection = db.get_or_create_collection(
name="documents",
metadata={"hnsw:space": "cosine"}
)
# Convert Perplexity's encoded embeddings into numerical vectors
def decode_embedding(encoded):
vector = np.frombuffer(base64.b64decode(encoded), dtype=np.int8).astype(np.float32)
norm = np.linalg.norm(vector)
if norm == 0:
raise ValueError("Received a zero-length embedding vector.")
return (vector / norm).tolist()
# 2. Read the document and split it into chunks
text = Path("document.txt").read_text(encoding="utf-8")
words = text.split()
chunk_size = 180
overlap = 30
step = chunk_size - overlap
chunks = [" ".join(words[i:i + chunk_size]) for i in range(0, len(words), step)]
if not chunks:
raise ValueError("document.txt is empty.")
# 3. Retrieve the chunks already stored in ChromaDB
existing = collection.get(include=["documents"])
stored_chunks = defaultdict(deque)
existing_ids = set(existing["ids"])
for chunk_id, document in zip(existing["ids"], existing["documents"]):
stored_chunks[document].append(chunk_id)
# 4. Identify unchanged and new chunks
new_chunks = []
new_ids = []
kept_ids = set()
unchanged = 0
for chunk in chunks:
if stored_chunks[chunk]:
chunk_id = stored_chunks[chunk].popleft()
kept_ids.add(chunk_id)
unchanged += 1
else:
chunk_hash = sha256(chunk.encode("utf-8")).hexdigest()
chunk_id = f"chunk-{chunk_hash}"
suffix = 1
while chunk_id in existing_ids or chunk_id in kept_ids or chunk_id in new_ids:
chunk_id = f"chunk-{chunk_hash}-{suffix}"
suffix += 1
new_chunks.append(chunk)
new_ids.append(chunk_id)
kept_ids.add(chunk_id)
# 5. Generate embeddings in batches of up to 100 chunks
batch_size = 100
for i in range(0, len(new_chunks), batch_size):
batch_chunks = new_chunks[i:i + batch_size]
batch_ids = new_ids[i:i + batch_size]
embeddings = ai.embeddings.create(
model="pplx-embed-v1-0.6b",
input=batch_chunks,
encoding_format="base64_int8"
)
vectors = [decode_embedding(item.embedding) for item in embeddings.data]
# 6. Store each batch in ChromaDB
collection.upsert(
ids=batch_ids,
documents=batch_chunks,
embeddings=vectors
)
print(f"Indexed batch {(i // batch_size) + 1}")
# 7. Remove outdated chunks
old_ids = [chunk_id for chunk_id in existing["ids"] if chunk_id not in kept_ids]
if old_ids:
collection.delete(ids=old_ids)
# 8. Display indexing results
print("Indexing complete!")
print("Unchanged chunks:", unchanged)
print("New or modified chunks:", len(new_chunks))
print("Removed chunks:", len(old_ids))
print("Total stored chunks:", collection.count())Шаг 5. Написание оркестратора для RAG-системы
Скопируйте приведенный ниже код и вставьте его в rag.py. Это полная, но минимальная реализация RAG, которая использует Perplexity SDK и функции локальной векторной базы данных ChromaDB.
Вот что произойдет при запуске программы:
- Подключается к Perplexity и вашей новой базе данных ChromaDB.
- Проверяет, что база данных содержит проиндексированные документы.
- Принимает вопрос от пользователя.
- Отправляет вопрос в модель эмбеддингов Perplexity.
- Декодирует и нормализует полученный эмбеддинг с помощью NumPy.
- Ищет в ChromaDB три наиболее похожих фрагмента документа.
- Формирует промпт, содержащий найденные фрагменты и вопрос пользователя.
- Отправляет промпт в GPT-5.6 Luna через Agent API от Perplexity.
- Выводит ответ и ожидает следующего вопроса.
import base64
import numpy as np
import chromadb
from dotenv import load_dotenv
from perplexity import Perplexity
# 1. Connect to Perplexity and the existing ChromaDB database
load_dotenv()
ai = Perplexity()
db = chromadb.PersistentClient(path="./rag_database_perplexity")
collection = db.get_collection("documents")
# Convert Perplexity's encoded embeddings into numerical vectors
def decode_embedding(encoded):
vector = np.frombuffer(base64.b64decode(encoded), dtype=np.int8).astype(np.float32)
norm = np.linalg.norm(vector)
if norm == 0:
raise ValueError("Received a zero-length embedding vector.")
return (vector / norm).tolist()
# 2. Check that the database contains indexed documents
count = collection.count()
if count == 0:
raise ValueError("The database is empty. Run index.py first.")
print("Connected to existing ChromaDB database.")
print("Stored chunks:", count)
# 3. Accept questions from the user
while True:
question = input("\nAsk a question (or type exit): ")
if question.lower().strip() == "exit":
break
if not question.strip():
continue
# 4. Convert the question into a Perplexity embedding
question_response = ai.embeddings.create(
model="pplx-embed-v1-0.6b",
input=[question],
encoding_format="base64_int8"
)
question_vector = decode_embedding(question_response.data[0].embedding)
# 5. Retrieve the most similar document chunks from ChromaDB
results = collection.query(
query_embeddings=[question_vector],
n_results=min(3, count)
)
context = "\n\n".join(results["documents"][0])
# 6. Construct a prompt and send it to the fixed LLM
response = ai.responses.create(
model="openai/gpt-5.6-luna",
instructions=(
"Answer using only the supplied document excerpts. "
"Do not use external information. "
"If the answer is not present, say you don't know."
),
input=f"Document excerpts:\n{context}\n\nQuestion: {question}"
)
# 7. Display the answer
print("\nAnswer:\n", response.output_text)Шаг 6: запуск приложений
В терминале PowerShell в VS Code введите следующую команду, чтобы создать эмбеддинги для содержимого document.txt:
.\.venv\Scripts\python.exe index.pyЕсли документ успешно проиндексирован, вы увидите примерно такой вывод:
Indexing complete!
Unchanged chunks: 0
New or modified chunks: 58
Removed chunks: 0
Total stored chunks: 58Теперь пора запустить основное приложение-оркестратор. Введите эту команду:
.\.venv\Scripts\python.exe rag.pyПри успешном запуске вы увидите следующее:
Connected to existing ChromaDB database.
Stored chunks: 58
Ask a question (or type exit): Теперь вы можете задавать вопросы, ответы на которые будут основаны на созданной вами векторной базе данных, например:
Ask a question (or type exit): What animals does William Blake talk about?
Answer: The excerpts mention sheep, lambs, birds, beasts, wolves, tigers, and a lion.Шаг 7: разбор созданной системы
Созданная вами система содержит два основных конвейера, присутствующих в любой архитектуре RAG: конвейер индексирования (или «загрузки данных») и конвейер извлечения и генерации.
Теперь ваша базовая реализация RAG выполняет все следующие функции:
- Хранение векторов: эмбеддинги на основе вашего файла знаний сохраняются в базе данных ChromaDB в локальной среде.
- Инкрементное индексирование: для неизмененных фрагментов документа эмбеддинги повторно не создаются, а сам документ можно обновлять в любое время.
- Семантическое извлечение: ChromaDB извлекает фрагменты на основе векторного сходства (по запросу оркестратора).
- Генерация обоснованных ответов: LLM получает найденные фрагменты в качестве контекста и использует их в ответе.
- Независимое индексирование и выполнение запросов: вы можете задавать вопросы без повторной обработки всего текста из файла знаний.
- Подключение к модели через API: все ответы генерируются с помощью GPT-5.6 Luna через Perplexity, а не моделью, запущенной на вашем компьютере.
Дальнейшие шаги: как развить эту реализацию
Цель этого руководства — создать простую базовую реализацию. Она охватывает некоторые ключевые компоненты архитектуры RAG, присутствующие в современных системах. Если вы хотите пойти дальше, ее можно развить разными способами.
Вот несколько идей для расширения возможностей вашей реализации:
- Поэкспериментируйте с LangChain: LangChain — один из самых популярных фреймворков оркестрации с открытым исходным кодом. Он содержит готовые компоненты для создания приложений, способных запускать сложные RAG-конвейеры.
- Расширьте функциональность поиска: объедините плотный векторный поиск с лексическим поиском (по ключевым словам), например BM25. Добавление веб-поиска через Perplexity Search API также позволит основывать ответы на актуальных данных.
- Добавьте модель реранжирования: интегрируйте модель реранжирования, которая ранжирует найденные фрагменты относительно исходного промпта и отбирает наиболее релевантные для включения во входной контекст, передаваемый в LLM.
- Создайте адаптивную RAG-систему: адаптивный RAG использует LLM или классификатор (компонент оркестратора), чтобы определить, какой метод извлечения применять в модульной RAG-системе. Создание такой системы — хороший способ освоить более сложные реализации RAG.
Хотя все это может показаться сложным, помните, что популярные ИИ-платформы, такие как Perplexity, могут оказать продвинутую помощь в программировании. Они отлично подходят для личных проектов и экспериментов, а расходы при этом можно удерживать на относительно низком уровне.
Примечание. В этом руководстве используется GPT-5.6 Luna. Доступность моделей может меняться, поэтому вам может потребоваться обновить вызов API.