Saltar al contenido principal

DeepSeek R1 RAG Chatbot con Chroma, Ollama y Gradio

Aprende a construir un chatbot RAG local utilizando DeepSeek-R1 con Ollama, LangChain y Chroma.
Actualizado 11 feb 2025  · 12 min de lectura

La generación aumentada por recuperación (GAR) ha surgido como un potente enfoque para crear aplicaciones de IA que generen respuestas precisas, fundamentadas y contextualmente relevantes, recuperando y sintetizando conocimientos de fuentes externas.

En este tutorial, explicaré paso a paso cómo construir un chatbot basado en RAG utilizando DeepSeek-R1 y un libro sobre los fundamentos de los LLM como base de conocimientos. Al final de este tutorial, serás capaz de crear una aplicación RAG local capaz de responder a las preguntas del libro e interactuar con los usuarios a través de una interfaz Gradio.

¿Por qué utilizar DeepSeek-R1 con RAG?

DeepSeek-R1 es ideal para los sistemas basados en RAG gracias a su rendimiento optimizado, sus funciones avanzadas de búsqueda vectorial y su flexibilidad en distintos entornos, desde configuraciones locales hasta despliegues escalables. He aquí algunas razones por las que es eficaz:

  1. Recuperación de alto rendimiento: DeepSeek-R1 maneja grandes colecciones de documentos con baja latencia.
  2. Clasificación fina de la relevancia: Garantiza una recuperación precisa de los pasajes mediante el cálculo de la similitud semántica.
  3. Coste y ventajas para la privacidad: Puedes ejecutar DeepSeek-R1 localmente para evitar las tasas de la API y mantener seguros los datos confidenciales.
  4. Fácil integración: Se integra fácilmente con bases de datos vectoriales como Chroma.
  5. Capacidades offline: Con DeepSeek-R1 puedes construir sistemas de recuperación que funcionen incluso sin acceso a Internet, una vez descargado el modelo.

Resumen: Construir un Chatbot RAG con DeepSeek-R1

Nuestro proyecto de demostración se centra en construir un chatbot RAG utilizando DeepSeek-R1 y Gradio.

Canalización del Chatbot RAG

El proceso comienza con la carga y división de un PDF en trozos de texto, seguido de la generación de incrustaciones para esos trozos. Estas incrustaciones se almacenan en una base de datos Chroma para una recuperación eficaz. Cuando un usuario envía una consulta, el sistema recupera los trozos de texto más relevantes y utiliza DeepSeek-R1 para generar una respuesta basada en el contexto recuperado.

Paso 1: Requisitos previos

Antes de empezar, asegurémonos de que tenemos instaladas las siguientes herramientas y bibliotecas:

  • Python 3.8+
  • Langchain
  • Chromadb
  • Gradio

Ejecuta los siguientes comandos para instalar las dependencias necesarias:

!pip install langchain chromadb gradio ollama pymypdf
!pip install -U langchain-community

Una vez instaladas las dependencias anteriores, ejecuta los siguientes comandos de importación:

import ollama
import re
import gradio as gr
from concurrent.futures import ThreadPoolExecutor
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_community.document_loaders import PyMuPDFLoader
from langchain_community.embeddings import OllamaEmbeddings
from chromadb.config import Settings
from chromadb import Client
from langchain.vectorstores import Chroma

Paso 2: Cargar el PDF con PyMuPDFLoader

Utilizaremos PyMuPDFLoader de LangChain para extraer el texto de la versión PDF del libro Fundamentos de los LLM de Tong Xiao y Jingbo Zhu: se trata de un libro con muchas matemáticas, lo que significa que nuestro chatbot debería ser capaz de explicar bien las matemáticas que hay detrás de los LLM. Puedes encontrar el libro en arXiv

# Load the document using PyMuPDFLoader
loader = PyMuPDFLoader("/path/to/Foundations_of_llms.pdf")

documents = loader.load()

Una vez cargado el documento, podemos empezar a dividir el texto en trozos para su posterior procesamiento.

Paso 3: Divide el documento en partes más pequeñas

Dividiremos el texto extraído en trozos más pequeños y superpuestos para una mejor recuperación del contexto. Puedes variar el tamaño del trozo y el solapamiento del trozo según tu sistema dentro de la función RecursiveCharacterTextSpilitter().

# Split the document into smaller chunks
text_splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=200)

chunks = text_splitter.split_documents(documents)

Ahora tenemos los trozos de texto extraídos, listos para convertirlos en incrustaciones.

Paso 4: Generar incrustaciones con DeepSeek-R1

Utilizaremos Ollama Embeddings basado en DeepSeek-R1 para generar las incrustaciones de documentos. Dependiendo del tamaño del documento, la generación de incrustaciones puede llevar tiempo, por lo que es preferible paralelizarla para un procesamiento más rápido.

Nota: model="deepseek-r1" considera por defecto el modelo de parámetros 7B. Puedes cambiarlo según sea necesario a 8B, 14B, 32B, 70B o 671B. Sustituye la X del nombre del modelo siguiente por el tamaño del modelo: model="deepseek-r1:X"

