គំរូភាសាដ៏ធំនាពេលបច្ចុប្បន្ននេះ មានលទ្ធភាពទទួលបានព័ត៌មានដែលកើនឡើងឥតឈប់ឈរ។ ទោះយ៉ាងណាក៏ដោយ វានៅតែមានទិន្នន័យឯកជនដ៏ច្រើនសន្ធឹកសន្ធាប់ ដែលម៉ូដែលទាំងនេះមិនអាចទាញយកបាន។ នេះហើយជាមូលហេតុដែលកម្មវិធីដ៏ពេញនិយមបំផុតមួយនៃ LLMs នៅក្នុងការកំណត់សហគ្រាសគឺការទាញយកជំនាន់ដែលបានបន្ថែម - RAG ក្នុងរយៈពេលខ្លី។ នៅ
អ្នកនឹងរៀនពីរបៀបប្រើប្រាស់ LangChain ដែលជាក្របខ័ណ្ឌដ៏ពេញនិយមសម្រាប់ការកសាងប្រព័ន្ធ RAG ដើម្បីបង្កើតប្រព័ន្ធ RAG ដ៏សាមញ្ញមួយ។ នៅចុងបញ្ចប់នៃការបង្រៀន យើងនឹងមាន chatbot (ជាមួយចំណុចប្រទាក់ Streamlit និងទាំងអស់) ដែលនឹង RAG ផ្លូវរបស់វាតាមរយៈទិន្នន័យឯកជនមួយចំនួនដើម្បីផ្តល់ចម្លើយចំពោះសំណួរ។
ដើម្បីបញ្ជាក់ថា RAG គឺជាអ្វី ចូរយើងពិចារណាឧទាហរណ៍សាមញ្ញមួយ។
និស្សិតឆ្នាំទី 1 នៃមហាវិទ្យាល័យ Chandler កំពុងពិចារណារំលងថ្នាក់មួយចំនួន ប៉ុន្តែចង់ធានាថាគាត់មិនបំពានលើគោលការណ៍ចូលរៀននៅសកលវិទ្យាល័យទេ។ ដូចទៅនឹងអ្វីទាំងអស់សព្វថ្ងៃនេះ គាត់សួរសំណួរ ChatGPT ។
ជាការពិតណាស់ ChatGPT មិនអាចឆ្លើយវាបានទេ។ chatbot មិនល្ងង់ទេ - វាគ្រាន់តែមិនមានសិទ្ធិចូលប្រើឯកសារសាកលវិទ្យាល័យរបស់ Chandler ។ ដូច្នេះ លោក Chandler ស្វែងរកឯកសារគោលនយោបាយដោយខ្លួនគាត់ ហើយបានរកឃើញថា វាជាការអានបច្ចេកទេសដ៏វែងដែលគាត់មិនចង់ឆ្លងកាត់។ ផ្ទុយទៅវិញ គាត់ផ្តល់ឯកសារទាំងមូលទៅ ChatGPT ហើយសួរសំណួរម្តងទៀត។ លើកនេះគាត់ទទួលបានចម្លើយរបស់គាត់។
នេះជាករណីបុគ្គលនៃជំនាន់ដែលបានបង្កើនការទាញយក។ ចំលើយរបស់គំរូភាសា (ជំនាន់) ត្រូវបានបន្ថែម (ពង្រឹង) ដោយបរិបទដែលបានទាញយកពីប្រភពដែលមិនមែនជាផ្នែកនៃការបណ្តុះបណ្តាលដើមរបស់វា។
កំណែដែលអាចធ្វើមាត្រដ្ឋានបាននៃប្រព័ន្ធ RAG នឹងអាចឆ្លើយសំណួររបស់សិស្សដោយស្វែងរកឯកសារសាកលវិទ្យាល័យដោយខ្លួនវា ស្វែងរកឯកសារដែលពាក់ព័ន្ធ និងការទាញយកផ្នែកនៃអត្ថបទដែលទំនងជាមានចម្លើយ។
និយាយជាទូទៅ នៅក្នុងប្រព័ន្ធ RAG អ្នកទាញយកព័ត៌មានពីប្រភពទិន្នន័យឯកជនមួយ ហើយបញ្ចូលវាទៅជាគំរូភាសា ដែលអាចឱ្យគំរូផ្តល់ចម្លើយដែលពាក់ព័ន្ធតាមបរិបទ។
ប្រព័ន្ធបែបនេះ ទោះបីជាស្តាប់ទៅដោយត្រង់ៗក៏ដោយ នឹងមានធាតុផ្សំផ្លាស់ទីច្រើន។ មុននឹងបង្កើតខ្លួនយើង យើងត្រូវពិនិត្យមើលថាពួកគេជាអ្វី និងរបៀបដែលពួកគេលេងជាមួយគ្នា។
សមាសធាតុទីមួយគឺឯកសារ ឬបណ្តុំឯកសារ។ ដោយផ្អែកលើប្រភេទនៃប្រព័ន្ធ RAG ដែលយើងកំពុងសាងសង់ ឯកសារអាចជាឯកសារអត្ថបទ PDF គេហទំព័រ (RAG លើទិន្នន័យដែលមិនមានរចនាសម្ព័ន្ធ) ឬក្រាហ្វ SQL ឬ NoSQL databases (RAG លើទិន្នន័យដែលមានរចនាសម្ព័ន្ធ)។ ពួកវាត្រូវបានប្រើដើម្បីបញ្ចូលទិន្នន័យប្រភេទផ្សេងៗទៅក្នុងប្រព័ន្ធ។
LangChain អនុវត្តថ្នាក់រាប់រយដែលហៅថា កម្មវិធីផ្ទុកឯកសារ ដើម្បីអានទិន្នន័យពីប្រភពឯកសារផ្សេងៗដូចជា PDFs, Slack, Notion, Google Drive ជាដើម។
ថ្នាក់កម្មវិធីផ្ទុកឯកសារនីមួយៗមានតែមួយគត់ ប៉ុន្តែពួកវាទាំងអស់ចែករំលែកវិធីសាស្ត្រ .load()
ដូចគ្នា។ ឧទាហរណ៍ នេះជារបៀបដែលអ្នកអាចផ្ទុកឯកសារ PDF និងគេហទំព័រនៅក្នុង LangChain៖
from langchain_community.document_loaders import PyPDFLoader, WebBaseLoader # pip install langchain-community pdf_loader = PyPDFLoader("framework_docs.pdf") web_loader = WebBaseLoader( "https://python.langchain.com/v0.2/docs/concepts/#document-loaders" ) pdf_docs = pdf_loader.load() web_docs = web_loader.load()
ថ្នាក់ PyPDFLoader គ្រប់គ្រងឯកសារ PDF ដោយប្រើកញ្ចប់ PyPDF2 នៅក្រោមក្រណាត់ ខណៈពេលដែល WebBaseLoader លុបមាតិកាគេហទំព័រដែលបានផ្តល់ឱ្យ។
pdf_docs
មានវត្ថុឯកសារចំនួនបួន ដែលមួយសម្រាប់ទំព័រនីមួយៗ៖
>>> len(pdf_docs) 4
ខណៈពេលដែល web_docs
មានតែមួយ៖
>>> print(web_docs[0].page_content[125:300].strip()) You can view the v0.1 docs here.IntegrationsAPI referenceLatestLegacyMorePeopleContributingCookbooks3rd party tutorialsYouTubearXivv0.2v0.2v0.1🦜️🔗LangSmithLangSmith DocsLangCh
វត្ថុឯកសារទាំងនេះត្រូវបានផ្តល់ឱ្យនៅពេលក្រោយដើម្បីបង្កប់គំរូដើម្បីយល់ពីអត្ថន័យអត្ថន័យនៅពីក្រោយអត្ថបទរបស់ពួកគេ។
សម្រាប់ព័ត៌មានជាក់លាក់អំពីប្រភេទផ្សេងទៀតនៃកម្មវិធីផ្ទុកឯកសារ LangChain ផ្តល់ជូន a
នៅពេលដែលអ្នកបានផ្ទុកឯកសាររបស់អ្នកហើយ វាជារឿងសំខាន់ក្នុងការបំបែកវាទៅជាបំណែកតូចៗ និងអាចគ្រប់គ្រងបានកាន់តែច្រើននៃអត្ថបទ។ នេះជាហេតុផលចម្បងៗ៖
LangChain ផ្តល់ជូននូវប្រភេទកម្មវិធីបំបែកអត្ថបទជាច្រើននៅក្រោមកញ្ចប់ langchain_text_splitters របស់វា ហើយពួកវាខុសគ្នាអាស្រ័យលើប្រភេទឯកសារ។
នេះជារបៀបប្រើ RecursiveCharacterTextSplitter
ដើម្បីបំបែកអត្ថបទធម្មតាដោយផ្អែកលើបញ្ជីនៃសញ្ញាបំបែក និងទំហំកំណាត់៖
!pip install langchain_text_splitters from langchain_text_splitters import RecursiveCharacterTextSplitter # Example text text = """ RAG systems combine the power of large language models with external knowledge sources. This allows them to provide up-to-date and context-specific information. The process involves several steps including document loading, text splitting, and embedding. """ # Create a text splitter text_splitter = RecursiveCharacterTextSplitter( chunk_size=50, chunk_overlap=10, length_function=len, separators=["\n\n", "\n", " ", ""], ) # Split the text chunks = text_splitter.split_text(text) # Print the chunks for i, chunk in enumerate(chunks): print(f"Chunk {i + 1}: {chunk}")
លទ្ធផល៖
Chunk 1: RAG systems combine the power of large language Chunk 2: language models with external knowledge sources. Chunk 3: This allows them to provide up-to-date and Chunk 4: and context-specific information. Chunk 5: The process involves several steps including Chunk 6: including document loading, text splitting, and Chunk 7: and embedding.
ឧបករណ៍បំបែកនេះមានភាពចម្រុះ និងដំណើរការល្អសម្រាប់ករណីប្រើប្រាស់ជាច្រើន។ វាបង្កើតកំណាត់នីមួយៗដោយចំនួនតួអក្សរជិតដល់ chunk_size
តាមដែលអាចធ្វើបាន។ វាអាចប្តូរម្តងទៀតរវាងសញ្ញាបំបែកណាដែលត្រូវបំបែកដើម្បីរក្សាចំនួនតួអក្សរ។
ក្នុងឧទាហរណ៍ខាងលើ អ្នកបំបែករបស់យើងព្យាយាមបំបែកនៅលើបន្ទាត់ថ្មីជាមុន បន្ទាប់មកដកឃ្លាតែមួយ ហើយចុងក្រោយរវាងតួអក្សរណាមួយដើម្បីឈានដល់ទំហំកំណាត់ដែលចង់បាន។
មានកម្មវិធីបំបែកជាច្រើនទៀតនៅក្នុងកញ្ចប់ langchain_text_splitters
។ នេះគឺជាមួយចំនួន៖
HTMLSectionSplitter
PythonCodeTexSplitter
RecursiveJsonSplitter
ហើយដូច្នេះនៅលើ។ ឧបករណ៍បំបែកមួយចំនួនបង្កើតកំណាត់ដែលមានអត្ថន័យ ដោយប្រើគំរូប្លែងនៅក្រោមក្រណាត់។
ឧបករណ៍បំបែកអត្ថបទត្រឹមត្រូវមានឥទ្ធិពលយ៉ាងសំខាន់លើដំណើរការនៃប្រព័ន្ធ RAG ។
សម្រាប់ព័ត៌មានលម្អិតអំពីរបៀបប្រើកម្មវិធីបំបែកអត្ថបទ សូមមើលដែលពាក់ព័ន្ធ
នៅពេលដែលឯកសារត្រូវបានបំបែកទៅជាអត្ថបទ ពួកវាចាំបាច់ត្រូវបញ្ចូលកូដទៅក្នុងតំណាងលេខរបស់ពួកគេ ដែលជាតម្រូវការសម្រាប់ម៉ូដែលគណនាទាំងអស់ដែលធ្វើការជាមួយទិន្នន័យអត្ថបទ។
នៅក្នុងបរិបទនៃ RAG ការអ៊ិនកូដនេះត្រូវបានគេហៅថា ការបង្កប់ និងធ្វើដោយ ការបង្កប់គំរូ ។ ពួកគេបង្កើតតំណាងវ៉ិចទ័រនៃអត្ថបទដែលចាប់យកអត្ថន័យនៃអត្ថន័យរបស់ពួកគេ។ តាមរយៈការបង្ហាញអត្ថបទតាមរបៀបនេះ អ្នកអាចធ្វើប្រតិបត្តិការគណិតវិទ្យាលើពួកវា ដូចជាការស្វែងរកមូលដ្ឋានទិន្នន័យឯកសាររបស់យើងសម្រាប់អត្ថបទដែលស្រដៀងគ្នាបំផុតក្នុងន័យ ឬស្វែងរកចម្លើយចំពោះសំណួររបស់អ្នកប្រើ។
LangChain គាំទ្រអ្នកផ្តល់គំរូបង្កប់សំខាន់ៗទាំងអស់ ដូចជា OpenAI, Cohere, HuggingFace ជាដើម។ ពួកវាត្រូវបានអនុវត្តជាថ្នាក់ Embedding
និងផ្តល់នូវវិធីសាស្រ្តពីរ៖ មួយសម្រាប់បង្កប់ឯកសារ និងមួយទៀតសម្រាប់បង្កប់សំណួរ (ប្រអប់បញ្ចូល)។
នេះជាកូដឧទាហរណ៍ដែលបង្កប់ផ្នែកនៃអត្ថបទដែលយើងបានបង្កើតក្នុងផ្នែកមុនដោយប្រើ OpenAI៖
from langchain_openai import OpenAIEmbeddings # Initialize the OpenAI embeddings embeddings = OpenAIEmbeddings() # Embed the chunks embedded_chunks = embeddings.embed_documents(chunks) # Print the first embedded chunk to see its structure print(f"Shape of the first embedded chunk: {len(embedded_chunks[0])}") print(f"First few values of the first embedded chunk: {embedded_chunks[0][:5]}")
លទ្ធផល៖
Shape of the first embedded chunk: 1536 First few values of the first embedded chunk: [-0.020282309502363205, -0.0015041005099192262, 0.004193042870610952, 0.00229285703971982, 0.007068077567964792]
លទ្ធផលខាងលើបង្ហាញថា គំរូបង្កប់គឺបង្កើតវ៉ិចទ័រទំហំ 1536 សម្រាប់កំណាត់ទាំងអស់នៅក្នុងឯកសាររបស់យើង។
ដើម្បីបង្កប់សំណួរតែមួយ អ្នកអាចប្រើវិធីសាស្ត្រ embed_query()
៖
query = "What is RAG?" query_embedding = embeddings.embed_query(query) print(f"Shape of the query embedding: {len(query_embedding)}") print(f"First few values of the query embedding: {query_embedding[:5]}")
លទ្ធផល៖
Shape of the query embedding: 1536 First few values of the query embedding: [-0.012426204979419708, -0.016619959846138954, 0.007880032062530518, -0.0170428603887558, 0.011404196731746197]
នៅក្នុងកម្មវិធី RAG ខ្នាតធំដែលអ្នកអាចមានឯកសារ gigabytes អ្នកនឹងបញ្ចប់ដោយកំណាត់អត្ថបទ gazillion ហើយដូច្នេះ វ៉ិចទ័រ។ វាគ្មានប្រយោជន៍អ្វីទេ ប្រសិនបើអ្នកមិនអាចរក្សាទុកពួកវាដោយភាពជឿជាក់
នេះជាមូលហេតុដែល ហាងវ៉ិចទ័រ ឬមូលដ្ឋានទិន្នន័យ កំពុងផ្ទុះឡើងឥឡូវនេះ។ ក្រៅពីការរក្សាទុកការបង្កប់របស់អ្នក មូលដ្ឋានទិន្នន័យវ៉ិចទ័របានយកចិត្តទុកដាក់ក្នុងការស្វែងរកវ៉ិចទ័រសម្រាប់អ្នក។ មូលដ្ឋានទិន្នន័យទាំងនេះត្រូវបានធ្វើឱ្យប្រសើរដើម្បីស្វែងរកវ៉ិចទ័រស្រដៀងគ្នាបំផុតនៅពេលផ្តល់វ៉ិចទ័រសំណួរ ដែលមានសារៈសំខាន់សម្រាប់ការទាញយកព័ត៌មានពាក់ព័ន្ធនៅក្នុងប្រព័ន្ធ RAG ។
នេះគឺជាព័ត៌មានសង្ខេបនៃកូដដែលបង្កប់ខ្លឹមសារនៃទំព័របណ្តាញ និងរក្សាទុកវ៉ិចទ័រទៅក្នុងមូលដ្ឋានទិន្នន័យវ៉ិចទ័រ Chroma ( Chroma គឺជាដំណោះស្រាយមូលដ្ឋានទិន្នន័យវ៉ិចទ័រប្រភពបើកចំហ ដែលដំណើរការទាំងស្រុងលើម៉ាស៊ីនរបស់អ្នក)៖
!pip install chromadb langchain_chroma from langchain_community.document_loaders import WebBaseLoader from langchain_text_splitters import RecursiveCharacterTextSplitter # Load the web page loader = WebBaseLoader("https://python.langchain.com/v0.2/docs/tutorials/rag/") docs = loader.load() # Split the documents into chunks text_splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=200) chunks = text_splitter.split_documents(docs)
ដំបូងយើងផ្ទុកទំព័រជាមួយ WebBaseLoader
ហើយបង្កើតកំណាត់របស់យើង។ បន្ទាប់មក យើងអាចបញ្ជូនកំណាត់ដោយផ្ទាល់ទៅវិធីសាស្ត្រ from_documents
នៃ Chroma
រួមជាមួយនឹងជម្រើសនៃគំរូបង្កប់របស់យើង៖
from langchain_openai import OpenAIEmbeddings from langchain_chroma import Chroma db = Chroma.from_documents(chunks, OpenAIEmbeddings())
វត្ថុមូលដ្ឋានទិន្នន័យវ៉ិចទ័រទាំងអស់នៅក្នុង LangChain បង្ហាញវិធីសាស្ត្រ similarity_search
ដែលទទួលយកខ្សែអក្សរសំណួរ៖
query = "What is indexing in the context of RAG?" docs = db.similarity_search(query) print(docs[1].page_content)
លទ្ធផល៖
If you are interested for RAG over structured data, check out our tutorial on doing question/answering over SQL data.ConceptsA typical RAG application has two main components:Indexing: a pipeline for ingesting data from a source and indexing it. This usually happens offline.Retrieval and generation: the actual RAG chain, which takes the user query at run time and retrieves the relevant data from the index, then passes that to the model.The most common full sequence from raw data to answer looks like:IndexingLoad: First we need to load our data. This is done with Document Loaders.Split: Text splitters break large Documents into smaller chunks. This is useful both for indexing data and for passing it in to a model, since large chunks are harder to search over and won't fit in a model's finite context window.Store: We need somewhere to store and index our splits, so that they can later be searched over. This is often done using a VectorStore and Embeddings model.Retrieval and
លទ្ធផលនៃ similarity_search
គឺជាបញ្ជីឯកសារដែលទំនងជាមានព័ត៌មានដែលយើងកំពុងសួរនៅក្នុងសំណួរ។
សម្រាប់ព័ត៌មានលម្អិតអំពីរបៀបប្រើហាងវ៉ិចទ័រ សូមមើលដែលពាក់ព័ន្ធ
ទោះបីជាហាងវ៉ិចទ័រទាំងអស់គាំទ្រការទាញយកក្នុងទម្រង់នៃការស្វែងរកភាពស្រដៀងគ្នាក៏ដោយ LangChain អនុវត្តចំណុចប្រទាក់ Retriever
ពិសេសដែលត្រឡប់ឯកសារដែលបានផ្ដល់ឱ្យនូវសំណួរដែលមិនមានរចនាសម្ព័ន្ធ។ អ្នកយកមកវិញគ្រាន់តែត្រូវការយកមកវិញ ឬយកឯកសារ មិនមែនរក្សាទុកវាទេ។
នេះជារបៀបដែលអ្នកអាចបំប្លែងហាងវ៉ិចទ័រណាមួយទៅជា retriever នៅក្នុង LangChain៖
# Convert the vector store to a retriever chroma_retriever = db.as_retriever() docs = chroma_retriever.invoke("What is indexing in the context of RAG?") >>> len(docs) 4
វាអាចធ្វើទៅបានដើម្បីកំណត់ចំនួនឯកសារពាក់ព័ន្ធដល់កំពូល k ដោយប្រើ search_kwargs
:
chroma_retriever = db.as_retriever(search_kwargs={"k": 1}) docs = chroma_retriever.invoke("What is indexing in the context of RAG?") >>> len(docs) 1
អ្នកអាចឆ្លងកាត់ប៉ារ៉ាម៉ែត្រដែលទាក់ទងនឹងការស្វែងរកផ្សេងទៀតទៅកាន់ search_kwargs ។ ស្វែងយល់បន្ថែមអំពីការប្រើឧបករណ៍ទាញយកពី
ឥឡូវនេះយើងបានគ្របដណ្តប់សមាសធាតុសំខាន់ៗនៃប្រព័ន្ធ RAG យើងនឹងបង្កើតខ្លួនឯង។ ខ្ញុំនឹងណែនាំអ្នកតាមរយៈការអនុវត្តជាជំហាន ៗ នៃ RAG chatbot ដែលត្រូវបានរចនាឡើងជាពិសេសសម្រាប់ឯកសារកូដ និងការបង្រៀន។ អ្នកនឹងឃើញថាវាមានប្រយោជន៍ជាពិសេសនៅពេលដែលអ្នកត្រូវការជំនួយការសរសេរកូដ AI សម្រាប់ក្របខ័ណ្ឌថ្មី ឬលក្ខណៈពិសេសថ្មីនៃក្របខ័ណ្ឌដែលមានស្រាប់ ដែលមិនទាន់ជាផ្នែកនៃមូលដ្ឋានចំណេះដឹងនៃ LLMs នាពេលបច្ចុប្បន្ននេះ។
ជាដំបូង បញ្ចូលបញ្ជីការងាររបស់អ្នកជាមួយនឹងរចនាសម្ព័ន្ធគម្រោងខាងក្រោម៖
rag-chatbot/ ├── .gitignore ├── requirements.txt ├── README.md ├── app.py ├── src/ │ ├── __init__.py │ ├── document_processor.py │ └── rag_chain.py └── .streamlit/ └── config.toml
នេះគឺជាពាក្យបញ្ជា៖
$ touch .gitignore requirements.txt README.md app.py $ mkdir src .streamlit $ touch src/{.env,__init__.py,document_processor.py,rag_chain.py} $ touch .streamlit/{.env,config.toml}
នៅក្នុងជំហាននេះ ដំបូងអ្នកបង្កើតបរិយាកាស Conda ថ្មី ហើយដំណើរការវា៖
$ conda create -n rag_tutorial python=3.9 -y $ conda activate rag_tutorial
បន្ទាប់មកបើកឯកសារ requirements.txt
ហើយបិទភ្ជាប់ភាពអាស្រ័យខាងក្រោម៖
langchain==0.2.14 langchain_community==0.2.12 langchain_core==0.2.35 langchain_openai==0.1.22 python-dotenv==1.0.1 streamlit==1.37.1 faiss-cpu pypdf
ហើយដំឡើងពួកវា៖
$ pip install -r requirements.txt
ផងដែរ បង្កើតឯកសារ .gitignore
ដើម្បីលាក់ឯកសារពីការធ្វើលិបិក្រម git៖
# .gitignore venv/ __pycache__/ .env *.pdf *.png *.jpg *.jpeg *.gif *.svg
បន្ទាប់មក បើកឯកសារ src/document_processor.py
ហើយបិទភ្ជាប់អត្ថបទបន្ទាប់នៃកូដ។
ការនាំចូលចាំបាច់៖
import logging from langchain.text_splitter import RecursiveCharacterTextSplitter from langchain.text_splitter import Language from langchain_community.document_loaders import PyPDFLoader from langchain_community.document_loaders.parsers.pdf import ( extract_from_images_with_rapidocr, ) from langchain.schema import Document
ការពន្យល់អំពីការនាំចូល៖
RecursiveCharacterTextSplitter
៖ បំបែកអត្ថបទជាកំណាត់តូចៗបន្តបន្ទាប់គ្នា។Language
៖ Enum សម្រាប់បញ្ជាក់ភាសាសរសេរកម្មវិធីនៅក្នុងការបំបែកអត្ថបទ។PyPDFLoader
៖ ផ្ទុក និងស្រង់អត្ថបទពីឯកសារ PDF ។extract_from_images_with_rapidocr
: មុខងារ OCR សម្រាប់ទាញយកអត្ថបទពីរូបភាព។Document
៖ តំណាងឱ្យឯកសារដែលមានខ្លឹមសារ និងទិន្នន័យមេតា។logging
៖ ផ្តល់នូវមុខងារកត់ត្រាសម្រាប់ការបំបាត់កំហុស និងព័ត៌មាន។
បន្ទាប់មក មុខងារសម្រាប់ដំណើរការ PDFs៖
def process_pdf(source): loader = PyPDFLoader(source) documents = loader.load() # Filter out scanned pages unscanned_documents = [doc for doc in documents if doc.page_content.strip() != ""] scanned_pages = len(documents) - len(unscanned_documents) if scanned_pages > 0: logging.info(f"Omitted {scanned_pages} scanned page(s) from the PDF.") if not unscanned_documents: raise ValueError( "All pages in the PDF appear to be scanned. Please use a PDF with text content." ) return split_documents(unscanned_documents)
នេះជារបៀបដែលវាដំណើរការ៖
PyPDFLoader
។ មុខងារនេះគ្រប់គ្រងករណីដែល PDF អាចមានអត្ថបទចម្រុះ និងទំព័រដែលបានស្កេន ដោយធានាថាមានតែទំព័រដែលផ្អែកលើអត្ថបទប៉ុណ្ណោះដែលត្រូវបានដំណើរការបន្ថែមទៀត។ នេះមានសារៈសំខាន់សម្រាប់កិច្ចការវិភាគអត្ថបទ ដែលទំព័រដែលបានស្កេនដោយគ្មាន OCR នឹងមិនអាចប្រើបាន។ យើងនឹងកំណត់មុខងារ split_documents
នៅពេលក្រោយ។
បន្ទាប់មក យើងសរសេរមុខងារសម្រាប់យកព័ត៌មានពីរូបភាព (រូបថតអេក្រង់នៃអត្ថបទកូដ និង/ឬគេហទំព័រ)៖
def process_image(source): # Extract text from image using OCR with open(source, "rb") as image_file: image_bytes = image_file.read() extracted_text = extract_from_images_with_rapidocr([image_bytes]) documents = [Document(page_content=extracted_text, metadata={"source": source})] return split_documents(documents)
មុខងារនេះដំណើរការឯកសាររូបភាពដោយទាញយកអត្ថបទដោយប្រើ OCR (Optical Character Recognition)។ វាអានឯកសាររូបភាព បំប្លែងវាទៅជាបៃ ហើយបន្ទាប់មកប្រើបណ្ណាល័យ RapidOCR ដើម្បីស្រង់អត្ថបទចេញពីរូបភាព។ បន្ទាប់មកអត្ថបទដែលបានស្រង់ចេញត្រូវបានរុំក្នុងវត្ថុឯកសារដែលមានទិន្នន័យមេតាដែលមានផ្លូវឯកសារប្រភព។ ជាចុងក្រោយ មុខងារបំបែកឯកសារទៅជាបំណែកតូចៗដោយប្រើមុខងារ split_documents
ដែលយើងកំណត់បន្ទាប់៖
def split_documents(documents): # Split documents into smaller chunks for processing text_splitter = RecursiveCharacterTextSplitter.from_language( language=Language.PYTHON, chunk_size=1000, chunk_overlap=200 ) return text_splitter.split_documents(documents)
មុខងារនេះប្រើថ្នាក់ RecursiveCharacterTextSplitter ជាមួយនឹងវាក្យសម្ព័ន្ធរបស់ Python ដើម្បីបំបែកអត្ថបទទៅជាបំណែកនៃ 1000 តួអក្សរ និង 200 តួអក្សរត្រួតគ្នា។
មុខងារចុងក្រោយរបស់យើងរួមបញ្ចូលគ្នានូវមុខងារញែក PDF និងរូបភាពទៅជាមុខងារតែមួយ៖
def process_document(source): # Determine file type and process accordingly if source.lower().endswith(".pdf"): return process_pdf(source) elif source.lower().endswith((".png", ".jpg", ".jpeg")): return process_image(source) else: raise ValueError(f"Unsupported file type: {source}")
មុខងារចុងក្រោយនេះនឹងត្រូវបានប្រើប្រាស់ដោយ Streamlit UI ក្រោមបន្ទាត់ដើម្បីបង្កើត បង្កប់ និងរក្សាទុកកំណាត់ពីឯកសារដែលបានផ្តល់ ហើយបញ្ជូនវាទៅផ្នែក RAG នៃប្រព័ន្ធរបស់យើង។
ឥឡូវនេះ សូមបើកឯកសារ src/rag_chain.py
ហើយបិទភ្ជាប់អត្ថបទបន្ទាប់នៃកូដ។
ជាដំបូង នាំចូលម៉ូឌុលចាំបាច់៖
import os from dotenv import load_dotenv from langchain.prompts import PromptTemplate from langchain_community.vectorstores import FAISS from langchain_core.output_parsers import StrOutputParser from langchain_core.runnables import RunnablePassthrough from langchain_openai import ChatOpenAI, OpenAIEmbeddings # Load the API key from env variables load_dotenv() api_key = os.getenv("OPENAI_API_KEY")
នេះជាការពន្យល់អំពីការនាំចូល៖
• os
: អន្តរកម្មនៃប្រព័ន្ធប្រតិបត្តិការ • dotenv
: ផ្ទុកអថេរបរិស្ថាន • សមាសធាតុ langchain
៖
PromptTemplate
៖ ការបង្កើតប្រអប់បញ្ចូលផ្ទាល់ខ្លួនFAISS
: ហាងលក់វ៉ិចទ័រស្រាលសម្រាប់ឯកសារStrOutputParser
៖ ការបំប្លែងវត្ថុសារ LLM ទៅជាលទ្ធផលខ្សែអក្សរRunnablePassthrough
៖ បង្កើតខ្សែសង្វាក់ដែលអាចផ្សំបាន។ChatOpenAI
, OpenAIEmbeddings
៖ អន្តរកម្មគំរូ OpenAI
បន្ទាប់យើងបង្កើតប្រអប់បញ្ចូលរបស់យើងសម្រាប់ប្រព័ន្ធ RAG៖
RAG_PROMPT_TEMPLATE = """ You are a helpful coding assistant that can answer questions about the provided context. The context is usually a PDF document or an image (screenshot) of a code file. Augment your answers with code snippets from the context if necessary. If you don't know the answer, say you don't know. Context: {context} Question: {question} """ PROMPT = PromptTemplate.from_template(RAG_PROMPT_TEMPLATE)
ការជម្រុញប្រព័ន្ធ RAG គឺជាកត្តាសំខាន់មួយនៅក្នុងភាពជោគជ័យរបស់វា។ កំណែរបស់យើងគឺជាកំណែសាមញ្ញមួយ ប៉ុន្តែនឹងទទួលបានការងារធ្វើច្រើនបំផុតនៃពេលវេលា។ នៅក្នុងការអនុវត្ត អ្នកនឹងចំណាយពេលច្រើនក្នុងការធ្វើឡើងវិញ និងកែលម្អភ្លាមៗ។
ប្រសិនបើអ្នកកត់សម្គាល់ យើងកំពុងប្រើថ្នាក់ PromptTemplate
សម្រាប់បង្កើតប្រអប់បញ្ចូល។ រចនាសម្ព័ន្ធនេះអនុញ្ញាតឱ្យយើងបញ្ចូលបរិបទដែលបានទាញយកពីឯកសារ និងសំណួររបស់អ្នកប្រើប្រាស់ទៅក្នុងប្រអប់បញ្ចូលចុងក្រោយ។
និយាយអំពីឯកសារ យើងត្រូវការមុខងារមួយដើម្បីធ្វើទ្រង់ទ្រាយពួកវា មុនពេលពួកវាត្រូវបានបញ្ជូនតាមបរិបទទៅក្នុងប្រអប់បញ្ចូលប្រព័ន្ធ៖
def format_docs(docs): return "\n\n".join(doc.page_content for doc in docs)
វាគឺជាមុខងារសាមញ្ញដែលភ្ជាប់មាតិកាទំព័រនៃឯកសារដែលបានយកមកវិញ។
ជាចុងក្រោយ យើងបង្កើតមុខងារមួយដែលនឹងអភិវឌ្ឍខ្សែសង្វាក់ RAG របស់យើង៖
def create_rag_chain(chunks): embeddings = OpenAIEmbeddings(api_key=api_key) doc_search = FAISS.from_documents(chunks, embeddings) retriever = doc_search.as_retriever( search_type="similarity", search_kwargs={"k": 5} ) llm = ChatOpenAI(model_name="gpt-4o-mini", temperature=0) rag_chain = ( {"context": retriever | format_docs, "question": RunnablePassthrough()} | PROMPT | llm | StrOutputParser() ) return rag_chain
មុខងារទទួលយកកំណាត់ឯកសារ ដែលនឹងត្រូវបានផ្តល់ដោយមុខងារ process_document
ខាងក្នុងស្គ្រីប document_processor.py
។
មុខងារចាប់ផ្តើមដោយកំណត់គំរូបង្កប់ និងរក្សាទុកឯកសារទៅក្នុងហាងវ៉ិចទ័រ FAISS ។ បន្ទាប់មក វាត្រូវបានបំប្លែងទៅជាចំណុចប្រទាក់ retriever ជាមួយនឹងការស្វែងរកស្រដៀងគ្នា ដែលបង្ហាញឯកសារកំពូលទាំងប្រាំដែលត្រូវគ្នានឹងសំណួររបស់អ្នកប្រើ។
សម្រាប់គំរូភាសា យើងនឹងប្រើ gpt-4o-mini
ប៉ុន្តែអ្នកអាចប្រើគំរូផ្សេងទៀតដូចជា GPT-4o អាស្រ័យលើថវិកា និងតម្រូវការរបស់អ្នក។
បន្ទាប់មក យើងនឹងដាក់សមាសធាតុទាំងអស់នេះចូលគ្នាដោយប្រើ LangChain Expression Language (LCEL)។ ធាតុផ្សំដំបូងនៃខ្សែសង្វាក់គឺជាវចនានុក្រមដែលមាន context
និង question
ជាកូនសោ។ តម្លៃនៃគ្រាប់ចុចទាំងនេះត្រូវបានផ្តល់ដោយ retriever ដែលធ្វើទ្រង់ទ្រាយដោយមុខងារធ្វើទ្រង់ទ្រាយរបស់យើង និង RunnablePassthrough()
រៀងគ្នា។ ថ្នាក់ក្រោយដើរតួជាកន្លែងដាក់សម្រាប់សំណួររបស់អ្នកប្រើ។
បន្ទាប់មកវចនានុក្រមត្រូវបានបញ្ចូលទៅក្នុងប្រអប់បញ្ចូលប្រព័ន្ធរបស់យើង។ ប្រអប់បញ្ចូលត្រូវបានបញ្ចូលទៅ LLM ដែលបង្កើតថ្នាក់សារលទ្ធផល។ ថ្នាក់សារត្រូវបានផ្តល់ឱ្យអ្នកញែកលទ្ធផលខ្សែអក្សរដែលត្រឡប់ការឆ្លើយតបអត្ថបទធម្មតា។
នៅក្នុងផ្នែកនេះ យើងនឹងបង្កើត UI ខាងក្រោមសម្រាប់កម្មវិធីរបស់យើង៖
វាគឺជាចំណុចប្រទាក់ស្អាត និងតិចតួចបំផុតជាមួយនឹងវាលបញ្ចូលពីរ - មួយសម្រាប់ឯកសារ មួយទៀតសម្រាប់សួរសំណួរអំពីឯកសារ។ នៅក្នុងរបារចំហៀងខាងឆ្វេង អ្នកប្រើប្រាស់ត្រូវបានស្នើឱ្យបញ្ចូលសោ API របស់ពួកគេ។
ដើម្បីបង្កើតចំណុចប្រទាក់ សូមបើកស្គ្រីប app.py
នៅកម្រិតកំពូលនៃថតការងាររបស់អ្នក ហើយបិទភ្ជាប់កូដខាងក្រោម៖
import streamlit as st import os from dotenv import load_dotenv from src.document_processor import process_document from src.rag_chain import create_rag_chain # Load environment variables load_dotenv() st.set_page_config(page_title="RAG Chatbot", page_icon="🤖") st.title("RAG Chatbot") # Initialize session state if "rag_chain" not in st.session_state: st.session_state.rag_chain = None # Sidebar for API key input with st.sidebar: api_key = st.text_input("Enter your OpenAI API Key", type="password") if api_key: os.environ["OPENAI_API_KEY"] = api_key # File uploader uploaded_file = st.file_uploader("Choose a file", type=["pdf", "png", "jpg", "jpeg"]) if uploaded_file is not None: if st.button("Process File"): if api_key: with st.spinner("Processing file..."): # Save the uploaded file temporarily with open(uploaded_file.name, "wb") as f: f.write(uploaded_file.getbuffer()) try: # Process the document chunks = process_document(uploaded_file.name) # Create RAG chain st.session_state.rag_chain = create_rag_chain(chunks) st.success("File processed successfully!") except ValueError as e: st.error(str(e)) finally: # Remove the temporary file os.remove(uploaded_file.name) else: st.error("Please provide your OpenAI API key.") # Query input query = st.text_input("Ask a question about the uploaded document") if st.button("Ask"): if st.session_state.rag_chain and query: with st.spinner("Generating answer..."): result = st.session_state.rag_chain.invoke(query) st.subheader("Answer:") st.write(result) elif not st.session_state.rag_chain: st.error("Please upload and process a file first.") else: st.error("Please enter a question.")
ទោះបីជាមានប្រវែងត្រឹមតែ 65 បន្ទាត់ក៏ដោយក៏វាអនុវត្តមុខងារដូចខាងក្រោមៈ
នៅសល់តែមួយជំហានទៀតប៉ុណ្ណោះ - ការដាក់ពង្រាយកម្មវិធី Streamlit របស់យើង។ មានជម្រើសជាច្រើននៅទីនេះ ប៉ុន្តែវិធីងាយស្រួលបំផុតគឺដោយប្រើ Streamlit Cloud ដែលឥតគិតថ្លៃ និងងាយស្រួលក្នុងការរៀបចំ។
ដំបូងបើកស្គ្រីប .streamlit/config.toml
ហើយបិទភ្ជាប់ការកំណត់ដូចខាងក្រោម៖
[theme] primaryColor = "#F63366" backgroundColor = "#FFFFFF" secondaryBackgroundColor = "#F0F2F6" textColor = "#262730" font = "sans serif"
នេះគឺជាការកែសម្រួលស្បែកមួយចំនួនដែលមកពីចំណូលចិត្តផ្ទាល់ខ្លួន។ បន្ទាប់មក សរសេរឯកសារ README.md (អ្នកអាចចម្លងមាតិការបស់វាពី ឯកសារដែលបានបង្ហោះនេះនៅលើ GitHub )។
ជាចុងក្រោយ សូមចូលទៅកាន់ GitHub.com ហើយបង្កើតឃ្លាំងថ្មីមួយ។ ចម្លងតំណរបស់វា ហើយត្រឡប់ទៅថតការងាររបស់អ្នកវិញ៖
$ git init $ git add . $ git commit -m "Initial commit" $ git remote add origin https://github.com/YourUsername/YourRepo.git $ git push --set-upstream origin master
ពាក្យបញ្ជាខាងលើចាប់ផ្តើម Git បង្កើតការប្តេជ្ញាចិត្តដំបូងហើយរុញអ្វីគ្រប់យ៉ាងទៅឃ្លាំង (កុំភ្លេចជំនួសតំណ repo របស់អ្នក) ។
ឥឡូវនេះ អ្នកត្រូវតែចុះឈ្មោះសម្រាប់គណនីឥតគិតថ្លៃនៅ Streamlit Cloud ។ ភ្ជាប់គណនី GitHub របស់អ្នក ហើយជ្រើសរើសឃ្លាំងដែលមានកម្មវិធីរបស់អ្នក។
បន្ទាប់មក កំណត់ការកំណត់កម្មវិធី៖
app.py
OPENAI_API_KEY
) នៅក្នុងការកំណត់កម្មវិធី
ចុងក្រោយចុច "Deploy"!
កម្មវិធីត្រូវតែដំណើរការក្នុងរយៈពេលប៉ុន្មាននាទី។ កម្មវិធីដែលខ្ញុំបានបង្កើតសម្រាប់ការបង្រៀននេះអាចរកបាននៅ តំណនេះ ។ សាកល្បងមើល!
ការបង្រៀននេះមើលទៅលើការលាយបញ្ចូលគ្នាដ៏មានឥទ្ធិពលនៃ Retrieval-Augmented Generation (RAG) និង Streamlit ដែលបង្កើតជាប្រព័ន្ធឆ្លើយសំណួរអន្តរកម្មដោយផ្អែកលើឯកសារ។ វាតម្រូវឱ្យអ្នកអានឆ្លងកាត់ដំណើរការទាំងមូល ចាប់ពីការបង្កើតបរិយាកាស និងដំណើរការឯកសារ ដើម្បីបង្កើតខ្សែសង្វាក់ RAG និងដាក់ពង្រាយកម្មវិធីគេហទំព័រដែលងាយស្រួលប្រើ។
ចំណុចសំខាន់ៗរួមមាន:
គម្រោងនេះបង្កើតជាមូលដ្ឋានសម្រាប់កម្មវិធីដែលមានកម្រិតខ្ពស់ជាង។ វាអាចត្រូវបានពង្រីកតាមវិធីសំខាន់ៗ ដូចជាការបញ្ចូលប្រភេទឯកសារជាច្រើន ភាពត្រឹមត្រូវនៃការទាញយកប្រសើរឡើង និងលក្ខណៈពិសេសដូចជាការសង្ខេបឯកសារជាដើម។ និងនៅឡើយទេ អ្វីដែលវាពិតជាបម្រើគឺជាការបង្ហាញពីអំណាចសក្តានុពលនៃបច្ចេកវិទ្យាទាំងនេះ ជាលក្ខណៈបុគ្គល និងរួមបញ្ចូលគ្នា។