paint-brush
Вычарпальны падручнік па стварэнні прыкладання RAG з выкарыстаннем LangChainпа@bexgboost
1,916 чытанні
1,916 чытанні

Вычарпальны падручнік па стварэнні прыкладання RAG з выкарыстаннем LangChain

па Bex19m2024/09/03
Read on Terminal Reader

Занадта доўга; Чытаць

Даведайцеся, як выкарыстоўваць LangChain, вельмі папулярную структуру для стварэння сістэм RAG. Да канца падручніка ў нас будзе чат-бот (з інтэрфейсам Streamlit і ўсім), які будзе праглядаць прыватныя даныя, каб даць адказы на пытанні.
featured image - Вычарпальны падручнік па стварэнні прыкладання RAG з выкарыстаннем LangChain
Bex HackerNoon profile picture

Сучасныя вялікія моўныя мадэлі маюць доступ да пастаянна расце колькасці інфармацыі. Тым не менш, застаецца велізарная колькасць прыватных даных, да якіх гэтыя мадэлі не звяртаюцца. Вось чаму адным з самых папулярных прымянення LLM у карпаратыўных наладах з'яўляецца генерацыя з дапоўненым пошукам - скарочана RAG. У Візлы , нашай платформы аналізу даных на базе AI, мы атрымалі каштоўную інфармацыю аб стварэнні эфектыўных сістэм RAG. У гэтым уроку мы падзелімся некаторымі з нашых ведаў і пакажам вам, як стварыць уласную сістэму RAG.


Вы даведаецеся, як выкарыстоўваць LangChain, шырока папулярную структуру для стварэння сістэм RAG, для стварэння простай сістэмы RAG. Да канца ўрока ў нас будзе чат-бот (з інтэрфейсам Streamlit і ўсім), які будзе прабівацца праз некаторыя асабістыя даныя, каб даць адказы на пытанні.

Што такое RAG?

Каб высветліць, што такое RAG, давайце разгледзім просты прыклад.


Студэнт першага курса каледжа Чандлер разглядае магчымасць прапусціць некалькі заняткаў, але хоча пераканацца, што не парушае правілы наведвання універсітэта. Як і ва ўсім у нашы дні, ён задае пытанне ChatGPT .


Вядома, ChatGPT не можа на гэта адказаць. Чат-бот не тупы — ён проста не мае доступу да ўніверсітэцкіх дакументаў Чандлера. Такім чынам, Чэндлер сам знаходзіць дакумент аб палітыцы і выяўляе, што гэта доўгае тэхнічнае чытанне, якое ён не хоча прабірацца. Замест гэтага ён перадае ўвесь дакумент ChatGPT і зноў задае пытанне. На гэты раз ён атрымлівае адказ.


Гэта асобны выпадак генерацыі з дапоўненым пошукам. Адказ (генерацыя) моўнай мадэлі дапаўняецца (узбагачаецца) кантэкстам, атрыманым з крыніцы, якая не з'яўляецца часткай першапачатковага навучання.


Маштабуемая версія сістэмы RAG магла б адказаць на любое пытанне студэнта, самастойна шукаючы ва ўніверсітэцкіх дакументах, знаходзячы адпаведныя і знаходзячы фрагменты тэксту, якія, хутчэй за ўсё, утрымліваюць адказ.


Наогул кажучы, у сістэме RAG вы атрымліваеце інфармацыю з прыватнай крыніцы даных і перадаеце яе ў моўную мадэль, што дазваляе мадэлі даваць кантэкстуальна адпаведны адказ.

Кампаненты прыкладання RAG

Такая сістэма, нягледзячы на ​​тое, што гучыць проста, будзе мець шмат рухомых кампанентаў. Перад тым, як ствараць яго самастойна, нам трэба разгледзець, што яны сабой уяўляюць і як яны гуляюць разам.

Дакументы

Першы кампанент – дакумент або зборнік дакументаў. У залежнасці ад тыпу сістэмы RAG, якую мы ствараем, дакументы могуць быць тэкставымі файламі, PDF-файламі, вэб-старонкамі (RAG замест неструктураваных даных) або базамі дадзеных SQL або NoSQL (RAG замест структураваных даных). Яны выкарыстоўваюцца для ўводу розных тыпаў даных у сістэму.

Праграмы для загрузкі дакументаў

LangChain рэалізуе сотні класаў, якія называюцца загрузчыкамі дакументаў, для чытання даных з розных крыніц дакументаў, такіх як PDF-файлы, 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 спецыяльная старонка інструкцый .

Раздзяляльнікі тэксту

