
С ростом популярности модели deepseek-r1:1.5b опасения по поводу конфиденциальности облачных решений становятся как никогда актуальными. Этот проект делает еще один шаг вперед, демонстрируя, как построить продвинутую систему RAG локально, используя DeepSeek, LangChain и Streamlit. Используя мощные возможности DeepSeek, эта система гарантирует, что ваши личные данные останутся на вашем компьютере, обеспечивая повышенную конфиденциальность и контроль.
Чатбот предоставляет ответы с учетом контекста, включая содержимое документов и историю разговора, а возможность показать или скрыть обоснование ответов моделей DeepSeek добавляет уровень прозрачности, делая работу в целом более безопасной и надежной. Локальный запуск DeepSeek с помощью Ollama не только позволяет обойти проблемы облачного хранения данных, но и позволяет обеспечить более безопасное и конфиденциальное взаимодействие с пользователем.
Давайте рассмотрим, как создать этот безопасный, контекстно-ориентированный и ориентированный на конфиденциальность RAG-чатбот на вашем компьютере.
Github Repo: Полный код этого проекта доступен на GitHub. Читайте далее, чтобы разобраться в реализации проекта шаг за шагом.
Необходимые условия
- Python 3.10 или новее 
Установите Python с сайта python.org. Для совместимости со всеми библиотеками рекомендуется использовать Python 3.10 или новее.
- Необходимые библиотеки 
Установите необходимые библиотеки, используя команду:
pip install -r requirements.txt  
- Для работы DeepSeek требуется установить Ollama 
Инструкция по установке для Ollama и deepseek-r1:1.5b
- Скачайте Ollama 
Перейдите на сайт Ollama, чтобы загрузить ее на свой компьютер.
- Распакуйте Zip-файл 
Распакуйте загруженный zip-файл и найдите исполняемый файл Ollama.
- Запустите Ollama 
После установки Ollama появится на панели задач, что указывает на то, что она работает через HTTP-порт 11434. Зайдите на сайт http://localhost:11434/, чтобы убедиться, что она работает.
- Запустите модель deepseek-r1:1.5b 
Откройте командную строку и выполните команду:
ollama run deepseek-r1:1.5b  
Описание кода
В этом разделе мы разберем код нашего чатбота RAG на основе PDF-файлов с использованием DeepSeek и Ollama, интегрированного со Streamlit для создания удобного пользовательского интерфейса. Давайте рассмотрим функциональность шаг за шагом:
1. Импорт и начальная настройка
import streamlit as st
from langchain.document_loaders import PyPDFLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain.vectorstores import FAISS
from langchain.embeddings import HuggingFaceEmbeddings
from langchain_ollama import OllamaLLM
import os
import re
from langchain.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain_core.runnables.history import RunnableWithMessageHistory
from langchain_community.chat_message_histories import ChatMessageHistory
from langchain.chains import create_retrieval_chain
from langchain.chains.combine_documents import create_stuff_documents_chain
from langchain_core.chat_history import BaseChatMessageHistory
from langchain.chains import create_history_aware_retrieverНачнем с импорта необходимых библиотек. Ключевыми из них являются streamlit для пользовательского интерфейса, langchain для загрузки, разделения и встраивания документов, а также OllamaLLM для использования локально запускаемой модели DeepSeek.
Инициализация состояния сессии
# Initialize session state for chat history
if "messages" not in st.session_state:
    st.session_state.messages = []Здесь мы инициализируем session_state для хранения истории чата, чтобы обеспечить сохранение диалога между взаимодействиями пользователей.
2. Обработка PDF
def process_pdf(pdf_path):
    st.info("Processing PDF... ⏳")
    loader = PyPDFLoader(pdf_path)
    docs = loader.load()
    text_splitter = RecursiveCharacterTextSplitter(chunk_size=512, chunk_overlap=256)
    split_docs = text_splitter.split_documents(docs)
    embedding_model = HuggingFaceEmbeddings(model_name="sentence-transformers/all-MiniLM-L6-v2")
    vectorstore = FAISS.from_documents(split_docs, embedding_model)
    return vectorstoreФункция process_pdf выполняет основную задачу по обработке загруженного PDF-файла:
