Создание RAG-агента на LlamaIndex: загрузка PDF-документов, индексация в ChromaDB, агент с инструментами поиска и ответы на вопросы по вашей базе знаний.
Устанавливаем LlamaIndex, ChromaDB, PyPDF для парсинга PDF и OpenAI SDK. ChromaDB будет нашим векторным хранилищем — она легковесная, работает в памяти и отлично подходит для прототипов. Для продакшена можно заменить на Pinecone или Weaviate.
# Создаём окружение mkdir rag-agent && cd rag-agent python3 -m venv venv source venv/bin/activate # Устанавливаем LlamaIndex и зависимости pip install llama-index llama-index-vector-stores-chroma chromadb pip install llama-index-embeddings-openai openai pip install pypdf # для чтения PDF pip install llama-index-llms-openai # Проверяем версии pip show llama-index | grep Version
Загружаем PDF-файлы из папки data/. SimpleDirectoryReader автоматически обрабатывает все форматы: PDF, TXT, DOCX. SentenceSplitter разбивает документы на чанки по 1024 токена с перекрытием 20 токенов — это обеспечивает сохранение контекста между фрагментами.
from llama_index.core import SimpleDirectoryReader from llama_index.core.node_parser import SentenceSplitter # Загружаем все документы из папки data/ documents = SimpleDirectoryReader( input_dir="./data", recursive=True, required_exts=[".pdf", ".txt", ".md"] ).load_data() print(f"Загружено документов: {len(documents)}") # Разбиваем на чанки (1024 токена, перекрытие 20) splitter = SentenceSplitter( chunk_size=1024, chunk_overlap=20, separator=" " ) nodes = splitter.get_nodes_from_documents(documents) print(f"Создано чанков: {len(nodes)}")
Создаём векторный индекс. Каждый чанк превращается в эмбеддинг с помощью OpenAI text-embedding-3-small (размерность 1536) и сохраняется в ChromaDB. При повторном запуске индекс загружается из коллекции — не нужно переиндексировать.
import chromadb from llama_index.vector_stores.chroma import ChromaVectorStore from llama_index.core import ( VectorStoreIndex, StorageContext, Settings ) from llama_index.embeddings.openai import OpenAIEmbedding # Настройка модели эмбеддингов Settings.embed_model = OpenAIEmbedding( model="text-embedding-3-small", dimensions=1536 ) # Инициализируем ChromaDB клиент chroma_client = chromadb.PersistentClient(path="./chroma_db") chroma_collection = chroma_client.get_or_create_collection( name="rag_docs" ) # Оборачиваем ChromaDB в StorageContext vector_store = ChromaVectorStore(chroma_collection=chroma_collection) storage_context = StorageContext.from_defaults( vector_store=vector_store ) # Строим индекс (или загружаем существующий) index = VectorStoreIndex( nodes, storage_context=storage_context, show_progress=True ) print(f"Индекс создан: {chroma_collection.count()} векторов")
Создаём движок запросов (QueryEngine) поверх индекса. Он выполняет семантический поиск и передаёт найденные чанки в LLM для генерации ответа. Проверяем работу на тестовых вопросах, используя gpt-4o.
from llama_index.llms.openai import OpenAI # Настраиваем LLM Settings.llm = OpenAI( model="gpt-4o", temperature=0.3, # низкая температура — фактологические ответы ) # Создаём query engine с топ-3 релевантными чанками query_engine = index.as_query_engine( similarity_top_k=3, response_mode="compact", # компактный промпт с чанками streaming=False ) # Тестируем questions = [ "Какие условия расторжения договора?", "Перечисли основные продукты компании", "Какая ответственность сторон при задержке?" ] for q in questions: response = query_engine.query(q) print(f"\n❓ {q}") print(f"📝 {response.response[:300]}...") print(f"📎 Источники: {len(response.source_nodes)} чанков")
Теперь создаём полноценного агента. Оборачиваем QueryEngine в QueryEngineTool и добавляем ReActAgent, который может использовать несколько инструментов одновременно. Агент сам решает, какой инструмент применить для ответа.
from llama_index.core.tools import QueryEngineTool, ToolMetadata from llama_index.core.agent import ReActAgent # Оборачиваем query engine в инструмент rag_tool = QueryEngineTool( query_engine=query_engine, metadata=ToolMetadata( name="document_search", description=( "Поиск по корпоративным документам, " "договорам и внутренним регламентам. " "Используй для вопросов о политиках компании." ) ) ) # Создаём ReAct-агента agent = ReActAgent.from_tools( tools=[rag_tool], llm=Settings.llm, verbose=True, # видим ход мыслей агента max_iterations=5 ) # Задаём сложный вопрос response = agent.chat( "Расскажи про штрафные санкции за просрочку поставки. " "Есть ли максимальный размер неустойки?" ) print(f"🤖 Агент: {response.response}")
Для реальных проектов одного индекса недостаточно. Создаём несколько индексов: для договоров, технической документации и HR-политик. Агент автоматически выбирает нужный индекс в зависимости от вопроса.
# Создаём несколько индексов collections = { "contracts": "./data/contracts", "technical": "./data/docs", "hr_policies": "./data/hr" } tools = [] for name, path in collections.items(): docs = SimpleDirectoryReader(input_dir=path).load_data() nodes = splitter.get_nodes_from_documents(docs) # Отдельная коллекция ChromaDB для каждого индекса coll = chroma_client.get_or_create_collection(name) vs = ChromaVectorStore(chroma_collection=coll) sc = StorageContext.from_defaults(vector_store=vs) idx = VectorStoreIndex(nodes, storage_context=sc) # Каждый индекс → свой инструмент tool = QueryEngineTool( query_engine=idx.as_query_engine(similarity_top_k=3), metadata=ToolMetadata( name=f"search_{name}", description=f"Поиск в разделе: {name}" ) ) tools.append(tool) # Мульти-индексный агент multi_agent = ReActAgent.from_tools( tools=tools, llm=Settings.llm, verbose=True ) multi_agent.chat("Какие условия отпуска для сотрудников?")
За час вы создали полноценного RAG-агента, способного отвечать на вопросы по вашим документам. LlamaIndex берёт на себя всю сложность: чанкинг, эмбеддинги, хранение векторов и интеграцию с LLM. Мульти-индексная архитектура позволяет масштабировать решение на десятки коллекций документов без потери точности. Для продакшена замените ChromaDB на Pinecone и добавьте кэширование ответов.