Головна / Статті

Повністю локальний PDF RAG з LangChain, Ollama та Chroma

Повністю локальний додаток PDF RAG: LangChain обробляє файли, Ollama вбудовує їх, Chroma зберігає вектори, а відповіді працюють офлайн без жодних ключів API хмари.

Видавець: REACTAPP.TOP5 хв читання1111 слів

Джерела: Implement RAG with Langchain and local Ollama models

Створіть повністю локальний пайплайн RAG для PDF за допомогою LangChain, Ollama та ChromaDB — спілкуйтесь з документами без використання хмарних API та без виходу даних за межі пристрою.

Чому важлива локальна обробка

Хмарні моделі LLM зручні, але кожен запит передає текст через чужу мережу. Юридичні контракти, медичні записи та внутрішні звіти часто не дозволяють йти на такий компроміс. Локальна система усуває ризик витоку даних, забезпечуючи одночасно можливість запитань та відповідей мовою природи на основі ваших власних файлів.

Що ви отримуєте

  • Завантаження PDF-файлів у постійний векторний сховище
  • Відповіді на запитання мовою природи, засновані на цих документах
  • Робота офлайн під час обробки запитів — не потрібні ключі API після завантаження моделей

Стек: LangChain для оркестрації, Ollama для локальних чатів та ембеддингів, ChromaDB як сховище векторів.

Архітектура

Шлях імпорту: байти PDF потрапляють у завантажувач на основі PyPDF, діляться на частини, після чого Ollama вставляє кожну частину до Chroma.

Шлях запиту: запит надходить до механізму пошуку, відповідні частини стають контекстом, а локальна модель чату Ollama формує відповідь.

Два етапи забезпечують прозорість витрат:

  1. Імпорт — один раз (або коли змінюються документи): парсинг, розділення на частини, ембеддинг, збереження
  2. Пошук — з кожним запитом: найкращі частини стають контекстом для LLM

Ви платите за ембеддинг, коли змінюється контент; інтерактивні запити залишаються швидкими.

Попередні вимоги

Встановіть Ollama та завантажте моделі, які використовуються тут:

ollama pull embeddinggemma:latest   # embedding model
ollama pull gemma4:latest           # chat model

gemma4 налаштований під конкретні інструкції. Щоб зменшити обсяг використання ресурсів, спробуйте gemma2:2b; для кращого міркування використовуйте gemma4:27b — відповідним чином оновіть рядки конфігурації.

Налаштування проекту

python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt

Ключові пакети зі списку вимог:

langchain
langchain-chroma
langchain-community
langchain-ollama
langchain-text-splitters
pypdf
chromadb
python-dotenv

Змініть стандартні налаштування через середовище без редагування коду:

OLLAMA_BASE_URL=http://localhost:11434
OLLAMA_MODEL=gemma4:latest
OLLAMA_EMBED_MODEL=embeddinggemma:latest

Канал отримання даних (src/ingest.py)

Завантаження документів

def load_documents(pdf_path: str | Path) -> list:
    path = Path(pdf_path)
    loader = (
        PyPDFLoader(str(path))
        if path.is_file()
        else PyPDFDirectoryLoader(str(path))
    )
    return loader.load()py

Один помічник обробляє або окремий файл, або папку. Завантажувачі LangChain у будь-якому випадку завжди повертають однаковий список об’єктів типу Document.

Розділення на частини

def split_documents(documents: list) -> list:
    splitter = RecursiveCharacterTextSplitter(
        chunk_size=1000,
        chunk_overlap=200,
    )
    return splitter.split_documents(documents)

RecursiveCharacterTextSplitter віддає перевагу розділенню на абзаци, потім речення, а вже після цього на слова, щоб зберегти семантичні межі. Надлишок у 200 символів дозволяє залишити речення, яке проходить через межу, у обох сусідніх фрагментах, що зменшує кількість невдалих пошуків на межах.

Вбудовування та зберігання

def build_vector_store(chunks: list) -> Chroma:
    embeddings = OllamaEmbeddings(model=EMBEDDING_MODEL, base_url=OLLAMA_BASE_URL)
    VECTOR_STORE_DIR.mkdir(parents=True, exist_ok=True)
    return Chroma.from_documents(
        documents=chunks,
        embedding=embeddings,
        collection_name=COLLECTION_NAME,
        persist_directory=str(VECTOR_STORE_DIR),
    )

Ollama надає ембеддинги локально на порту 11434. Chroma зберігає їх у каталозі vector_store/ (SQLite разом із файлами індексу HNSW), тож після перезапуску процесу не потрібно повторно створювати ембеддинги.

Ланцюжок пошуку (src/retrieval.py)

LangChain LCEL об’єднує кроки за допомогою оператора |.

Шаблон запиту

RAG_PROMPT = ChatPromptTemplate.from_messages([
    (
        "system",
        "You are an assistant that answers questions based on the provided context "
        "from PDF documents. Use only the information in the context to answer. "
        "If the answer is not in the context, say so clearly.\n\nContext:\n{context}"
    ),
    ("human", "{question}"),
])

Системне повідомлення прив’язує відповіді до отриманого контексту — це основний механізм захисту RAG від галюцинацій, спричинених лише попередньою обробкою даних.

Створення ланцюжка

def build_rag_chain(k: int = 4):
    store = load_vector_store()
    retriever = store.as_retriever(search_kwargs={"k": k})
    llm = ChatOllama(model=MODEL_NAME, base_url=OLLAMA_BASE_URL)
    return (
        {"context": retriever | _format_docs, "question": RunnablePassthrough()}
        | RAG_PROMPT
        | llm
        | StrOutputParser()
    )

Зліва направо: ретривер вибирає k найбільш схожих фрагментів за допомогою метрики схожості ембеддингів, допоміжні скрипти форматують ці фрагменти в один рядок контексту, шаблон запиту поєднує цей контекст із запитанням користувача, а ChatOllama генерує відповідь.

Інтерактивний цикл запитань

# Index a directory of PDFs (default: data/pdfs/)
python main.py ingest

Простий інтерфейс командного рядка (або HTTP-обгортка) завантажує ланцюжок обробки один раз та приймає запитання до моменту завершення роботи. Залишайте значення k налаштовуваним, щоб можна було змінювати розмір контексту та час відгуку.

Робота від початку до кінця

# Index a specific file
python main.py ingest --path /path/to/contract.pdf
# Ask a one-off question
python main.py query "What are the payment terms?"
# Interactive session
python main.py chat

Вкажіть шлях до вашої папки PDF, переконайтеся, що каталог сховища векторів заповнений, а потім виконайте запит. Усе — ембеддинги, вектори, генерація відповідей — залишається на локальному комп’ютері.

Висновки

Локальний RAG більше не є чимось екзотичним. Ollama забезпечує моделі, Chroma зберігає вектори, а LangChain об’єднує всі ці елементи в єдину систему. Для приватних корпусів даних ця трійка достатня для створення корисного асистента з обробки документів без необхідності використання хмарних послуг чи передачі даних назовні.