Я создаю конвейер Retrieval Augmented Generation (RAG) с помощью LangChain и сталкиваюсь с проблемой, когда мой vectorstore, кажется, пересчитывается каждый раз, когда я передаю новый вопрос в конвейер. Это приводит к значительному снижению производительности, особенно при работе с большими наборами данных.
Вот моя настройка:
Я использую Chroma в качестве моего vectorstore, EnsembleRetirever в качестве моего извлекателя, UnstructuredPDFLoader() из langchain для извлечения текста вместе с метаданными документов. Я загружаю свои документы из файла/базы данных и разбиваю их на фрагменты с помощью текстового разделителя. Я встраиваю эти фрагменты с помощью OllamaEmbeddings. Я использую функцию цепочки извлечения для объединения извлекателя и цепочки вопросов и ответов. Проблема:
Когда я запускаю конвейер с разными вопросами, кажется, что vectorstore каждый раз создается заново, вместо того чтобы повторно использовать существующие вложения. Это вызывает значительное узкое место производительности. Например, ниже приведен вывод приложения streamlit, я тщательно пытаюсь засечь время той части, которая занимает больше всего времени в моем конвейере, и это ясно показывает, что он снова и снова выполняет переразбиение и пересчет vectorstore.
Мои вопросы:
Ожидаемое ли это поведение для конвейера RAG LangChain? Должен ли vectorstore пересчитываться для каждого вопроса? Если нет, как я могу гарантировать, что vectorstore вычисляется только один раз и повторно используется в нескольких запросах? Есть ли какие-либо рекомендации или конфигурации для LangChain, которые могут помочь оптимизировать создание и использование vectorstore в конвейере RAG?
Я попробовал установить retrieved_chunks и vectorstore вне функции, тем самым сделав их глобальными, чтобы они были доступны всем другим функциям в конвейере, но они все равно пересчитывают все фрагменты и vectorestore заново, делая мое приложение RAG медленнее, чем должно быть.
Вот мой code
streamlit run IMS_RAG_m2.py
You can now view your Streamlit app in your browser.
Local URL: http://localhost:8504
Network URL: http://10.54.27.49:8504
---------->Entered LOAD-CHUNK-EMBED<--------------
Collection already exists
Extracting text took this much time --> 306.8951711868867
Created a chunk of size 2274, which is longer than the specified 1024
Created a chunk of size 1538, which is longer than the specified 1024
Created a chunk of size 1742, which is longer than the specified 1024
Created a chunk of size 1040, which is longer than the specified 1024
Created a chunk of size 1730, which is longer than the specified 1024
Created a chunk of size 1052, which is longer than the specified 1024
Created a chunk of size 1128, which is longer than the specified 1024
Created a chunk of size 1561, which is longer than the specified 1024
Created a chunk of size 1368, which is longer than the specified 1024
Created a chunk of size 3190, which is longer than the specified 1024
Created a chunk of size 1467, which is longer than the specified 1024
no of chunks 637
Chunking took this much time --> 0.02095871907658875
Collection already exists
Embedding took this much time --> 16.116066633025184
Retrieval took this much time --> 0.04899920406751335
/home/sarmad/anaconda3/envs/RAG/lib/python3.10/site-packages/langchain_core/_api/deprecation.py:141: LangChainDeprecationWarning: Since Chroma 0.4.x the manual persistence method is no longer supported as docs are automatically persisted.
warn_deprecated(
The timetaken for a global load and chunk and embed 323.08347079413943
Query Processing.......
---------->Entered LOAD-CHUNK-EMBED<--------------
Collection already exists
Extracting text took this much time --> 299.51400842610747
Created a chunk of size 2274, which is longer than the specified 1024
Created a chunk of size 1538, which is longer than the specified 1024
Created a chunk of size 1742, which is longer than the specified 1024
Created a chunk of size 1040, which is longer than the specified 1024
Created a chunk of size 1730, which is longer than the specified 1024
Created a chunk of size 1052, which is longer than the specified 1024
Created a chunk of size 1128, which is longer than the specified 1024
Created a chunk of size 1561, which is longer than the specified 1024
Created a chunk of size 1368, which is longer than the specified 1024
Created a chunk of size 3190, which is longer than the specified 1024
Created a chunk of size 1467, which is longer than the specified 1024
no of chunks 637
Chunking took this much time --> 0.02046454302035272
Collection already exists
Embedding took this much time --> 15.863782780012116
Retrieval took this much time --> 0.06083990586921573
The timetaken for a global load and chunk and embed 315.4610354742035
Query Processing.......
from langchain.text_splitter import CharacterTextSplitter
from langchain.chains.question_answering import load_qa_chain
from langchain_community.vectorstores import Chroma
from langchain_community.embeddings import OllamaEmbeddings
import chromadb
from langchain_community.llms import Ollama
from langchain_community.document_loaders import UnstructuredPDFLoader
from htmlTemplates import css, bot_template, user_template
from langchain.load import dumps, loads
from operator import itemgetter
from langchain_core.runnables import RunnablePassthrough
from langchain_core.output_parsers import StrOutputParser
from langchain_community.retrievers import BM25Retriever
from langchain.retrievers import EnsembleRetriever
from langchain.prompts import ChatPromptTemplate
import timeit
def load_chunk_persist_pdf() -> Chroma:
pdf_folder_path = "./IMS/"
documents = []
for file in os.listdir(pdf_folder_path):
if file.endswith('.pdf'):
pdf_path = os.path.join(pdf_folder_path, file)
loader = UnstructuredPDFLoader(pdf_path)
documents.extend(loader.load())
text_splitter = CharacterTextSplitter(chunk_size=1024, chunk_overlap=200)
start0 = timeit.default_timer()
chunked_documents = text_splitter.split_documents(documents)
print("no of chunks", len(chunked_documents))
print("Chunking took this much time -->",timeit.default_timer()-start0)
client = chromadb.Client()
if client.list_collections():
consent_collection = client.create_collection("consent_collection")
print("----------Fresh Collection created-----------")
else:
print("----------Collection already exists-----------")
print(chunked_documents[2])
embedding_function = OllamaEmbeddings(base_url="http://localhost:11434", model="nomic-embed-text")
start11 = timeit.default_timer()
vectordb = Chroma.from_documents(chunked_documents, embedding_function, persist_directory="./testing_space_IMS_TESTING/chroma_store/")
print("Embedding took this much time -->",timeit.default_timer()-start11)
retriever = vectordb.as_retriever()
keyword_retriever = BM25Retriever.from_documents(chunked_documents)
keyword_retriever.k = 3
ensemble_retriever = EnsembleRetriever(retrievers=[retriever,
keyword_retriever],
weights=[0.3, 0.7])
vectordb.persist()
return ensemble_retriever
def get_llm_response(query):
start = timeit.default_timer()
en_retriever = load_chunk_persist_pdf()
print("The time taken for testing version to load and embed:",
timeit.default_timer() - start)
llm = Ollama(model="llama3.1:70b")
template1 = """You are an assistant for question-answering tasks.
Use the following pieces of retrieved context to answer the question.
If you don't know the answer, just say that you don't know.
Question: {question}
Context: {context}
Answer:
"""
Retrieved_docs = en_retriever.invoke(query)
print("retrieved-documents",Retrieved_docs)
prompt1 = ChatPromptTemplate.from_template(template1)
rag_chain = (
{"context": en_retriever, "question": RunnablePassthrough()}
| prompt1
| llm
| StrOutputParser()
)
start1 = timeit.default_timer()
answer1 = rag_chain.invoke(query)
print("The time taken for query processing is :",
timeit.default_timer() - start1)
return answer1
def save_uploaded_file(uploadedfile):
with open(os.path.join(doc_path,uploadedfile.name),"wb") as f:
f.write(uploadedfile.getbuffer())
return st.success("File Saved".format(uploadedfile.name))
doc_path = "./IMS/"
st.set_page_config(page_title="Chat with IMS Documents - TESTING", page_icon=":books:")
st.header("Chat with Custom LLM about IMS Documents - TESTING :books:")
# Set up the sidebar
st.sidebar.title("Document's List:")
with st.sidebar:
st.subheader("Your documents")
doc = st.file_uploader("Upload your PDF documents here...", accept_multiple_files=True)
if doc is not None:
for file in doc:
save_uploaded_file(file)
print("New_file", file)
# Display the names in the sidebar
file_names = [f for f in os.listdir(doc_path) if os.path.isfile(os.path.join(doc_path, f))]
for name in file_names:
st.sidebar.write(name)
form_input = st.text_input('Ask questions from your documents..')
#print("Query Processing.......")
#
if len(form_input) != 0:
st.write(bot_template.replace("{{MSG}}", get_llm_response(form_input)), unsafe_allow_html=True)