Creare app RAG senza OpenAI - Parte due: Mixtral, Milvus e OctoAI
Questo blog è scritto da Yujian Tang e Thierry Moreau.
Retrieval Augmented Generation (RAG) è il caso d’uso più popolare per gli LLM emerso nel 2023. Sebbene la maggior parte degli esempi mostri come sia possibile creare RAG con gli LLM GPT di OpenAI, questa serie tratta come creare RAG senza OpenAI. Vedi Creare RAG senza OpenAI Parte Uno qui.
Questo tutorial è il secondo capitolo di questa serie, che tratta la creazione di RAG con Milvus, Mixtral ospitato tramite OctoAI e LangChain. In particolare, in questo tutorial condividiamo solo la punta dell’iceberg dei numerosi vantaggi di Mixtral: le sue capacità multilingue.
In questo blog tratteremo:
- Stack tecnologico RAG open source MMO(L)
- Mixtral
- Milvus
- OctoAI
- LangChain
- L’architettura dell’app RAG
- Configura i tuoi strumenti RAG
- Seleziona e carica i tuoi dati
- Interroga i tuoi dati con OctoAI e Mixtral
- Riepilogo
Trova il notebook su GitHub.
Stack tecnologico RAG open source MMO(L)
Ci sono molti modi per creare app RAG e, nel mio ultimo sondaggio di mercato, ho trovato oltre 50 strumenti diversi nello stack LLM. In questo esempio ci concentriamo su quattro: Mixtral come LLM, Milvus come database vettoriale, OctoAI per servire l’LLM e il modello di embedding, e LangChain come nostro orchestratore. Prima di addentrarci nell’architettura, scopriamo qualcosa sugli strumenti coinvolti.
Mixtral
Mixtral 8x7B, o semplicemente “Mixtral” in breve, è l’ultimo modello rilasciato dalla pionieristica startup francese di AI Mistral. Lanciato a dicembre 2023 (con il paper uscito a gennaio 2024), rappresenta una significativa estensione del precedente modello linguistico di grandi dimensioni fondamentale, Mistral 7B. Mixtral è un modello linguistico Sparse Mixture of Experts (SMoE) 8x7B con capacità più sostanziali rispetto all’originale Mistral 7B. È più grande, utilizza 13B parametri attivi durante l’inferenza su 47B parametri, e supporta più lingue, codice e una finestra di contesto da 32k. All’inizio del 2024, Mixtral è il modello open-source con il punteggio più alto nelle classifiche LLM.
Milvus
Il fulcro della memoria della nostra app RAG è il database vettoriale. Milvus è un database vettoriale altamente scalabile pensato per applicazioni enterprise. La sua struttura intrinsecamente distribuita consente una scalabilità senza soluzione di continuità man mano che ci si avvicina ai veri livelli di produzione dei vettori. Milvus offre anche altre funzionalità enterprise come multi-tenancy, controllo degli accessi basato sui ruoli e alta disponibilità.
OctoAI
OctoAI fornisce l’infrastruttura per eseguire modelli LLM su scala di produzione. OctoAI rende semplice per gli sviluppatori AI integrare i modelli ospitati da OctoAI, che offrono una selezione di potenti modelli open-source, tra cui Mixtral e finetune della community come Nous Hermes. Circa 9 nuove registrazioni a OctoAI su 10 iniziano con Mixtral come modello LLM di scelta, e oggi Mixtral su OctoAI genera ogni giorno miliardi di token per i clienti. In questo tutorial sostituiremo GPT con il popolarissimo modello Mixtral.
LangChain
LangChain è probabilmente il framework per app LLM più popolare sul mercato. LangChain include integrazioni con quasi tutti gli strumenti che puoi immaginare. Sebbene sia possibile usarlo in molti modi, in questo esempio lo usiamo per collegare tutto. Carichiamo Milvus e l’endpoint OctoAI tramite LangChain e poi lo usiamo per “concatenare” tutto insieme.
L’architettura dell’app RAG
Ogni app RAG ha quattro componenti critici: l’LLM, il database vettoriale, il modello di embedding e l’orchestratore. Sotto tutto questo si trova il livello di infrastruttura. In questa configurazione, stiamo usando Mixtral come LLM, Milvus come database vettoriale, GTE Large come modello di embedding, LangChain come orchestratore e OctoAI come livello di infrastruttura che serve GTE Large e Mixtral.
Configura i tuoi strumenti RAG
Iniziamo configurando i nostri strumenti RAG. In questa sezione, configuriamo i nostri endpoint di inferenza per l’LLM e gli embedding e avviamo il nostro database vettoriale. Cominciamo installando i prerequisiti. Abbiamo bisogno di pymilvus e milvus per lavorare con Milvus. Abbiamo bisogno di langchain, sentence-transformers e tiktoken per utilizzare la funzionalità di LangChain per questo esempio. Infine, abbiamo anche bisogno di octoai-sdk per interfacciarci con le API di embedding e completamento del testo di OctoAI.
Usiamo questo primo blocco di codice per caricare la maggior parte delle importazioni necessarie da LangChain, incluse le importazioni di Milvus e OctoAI. Carichiamo anche il modulo LLMChain, che ci permette di concatenare insieme le funzioni, e il modulo PromptTemplate, che usiamo per passare i prompt ai nostri LLM. Dobbiamo anche caricare le nostre variabili d’ambiente in memoria. Ci sono molti modi per farlo, ma per questo esempio usiamo load_dotenv dalla libreria 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")
Il passo successivo è inizializzare il nostro accesso all’LLM. OctoAI ci permette di accedere facilmente a modelli ospitati e di personalizzarli per il nostro caso d’uso. Usiamo OctoAIEndpoint da LangChain e passiamo l’endpoint (mostrato nel codice) per accedere a un modello. Passiamo anche i parametri LLM richiesti. Per questo esempio, sono il nome del modello, il numero massimo di token (quanto è lungo il prompt di output), così come altri parametri per dire al modello cosa deve fare (ad es., prompt di sistema) e quanto deve essere creativo con l’output (“presence_penalty”, “temperature”, “top_p”). Per gli embedding, passiamo semplicemente l’URL dell’endpoint OctoAI. Per impostazione predefinita, userà GTE Large. Nel tempo, altri modelli di embedding verranno aggiunti 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")
L’ultimo elemento di questo è il database vettoriale. Usiamo Milvus Lite come nostro database vettoriale. Importa default_server da Milvus, quindi chiama la funzione start() per avviare il server.
from milvus import default_server
default_server.start()
Seleziona e carica i tuoi dati
Con tutto configurato, è il momento di caricare i nostri dati. Per questo esempio, puoi trovare i dati nel GitHub Repo. Se vuoi ottenere i dati, sono semplicemente estratti da Wikipedia. Una volta che abbiamo i nostri dati, è il momento di caricarli in un database vettoriale. Usiamo LangChain e Milvus per questo compito.
I due import di cui abbiamo bisogno da LangChain per caricare questi documenti sono uno splitter di testo - CharacterTextSplitter in questo caso - e Document. Ora possiamo caricare l’intera directory dei dati come un elenco di “Documenti”, un’astrazione di LangChain. Per questo esempio, stiamo caricando una directory chiamata “data”. Creiamo anche una lista vuota per contenere il nostro elenco di Documenti.
Successivamente, iteriamo su ciascuno dei file nella directory. Leggiamo il file e usiamo uno splitter di testo per suddividere il testo in chunk. Per questo esempio, usiamo una dimensione del chunk di 512 e una sovrapposizione dei chunk di 64. Questi valori sono stati scelti semplicemente perché di solito tendono ad avere senso. Sentiti libero di modificarli come preferisci per vedere come i risultati potrebbero differire.
Una volta suddiviso il testo in chunk, lo memorizziamo come documento con alcuni metadati. I metadati che conserviamo con il testo sono il titolo del documento e il numero del chunk, per farci sapere dove si trova il chunk nel 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 i Documenti pronti, è il momento di inserirli nel database vettoriale. Usiamo l’integrazione Milvus di LangChain e chiamiamo la funzione from_documents. Passiamo i documenti, il modello di embedding, gli argomenti di connessione per l’istanza Milvus Lite e un nome per le collection. Chiamiamo semplicemente il metodo as_retriever() per mettere un LLM sopra e iniziare a lavorare con Milvus come nostro database vettoriale per questo esempio base di 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()
Interroga i tuoi dati con OctoAI e Mixtral
Tutto è pronto. Ora possiamo usare LangChain per orchestrare la parte di retrieval del nostro puzzle. Iniziamo dando a LangChain un template. Dobbiamo passare due variabili al template del prompt: il contesto che recuperiamo e la domanda che vogliamo porre. Possiamo trattare la stringa del template come una f-string e poi passarla alla funzione from_template di PromptTemplate.
Dopo il template, impostiamo la parte della “chain”. Abbiamo bisogno del modulo RunnablePassthrough per passare il contesto e dello StrOutputParser per analizzare l’output. Poi, impostiamo la chain. Per prima cosa, diciamo a LangChain dove ottenere il contesto e la domanda, poi li passiamo tramite pipe al prompt, che viene passato tramite pipe all’LLM e infine al parser dell’output stringa per l’analisi. Per fare una domanda, semplicemente invoke la chain.
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?")
Per l’esempio di “Quanto è grande la città di Seattle”, possiamo vedere di seguito un output atteso. La risposta è accurata e supportata dalle informazioni di Wikipedia che abbiamo archiviato nel database vettoriale.
Sfruttare le capacità multilingue di Mixtral tramite OctoAI
Poiché stiamo usando Mixtral, sfruttiamo alcune capacità uniche, come la competenza multilingue. L’aspetto positivo dell’uso di OctoAI è che possiamo sfruttare lo stesso endpoint con un’istruzione diversa per fare cose diverse. In questo caso, diremo al modello di rispondere in francese e non in inglese.
# 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.",
},
],
},
)
Usiamo anche un template leggermente modificato per chiedere a Mixtral di rispondere in francese. Il prompt deve essere ricreato dal nuovo template, e la chain deve essere ricreata dal nuovo prompt, dal template e dall’LLM. La chain può essere invocata nello stesso modo. Poniamo la stessa domanda in inglese e ci aspettiamo una risposta in francese.
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 risposta dovrebbe assomigliare all’immagine qui sotto.
Riepilogo
In questo tutorial abbiamo esplorato un altro modo per creare RAG senza OpenAI. Nella parte 1, abbiamo usato Symbl.AI come LLM; in questo, abbiamo usato Mixtral di Mistral ospitato da OctoAI. Gli altri componenti del framework RAG che abbiamo usato sono Milvus come database vettoriale, LangChain come orchestratore e GTE-Large, anch’esso ospitato da OctoAI, come modello di embedding.
Abbiamo configurato i nostri strumenti RAG e caricato alcuni contenuti di Wikipedia come dati di esempio. Poi usiamo LangChain per leggere i dati in Milvus e stratificare “le big model” sopra. Alla fine, ci siamo anche presi il tempo di esplorare una delle capacità uniche di Mixtral: la capacità di lavorare in più lingue. Per questo esempio, abbiamo usato il francese. La prossima volta esploreremo anche alcune altre lingue!
Continua a leggere

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.

Context Engineering Strategies for AI Agents: A Developer’s Guide
Learn practical context engineering strategies for AI agents. Explore frameworks, tools, and techniques to improve reliability, efficiency, and cost.

Announcing VDBBench 1.0: Open-Source VectorDB Benchmarking with Your Real-World Production Workloads
Discover VDBBench 1.0, an open-source tool for benchmarking vector databases with real-world production data, streaming ingestion, and concurrent workloads.



