Основна имплементация на RAG: ръководство стъпка по стъпка

Това ръководство, подходящо за начинаещи, обяснява как работи основната имплементация на RAG, разглежда най-добрите практики и ви превежда през изграждането на работеща RAG система за командния ред с Python, ChromaDB, както и с модела за ембединги и езиковите модели на Perplexity.

Basic RAG implementation process diagram: the interface sends input to the orchestrator, which retrieves data from a vector database, builds the input context for the LLM, and returns the output to the interface.

RAG е съществен компонент на съвременната инфраструктура, базирана на LLM. Като дава на ИИ моделите достъп до външни данни, той помага за справяне с проблеми като халюцинациите и непълните данни за обучение. Освен това компаниите могат да използват вътрешни бази знания, съдържащи чувствителна или поверителна фирмена информация.

Имплементацията на RAG може да бъде много сложна. Основна RAG система обаче се изгражда изненадващо лесно. За начинаещите създаването на проста архитектура може да изясни как RAG работи в реалния свят и да послужи като отправна точка за по-нататъшни експерименти.

В тази статия ще очертаем основна имплементация на RAG, ще разгледаме най-добрите практики и след това ще изградим работеща система с Python, ChromaDB и два модела на Perplexity – за ембединги и за генериране на отговори.

Процес на основна имплементация на RAG: общ преглед

Една основна RAG система има три главни компонента: оркестратор, източник на информация и LLM. В типичен работен процес потребителят въвежда промпт, а оркестраторът го модифицира, за да търси във всички свързани източници на информация. След това извлечената информация се добавя към първоначалния входен контекст и се изпраща към LLM, за да бъде генериран отговор.

При изграждането на своята имплементация на RAG ще трябва да вземете предвид следните стъпки:

  1. Индексиране и създаване на ембединги: На този етап документите ви се преобразуват в източник на информация, в който оркестраторът може да търси. Един от най-разпространените начини за това е да се изгради векторен индекс с възможност за търсене. Ще използваме специализиран модел на Perplexity, за да преобразуваме фрагментите от документите ви в ембединги. След това ще съхраним ембедингите в ChromaDB – популярна векторна база данни с отворен код.
  2. Оркестрация: Това е приложението, което стои между външния източник на информация (в нашия случай векторна база данни) и LLM. Ще създадем прост скрипт на Python, който да играе ролята на оркестратор.
  3. Извличане и генериране: Когато потребител зададе въпрос чрез чат интерфейса, оркестраторът ще го приеме, ще изпълни векторно търсене и ще подаде разширения вход към 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:

bash
py -3.12 -m venv .venv

Сега трябва да инсталирате четири библиотеки на Python: perplexityai, chromadb, python-dotenv и numpy. Perplexity SDK ще ви позволи да се свържете с моделите на Perplexity чрез API. ChromaDB изгражда векторната база данни и ще работи локално. Python-dotenv зарежда сигурно вашите API ключове от локален файл, а NumPy е необходим по време на процеса на въвеждане на данни.

Инсталирайте ги със следната команда:

bash
.\.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: Тук ще съхранявате своя API ключ за Perplexity.

Във файла .env въведете следното:

plaintext
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 (достъпен чрез API на Perplexity), който ще обработва извлечената информация.

Във файла document.txt добавете текста, който вашият RAG конвейер ще извлича. Ако нямате подръка документ, за това ръководство използвайте Songs of Innocence and of Experience на Уилям Блейк от Project Gutenberg. Това е кратък и лесен за разбиране текст, който е обществено достояние.

Стъпка 4: Напишете програмата за индексиране

Копирайте и поставете кода по-долу в index.py. Всяко подзаглавие дава кратко обяснение какво прави следващият сегмент от кода. Това е програмата, която ще преобразува изходните ви материали във векторни ембединги и ще ги съхрани в база данни на ChromaDB, като използва модела за ембединги на Perplexity pplx-embed-v1-0.6b и библиотеката NumPy.

Ето какво прави програмата:

  1. Отваря document.txt.
  2. Разделя текста на припокриващи се фрагменти.
  3. Изпраща тези фрагменти към модела за ембединги pplx-embed-v1-0.6b на Perplexity.
  4. Декодира и нормализира върнатите вектори с помощта на NumPy – библиотека на Python с изчислителни функции.
  5. Съхранява векторите и оригиналните фрагменти в база данни на ChromaDB (ще я видите да се появява в директорията на проекта ви).

Програмата е настроена така, че да не индексира документа ви наново при всяко стартиране. Ако добавите информация в document.txt, тя ще създаде ембединги само за новия текст.

python
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.

Ето какво ще се случи, когато стартирате програмата:

  1. Свързва се с Perplexity и с новата ви база данни на ChromaDB.
  2. Проверява дали базата данни съдържа индексирани документи.
  3. Приема въпрос от потребителя.
  4. Изпраща въпроса към модела за ембединги на Perplexity.
  5. Декодира и нормализира получения ембединг с помощта на NumPy.
  6. Търси в ChromaDB трите най-сходни фрагмента от документа.
  7. Съставя промпт, съдържащ извлечените фрагменти и въпроса на потребителя.
  8. Изпраща промпта към GPT-5.6 Luna чрез Agent API на Perplexity.
  9. Показва отговора и изчаква следващ въпрос.
python
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:

bash
.\.venv\Scripts\python.exe index.py

Ако документът ви е индексиран успешно, трябва да видите резултат, подобен на този:

plaintext
Indexing complete! Unchanged chunks: 0 New or modified chunks: 58 Removed chunks: 0 Total stored chunks: 58

Сега е време да стартирате основното приложение – оркестратора. Въведете тази команда:

bash
.\.venv\Scripts\python.exe rag.py

Ако се изпълни успешно, ще видите следното:

plaintext
Connected to existing ChromaDB database. Stored chunks: 58 Ask a question (or type exit):

Вече можете да задавате въпроси, чиито отговори ще черпят информация от създадената от вас векторна база данни, например:

plaintext
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 извикването.