Пасля загрузкі дакументаў вельмі важна разбіць іх на меншыя і больш зручныя фрагменты тэксту. Вось асноўныя прычыны:

  1. Многія мадэлі ўбудавання (падрабязней пра іх пазней) маюць максімальны ліміт токенаў.
  2. Атрыманне больш дакладнае, калі ў вас ёсць меншыя кавалкі.
  3. Моўная мадэль сілкуецца дакладным кантэкстам.


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, дзе ў вас могуць быць гігабайты дакументаў, у канчатковым выніку вы атрымаеце некалькі мільярдаў фрагментаў тэксту і, такім чынам, вектараў. Ад іх няма ніякай карысці, калі вы не можаце іх надзейна захоўваць.


Вось чаму вектарныя сховішчы або базы даных цяпер у модзе. Акрамя захоўвання вашых убудаваных файлаў, вектарныя базы дадзеных клапоцяцца аб выкананні вектарнага пошуку за вас. Гэтыя базы даных аптымізаваны для хуткага пошуку найбольш падобных вектараў пры атрыманні вектара запыту, што вельмі важна для атрымання адпаведнай інфармацыі ў сістэмах 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.Concepts​A 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:Indexing​Load: 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 , які вяртае дакументы па неструктураваным запыце. Рэтрывер павінен толькі вяртаць або здабываць дакументы, а не захоўваць іх.


Вось як вы можаце пераўтварыць любую вектарную краму ў рэтрывер у 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


Можна абмежаваць колькасць адпаведных дакументаў да top 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 у LangChain

Цяпер, калі мы разгледзелі асноўныя кампаненты сістэмы RAG, мы пабудуем яе самастойна. Я правяду вас праз пакрокавую рэалізацыю чат-бота RAG, распрацаванага спецыяльна для дакументацыі па коду і падручнікаў. Вы знойдзеце гэта асабліва карысным, калі вам патрэбна дапамога ў кадзіраванні AI для новых фрэймворкаў або новых функцый існуючых фрэймворкаў, якія яшчэ не ўваходзяць у базу ведаў сучасных LLM.

0. Стварэнне структуры праекта

Спачатку запоўніце свой працоўны каталог наступнай структурай праекта:

 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}

1. Наладжванне асяроддзя

На гэтым этапе вы спачатку ствараеце новае асяроддзе 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

2. Настройка загрузчыкаў дакументаў

Затым адкрыйце файл 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 : Пералік для ўказання моў праграмавання пры падзеле тэксту.
  • PyPDFLoader : загружае і здабывае тэкст з файлаў PDF.
  • extract_from_images_with_rapidocr : Функцыя OCR для вымання тэксту з малюнкаў.
  • Document : уяўляе сабой дакумент са зместам і метададзенымі.
  • logging : забяспечвае функцыянальнасць вядзення часопіса для адладкі і інфармацыі.


Затым функцыя для апрацоўкі PDF-файлаў:

 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)


Вось як гэта працуе:

  1. Ён загружае PDF з дапамогай PyPDFLoader .
  2. Ён адфільтроўвае адсканаваныя старонкі, выдаляючы дакументы з пустым змесцівам.
  3. Ён рэгіструе колькасць прапушчаных адсканаваных старонак, калі такія маюцца.
  4. Калі скануюцца ўсе старонкі (г.зн. без тэкставага змесціва), узнікае ValueError.
  5. Нарэшце, ён разбівае астатнія неадсканаваныя дакументы на больш дробныя кавалкі з дапамогай функцыі split_documents.

Функцыя апрацоўвае выпадкі, калі 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 (аптычнага распазнавання сімвалаў). Ён счытвае файл выявы, пераўтворыць яе ў байты, а затым выкарыстоўвае бібліятэку RapidOCR для здабывання тэксту з выявы. Затым выняты тэкст загортваецца ў аб'ект Document з метададзенымі, якія змяшчаюць шлях да зыходнага файла. Нарэшце, функцыя разбівае дакумент на больш дробныя кавалкі з дапамогай функцыі 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 далей для стварэння, убудавання і захоўвання фрагментаў з прадстаўленых дакументаў і перадачы іх у кампанент RAG нашай сістэмы.

3. Настройка 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. Затым ён пераўтворыцца ў інтэрфейс аднаўлення з пошукам падабенства, які вяртае пяць лепшых дакументаў, якія адпавядаюць запыту карыстальніка.


Для моўнай мадэлі мы будзем выкарыстоўваць gpt-4o-mini але вы можаце выкарыстоўваць іншыя мадэлі, такія як GPT-4o, у залежнасці ад вашага бюджэту і патрэбаў.

