Локальный ИИ-ассистент с RAG на Qwen2.5: полный гайд для учёбы | AiManual
AiManual Logo Ai / Manual.
05 Мар 2026 Гайд

Полный гайд: Создаём локальный ИИ-ассистент с RAG для учёбы на базе Qwen2.5

Пошаговое руководство по созданию локального ИИ-ассистента для учёбы с использованием Qwen2.5, ChromaDB и Gradio. Работает без интернета, сохраняет конфиденциал

Зачем вообще это нужно? Проблема студента 2026 года

Сидишь, готовишься к экзамену. Нужно быстро найти определение в учебнике на 500 страниц. Или понять сложную формулу из лекции, которую преподаватель объяснил криво. ChatGPT платный, у бесплатного ограничения, а главное – все твои вопросы улетают в облако. Конфиденциальность? Забудь. Интернет отвалился? Ты отвалился.

Решение – коробка под столом (или просто твой ноутбук), которая знает ВСЕ твои учебники, конспекты и статьи. Отвечает мгновенно, без интернета, и никогда не скажет декану, какие глупые вопросы ты задавал в 3 часа ночи. Собираем такого монстра.

Важно: На дворе 05.03.2026. Qwen2.5 – не вчерашняя новость, а текущий стандарт для локальных моделей с отличным балансом качества и размера. Используем самые свежие инструменты.

Архитектура: что внутри чёрного ящика

Система простая, но эффективная. Не Agentic RAG, о котором я писал в другом гайде, а точный инструмент для вопрос-ответа.

  • Документы: PDF, EPUB, TXT – всё, что ты сбросил в папку data/.
  • Индексатор: Разбивает текст на чанки, создаёт эмбеддинги (векторные представления).
  • ChromaDB: Векторная база данных. Хранит чанки и их эмбеддинги. Ищет похожие.
  • Qwen2.5 (GGUF): Мозги системы. Запускается через llama.cpp. Принимает вопрос и найденные чанки, генерирует ответ.
  • Gradio: Веб-интерфейс. Просто открываешь в браузере и общаешься.
💡
Железо: на 7-миллиардной модели (Qwen2.5-7B) комфортно работать можно на ноутбуке с 16 ГБ ОЗУ. Если у тебя слабее – присмотрись к 3- или 1.5-миллиардным версиям. На Raspberry Pi 5 тоже можно, но это уже отдельная история.

1 Готовим среду: не пропусти этот шаг

Создаём чистую песочницу. Иначе через месяц не поймёшь, почему всё сломалось.

mkdir study_assistant && cd study_assistant
python -m venv venv
# Активация для Linux/macOS
source venv/bin/activate
# Для Windows
# venv\Scripts\activate

2 Ставим зависимости: не просто pip install

Тут есть подводные камни. Llama-cpp-python нужно собирать под твою систему, особенно если хочешь использовать GPU (CUDA).

# Базовые для работы с документами
pip install chromadb langchain langchain-community pypdf2 python-docx
# Для интерфейса
pip install gradio
# Критически важная зависимость - собираем llama-cpp-python с поддержкой GPU
# Для CUDA (если есть NVIDIA карта)
CMAKE_ARGS="-DLLAMA_CUBLAS=on" pip install llama-cpp-python --force-reinstall --upgrade
# Для чистого CPU (работает, но медленнее)
# pip install llama-cpp-python

Если сборка с CUDA падает с ошибкой – забей. Запусти на CPU. Да, будет не так шустро, но для учёбы сойдёт. Главное – начать.

3 Качаем модель: берём правильную версию

Не качай первое попавшееся. Нам нужен GGUF формат (оптимизированный для llama.cpp) и желательно версия с квантованием Q4_K_M – лучший баланс качества и размера. На 05.03.2026 актуальна модель Qwen2.5-7B-Instruct.

Иди на Hugging Face, например, к TheBloke: https://huggingface.co/TheBloke/Qwen2.5-7B-Instruct-GGUF. Скачивай файл qwen2.5-7b-instruct-q4_k_m.gguf. Положи в папку models/.

mkdir models
cd models
wget https://huggingface.co/TheBloke/Qwen2.5-7B-Instruct-GGUF/resolve/main/qwen2.5-7b-instruct-q4_k_m.gguf
# Или качай вручную через браузер

Если 7B велика, есть Qwen2.5-3B или даже 1.5B. Они слабее, но намного шустрее.

4 Пишем индексатор: не просто split_text

Самый важный код во всей системе. Плохо разобьёшь текст – модель будет получать мусор и генерировать ерунду. Не используй наивное разбиение по символам.

import os
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_community.document_loaders import PyPDFLoader, TextLoader, Docx2txtLoader

