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

Announcing the General Availability of Single Sign-On (SSO) on Zilliz Cloud
SSO is GA on Zilliz Cloud, delivering the enterprise-grade identity management capabilities your teams need to deploy vectorDB with confidence.

Democratizing AI: Making Vector Search Powerful and Affordable
Zilliz democratizes AI vector search with Milvus 2.6 and Zilliz Cloud for powerful, affordable scalability, cutting costs in infrastructure, operations, and development.

Why Deepseek is Waking up AI Giants Like OpenAI And Why You Should Care
Discover how DeepSeek R1's open-source AI model with superior reasoning capabilities and lower costs is disrupting the AI landscape and challenging tech giants like OpenAI.