Затым мы аб'яднаем усе гэтыя кампаненты з дапамогай мовы выразаў LangChain (LCEL). Першы кампанент ланцужка - гэта слоўнік з context і question у якасці ключоў. Значэнні гэтых ключоў забяспечваюцца рэтрыверам, адфарматаваным нашай функцыяй фарматавання і RunnablePassthrough() адпаведна. Апошні клас дзейнічае як запаўняльнік для запыту карыстальніка.


Затым слоўнік перадаецца ў нашу сістэмную падказку; падказка перадаецца ў LLM, які генеруе клас выходных паведамленняў. Клас паведамлення даецца аналізатару вываду радка, які вяртае адказ у выглядзе звычайнага тэксту.

4. Стварэнне інтэрфейсу Streamlit

У гэтым раздзеле мы створым наступны інтэрфейс для нашага прыкладання:


Здымак экрана нашага інтэрфейсу Streamlit.

Гэта чысты, мінімальны інтэрфейс з двума палямі ўводу - адно для дакумента, другое для задавання пытанняў аб дакуменце. На левай бакавой панэлі карыстальніку прапануецца ўвесці ключ 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 радкоў, ён рэалізуе наступныя функцыі:

  1. Увод ключа API: дазваляе карыстальнікам бяспечна ўводзіць ключ OpenAI API.
  2. Загрузка файлаў: падтрымлівае загрузку файлаў PDF, PNG, JPG і JPEG.
  3. Апрацоўка дакумента: апрацоўвае загружаны файл і стварае фрагменты тэксту.
  4. Стварэнне ланцужка RAG: стварае ланцужок генерацыі з дапоўненым пошукам з выкарыстаннем апрацаваных фрагментаў дакументаў.
  5. Апрацоўка запытаў: прымае пытанні карыстальнікаў аб загружаным дакуменце.
  6. Генерацыя адказаў: выкарыстоўвае ланцужок RAG для стварэння адказаў на аснове загружанага дакумента і запыту карыстальніка.
  7. Апрацоўка памылак: дае адпаведныя паведамленні пра памылкі для адсутных ключоў API, неапрацаваных файлаў або пустых запытаў.
  8. Зваротная сувязь з карыстальнікам: адлюстроўвае круцёлкі падчас апрацоўкі і паведамленні пра поспех/памылку, каб трымаць карыстальніка ў курсе.
  9. Кіраванне станам: выкарыстоўвае стан сеанса Streamlit для падтрымання ланцужка RAG паміж узаемадзеяннямі.

5. Разгортванне ў якасці чат-бота Streamlit

Застаўся толькі адзін крок — разгарнуць нашу праграму 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, ствараюць пачатковую фіксацыю і адпраўляюць усё ў сховішча (не забудзьцеся замяніць спасылку на рэпазітар вашай уласнай).


Цяпер вы павінны зарэгістравацца для бясплатнага акаўнта на Streamlit Cloud . Падключыце свой уліковы запіс GitHub і абярыце рэпазітар, які змяшчае вашу праграму.


Затым наладзьце параметры праграмы:

  • Усталюйце версію Python (напрыклад, 3.9)
  • Усталюйце асноўны шлях да файла app.py
  • Дадайце неабходныя сакрэты (напрыклад, OPENAI_API_KEY ) у наладах праграмы


Нарэшце, націсніце «Разгарнуць»!


Праграма павінна запрацаваць на працягу некалькіх хвілін. Прыкладанне, якое я стварыў для гэтага падручніка, можна знайсці па гэтай спасылцы . Паспрабуйце!

Сістэма RAG у дзеянні

Заключэнне

У гэтым падручніку разглядаецца магутнае спалучэнне Retrieval-Augmented Generation (RAG) і Streamlit, якое ўтварае інтэрактыўную сістэму адказаў на пытанні, заснаваную на дакументах. Чытач праходзіць увесь працэс, пачынаючы ад наладжвання асяроддзя і апрацоўкі дакументаў і заканчваючы стварэннем ланцужка RAG і разгортваннем дружалюбнага вэб-прыкладання.


Важныя моманты ўключаюць:

  • RAG для больш разумнай (у сэнсе знешніх ведаў) моўнай мадэлі
  • Ланцужкі RAG можна ствараць з выкарыстаннем мадэляў LangChain, OpenAI і інтэграцый старонніх супольнасцей.
  • Дадатак можна зрабіць інтэрактыўным з дапамогай Streamlit і разгарнуць для агульнага карыстання.


Гэты праект з'яўляецца асновай для больш прасунутых прыкладанняў. Яго можна значна пашырыць, напрыклад, уключыць некалькі тыпаў дакументаў, павысіць дакладнасць пошуку і такія функцыі, як суміраванне дакументаў. І тым не менш, на самай справе гэта служыць дэманстрацыяй патэнцыйнай магутнасці гэтых тэхналогій, паасобку і ў сукупнасці.