Construir um RAG multilingue com Milvus, LangChain e OpenAI LLM
Durante os últimos dois anos, a Retrieval Augmented Generation (RAG) tornou-se rapidamente numa das técnicas mais populares para a criação de aplicações GenAI baseadas em modelos de linguagem de grande dimensão (LLMs). Na sua essência, o RAG melhora os resultados de um LLM fornecendo informações contextuais sobre as quais o modelo não foi pré-treinado. O RAG multilingue é um RAG alargado que lida com dados de texto em várias línguas.
Yujian Tang, CEO da OSS4AI, falou recentemente num Unstructured Data Meetup organizado por Zilliz. Discutiu o RAG e os seus componentes fundamentais e demonstrou como construir um RAG multilingue para responder a diversos desafios linguísticos do mundo real.
Neste post, vamos recapitular as principais ideias da apresentação de Yujian e guiá-lo na implementação de um RAG multilingue. Se quiser saber mais sobre a palestra de Yujian, recomendamos que veja a sua apresentação no YouTube.
O que é o RAG e como funciona?
Uma das principais limitações das aplicações baseadas em LLM é a sua dependência dos dados em que foram treinadas. Se o LLM não foi exposto a certas informações ou a todo um domínio de conhecimento durante o pré-treinamento, ele não entenderia as relações lingüísticas necessárias para gerar respostas precisas. Esta falta de dados pode levar o LLM a admitir que não sabe a resposta ou, pior ainda, a "alucinar" e fornecer informações incorrectas.
O RAG é uma técnica popular que aborda os problemas de alucinação dos LLMs, fornecendo-lhes informações contextuais adicionais. Também permite que os programadores e as empresas acedam aos seus dados privados ou proprietários sem se preocuparem com questões de segurança.
Figura 1- Como funciona o RAG](https://assets.zilliz.com/Figure_1_How_RAG_works_246044aacf.png)
O RAG começa com um modelo de incorporação que transforma os dados de texto em vetor embeddings, representações numéricas que captam o significado semântico do texto. O sistema RAG armazena então estes vectores numa base de dados vetorial como Milvus ou Zilliz Cloud, que os indexa para pesquisa de similaridadeh.
Quando um utilizador submete uma consulta, o modelo de incorporação também converte a entrada num vetor. Em seguida, o sistema RAG compara a semelhança deste vetor de consulta com os vectores da base de dados vetorial, calculando a sua distância no espaço vetorial de alta dimensão. Se forem encontrados dados relevantes, o sistema RAG recupera esta informação e adiciona-a à consulta original para formar um novo pedido para o LLM. O LLM utiliza esta informação extra para gerar uma resposta mais exacta e contextualmente relevante, ultrapassando o que poderia produzir apenas com base nos seus dados de treino.
O que é o Multilingual RAG?
O RAG multilingue expande as capacidades do RAG tradicional para suportar vários idiomas. Integra um modelo de incorporação treinado em várias línguas, permitindo que o sistema processe e gere respostas em diferentes línguas. Utilizando esta abordagem multilingue, o sistema RAG pode tratar consultas em qualquer língua, recuperar informações relevantes independentemente da sua língua original e fornecer respostas precisas e contextualmente relevantes na língua preferida do utilizador.
Como criar uma aplicação RAG multilingue: um guia passo-a-passo
Agora que aprendemos os principais conceitos e componentes do RAG, vamos implementar uma aplicação RAG multilingue passo a passo.
Esta aplicação de exemplo contém duas partes: um web scraper e a aplicação principal.
O web scraper** extrai o conjunto de dados necessário da Internet.
A aplicação principal** cria embeddings vectoriais, efectua a pesquisa de semelhanças vectoriais e gera as respostas.
O raspador da Web
Primeiro, vamos recolher dados da Wikipedia e usá-los como informação contextual para este exemplo RAG.
Definir os títulos: Começamos definindo uma lista chamada
wiki_titles, que contém uma lista de cidades. Cada cidade representa um ficheiro de texto que o web scraper irá preencher com o conteúdo da sua entrada correspondente na Wikipedia. Por exemplo, "Atlanta.txt" conterá texto extraído da página Atlanta na Wikipedia.Scrapear os dados: Nós iteramos sobre cada cidade em
wiki_titles, fazemos uma requisição GET para a API da Wikipedia, e extraímos o conteúdo da página da resposta JSON. O texto é então salvo em um arquivo de texto correspondente para cada cidade.
from pathlib import Path
importar pedidos
títulos_do_wiki = [
"Atlanta",
"Berlim",
"Boston",
"Cairo",
"Chicago",
"Copenhaga",
"Houston",
"Karachi",
"Lisboa",
"Londres",
"Moscovo",
"Munique",
"Paris",
"Pékin", # francês para Pequim
"São Francisco",
"Seattle",
"Shanghai",
"Tóquio",
"Toronto",
]
caminho_dos_dados = Caminho("./dados_da_cidade_francesa")
data_path.mkdir(exist_ok=True) # Assegurar que o diretório existe
for title in wiki_titles:
response = requests.get(
"https://fr.wikipedia.org/w/api.php",
params={
"ação": "query",
"formato": "json",
"titles": título,
"prop": "extractos",
"explaintext": True,
},
).json()
página = seguinte(iter(resposta["consulta"]["páginas"].valores()))
wiki_text = page.get("extract", "") # Utilize .get() para evitar KeyError
if wiki_text: # Verificar se o extrato não está vazio
with open(data_path / f"{title}.txt", "w") as fp:
fp.write(wiki_text)
senão:
print(f "Não foi encontrado nenhum extrato para {title}")
Preparando seu ambiente
Primeiro, configure o seu ambiente de desenvolvimento instalando as bibliotecas necessárias: a Milvus vetor database, LangChain, OpenAI, e sentence transformers.
Além disso, terá de incluir a sua chave API se estiver a ligar-se a um LLM através de uma API, como o OpenAI. Esta chave pode ser armazenada num ficheiro .env separado e acedida utilizando load_dotenv() e os .
Aqui está o código para instalar as bibliotecas e carregar sua chave de API:
!pip install -qU pymilvus langchain sentence-transformers tiktoken openai
from dotenv import load_dotenv
importar os
load_dotenv() # Carrega as variáveis de ambiente do ficheiro .env
os.environ["OPENAI_API_KEY"] = os.getenv("OPENAI_API_KEY") # Define a chave da API
Instalar Bibliotecas: Utilize o
pippara instalar opymilvus,langchain,sentence-transformers,tiktokeneopenai.Carregar Variáveis de Ambiente: Utilize
dotenvpara carregar variáveis de ambiente a partir de um arquivo.env.Set API Key: Recupera a chave da API OpenAI das variáveis de ambiente e a define.
Certifique-se de que seu arquivo .env contém sua chave de API OpenAI no seguinte formato:
OPENAI_API_KEY=sua_chave_da_api_aqui
Inicializando o LLM
Com o ambiente configurado, o próximo passo é definir o LLM que será usado na sua aplicação. No trecho de código abaixo, conseguimos isso importando a biblioteca OpenAI e definindo o LLM com o construtor do OpenAI.
from langchain.llms import OpenAI
llm = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
Você pode optar por um LLM de código aberto para evitar os custos associados às chamadas de API do OpenAI. O Hugging Face oferece centenas de milhares de modelos de aprendizado profundo com os quais você pode fazer experiências. Para utilizar um LLM de código aberto da HuggingFace, terá de importar a biblioteca Transformers.
from transformers import pipeline
llm = pipeline('text-generation', model='gpt2') # Substitua 'gpt2' pelo modelo desejado
É possível alternar entre o OpenAI e um modelo de código aberto modificando o código de importação e inicialização relevante.
Escolhendo um modelo de incorporação apropriado
Ao construir um sistema RAG multilingue, [a nossa escolha de modelo de incorporação] (https://zilliz.com/blog/choosing-the-right-embedding-model-for-your-data) é tão importante como a nossa escolha de LLM, uma vez que o modelo de incorporação deve ser compatível com a língua com que se está a trabalhar. Para este exemplo, em que a língua é o francês, as incorporações HuggingFace predefinidas são suficientes. No entanto, é necessário identificar e utilizar o modelo de incorporação mais adequado para outras línguas.
O [MTEB Leaderboard] (https://huggingface.co/spaces/mteb/leaderboard) no HuggingFace é um recurso valioso para encontrar modelos de incorporação. Esta tabela de classificação lista os modelos de incorporação com melhor desempenho para várias línguas, como as incorporações em chinês e polaco identificadas por Yujian. Ao selecionar um modelo de incorporação, é necessário especificar o nome do modelo como parâmetro.
Eis como configurar o modelo de incorporação:
from langchain_community.embeddings import HuggingFaceEmbeddings
embeddings = HuggingFaceEmbeddings()
# Em alternativa, para os embeddings chineses, o modelo é passado como parâmetro, por exemplo,
# HuggingFaceEmbeddings(nome_do_modelo="TownsWu/PEG")
Carregando e dividindo os dados em pedaços
Em seguida, carregamos os arquivos de dados da cidade que extraímos da Wikipédia e os dividimos em segmentos, ou pedaços. Ao dividir o texto em pedaços, evitamos comparar uma consulta com o documento inteiro, o que aumenta a eficiência da recuperação de informações. Quanto mais pequeno for o pedaço, como determinado pelo parâmetro chunk_size, maior será a precisão, mas mais operações de recuperação serão necessárias. Quanto maior for a sobreposição entre os pedaços, conforme definido pelo parâmetro chunk_overlap, menor será a probabilidade de perda de contexto - ao custo de uma maior redundância.
from langchain.text_splitter import CharacterTextSplitter
from langchain.schema import Document
ficheiros = os.listdir("./french_city_data")
textos_do_ficheiro = []
for file in files:
with open(f"./french_city_data/{file}") as f:
texto_do_ficheiro = 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}))
Carregando documentos no Milvus
Depois de dividir os arquivos de dados da cidade e armazená-los como uma lista de documentos, precisamos carregá-los em um armazenamento vetorial - neste caso, o banco de dados vetorial do Milvus. O código abaixo trata do carregamento inicial e das atualizações quando os dados da cidade são armazenados no Milvus.
from langchain_community.vectorstores import Milvus
# Para a primeira execução
vector_store = Milvus.from_documents(
textos_do_ficheiro,
embedding=embeddings,
connection_args={"host": "localhost", "port": 19530},
nome_da_colecção="cidades_francesas"
)
# se os seus dados já estiverem armazenados em Milvus
vector_store = Milvus(
embedding_function=embeddings,
connection_args={"host": "localhost", "port": 19530},
nome_da_colecção="cidades francesas"
)
Criar um Retriever
Em seguida, vamos inicializar nosso retriever, uma interface que retorna documentos de uma fonte específica com base em uma determinada consulta. O código abaixo usa o armazenamento de vetores que criamos na última etapa como um recuperador e o atribui a uma variável.
recuperador = vector_store.as_retriever()
Criar modelo de prompt usando LangChain
Os modelos de prompt permitem-lhe formatar com precisão a sua entrada para um LLM dentro da sua aplicação. São especialmente úteis para casos em que se queira reutilizar o mesmo esquema de prompt, mas com pequenos ajustes - como na nossa aplicação RAG multilingue, em que podemos usar o mesmo modelo de prompt para várias línguas.
Os modelos de mensagens também permitem construir uma mensagem a partir de uma entrada dinâmica, por exemplo, a entrada do utilizador ou dados obtidos de um armazenamento de vectores. Na nossa aplicação, incluiremos dinamicamente a pergunta, que será passada diretamente para a cadeia, e o contexto que o recuperador obteve do armazenamento de vectores.
from langchain.prompts import ChatPromptTemplate
template="""
É um assistente para tarefas de resposta a perguntas. Utiliza as seguintes peças de contexto recuperadas para responder à pergunta. Se não souberes a resposta, basta dizeres que não sabes.
Utiliza no máximo três frases e mantém a resposta concisa.
Responda em francês.
Pergunta: {question}
Contexto: {context}
Resposta:"""
prompt = ChatPromptTemplate.from_template(template)
Encadeando os componentes juntos para criar o aplicativo RAG
O encadeamento é um processo que conecta componentes para criar aplicações de IA de ponta a ponta, que é um dos principais recursos do LangChain.
O código abaixo demonstra como construir uma cadeia que inclui os seguintes elementos: o contexto do recuperador, o prompt de entrada processado pela função runnablepassthrough(), o modelo de prompt, o LLM e o stroutparser(), que produz a resposta da invocação da cadeia.
from langchain_core.runnables import RunnablePassthrough
from langchain_core.output_parsers import StrOutputParser
chain = (
{"context": retriever, "question": RunnablePassthrough()}
| prompt
| llm
| StrOutputParser()
)
Fazendo consultas com a cadeia
Uma vez que a cadeia é construída, você pode invocá-la com várias consultas. Por exemplo:
response = chain.invoke("Diga-me um facto histórico sobre Karachi.")
Esta consulta produz a seguinte resposta em francês!
"Karachi foi mencionada pela primeira vez no livro Histoire des plantes de Théophraste au IIIe siècle av. J.-C. Elle a été occupée par les Britanniques au début du XIXe siècle et est devenue la capitale du Sind en 1839. Em 1876, o futuro fundador do Paquistão, Muhammad Ali Jinnah, nasceu e entrou em Karachi."
Para demonstrar as capacidades multilingues, aqui está outra pergunta - desta vez em francês:
resposta_2 = chain.invoke("Racontez-moi un fait historique sur Karachi.")
Apesar da mesma pergunta subjacente, as diferentes línguas (inglês e francês) conduzem a diferentes embeddings, resultando num output distinto:
"Karachi est une ville qui a été fondée par les Britanniques au début du XIXe siècle et qui est devenue la capitale du Sind. Elle a été un important centre économique et a connu une croissance rapide, notamment grâce à son port. Depuis les années 1980, la ville a été le théâtre de conflits ethniques et religieux, et en 2012, elle a été le site de l'incendie industriel le plus meurtrier de l'histoire."
Parabéns! Construiu com sucesso uma aplicação RAG multilingue. Tenha em mente que os embeddings são fundamentais para a forma como o LLM interpreta as línguas. Selecione o embedding mais adequado para suportar várias línguas e integre-o na sua aplicação.
Resumo
Uau! Este é um post bastante longo. Vamos recapitular alguns dos seus pontos-chave.
A Retrieval augmented generation (RAG) é uma estrutura que aumenta a saída do LLM inserindo dados adicionais nos avisos de entrada. O RAG pode resolver os problemas irritantes de alucinação do LLM.
O RAG multilingue é um RAG alargado que lida com documentos multilingues.
Os modelos de incorporação, as bases de dados vectoriais e os LLM são três componentes essenciais das aplicações RAG.
A principal consideração no desenvolvimento de aplicações RAG multilingues é a escolha do modelo de incorporação. A tabela de classificação MTEB da HuggingFace é um excelente recurso para encontrar o modelo certo para a sua aplicação.
Outros recursos
Modelos de incorporação de alto desempenho para seus aplicativos GenAI | Zilliz
Técnicas de melhoramento de RAG:
Melhorar os RAG com gráficos de conhecimento usando WhyHow](https://zilliz.com/blog/enhance-rag-with-knowledge-graphs)
Dicas e truques práticos para programadores que criam aplicações RAG
Desafios de infraestrutura no dimensionamento de RAG com modelos de IA personalizados](https://zilliz.com/blog/infrastructure-challenges-in-scaling-rag-with-custom-ai-models)
Continue lendo

Zilliz Cloud Audit Logs Goes GA: Security, Compliance, and Transparency at Scale
Zilliz Cloud Audit Logs are now GA, giving enterprises real-time visibility, compliance-ready trails, and stronger security across AWS, GCP, and Azure.

AI Integration in Video Surveillance Tools: Transforming the Industry with Vector Databases
Discover how AI and vector databases are revolutionizing video surveillance with real-time analysis, faster threat detection, and intelligent search capabilities for enhanced security.

DeepRAG: Thinking to Retrieval Step by Step for Large Language Models
Discover DeepRAG, an advanced retrieval-augmented generation (RAG) model that improves LLM accuracy by retrieving only essential data through step-by-step reasoning.