def load_documents(data_dir="data"):
    documents = []
    for filename in os.listdir(data_dir):
        filepath = os.path.join(data_dir, filename)
        try:
            if filename.endswith('.pdf'):
                loader = PyPDFLoader(filepath)
            elif filename.endswith('.txt'):
                loader = TextLoader(filepath, encoding='utf-8')
            elif filename.endswith('.docx'):
                loader = Docx2txtLoader(filepath)
            else:
                continue
            loaded_docs = loader.load()
            documents.extend(loaded_docs)
            print(f"Загружен: {filename}")
        except Exception as e:
            print(f"Ошибка загрузки {filename}: {e}")
    return documents

def chunk_documents(documents, chunk_size=512, chunk_overlap=50):
    # Перекрытие (overlap) критически важно для сохранения контекста
    text_splitter = RecursiveCharacterTextSplitter(
        chunk_size=chunk_size,
        chunk_overlap=chunk_overlap,
        length_function=len,
        separators=["\n\n", "\n", ". ", " ", ""]
    )
    chunks = text_splitter.split_documents(documents)
    print(f"Создано {len(chunks)} чанков.")
    return chunks

Ключевой параметр – chunk_size. Для Qwen2.5 и учебных текстов 512-1024 токена – нормально. Overlap в 50-100 токенов не даст потерять мысль на границе чанков.

5 Запускаем ChromaDB: не усложняй

Локальная база в папке db/. Для эмбеддингов используем легковесную модель all-MiniLM-L6-v2 – она быстро работает и нормально отрабатывает для академических текстов.

from langchain_community.vectorstores import Chroma
from langchain_community.embeddings import HuggingFaceEmbeddings

def create_vector_store(chunks, persist_directory="db"):
    # Модель для эмбеддингов - локальная и быстрая
    embedding_model = HuggingFaceEmbeddings(
        model_name="sentence-transformers/all-MiniLM-L6-v2",
        model_kwargs={'device': 'cpu'},  # Можешь попробовать 'cuda', если есть GPU
        encode_kwargs={'normalize_embeddings': False}
    )
    
    # Создаём и сохраняем базу
    vector_db = Chroma.from_documents(
        documents=chunks,
        embedding=embedding_model,
        persist_directory=persist_directory
    )
    vector_db.persist()
    print(f"Векторная база сохранена в {persist_directory}")
    return vector_db

# Использование:
docs = load_documents()
chunks = chunk_documents(docs)
vector_db = create_vector_store(chunks)

6 Собираем конвейер RAG: где магия

Теперь свяжем поиск по базе и модель. Промпт – это святое. Если напишешь криво, Qwen2.5 начнёт выдумывать или цитировать не то.

from langchain.llms import LlamaCpp
from langchain.chains import RetrievalQA
from langchain.callbacks.manager import CallbackManager
from langchain.callbacks.streaming_stdout import StreamingStdOutCallbackHandler

def load_llama_model(model_path, n_ctx=2048):
    # n_ctx - размер контекста. 2048 хватит для вопроса + несколько чанков.
    callback_manager = CallbackManager([StreamingStdOutCallbackHandler()])
    llm = LlamaCpp(
        model_path=model_path,
        temperature=0.1,  # Низкая температура для фактовых ответов
        max_tokens=512,   # Не даём болтать лишнего
        top_p=0.9,
        n_ctx=n_ctx,
        callback_manager=callback_manager,
        verbose=False,
        n_gpu_layers=33  # Раскомментируй и укажи число слоёв для GPU, если используешь CUDA
        # n_gpu_layers=-1  # Для загрузки всех слоёв на GPU
    )
    return llm

def create_rag_chain(vector_db, llm):
    # Промпт-шаблон. Обрати внимание на строгое указание контекста.
    QA_PROMPT = """Используй только следующий контекст для ответа на вопрос. Если ответа нет в контексте, скажи 'В предоставленных материалах этого нет.' Не выдумывай.

Контекст: {context}

Вопрос: {question}

Точный и краткий ответ:"""
    
    qa_chain = RetrievalQA.from_chain_type(
        llm=llm,
        chain_type="stuff",
        retriever=vector_db.as_retriever(search_kwargs={"k": 3}),  # Берём 3 самых похожих чанка
        chain_type_kwargs={"prompt": QA_PROMPT},
        return_source_documents=True
    )
    return qa_chain

7 Лепим интерфейс на Gradio: просто и функционально

Делаем одну страницу с полем ввода, кнопкой и областью вывода. Добавим чекбокс, чтобы показывать источники (по каким чанкам был дан ответ).

import gradio as gr