# Initialize Ollama embeddings using DeepSeek-R1
embedding_function = OllamaEmbeddings(model="deepseek-r1")
# Parallelize embedding generation
def generate_embedding(chunk):
    return embedding_function.embed_query(chunk.page_content)
with ThreadPoolExecutor() as executor:
    embeddings = list(executor.map(generate_embedding, chunks))

La función anterior inicializa DeepSeek-R1 a través de Ollama para generar incrustaciones semánticas de alta dimensión, que luego se utilizarán para la recuperación de documentos basada en similitudes.

La función generate_embedding() toma el texto de un fragmento de documento y genera su incrustación. Por último, ThreadPoolExecutor() aplica generate_embedding() a cada trozo de forma concurrente, recopilando las incrustaciones en una lista para un procesamiento más rápido en comparación con la ejecución secuencial.

Paso 5: Almacenar incrustaciones en el almacén de vectores cromáticos

Almacenaremos las incrustaciones y los trozos de texto correspondientes en una base de datos vectorial de alto rendimiento, Chroma.

# Initialize Chroma client and create/reset the collection
client = Client(Settings())
client.delete_collection(name="foundations_of_llms")  # Delete existing collection (if any)
collection = client.create_collection(name="foundations_of_llms")
# Add documents and embeddings to Chroma
for idx, chunk in enumerate(chunks):
    collection.add(
        documents=[chunk.page_content], 
        metadatas=[{'id': idx}], 
        embeddings=[embeddings[idx]], 
        ids=[str(idx)]  # Ensure IDs are strings
    )

Empezamos siguiendo estos pasos para almacenar las incrustaciones:

1. Inicializa el cliente Chroma y restablece la colección:

  • La página Client(Settings()) inicializa el cliente Chroma para gestionar el almacén de vectores. 
  • Elimina cualquier colección existente similar al nombre de tu colección utilizando client.delete_collection() para evitar errores. Por último, utiliza client.create_collection() para crear una nueva colección que almacene los trozos de documentos y sus incrustaciones.

2. Iterar a través de trozos de documentos:

  • Recorre cada fragmento de documento y su correspondiente incrustación utilizando su ID de cadena único.

3. Añade trozos e incrustaciones a Chroma:

  • Para cada trozo, collection.add() almacena:
    • El contenido del documento (chunk.page_content)
    • Metadatos ({'id': idx}) para hacer referencia al fragmento
    • Su correspondiente vector de incrustación para la recuperación
    • A único ID para identificar la entrada

Esta configuración garantiza que cada trozo de documento se indexe correctamente para una recuperación eficaz basada en vectores.

Paso 6: Inicializar el Recuperador

Inicializaremos el recuperador Chroma, asegurándonos de que utiliza las mismas incrustaciones DeepSeek-R1 para las consultas.

# Initialize retriever using Ollama embeddings for queries
retriever = Chroma(collection_name="foundations_of_llms", client=client, embedding_function=embedding_function).as_retriever()

El recuperador Chroma se conecta a la colección "foundations_of_llms" y utiliza incrustaciones DeepSeek-R1 a través de Ollama para incrustar las consultas de los usuarios. Recupera los trozos de documentos más relevantes basándose en la similitud vectorial para obtener respuestas conscientes del contexto.

Paso 7: Definir la tubería RAG

A continuación, recuperaremos los trozos de texto más relevantes y les daremos formato para que DeepSeek-R1 genere respuestas.

def retrieve_context(question):
    # Retrieve relevant documents
    results = retriever.invoke(question)
    # Combine the retrieved content
    context = "\n\n".join([doc.page_content for doc in results])
    return context

La función retrieve_context incorpora la consulta del usuario mediante DeepSeek-R1 y recupera los trozos de documentos más relevantes mediante el recuperador Chroma. A continuación, combina el contenido de los trozos recuperados en una única cadena de contexto para su posterior procesamiento.

Paso 8: Consulta DeepSeek-R1 para obtener respuestas contextuales

Ahora, tenemos la pregunta y el contexto recuperado. A continuación, envíalo a DeepSeek-R1 a través de Ollama para obtener nuestra respuesta final.

def query_deepseek(question, context):
    # Format the input prompt
    formatted_prompt = f"Question: {question}\n\nContext: {context}"
    # Query DeepSeek-R1 using Ollama
    response = embedding_function.chat(
        model="deepseek-r1",
        messages=[{'role': 'user', 'content': formatted_prompt}]
    )
    # Clean and return the response
    response_content = response['message']['content']
    final_answer = re.sub(r'<think>.*?</think>', '', response_content, flags=re.DOTALL).strip()
    return final_answer

Para obtener la respuesta final, empezamos combinando la pregunta del usuario y el contexto recuperado en una pregunta estructurada. A continuación, envía esta consulta al modelo DeepSeek-R1 a través de Ollama para recibir una respuesta. Para que el resultado final sea presentable, eliminamos las etiquetas innecesarias y devolvemos la respuesta final.

Paso 9: Construye la interfaz de Gradio

