Geração Aumentada por Recuperação (RAG) com Milvus e LlamaIndex
Este guia demonstra como construir um sistema de Geração Aumentada por Recuperação (RAG) utilizando o LlamaIndex e o Milvus.
O sistema RAG combina um sistema de recuperação com um modelo generativo para gerar novo texto com base num determinado pedido. O sistema começa por recuperar documentos relevantes de um corpus utilizando o Milvus e, em seguida, utiliza um modelo generativo para gerar novo texto com base nos documentos recuperados.
O LlamaIndex é uma estrutura de dados simples e flexível para ligar fontes de dados personalizadas a modelos de linguagem de grande dimensão (LLM). Milvus é a base de dados vetorial de código aberto mais avançada do mundo, criada para alimentar a pesquisa de semelhanças de incorporação e as aplicações de IA.
Neste bloco de notas, vamos apresentar uma demonstração rápida da utilização do MilvusVectorStore.
Antes de começar
Instalar dependências
Os snippets de código nesta página requerem as dependências pymilvus e llamaindex. Você pode instalá-las usando os seguintes comandos:
$ pip install pymilvus>=2.4.2 milvus-lite
$ pip install llama-index-vector-stores-milvus
$ pip install llama-index
Se estiver a utilizar o Google Colab, para ativar as dependências acabadas de instalar, poderá ter de reiniciar o tempo de execução. (Clique no menu "Runtime" (Tempo de execução) na parte superior do ecrã e selecione "Restart session" (Reiniciar sessão) no menu pendente).
Configurar o OpenAI
Vamos começar por adicionar a chave openai api. Isto permitir-nos-á aceder ao chatgpt.
import openai
openai.api_key = "sk-***********"
Preparar dados
Pode descarregar dados de amostra com os seguintes comandos:
! mkdir -p 'data/'
! wget 'https://raw.githubusercontent.com/run-llama/llama_index/main/docs/docs/examples/data/paul_graham/paul_graham_essay.txt' -O 'data/paul_graham_essay.txt'
! wget 'https://raw.githubusercontent.com/run-llama/llama_index/main/docs/docs/examples/data/10k/uber_2021.pdf' -O 'data/uber_2021.pdf'
Introdução
Gerar os nossos dados
Como primeiro exemplo, vamos gerar um documento a partir do ficheiro paul_graham_essay.txt. Trata-se de um único ensaio de Paul Graham, intitulado What I Worked On. Para gerar os documentos, usaremos o SimpleDirectoryReader.
from llama_index.core import SimpleDirectoryReader
# load documents
documents = SimpleDirectoryReader(
input_files=["./data/paul_graham_essay.txt"]
).load_data()
print("Document ID:", documents[0].doc_id)
Document ID: 95f25e4d-f270-4650-87ce-006d69d82033
Criar um índice nos dados
Agora que temos um documento, podemos criar um índice e inserir o documento. Para o índice, vamos utilizar um MilvusVectorStore. O MilvusVectorStore recebe alguns argumentos:
argumentos básicos
uri (str, optional): O URI a que se pretende ligar, vem sob a forma de "https://address:port" para o serviço Milvus ou Zilliz Cloud, ou "path/to/local/milvus.db" para o Milvus local. A predefinição é "./milvus_llamaindex.db".token (str, optional): O token para iniciar sessão. Vazio se não estiver a usar o rbac, se estiver a usar o rbac será provavelmente "username:password".collection_name (str, optional): O nome da coleção onde os dados serão armazenados. A predefinição é "llamalection".overwrite (bool, optional): Se deve substituir a coleção existente com o mesmo nome. A predefinição é Falso.
campos escalares incluindo id do documento e texto
doc_id_field (str, optional): O nome do campo doc_id para a coleção. A predefinição é DEFAULT_DOC_ID_KEY.text_key (str, optional): O texto da chave que é armazenado na coleção transmitida. Utilizado quando se traz a sua própria coleção. A predefinição é DEFAULT_TEXT_KEY.scalar_field_names (list, optional): Os nomes dos campos escalares extra a serem incluídos no esquema da coleção.scalar_field_types (list, optional): Os tipos dos campos escalares extra.
campo denso
enable_dense (bool): Um sinalizador booleano para ativar ou desativar a incorporação densa. A predefinição é True.dim (int, optional): A dimensão dos vectores de incorporação para a coleção. Necessário ao criar uma nova coleção com enable_sparse em False.embedding_field (str, optional): O nome do campo de incorporação denso para a coleção; a predefinição é DEFAULT_EMBEDDING_KEY.index_config (dict, optional): A configuração usada para criar o índice de incorporação densa. A predefinição é Nenhum.search_config (dict, optional): A configuração utilizada para pesquisar o índice denso Milvus. Note que isto tem de ser compatível com o tipo de índice especificado porindex_config. A predefinição é Nenhum.similarity_metric (str, optional): A métrica de similaridade a utilizar para a incorporação densa, atualmente suporta IP, COSINE e L2.
campo esparso
enable_sparse (bool): Um sinalizador booleano para ativar ou desativar a incorporação esparsa. O padrão é False.sparse_embedding_field (str): O nome do campo de incorporação esparsa, com a predefinição DEFAULT_SPARSE_EMBEDDING_KEY.sparse_embedding_function (Union[BaseSparseEmbeddingFunction, BaseMilvusBuiltInFunction], optional): Se enable_sparse for True, este objeto deve ser fornecido para converter o texto numa incorporação esparsa. Se None, será utilizada a função de incorporação esparsa predefinida (BGEM3SparseEmbeddingFunction).sparse_index_config (dict, optional): A configuração utilizada para construir o índice de incorporação esparso. A predefinição é Nenhum.
classificador híbrido
hybrid_ranker (str): Especifica o tipo de classificador usado em consultas de pesquisa híbridas. Atualmente apenas suporta ["RRFRanker", "WeightedRanker"]. A predefinição é "RRFRanker".hybrid_ranker_params (dict, optional): Parâmetros de configuração para o classificador híbrido. A estrutura deste dicionário depende do classificador específico que está a ser utilizado:- Para "RRFRanker", deve incluir:
- "k" (int): Um parâmetro utilizado no Reciprocal Rank Fusion (RRF). Este valor é utilizado para calcular as pontuações de classificação como parte do algoritmo RRF, que combina várias estratégias de classificação numa única pontuação para melhorar a relevância da pesquisa.
- Para "WeightedRanker", espera-se:
- "weights" (lista de float): Uma lista de exatamente dois pesos:
- O peso para o componente de incorporação densa.
- Estes pesos são utilizados para ajustar a importância dos componentes densos e esparsos das incorporações no processo de recuperação híbrido. A predefinição é um dicionário vazio, o que implica que o classificador irá funcionar com as suas definições predefinidas.
- "weights" (lista de float): Uma lista de exatamente dois pesos:
- Para "RRFRanker", deve incluir:
outros
collection_properties (dict, optional): As propriedades da coleção, como TTL (Time-To-Live) e MMAP (mapeamento de memória). A predefinição é Nenhum. Pode incluir:- "collection.ttl.seconds" (int): Quando esta propriedade é definida, os dados na coleção atual expiram no tempo especificado. Os dados expirados na coleção serão limpos e não serão envolvidos em pesquisas ou consultas.
- "mmap.enabled" (bool): Se deve ser ativado o armazenamento mapeado na memória ao nível da coleção.
index_management (IndexManagement): Especifica a estratégia de gestão de índices a utilizar. O padrão é "create_if_not_exists".batch_size (int): Configura o número de documentos processados num lote aquando da inserção de dados no Milvus. A predefinição é DEFAULT_BATCH_SIZE.consistency_level (str, optional): Nível de consistência a utilizar para uma coleção recém-criada. A predefinição é "Session".
# Create an index over the documents
from llama_index.core import VectorStoreIndex, StorageContext
from llama_index.vector_stores.milvus import MilvusVectorStore
vector_store = MilvusVectorStore(uri="./milvus_demo.db", dim=1536, overwrite=True)
storage_context = StorageContext.from_defaults(vector_store=vector_store)
index = VectorStoreIndex.from_documents(documents, storage_context=storage_context)
Para os parâmetros de MilvusVectorStore:
- Definir o
uricomo um ficheiro local, por exemplo,./milvus.db, é o método mais conveniente, uma vez que utiliza automaticamente o Milvus Lite para armazenar todos os dados neste ficheiro. - Se tiver uma grande escala de dados, pode configurar um servidor Milvus mais eficiente em docker ou kubernetes. Nesta configuração, utilize o uri do servidor, por exemplo,
http://localhost:19530, como o seuuri. - Se pretender utilizar o Zilliz Cloud, o serviço de nuvem totalmente gerido para o Milvus, ajuste
urietoken, que correspondem ao Public Endpoint e à chave Api no Zilliz Cloud.
Consultar os dados
Agora que temos o nosso documento armazenado no índice, podemos fazer perguntas ao índice. O índice utilizará os dados armazenados nele próprio como base de conhecimento para o chatgpt.
query_engine = index.as_query_engine()
res = query_engine.query("What did the author learn?")
print(res)
The author learned that philosophy courses in college were boring to him, leading him to switch his focus to studying AI.
res = query_engine.query("What challenges did the disease pose for the author?")
print(res)
The disease posed challenges for the author as it affected his mother's health, leading to a stroke caused by colon cancer. This resulted in her losing her balance and needing to be placed in a nursing home. The author and his sister were determined to help their mother get out of the nursing home and back to her house.
Este próximo teste mostra que a substituição remove os dados anteriores.
from llama_index.core import Document
vector_store = MilvusVectorStore(uri="./milvus_demo.db", dim=1536, overwrite=True)
storage_context = StorageContext.from_defaults(vector_store=vector_store)
index = VectorStoreIndex.from_documents(
[Document(text="The number that is being searched for is ten.")],
storage_context,
)
query_engine = index.as_query_engine()
res = query_engine.query("Who is the author?")
print(res)
The author is the individual who created the context information.
O teste seguinte mostra a adição de dados adicionais a um índice já existente.
del index, vector_store, storage_context, query_engine
vector_store = MilvusVectorStore(uri="./milvus_demo.db", overwrite=False)
storage_context = StorageContext.from_defaults(vector_store=vector_store)
index = VectorStoreIndex.from_documents(documents, storage_context=storage_context)
query_engine = index.as_query_engine()
res = query_engine.query("What is the number?")
print(res)
The number is ten.
res = query_engine.query("Who is the author?")
print(res)
Paul Graham
Filtragem de metadados
Podemos gerar resultados através da filtragem de fontes específicas. O exemplo seguinte ilustra o carregamento de todos os documentos do diretório e a sua posterior filtragem com base nos metadados.
from llama_index.core.vector_stores import ExactMatchFilter, MetadataFilters
# Load all the two documents loaded before
documents_all = SimpleDirectoryReader("./data/").load_data()
vector_store = MilvusVectorStore(uri="./milvus_demo.db", dim=1536, overwrite=True)
storage_context = StorageContext.from_defaults(vector_store=vector_store)
index = VectorStoreIndex.from_documents(documents_all, storage_context)
Queremos recuperar apenas documentos do ficheiro uber_2021.pdf.
filters = MetadataFilters(
filters=[ExactMatchFilter(key="file_name", value="uber_2021.pdf")]
)
query_engine = index.as_query_engine(filters=filters)
res = query_engine.query("What challenges did the disease pose for the author?")
print(res)
The disease posed challenges related to the adverse impact on the business and operations, including reduced demand for Mobility offerings globally, affecting travel behavior and demand. Additionally, the pandemic led to driver supply constraints, impacted by concerns regarding COVID-19, with uncertainties about when supply levels would return to normal. The rise of the Omicron variant further affected travel, resulting in advisories and restrictions that could adversely impact both driver supply and consumer demand for Mobility offerings.
Desta vez, obtemos um resultado diferente quando recuperamos do ficheiro paul_graham_essay.txt.
filters = MetadataFilters(
filters=[ExactMatchFilter(key="file_name", value="paul_graham_essay.txt")]
)
query_engine = index.as_query_engine(filters=filters)
res = query_engine.query("What challenges did the disease pose for the author?")
print(res)
The disease posed challenges for the author as it affected his mother's health, leading to a stroke caused by colon cancer. This resulted in his mother losing her balance and needing to be placed in a nursing home. The author and his sister were determined to help their mother get out of the nursing home and back to her house.