- Загрузка PDF. PyPDFLoader считывает PDF-файл. 
- Разбивка текста. Текст разбивается на более мелкие фрагменты с помощью RecursiveCharacterTextSplitter для эффективной обработки. 
- Встраивание и хранение векторов. Мы используем модель HuggingFaceEmbeddings для встраивания фрагментов текста и хранения их в индексе FAISS, что позволяет быстро находить их при запросах. 
3. Создание цепочки разговоров
def get_conversation_chain(retriever):
    llm = OllamaLLM(model=llm_model)
    contextualize_q_system_prompt = (
        "Given the chat history and the latest user question, "
        "provide a response that directly addresses the user's query based on the provided documents. "
        "Do not rephrase the question or ask follow-up questions."
    )
    contextualize_q_prompt = ChatPromptTemplate.from_messages(
        [
            ("system", contextualize_q_system_prompt),
            MessagesPlaceholder("chat_history"),
            ("human", "{input}"),
        ]
    )
    history_aware_retriever = create_history_aware_retriever(
        llm, retriever, contextualize_q_prompt
    )
    system_prompt = (
        "As a personal chat assistant, provide accurate and relevant information based on the provided documents. "
        "Limit answers to 2-3 sentences and 50 words max. "
        "Do not form standalone questions, suggest selections, or ask further questions."
        "\n\nContext:\n{context}"
    )
    qa_prompt = ChatPromptTemplate.from_messages(
        [
            ("system", system_prompt),
            MessagesPlaceholder("chat_history"),
            ("human", "{input}"),
        ]
    )
    question_answer_chain = create_stuff_documents_chain(llm, qa_prompt)
    rag_chain = create_retrieval_chain(history_aware_retriever, question_answer_chain)
    store = {}
    def get_session_history(session_id: str) -> BaseChatMessageHistory:
        if session_id not in store:
            store[session_id] = ChatMessageHistory()
        return store[session_id]
    conversational_rag_chain = RunnableWithMessageHistory(
        rag_chain,
        get_session_history,
        input_messages_key="input",
        history_messages_key="chat_history",
        output_messages_key="answer",
    )
    print("Conversational chain created ✅")
    return conversational_rag_chainЭта функция создает цепочку генерации, дополненную извлечением (RAG), с возможностями учета истории:
- Извлечение контекста. - History_aware_retrieverгарантирует, что чатбот учтет всю историю разговора для получения контекста.
- Системные запросы. - contextualize_q_system_promptи- system_promptпредоставляют чатботу инструкции для кратких и релевантных ответов.
- Управление историей. - get_session_historyпомогает отслеживать историю чата, чтобы ответы были контекстными на основе прошлых сообщений.
Обработка тегов рассуждения
def extract_think_content(text):
    match = re.search(r'<think>(.*?)</think>', text, flags=re.DOTALL)
    return match.group(1).strip() if match else ""Эта функция извлекает рассуждения, содержащиеся в тегах <think>  в выходных данных модели. Это гарантирует, что рассуждения чатбота могут быть показаны при желании.
Приведенная выше функция удаляет из окончательного ответа все рассуждения внутри тегов <think>, оставляя только очищенный ответ.
4. Пользовательский интерфейс Streamlit
st.set_page_config(page_title="Chat with Documents", layout="wide")
st.title("? Chat with Your PDF using RAG ?")
st.sidebar.header("Upload Your PDF")
uploaded_file = st.sidebar.file_uploader("Choose a PDF file", type=["pdf"])
if uploaded_file:
    pdf_path = os.path.join("uploads", uploaded_file.name)
    os.makedirs("uploads", exist_ok=True)
    
    with open(pdf_path, "wb") as f:
        f.write(uploaded_file.getbuffer())
    vectorstore = process_pdf(pdf_path)
    retriever = vectorstore.as_retriever()
    st.session_state.chatbot = get_conversation_chain(retriever)
    st.success("PDF uploaded and processed! ✅ Start asking questions below.")Интерфейс Streamlit позволяет пользователям загружать PDF-файлы, обрабатывать их и создавать векторное хранилище для запросов. После загрузки файла он обрабатывается, сохраняется локально в папке uploads, и чатбот готов к взаимодействию.