Ya tenemos en marcha nuestra tubería RAG. Ahora, utilizaremos Gradio para crear una interfaz interactiva para que los usuarios formulen preguntas relacionadas con su base de conocimientos (Fundamentos de los LLM en este caso).

def ask_question(question):
    # Retrieve context and generate an answer using RAG
    context = retrieve_context(question)
    answer = query_deepseek(question, context)
    return answer
# Set up the Gradio interface
interface = gr.Interface(
    fn=ask_question,
    inputs="text",
    outputs="text",
    title="RAG Chatbot: Foundations of LLMs",
    description="Ask any question about the Foundations of LLMs book. Powered by DeepSeek-R1."
)
interface.launch()

La función ask_question() recupera el contexto relevante utilizando el recuperador Chroma y genera la respuesta final mediante DeepSeek-R1. La interfaz de Gradio, construida con gr.Interface(), permite a los usuarios hacer preguntas de forma interactiva y recibir respuestas contextualmente precisas y fundamentadas.

¡Enhorabuena! Ahora tienes un chatbot que funciona localmente, listo para hablar de cualquier cosa relacionada con los LLM.

Aplicación RAG con DeekSeek-R1 y Gradio

Optimizaciones

La demostración anterior cubre una implementación muy básica de la GAR, que puede optimizarse aún más para aumentar su eficacia. Aquí tienes algunas cosas que puedes probar:

  • Ajuste del tamaño de los trozos: Ajusta los parámetros chunk_size y chunk_overlap para equilibrar el rendimiento y la calidad de la recuperación.
  • Versiones de modelos más pequeños: Si DeepSeek-R1 consume demasiados recursos, puedes utilizar versiones diferentes (deepseek-r1:7b o deepseek-r1:8b o deepseek-r1:14b) a través de Ollama.
  • Escala utilizando Faiss: Para documentos de mayor tamaño, considera la posibilidad de integrar Faiss para una recuperación más rápida.
  • Procesamiento por lotes: Si la generación de incrustaciones es lenta, agrupa los trozos por lotes para mejorar la eficacia.

Conclusión

En este tutorial, construimos un chatbot local basado en RAG utilizando DeepSeek-R1 y Chroma para la recuperación, lo que garantiza respuestas precisas y ricas en contexto a preguntas basadas en una gran base de conocimientos.

Para saber más sobre DeepSeek, te recomiendo estos blogs:


Aashi Dutt's photo
Author
Aashi Dutt
LinkedIn
Twitter

Soy una Google Developers Expert en ML(Gen AI), una Kaggle 3x Expert y una Women Techmakers Ambassador con más de 3 años de experiencia en tecnología. Cofundé una startup de tecnología sanitaria en 2020 y estoy cursando un máster en informática en Georgia Tech, especializándome en aprendizaje automático.

Temas

Aprende IA con estos cursos

curso

Retrieval Augmented Generation (RAG) with LangChain

3 hr
2.9K
Learn cutting-edge methods for integrating external data with LLMs using Retrieval Augmented Generation (RAG) with LangChain.
Ver detallesRight Arrow
Comienza el curso
Ver másRight Arrow
Relacionado

tutorial

RAG Con Llama 3.1 8B, Ollama y Langchain: Tutorial

Aprende a crear una aplicación RAG con Llama 3.1 8B utilizando Ollama y Langchain, configurando el entorno, procesando documentos, creando incrustaciones e integrando un recuperador.
Ryan Ong's photo

Ryan Ong

12 min

tutorial

Tutorial de DeepSeek-Coder-V2: Ejemplos, instalación, puntos de referencia

DeepSeek-Coder-V2 es un modelo de lenguaje de código de código abierto que rivaliza con el rendimiento de GPT-4, Gemini 1.5 Pro, Claude 3 Opus, Llama 3 70B o Codestral.
Dimitri Didmanidze's photo

Dimitri Didmanidze

8 min

tutorial

Tutorial sobre cómo crear aplicaciones LLM con LangChain

Explore el potencial sin explotar de los grandes modelos lingüísticos con LangChain, un marco Python de código abierto para crear aplicaciones avanzadas de IA.
Moez Ali's photo

Moez Ali

12 min

tutorial

Construir agentes LangChain para automatizar tareas en Python

Un tutorial completo sobre la construcción de agentes LangChain multiherramienta para automatizar tareas en Python utilizando LLMs y modelos de chat utilizando OpenAI.
Bex Tuychiev's photo

Bex Tuychiev

14 min

tutorial

Cómo utilizar ChatGPT Code Interpreter

Todo lo que necesitas saber sobre ChatGPT Code Interpreter de OpenAI
Adel Nehme's photo

Adel Nehme

9 min

tutorial

Ajuste fino de LLaMA 2: Guía paso a paso para personalizar el modelo de lenguaje grande

Aprende a ajustar Llama-2 en Colab utilizando nuevas técnicas para superar las limitaciones de memoria y computación y hacer más accesibles los grandes modelos lingüísticos de código abierto.
Abid Ali Awan's photo

Abid Ali Awan

12 min

Ver másVer más