def gradio_interface(rag_chain):
    def respond(question, show_sources):
        result = rag_chain({"query": question})
        answer = result["result"]
        sources = ""
        if show_sources and "source_documents" in result:
            sources = "\n\n--- Источники ---\n"
            for i, doc in enumerate(result["source_documents"]):
                sources += f"{i+1}. {doc.page_content[:200]}...\n"
        return answer + sources
    
    demo = gr.Interface(
        fn=respond,
        inputs=[
            gr.Textbox(label="Задай вопрос по материалам", lines=3),
            gr.Checkbox(label="Показать источники", value=False)
        ],
        outputs=gr.Textbox(label="Ответ ассистента", lines=10),
        title="Локальный ИИ-ассистент для учёбы",
        description="Загрузи учебники в папку 'data' и задавай вопросы. Работает без интернета."
    )
    return demo

# Финальная сборка и запуск
if __name__ == "__main__":
    # Загрузка модели (указывай свой путь)
    llm = load_llama_model("models/qwen2.5-7b-instruct-q4_k_m.gguf")
    # Загрузка существующей базы (если индексировал заранее)
    embedding_model = HuggingFaceEmbeddings(model_name="sentence-transformers/all-MiniLM-L6-v2")
    vector_db = Chroma(persist_directory="db", embedding_function=embedding_model)
    
    rag_chain = create_rag_chain(vector_db, llm)
    demo = gradio_interface(rag_chain)
    demo.launch(server_name="0.0.0.0", server_port=7860)  # Доступно по http://localhost:7860

Где собака зарыта: нюансы, которые сломают твой проект

Теперь о том, что обычно умалчивают в туториалах.

  • Формат PDF. Если PDF – это сканы страниц (картинки), PyPDFLoader вытащит только пустоту. Нужен OCR. Для начала используй текстовые PDF или конвертируй через Adobe или онлайн-сервисы.
  • Размер контекста (n_ctx). У Qwen2.5 в GGUF он часто ограничен 4096 или 8192 токенов. Если поставишь больше, чем поддерживает модель – упадёшь с ошибкой. Проверяй спецификацию файла GGUF.
  • Первый запуск модели. Он всегда долгий – модель загружается в память. Может занять минуту. Не пугайся.
  • Температура (temperature). Для учёбы ставь 0.1 или даже 0. Это снизит креативность, но повысит фактологическую точность.
  • Индексация больших библиотек. 100 учебников? Индексация может занять часы. Делай это один раз и сохраняй базу (persist_directory).
Ошибка Причина Решение
"Failed to load module..." при запуске llama-cpp-python Нет совместимых библиотек C++ (например, libstdc++). Установи build-essential на Ubuntu или Xcode Command Line Tools на macOS.
Медленный ответ (секунды) Модель работает на CPU или чанков слишком много.
  • Собери llama-cpp-python с CUDA.
  • Уменьши max_tokens в LlamaCpp.
  • В search_kwargs поставь {"k": 2} вместо 3.
Модель "галлюцинирует" Слабый промпт или чанки не релевантны. Ужесточи промпт (как в примере). Увеличь chunk_size до 1024.

А что дальше? Куда развивать систему

Базовая система работает. Но это только начало. Вот что можно добавить, когда освоишься:

  • Память диалога. Чтобы ассистент помнил, о чём вы говорили 5 минут назад. Реализуется через сохранение истории в промпт или отдельную базу. Есть отдельный гайд на эту тему.
  • Голосовой интерфейс. Говоришь вопрос – получаешь голосовой ответ. Пригодится, когда руки заняты. Смотри проект с n8n.
  • Автоматическое обновление базы. Кинул новый PDF в папку – система сама переиндексировала его. Реализуется через watchdog.
  • Агентские возможности. Чтобы ассистент не только отвечал, но и что-то делал – искал в интернете (осторожно!), считал на Python. Это уровень AI-монстра.

Самое главное – теперь у тебя есть фундамент. Система, которая принадлежит только тебе. Не зависит от аптайма OpenAI, не продаёт твои данные, и ты можешь ковыряться в её кишках сколько угодно. Это чувство свободы стоит тех трёх часов, которые ты потратишь на сборку.

Хочешь глубже понять, как работать с ИИ не как пользователь, а как инженер? Курс "Нейросети с нуля: ваш ИИ-ассистент для жизни и работы" даст системные знания – от основ ML до развёртывания таких систем, как наша.

Последний совет. Не пытайся сделать всё идеально с первого раза. Сначала добейся, чтобы система просто запустилась и ответила на вопрос по маленькому текстовому файлу. Потом добавляй PDF. Потом улучшай промпт. Итерации – ключ к тому, чтобы не забросить проект на полпути. Удачи.

Подписаться на канал