5. Интерфейс чата и обработка ответов
show_think_content = st.sidebar.checkbox("Show Reasoning")
st.subheader("Chat with the PDF ?")
if st.session_state.messages:
    for message in st.session_state.messages:
        with st.chat_message(message["role"]):
            st.write(message["content"])
if prompt := st.chat_input("Ask a question about your document"):
    st.session_state.messages.append({"role": "user", "content": prompt})
    try:
        response = st.session_state.chatbot.invoke(
            {"input": prompt, "chat_history": st.session_state.messages},
            {"configurable": {"session_id": "user_session"}} 
        )
        answer = response["answer"]
        think_content = extract_think_content(answer)
        answer = remove_think_content(answer)
        if "I could not find relevant information" in answer:
            st.warning("⚠️ No relevant information found in the uploaded PDF.")
    except Exception as e:
        answer = f"An error occurred: {e}"
        think_content = ""
    st.session_state.messages.append({"role": "assistant", "content": answer})
    with st.chat_message("user"):
        st.write(prompt)
    with st.chat_message("assistant"):
        st.write(answer)
        if show_think_content and think_content:
            with st.expander("? Show/Hide Reasoning"):
                st.write(think_content)Интерфейс чата позволяет пользователям взаимодействовать с чатботом:
- Отображение сообщений. Предыдущие сообщения отображаются с помощью - st.chat_message.
- Ввод вопросов. Пользователи могут вводить вопросы, которые добавляются в историю сессии. 
- Генерация ответа. Чатбот обрабатывает запрос и отвечает на него в соответствии с системой RAG. Он также отображает аргументацию, если пользователь желает просмотреть ее, если функция активирована в чекбоксе. 

В этой статье я рассказал о том, как создать локально размещаемый чатбот Retrieval-Augmented Generation (RAG) с помощью модели DeepSeek deepseek-r1:1.5b, LangChain и Streamlit. Используя передовые возможности DeepSeek, можно гарантировать, что ваши личные данные останутся на вашем компьютере, что повышает уровень конфиденциальности и безопасности. Чатбот отвечает с учетом контекста, предоставляя пользователям релевантную информацию на основе загруженного PDF-файла. Кроме того, возможность показать или скрыть «мысли» модели позволяет сделать процесс работы более прозрачным и настраиваемым.
Модель deepseek-r1:1.5b от компании DeepSeek в последнее время привлекает к себе большое внимание. Благодаря открытому исходному коду и расширенным возможностям рассуждений она стала сильным соперником в сфере ИИ, являясь альтернативой другим моделям, таким как модели GPT от OpenAI. Возможность запускать эту модель локально еще больше повышает ее привлекательность, предоставляя пользователям больший контроль над собственными данными и действиями.
Это приложение является прекрасным началом для создания более сложных систем взаимодействия с документами и может быть легко доработано для поддержки более широкого спектра сценариев использования, от бизнес-отчетов до научных работ. Интегрировав модель DeepSeek deepseek-r1:1.5b в свой локальный чатбот RAG, вы сможете создать сложную систему разговорного ИИ, которая будет уделять первостепенное внимание конфиденциальности пользователей и выдавать точные ответы с учетом контекста.
Друзья, буду рад, если вы подпишетесь на мой телеграм-канал про нейросети, чтобы не пропускать анонсы статей, и про генерацию изображений - я стараюсь делиться только полезной информацией.
 
           
 
BelerafonL
Было бы неплохо показать на основе практических примеров, чем чат-бот по системе RAG лучше, чем просто поставить окно контекста побольше, скопировать в окно чата туда текст документа и в конце спросить желаемое. У deepseek-r1 можно окно контекста ставить сотни тысяч токенов, хватает для большинства практических применений. Непонятно для чего написанное выше всё делать... Лучше будут ответы, экономия памяти, быстрее будет работать?