Creación de aplicaciones RAG sin OpenAI - Parte dos: Mixtral, Milvus y OctoAI
Este blog está escrito por Yujian Tang y Thierry Moreau.
La generación aumentada por recuperación (RAG) es el caso de uso más popular para los LLMs surgido en 2023. Aunque la mayoría de los ejemplos muestran cómo puedes crear RAG con los LLMs GPT de OpenAI, esta serie aborda cómo crear RAG sin OpenAI. Consulta Building RAG without OpenAI Part One aquí.
Este tutorial es la segunda entrega de esta serie, que cubre la creación de RAG con Milvus, Mixtral alojado a través de OctoAI y LangChain. En particular, en este tutorial compartimos solo la punta del iceberg de las numerosas ventajas de Mixtral: sus capacidades multilingües.
En este blog, cubriremos:
- Stack tecnológico RAG de código abierto MMO(L)
- Mixtral
- Milvus
- OctoAI
- LangChain
- La arquitectura de la app RAG
- Configura tus herramientas RAG
- Selecciona y carga tus datos
- Consulta tus datos con OctoAI y Mixtral
- Resumen
Encuentra el notebook en GitHub.
Stack tecnológico RAG de código abierto MMO(L)
Hay muchas formas de crear apps RAG, y en mi última encuesta de mercado encontré más de 50 herramientas diferentes en el stack de LLM. En este ejemplo, nos centramos en cuatro: Mixtral como el LLM, Milvus como la base de datos vectorial, OctoAI para servir el LLM y el modelo de embeddings, y LangChain como nuestro orquestador. Antes de profundizar en la arquitectura, aprendamos un poco sobre las herramientas involucradas.
Mixtral
Mixtral 8x7B, o simplemente “Mixtral” para abreviar, es el modelo más reciente lanzado por la startup francesa pionera en IA Mistral. Lanzado en diciembre de 2023 (con el artículo publicado en enero de 2024), representa una extensión significativa del modelo fundacional de lenguaje grande anterior, Mistral 7B. Mixtral es un modelo de lenguaje Sparse Mixture of Experts (SMoE) 8x7B con capacidades más sustanciales que el Mistral 7B original. Es más grande, utiliza 13B parámetros activos durante la inferencia de un total de 47B parámetros, y admite múltiples idiomas, código y una ventana de contexto de 32k. A principios de 2024, Mixtral es el modelo de código abierto con mayor puntuación en los rankings de LLM.
Milvus
El núcleo de la memoria de nuestra app RAG es la base de datos vectorial. Milvus es una base de datos vectorial altamente escalable orientada a aplicaciones empresariales. Su estructura inherente de sistema distribuido permite un escalado fluido a medida que te acercas a los verdaderos niveles de producción de vectores. Milvus también proporciona otras funciones empresariales como multitenencia, control de acceso basado en roles y alta disponibilidad.
OctoAI
OctoAI proporciona la infraestructura para ejecutar modelos LLM a escala de producción. OctoAI facilita a los desarrolladores de IA integrar los modelos alojados de OctoAI, que ofrecen una selección de potentes modelos de código abierto, incluidos Mixtral y ajustes finos de la comunidad como Nous Hermes. Aproximadamente 9 de cada 10 nuevos registros en OctoAI comienzan con Mixtral como su modelo LLM preferido, y hoy Mixtral en OctoAI genera miles de millones de tokens para clientes a diario. En este tutorial, sustituiremos GPT por el muy popular modelo Mixtral.
LangChain
LangChain es posiblemente el framework de apps LLM más popular del mercado. LangChain incluye integraciones con casi todas las herramientas que puedas imaginar. Aunque puedes usarlo de muchas maneras, en este ejemplo lo usamos para conectar todo. Cargamos Milvus y el endpoint de OctoAI a través de LangChain y luego lo usamos para “encadenar” todo.
La arquitectura de la app RAG
Cada aplicación RAG tiene cuatro componentes críticos: el LLM, la base de datos vectorial, el modelo de embeddings y el orquestador. Debajo de todo eso se encuentra la capa de infraestructura. En esta configuración, usamos Mixtral como LLM, Milvus como base de datos vectorial, GTE Large como modelo de embeddings, LangChain como orquestador y OctoAI como la capa de infraestructura que sirve GTE Large y Mixtral.
Configura tus herramientas RAG
Comencemos configurando nuestras herramientas RAG. En esta sección, configuramos nuestros endpoints de inferencia para el LLM y los embeddings, y ponemos en marcha nuestra base de datos vectorial. Empecemos instalando los requisitos previos. Necesitamos pymilvus y milvus para trabajar con Milvus. Necesitamos langchain, sentence-transformers y tiktoken para usar la funcionalidad de LangChain en este ejemplo. Por último, también necesitamos octoai-sdk para interactuar con las API de embeddings y completado de texto de OctoAI.
Usamos este primer bloque de código para cargar la mayoría de nuestras importaciones necesarias desde LangChain, incluidas las importaciones de Milvus y OctoAI. También cargamos el módulo LLMChain, que nos permite encadenar funciones, y el módulo PromptTemplate, que usamos para pasar prompts a nuestros LLM. También necesitamos cargar nuestras variables de entorno en memoria. Hay muchas formas de hacer esto, pero para este ejemplo usamos load_dotenv de la biblioteca python-dotenv.
# ! pip install pymilvus milvus langchain sentence-transformers tiktoken octoai-sdk python-dotenv
from langchain.chains import LLMChain
from langchain.prompts import PromptTemplate
from langchain_community.llms.octoai_endpoint import OctoAIEndpoint
from langchain_community.embeddings import OctoAIEmbeddings
from langchain_community.vectorstores import Milvus
from dotenv import load_dotenv
import os
load_dotenv()
# the line below is just to show that you need to have your OCTOAI_API_TOKEN
os.environ["OCTOAI_API_TOKEN"] = os.getenv("OCTOAI_API_TOKEN")
El siguiente paso es inicializar nuestro acceso al LLM. OctoAI nos permite acceder fácilmente a modelos alojados y personalizarlos para nuestro caso de uso. Usamos OctoAIEndpoint de LangChain y pasamos el endpoint (mostrado en el código) para acceder a un modelo. También pasamos los parámetros requeridos del LLM. Para este ejemplo, son el nombre del modelo, el número máximo de tokens (cuánto mide el prompt de salida), así como otros parámetros para indicar al modelo qué necesitas que haga (por ejemplo, el prompt del sistema) y qué tan creativo debe ser con la salida (“presence_penalty”, “temperature”, “top_p”). Para los embeddings, simplemente pasamos la URL del endpoint de OctoAI. De forma predeterminada, usará GTE Large. Con el tiempo, se añadirán más modelos de embeddings a OctoAI.
llm = OctoAIEndpoint(
endpoint_url="https://text.octoai.run/v1/chat/completions",
model_kwargs={
"model": "mixtral-8x7b-instruct-fp16",
"max_tokens": 128,
"presence_penalty": 0,
"temperature": 0.01,
"top_p": 0.9,
"messages": [
{
"role": "system",
"content": "You are a helpful assistant. Keep your responses limited to one short paragraph if possible.",
},
],
},
)
embeddings = OctoAIEmbeddings(endpoint_url="https://text.octoai.run/v1/embeddings")
La última pieza de esto es la base de datos vectorial. Usamos Milvus Lite como nuestra base de datos vectorial. Importa default_server desde Milvus y luego llama a la función start() para iniciar el servidor.
from milvus import default_server
default_server.start()
Selecciona y carga tus datos
Con todo configurado, es hora de cargar nuestros datos. Para este ejemplo, puedes encontrar los datos en el Repositorio de GitHub. Si quieres obtener los datos, simplemente se extrajeron de Wikipedia. Una vez que tenemos nuestros datos, es hora de cargarlos en una base de datos vectorial. Usamos LangChain y Milvus para esta tarea.
Las dos importaciones que necesitamos de LangChain para cargar estos documentos son un divisor de texto - CharacterTextSplitter en este caso - y Document. Ahora, podemos cargar todo el directorio de datos como una lista de “Documents”, una abstracción de LangChain. Para este ejemplo, estamos cargando un directorio titulado “data”. También creamos una lista vacía para contener nuestra lista de Documents.
A continuación, recorremos cada uno de los archivos del directorio. Leemos el archivo y usamos un divisor de texto para dividir el texto en fragmentos. Para este ejemplo, usamos un tamaño de fragmento de 512 y una superposición de fragmentos de 64. Estos se eligieron simplemente porque suelen tener sentido por lo general. Siéntete libre de ajustarlos como quieras para ver cómo pueden diferir los resultados.
Una vez que dividimos el texto en fragmentos, lo almacenamos como un documento con algunos metadatos. Los metadatos que conservamos con el texto son el título del documento y el número de fragmento para permitirnos saber dónde está el fragmento en el documento.
from langchain.text_splitter import CharacterTextSplitter
from langchain.schema import Document
files = os.listdir("./data")
file_texts = []
for file in files:
with open(f"./data/{file}") as f:
file_text = f.read()
text_splitter = CharacterTextSplitter.from_tiktoken_encoder(
chunk_size=512, chunk_overlap=64,
)
texts = text_splitter.split_text(file_text)
for i, chunked_text in enumerate(texts):
file_texts.append(Document(page_content=chunked_text,
metadata={"doc_title": file.split(".")[0], "chunk_num": i}))
Con los Documents listos, es hora de insertarlos en la base de datos vectorial. Usamos la integración de Milvus de LangChain y llamamos a la función from_documents. Pasa los documentos, el modelo de embedding, los argumentos de conexión para la instancia de Milvus Lite y un nombre para las colecciones. Simplemente llamamos al método as_retriever() para colocar un LLM encima y empezar a trabajar con Milvus como nuestra base de datos vectorial para este ejemplo básico de RAG.
vector_store = Milvus.from_documents(
file_texts,
embedding=embeddings,
connection_args={"host": "localhost", "port": default_server.listen_port},
collection_name="cities"
)
retriever = vector_store.as_retriever()
Consulta tus datos con OctoAI y Mixtral
Todo está listo. Ahora podemos usar LangChain para orquestar la parte de recuperación de nuestro rompecabezas. Empezamos dándole a LangChain una plantilla. Debemos pasar dos variables a la plantilla de prompt: el contexto que recuperamos y la pregunta que queremos hacer. Podemos tratar la cadena de plantilla como una f-string y luego pasarla a la función from_template de PromptTemplate.
Después de la plantilla, configuramos la parte de la “chain”. Necesitamos el módulo RunnablePassthrough para pasar el contexto y el StrOutputParser para analizar la salida. Luego, configuramos la cadena. Primero, le indicamos a LangChain de dónde obtener el contexto y la pregunta, luego los canalizamos al prompt, que se canaliza al LLM y finalmente al analizador de salida de cadena para analizar. Para hacer una pregunta, simplemente hacemos invoke de la cadena.
template = """Answer the question based only on the following context:
{context}
Question: {question}
"""
prompt = PromptTemplate.from_template(template)
from langchain_core.runnables import RunnablePassthrough
from langchain_core.output_parsers import StrOutputParser
chain = (
{"context": retriever, "question": RunnablePassthrough()}
| prompt
| llm
| StrOutputParser()
)
chain.invoke("How big is the city of Seattle?")
Para el ejemplo de “Qué tan grande es la ciudad de Seattle”, podemos ver un resultado esperado a continuación. La respuesta es precisa y está respaldada por la información de Wikipedia que hemos almacenado en la base de datos vectorial.
Aprovechar las capacidades multilingües de Mixtral a través de OctoAI
Como estamos usando Mixtral, aprovechemos algunas capacidades únicas, como la experiencia multilingüe. Lo bueno de usar OctoAI es que podemos aprovechar el mismo endpoint con una instrucción diferente para hacer cosas distintas. En este caso, le diremos al modelo que responda en francés y no en inglés.
# Let's make this a bit more fun and showcase the multilingual capabilities of Mixtal which really outshine other open source models
# Our Vector DB is populated with entries from english text - even the embedding model we're using here, GTE-Large
# works best on english text. However Mixtral has good mutlilingual capabilities in French, German, Spanish and Italian.
# So what we'll do is ask the assistant to only answer in french in the system and user prompt. RAG here is performed based on
# english text, but upon producing the user response, the Mixtral LLM will generate tokens in a different language here (french)
llm = OctoAIEndpoint(
endpoint_url="https://text.octoai.run/v1/chat/completions",
model_kwargs={
"model": "mixtral-8x7b-instruct-fp16",
"max_tokens": 128,
"presence_penalty": 0,
"temperature": 0.1,
"top_p": 0.9,
"messages": [
{
"role": "system",
"content": "You are a helpful assistant who responds in french and not in english.",
},
],
},
)
También usamos una plantilla ligeramente modificada para pedirle a Mixtral que responda en francés. El prompt tiene que rehacerse a partir de la nueva plantilla, y la cadena tiene que rehacerse a partir del nuevo prompt, la plantilla y el LLM. La cadena se puede invocar de la misma manera. Hacemos la misma pregunta en inglés y esperamos una respuesta en francés.
template = """Answer the question in french based only on the following context:
{context}
Question: {question}
"""
prompt = PromptTemplate.from_template(template)
chain = (
{"context": retriever, "question": RunnablePassthrough()}
| prompt
| llm
| StrOutputParser()
)
chain.invoke("How big is the city of Seattle?")
La respuesta debería verse más o menos como la imagen de abajo.
Resumen
En este tutorial exploramos otra forma de crear RAG sin OpenAI. En la parte 1, usamos Symbl.AI como LLM; en este, usamos Mixtral de Mistral alojado por OctoAI. Las otras piezas del marco RAG que usamos son Milvus como base de datos vectorial, LangChain como orquestador y GTE-Large, también alojado por OctoAI, como modelo de embeddings.
Configuramos nuestras herramientas RAG y cargamos algo de Wikipedia como datos de ejemplo. Luego, usamos LangChain para leer los datos en Milvus y superponer “le big model” encima. Al final, también nos tomamos el tiempo de explorar una de las capacidades únicas de Mixtral: la capacidad de trabajar en varios idiomas. Para este ejemplo, usamos francés. ¡La próxima vez, exploraremos también algunos otros idiomas!
Sigue leyendo

What Is a Vector Lakebase?
A Vector Lakebase is a unified, lake-native data architecture for AI that combines vector-database-grade serving with open lake storage, reusable lake-level indexes, and a shared semantic layer.

Zilliz Cloud Just Landed in Claude Code
The Zilliz Cloud Plugin brings the full power of Zilliz Cloud directly into your Claude Code terminal as natural-language conversations.

Zilliz Cloud Now Available in Azure North Europe: Bringing AI-Powered Vector Search Closer to European Customers
The addition of the Azure North Europe (Ireland) region further expands our global footprint to better serve our European customers.



