Với sự nổi lên của Mô hình ngôn ngữ lớn và khả năng ấn tượng của chúng, nhiều ứng dụng ưa thích đang được xây dựng dựa trên các nhà cung cấp LLM khổng lồ như OpenAI và Anthropic. Huyền thoại đằng sau những ứng dụng như vậy là khung RAG, đã được giải thích kỹ lưỡng trong các bài viết sau:
Để làm quen với RAG, tôi khuyên bạn nên xem qua các bài viết này. Tuy nhiên, bài đăng này sẽ bỏ qua những điều cơ bản và hướng dẫn bạn trực tiếp cách xây dựng ứng dụng RAG của riêng bạn có thể chạy cục bộ trên máy tính xách tay của bạn mà không phải lo lắng về quyền riêng tư dữ liệu và chi phí mã thông báo.
Chúng tôi sẽ xây dựng một ứng dụng tương tự như ChatPD F nhưng đơn giản hơn. Nơi người dùng có thể tải lên tài liệu PDF và đặt câu hỏi thông qua giao diện người dùng đơn giản. Kho công nghệ của chúng tôi cực kỳ dễ dàng với Langchain, Ollama và Streamlit.
Được rồi, hãy bắt đầu thiết lập nó.
Như đã đề cập ở trên, việc thiết lập và chạy Ollama rất đơn giản. Lân đâu tơi thăm
Tiếp theo, mở terminal của bạn và thực hiện lệnh sau để lấy phiên bản mới nhất
ollama pull mistral
Sau đó, chạy ollama list
để xác minh xem mô hình có được kéo chính xác hay không. Đầu ra của thiết bị đầu cuối sẽ giống như sau:
Bây giờ, nếu máy chủ LLM chưa chạy, hãy khởi chạy nó bằng ollama serve
. Nếu bạn gặp thông báo lỗi như "Error: listen tcp 127.0.0.1:11434: bind: address already in use"
, điều đó cho biết máy chủ đã chạy theo mặc định và bạn có thể tiến hành bước tiếp theo.
Bước thứ hai trong quy trình của chúng tôi là xây dựng đường dẫn RAG. Do ứng dụng của chúng tôi đơn giản nên chúng tôi chủ yếu cần hai phương thức: ingest
và ask
.
Phương thức ingest
vào chấp nhận đường dẫn tệp và tải nó vào bộ lưu trữ vectơ theo hai bước: đầu tiên, nó chia tài liệu thành các phần nhỏ hơn để đáp ứng giới hạn mã thông báo của LLM; thứ hai, nó vector hóa các khối này bằng cách sử dụng Qdrant FastEmbeddings và lưu trữ chúng vào Chroma.
Phương thức ask
xử lý các truy vấn của người dùng. Người dùng có thể đặt câu hỏi và sau đó RetrievalQAChain truy xuất các ngữ cảnh có liên quan (các đoạn tài liệu) bằng cách sử dụng các kỹ thuật tìm kiếm tương tự vectơ.
Với câu hỏi của người dùng và bối cảnh được truy xuất, chúng tôi có thể soạn lời nhắc và yêu cầu dự đoán từ máy chủ LLM.
from langchain.vectorstores import Chroma from langchain.chat_models import ChatOllama from langchain.embeddings import FastEmbedEmbeddings from langchain.schema.output_parser import StrOutputParser from langchain.document_loaders import PyPDFLoader from langchain.text_splitter import RecursiveCharacterTextSplitter from langchain.schema.runnable import RunnablePassthrough from langchain.prompts import PromptTemplate from langchain.vectorstores.utils import filter_complex_metadata class ChatPDF: vector_store = None retriever = None chain = None def __init__(self): self.model = ChatOllama(model="mistral") self.text_splitter = RecursiveCharacterTextSplitter(chunk_size=1024, chunk_overlap=100) self.prompt = PromptTemplate.from_template( """ <s> [INST] 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. Use three sentences maximum and keep the answer concise. [/INST] </s> [INST] Question: {question} Context: {context} Answer: [/INST] """ ) def ingest(self, pdf_file_path: str): docs = PyPDFLoader(file_path=pdf_file_path).load() chunks = self.text_splitter.split_documents(docs) chunks = filter_complex_metadata(chunks) vector_store = Chroma.from_documents(documents=chunks, embedding=FastEmbedEmbeddings()) self.retriever = vector_store.as_retriever( search_type="similarity_score_threshold", search_kwargs={ "k": 3, "score_threshold": 0.5, }, ) self.chain = ({"context": self.retriever, "question": RunnablePassthrough()} | self.prompt | self.model | StrOutputParser()) def ask(self, query: str): if not self.chain: return "Please, add a PDF document first." return self.chain.invoke(query) def clear(self): self.vector_store = None self.retriever = None self.chain = None
Lời nhắc có nguồn gốc từ trung tâm Langchain:
Bạn có thể tìm hiểu thêm về kỹ thuật nhắc LLM
Thêm chi tiết về việc thực hiện:
ingest
: Chúng tôi sử dụng PyPDFLoader để tải tệp PDF do người dùng tải lên. RecursiveCharacterSplitter, do Langchain cung cấp, sau đó chia tệp PDF này thành các phần nhỏ hơn. Điều quan trọng là phải lọc ra siêu dữ liệu phức tạp không được ChromaDB hỗ trợ bằng cách sử dụng hàm filter_complex_metadata
từ Langchain.
Để lưu trữ vector, Chroma được sử dụng kết hợp với
ask
: Phương thức này chỉ đơn giản chuyển câu hỏi của người dùng vào chuỗi được xác định trước của chúng tôi và sau đó trả về kết quả.
clear
: Phương pháp này được sử dụng để xóa phiên trò chuyện và bộ nhớ trước đó khi tệp PDF mới được tải lên. Để có giao diện người dùng đơn giản, chúng tôi sẽ sử dụng
import os import tempfile import streamlit as st from streamlit_chat import message from rag import ChatPDF st.set_page_config(page_title="ChatPDF") def display_messages(): st.subheader("Chat") for i, (msg, is_user) in enumerate(st.session_state["messages"]): message(msg, is_user=is_user, key=str(i)) st.session_state["thinking_spinner"] = st.empty() def process_input(): if st.session_state["user_input"] and len(st.session_state["user_input"].strip()) > 0: user_text = st.session_state["user_input"].strip() with st.session_state["thinking_spinner"], st.spinner(f"Thinking"): agent_text = st.session_state["assistant"].ask(user_text) st.session_state["messages"].append((user_text, True)) st.session_state["messages"].append((agent_text, False)) def read_and_save_file(): st.session_state["assistant"].clear() st.session_state["messages"] = [] st.session_state["user_input"] = "" for file in st.session_state["file_uploader"]: with tempfile.NamedTemporaryFile(delete=False) as tf: tf.write(file.getbuffer()) file_path = tf.name with st.session_state["ingestion_spinner"], st.spinner(f"Ingesting {file.name}"): st.session_state["assistant"].ingest(file_path) os.remove(file_path) def page(): if len(st.session_state) == 0: st.session_state["messages"] = [] st.session_state["assistant"] = ChatPDF() st.header("ChatPDF") st.subheader("Upload a document") st.file_uploader( "Upload document", type=["pdf"], key="file_uploader", on_change=read_and_save_file, label_visibility="collapsed", accept_multiple_files=True, ) st.session_state["ingestion_spinner"] = st.empty() display_messages() st.text_input("Message", key="user_input", on_change=process_input) if __name__ == "__main__": page()
Chạy mã này bằng lệnh streamlit run app.py
để xem nó trông như thế nào.
Được rồi, thế thôi! Hiện chúng tôi có ứng dụng ChatPDF chạy hoàn toàn trên máy tính xách tay của bạn. Vì bài đăng này chủ yếu tập trung vào việc cung cấp cái nhìn tổng quan cấp cao về cách xây dựng ứng dụng RAG của riêng bạn nên có một số khía cạnh cần tinh chỉnh. Bạn có thể xem xét các đề xuất sau để nâng cao ứng dụng của mình và phát triển hơn nữa các kỹ năng của mình:
Cuối cùng, cảm ơn bạn đã đọc. Nếu bạn thấy thông tin này hữu ích, vui lòng cân nhắc đăng ký kênh của tôi
Mã nguồn đầy đủ: