paint-brush
Omvattende handleiding oor die bou van 'n JOOL-toepassing met behulp van LangChaindeur@bexgboost
354 lesings
354 lesings

Omvattende handleiding oor die bou van 'n JOOL-toepassing met behulp van LangChain

deur Bex19m2024/09/03
Read on Terminal Reader

Te lank; Om te lees

Leer hoe om LangChain te gebruik, die baie gewilde raamwerk vir die bou van JOOL-stelsels. Teen die einde van die tutoriaal sal ons 'n kletsbot (met 'n Streamlit-koppelvlak en al) hê wat deur 'n paar private data sal JULLE om antwoorde op vrae te gee.
featured image - Omvattende handleiding oor die bou van 'n JOOL-toepassing met behulp van LangChain
Bex HackerNoon profile picture

Vandag se groot taalmodelle het toegang tot 'n steeds groeiende hoeveelheid inligting. Daar is egter steeds 'n groot magdom private data wat hierdie modelle nie gebruik nie. Dit is hoekom een ​​van die gewildste toepassings van LLM's in ondernemingsinstellings herwinning-vergrote generasie is - kortweg JOOL. By Vizly , ons KI-aangedrewe data-ontledingsplatform, het ons waardevolle insigte verkry oor die bou van effektiewe JOOL-stelsels. In hierdie tutoriaal sal ons 'n paar van ons leerstellings deel en jou wys hoe om jou eie JOOL-stelsel te skep.


Jy sal leer hoe om LangChain, die baie gewilde raamwerk vir die bou van JOOL-stelsels, te gebruik om 'n eenvoudige JOOL-stelsel te bou. Teen die einde van die tutoriaal sal ons 'n kletsbot (met 'n Streamlit-koppelvlak en al) hê wat deur 'n paar private data sal raas om antwoorde op vrae te gee.

Wat is JOOL?

Om te verduidelik wat JOOL is, kom ons kyk na 'n eenvoudige voorbeeld.


'n Eerstejaarstudent, Chandler, oorweeg dit om 'n paar klasse oor te slaan, maar wil verseker dat hy nie die universiteitsbywoningsbeleid oortree nie. Soos met enigiets deesdae, vra hy ChatGPT die vraag.


Natuurlik kan ChatGPT dit nie beantwoord nie. Die kletsbot is nie dom nie - dit het net nie toegang tot Chandler se universiteitsdokumente nie. So, Chandler vind die beleidsdokument self en ontdek dat dit 'n lang, tegniese lees is waaroor hy nie wil waad nie. In plaas daarvan gee hy die hele dokument aan ChatGPT en vra die vraag weer. Hierdie keer kry hy sy antwoord.


Dit is 'n individuele geval van herwinning-vergrote generasie. Die taalmodel se antwoord (generasie) word aangevul (verryk) deur konteks wat verkry is uit 'n bron wat nie deel van sy oorspronklike opleiding was nie.


'n Skaalbare weergawe van 'n JOOL-stelsel sal in staat wees om enige studentevraag te beantwoord deur self universiteitsdokumente te deursoek, die relevantes te vind, en stukkies teks te herwin wat heel waarskynlik die antwoord bevat.


Oor die algemeen, in 'n JOOL-stelsel, haal jy inligting uit 'n private databron en voer dit na 'n taalmodel, wat die model in staat stel om 'n kontekstueel relevante antwoord te gee.

Komponente van 'n JOOL Aansoek

So 'n stelsel sal, al klink dit reguit, baie bewegende komponente. Voordat ons self een bou, moet ons hersien wat hulle is en hoe hulle saamspeel.

Dokumente

Die eerste komponent is 'n dokument of 'n versameling dokumente. Gebaseer op die tipe JOOL-stelsel wat ons bou, kan die dokumente tekslêers, PDF's, webblaaie (JOOL oor ongestruktureerde data) of grafiek-, SQL- of NoSQL-databasisse (JOOL oor gestruktureerde data) wees. Hulle word gebruik om verskillende tipes data in die stelsel in te neem.

Dokumentlaaiers

LangChain implementeer honderde klasse genaamd dokumentlaaiers om data van verskeie dokumentbronne soos PDF's, Slack, Notion, Google Drive, ensovoorts te lees.


Elke dokumentlaaierklas is uniek, maar hulle deel almal dieselfde .load() -metode. Byvoorbeeld, hier is hoe jy 'n PDF-dokument en 'n webblad in LangChain kan laai:

 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()


Die PyPDFLoader-klas hanteer PDF-lêers met behulp van die PyPDF2-pakket onder die enjinkap, terwyl die WebBaseLoader die gegewe webbladinhoud skraap.


pdf_docs bevat vier dokumentobjekte, een vir elke bladsy:


 >>> len(pdf_docs) 4


Terwyl web_docs slegs een bevat:

 >>> 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


Hierdie dokumentobjekte word later aan inbeddingsmodelle gegee om die semantiese betekenis agter hul teks te verstaan.


Vir besonderhede oor ander soorte dokumentlaaiers, bied LangChain 'n toegewyde hoe-om-te-bladsy .

Teksverdelers

Sodra jy jou dokumente gelaai het, is dit van kardinale belang om dit op te breek in kleiner en meer hanteerbare stukke teks. Hier is die hoofredes:

  1. Baie inbeddingsmodelle (meer daaroor later) het 'n maksimum tekenlimiet.
  2. Herwinning is meer akkuraat as jy kleiner stukke het.
  3. Die taalmodel word die presiese konteks gevoer.


LangChain bied baie soorte teksverdelers onder sy langchain_text_splitters-pakket aan, en hulle verskil op grond van dokumenttipe.

Hier is hoe om RecursiveCharacterTextSplitter te gebruik om gewone teks te verdeel op grond van 'n lys skeiers en stukgrootte:

 !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}")

Uitset:

 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.

Hierdie splitter is veelsydig en werk goed vir baie gebruiksgevalle. Dit skep elke stuk met 'n karaktertelling so na as moontlik aan chunk_size . Dit kan rekursief wissel tussen watter skeiers om te verdeel om die karaktertelling te behou.


In die voorbeeld hierbo probeer ons splitter om eers op nuwelyne te verdeel, dan enkele spasies, en uiteindelik tussen enige karakters om die verlangde stukgrootte te bereik.


Daar is baie ander splitters binne langchain_text_splitters pakket. Hier is 'n paar:

  • HTMLSectionSplitter
  • PythonCodeTexSplitter
  • RecursiveJsonSplitter

en so aan. Sommige van die splitters skep semanties betekenisvolle stukke deur 'n transformatormodel onder die enjinkap te gebruik.


Die regte teksverdeler het 'n beduidende impak op die werkverrigting van 'n JOOL-stelsel.


Vir besonderhede oor hoe om teksverdelers te gebruik, sien die relevante hoe-om-gidse hier .

Inbedding van modelle

Sodra dokumente in teks verdeel is, moet hulle in hul numeriese voorstelling geënkodeer word, wat 'n vereiste is vir alle berekeningsmodelle wat met teksdata werk.


In die konteks van JOOL word hierdie enkodering inbedding genoem en gedoen deur inbedding van modelle . Hulle skep 'n vektorvoorstelling van 'n stuk teks wat hul semantiese betekenis vasvang. Deur teks op hierdie manier aan te bied, kan jy wiskundige bewerkings daarop doen, soos om in ons dokumentdatabasis te soek vir teks wat die meeste soortgelyk in betekenis is of 'n antwoord op 'n gebruikernavraag vind.


LangChain ondersteun alle groot inbeddingsmodelverskaffers, soos OpenAI, Cohere, HuggingFace, ensovoorts. Hulle word geïmplementeer as Embedding -klasse en bied twee metodes: een vir die inbedding van dokumente en een vir die inbedding van navrae (vrae).


Hier is 'n voorbeeldkode wat die stukke teks wat ons in die vorige afdeling geskep het met OpenAI insluit:

 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]}")


Uitset:

 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]

Die uitvoer hierbo toon dat die inbeddingsmodel 'n 1536-dimensionele vektor skep vir alle stukke in ons dokumente.


Om 'n enkele navraag in te sluit, kan jy die embed_query() metode gebruik:

 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]}")


Uitset:

 Shape of the query embedding: 1536 First few values of the query embedding: [-0.012426204979419708, -0.016619959846138954, 0.007880032062530518, -0.0170428603887558, 0.011404196731746197]

Vector winkels

In grootskaalse JOOL-toepassings waar jy dalk gigagrepe van dokumente het, sal jy met gazillion teksstukke en dus vektore eindig. Daar is geen nut aan hulle as jy dit nie betroubaar kan stoor nie.


Dit is hoekom vektorwinkels of databasisse nou woedend is. Afgesien van die stoor van jou inbeddings, sorg vektordatabasisse om vektorsoektogte vir jou uit te voer. Hierdie databasisse is geoptimaliseer om vinnig die mees soortgelyke vektore te vind wanneer 'n navraagvektor gegee word, wat noodsaaklik is vir die herwinning van relevante inligting in JOOL-stelsels.


Hier is 'n stukkie kode wat die inhoud van 'n webblad insluit en die vektore in 'n Chroma vektor databasis stoor ( Chroma is 'n oopbron vektor databasis oplossing wat geheel en al op jou masjien loop):

 !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)


Eerstens laai ons die bladsy met WebBaseLoader en skep ons stukke. Dan kan ons die stukke direk na die from_documents metode van Chroma deurgee saam met ons inbeddingsmodel van keuse:

 from langchain_openai import OpenAIEmbeddings from langchain_chroma import Chroma db = Chroma.from_documents(chunks, OpenAIEmbeddings())


Alle vektordatabasisobjekte in LangChain stel 'n similarity_search metode bloot wat 'n navraagstring aanvaar:

 query = "What is indexing in the context of RAG?" docs = db.similarity_search(query) print(docs[1].page_content)


Uitset:

 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

Die resultaat van similarity_search is 'n lys dokumente wat heel waarskynlik die inligting bevat wat ons in die navraag vra.


Vir besonderhede oor hoe om vektorwinkels te gebruik, sien die relevante hoe-om-gidse hier .

Retrievers

Alhoewel alle vektorwinkels herwinning in die vorm van ooreenkomssoektog ondersteun, implementeer LangChain 'n toegewyde Retriever koppelvlak wat dokumente terugstuur met 'n ongestruktureerde navraag. 'n Retriever hoef slegs dokumente terug te gee of te herwin, nie om dit te stoor nie.


Hier is hoe jy enige vektorwinkel kan omskakel na 'n retriever in 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


Dit is moontlik om die aantal relevante dokumente tot bo k te beperk deur search_kwargs te gebruik:

 chroma_retriever = db.as_retriever(search_kwargs={"k": 1}) docs = chroma_retriever.invoke("What is indexing in the context of RAG?") >>> len(docs) 1

Jy kan ander soek-verwante parameters deurgee na search_kwargs. Kom meer te wete oor die gebruik van retrievers van spesifieke hoe-om-gidse .

Stap-vir-stap werkvloei om 'n JOOL-toepassing in LangChain te bou

Noudat ons die sleutelkomponente van 'n JOOL-stelsel gedek het, sal ons self een bou. Ek sal jou deur 'n stap-vir-stap implementering van 'n JOOL-kletsbot lei wat spesifiek ontwerp is vir kodedokumentasie en tutoriale. U sal dit veral nuttig vind wanneer u KI-koderingsbystand benodig vir nuwe raamwerke of nuwe kenmerke van bestaande raamwerke wat nog nie deel is van die kennisbasis van vandag se LLM's nie.

0. Skep die projekstruktuur

Vul eers jou werkgids in met die volgende projekstruktuur:

 rag-chatbot/ ├── .gitignore ├── requirements.txt ├── README.md ├── app.py ├── src/ │ ├── __init__.py │ ├── document_processor.py │ └── rag_chain.py └── .streamlit/ └── config.toml


Hier is die opdragte:

 $ 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. Die opstel van die omgewing

In hierdie stap skep jy eers 'n nuwe Conda-omgewing en aktiveer dit:

 $ conda create -n rag_tutorial python=3.9 -y $ conda activate rag_tutorial


Maak dan die requirements.txt lêer oop en plak die volgende afhanklikhede:

 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

en installeer hulle:

 $ pip install -r requirements.txt


Skep ook 'n .gitignore -lêer om lêers van git-indeksering te versteek:

 # .gitignore venv/ __pycache__/ .env *.pdf *.png *.jpg *.jpeg *.gif *.svg

2. Opstel van dokumentlaaiers

Maak dan die src/document_processor.py lêer oop en plak die komende kodebrokkies.


Die nodige invoere:

 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


Verduideliking van die invoer:

  • RecursiveCharacterTextSplitter : Verdeel teks rekursief in kleiner stukke.
  • Language : Opsomming vir die spesifikasie van programmeertale in teksverdeling.
  • PyPDFLoader : Laai en onttrek teks uit PDF-lêers.
  • extract_from_images_with_rapidocr : OCR-funksie om teks uit beelde te onttrek.
  • Document : Verteenwoordig 'n dokument met inhoud en metadata.
  • logging : Verskaf aantekenfunksies vir ontfouting en inligting.


Dan, 'n funksie vir die verwerking van PDF's:

 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)


Hier is hoe dit werk:

  1. Dit laai die PDF met PyPDFLoader .
  2. Dit filtreer geskandeerde bladsye uit deur dokumente met leë inhoud te verwyder.
  3. Dit teken die aantal geskandeerde bladsye wat weggelaat is, indien enige, aan.
  4. As alle bladsye geskandeer word (dws geen teksinhoud nie), bring dit 'n ValueError op.
  5. Laastens verdeel dit die oorblywende ongeskandeerde dokumente in kleiner stukke met behulp van die split_documents-funksie.

Die funksie hanteer gevalle waar 'n PDF 'n mengsel van teks en geskandeerde bladsye kan bevat, om te verseker dat slegs teksgebaseerde bladsye verder verwerk word. Dit is noodsaaklik vir teksontledingstake waar geskandeerde bladsye sonder OCR onbruikbaar sou wees. Ons sal die split_documents funksie later definieer.


Vervolgens skryf ons 'n funksie vir die herwinning van inligting uit beelde (skermkiekies van kodebrokkies en/of webblaaie):


 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)


Hierdie funksie verwerk 'n beeldlêer deur teks te onttrek met OCR (Optical Character Recognition). Dit lees die prentlêer, skakel dit om na grepe, en gebruik dan die RapidOCR-biblioteek om teks uit die prent te onttrek. Die onttrekte teks word dan toegedraai in 'n Dokument-objek met metadata wat die bronlêerpad bevat. Laastens verdeel die funksie die dokument in kleiner stukke met behulp van die split_documents funksie, wat ons volgende definieer:


 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)


Die funksie gebruik die RecursiveCharacterTextSplitter-klas met Python se sintaksis om teks in stukke van 1000 karakters te verdeel en 200 karakters oorvleuel.


Ons finale funksie kombineer die PDF- en beeldontleder-funksies in een:


 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}")


Hierdie laaste funksie sal deur die Streamlit UI gebruik word om stukke van verskafde dokumente te skep, in te bed en te stoor en dit na die JOOL-komponent van ons stelsel oor te dra.

3. Die opstel van JOOL

Maak nou die src/rag_chain.py lêer oop en plak die komende kodebrokkies.


Voer eers nodige modules in:


 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")


Hier is 'n verduideliking van die invoer:

os : Bedryfstelselinteraksies • dotenv : Laai omgewingsveranderlikes • langchain :

  • PromptTemplate : Gepasmaakte vinnige skepping
  • FAISS : 'n Liggewig vektorwinkel vir dokumente
  • StrOutputParser : Omskakeling van LLM-boodskapobjekte in string-uitsette
  • RunnablePassthrough : Skep saamstelbare kettings
  • ChatOpenAI , OpenAIEmbeddings : OpenAI-modelinteraksies


Vervolgens skep ons ons versoek vir die JOOL-stelsel:


 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)


JOOL-stelselspoed is een van die kritieke faktore in die sukses daarvan. Ons weergawe is 'n eenvoudige een, maar sal die meeste van die tyd die werk gedoen kry. In die praktyk sal jy baie tyd spandeer om te herhaal en te verbeter op die opdrag.


As jy agterkom, gebruik ons ​​'n PromptTemplate klas om die prompt te bou. Hierdie konstruk stel ons in staat om die konteks wat uit dokumente en die gebruiker se navraag gehaal is, dinamies in 'n finale prompt in te neem.


Van dokumente gepraat, ons het 'n funksie nodig om dit te formateer voordat dit as konteks in die stelselprompt deurgegee word:


 def format_docs(docs): return "\n\n".join(doc.page_content for doc in docs)


Dit is 'n eenvoudige funksie wat die bladsyinhoud van opgespoorde dokumente aaneenskakel.


Laastens skep ons 'n funksie wat ons JOOL-ketting sal ontwikkel:


 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


Die funksie aanvaar dokumentstukke, wat verskaf sal word deur die process_document funksie binne document_processor.py skrip.

Die funksie begin deur die inbeddingsmodel te definieer en die dokumente in 'n FAISS-vektorstoor te stoor. Dan word dit omgeskakel na die retriever-koppelvlak met ooreenkomssoektog wat top vyf dokumente terugstuur wat ooreenstem met die gebruiker se navraag.


Vir die taalmodel sal ons gpt-4o-mini gebruik, maar jy kan ander modelle soos GPT-4o gebruik, afhangende van jou begroting en behoeftes.

Dan sal ons al hierdie komponente saamvoeg met behulp van LangChain Expression Language (LCEL). Die eerste komponent van die ketting is 'n woordeboek met context en question as sleutels. Die waardes van hierdie sleutels word verskaf deur die retriever wat onderskeidelik deur ons formateringfunksie en die RunnablePassthrough() geformateer is. Laasgenoemde klas dien as 'n plekhouer vir die gebruiker se navraag.


Die woordeboek word dan in ons stelselprompt deurgegee; die prompt word na die LLM gevoer, wat uitsetboodskapklas genereer. Die boodskapklas word gegee aan 'n string-afvoerontleder wat 'n gewone teks-antwoord terugstuur.

4. Skep 'n Streamlit UI

In hierdie afdeling sal ons die onderstaande UI vir ons toepassing bou:


Die skermkiekie van ons Streamlit UI.

Dit is 'n skoon, minimale koppelvlak met twee invoervelde - een vir die dokument, die ander om vrae oor die dokument te vra. In die linkerkantbalk word die gebruiker gevra om hul API-sleutel in te voer.


Om die koppelvlak te bou, maak die app.py -skrip in die boonste vlak van jou werkgids oop en plak die volgende kode:


 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.")


Ten spyte daarvan dat dit slegs 65 reëls lank is, implementeer dit die volgende funksionaliteit:

  1. API-sleutelinvoer: Laat gebruikers toe om hul OpenAI API-sleutel veilig in te voer.
  2. Lêeroplaai: Ondersteun die oplaai van PDF-, PNG-, JPG- en JPEG-lêers.
  3. Dokumentverwerking: Verwerk die opgelaaide lêer en skep teksstukke.
  4. JOOL-kettingskepping: Bou 'n Herwinning-Augmented Generation-ketting met behulp van die verwerkte dokumentstukke.
  5. Navraaghantering: Aanvaar gebruikersvrae oor die opgelaaide dokument.
  6. Antwoordgenerering: Gebruik die JOOL-ketting om antwoorde te genereer gebaseer op die opgelaaide dokument en gebruikernavraag.
  7. Fouthantering: Verskaf toepaslike foutboodskappe vir ontbrekende API-sleutels, onverwerkte lêers of leë navrae.
  8. Gebruikerterugvoer: Wys draaiers tydens verwerking en sukses/foutboodskappe om die gebruiker op hoogte te hou.
  9. Staatsbestuur: Gebruik Streamlit se sessietoestand om die JOOL-ketting oor interaksies heen in stand te hou.

5. Ontplooi as 'n Streamlit chatbot

Daar is net een stap oor—om ons Streamlit-toepassing te ontplooi. Daar is baie opsies hier, maar die maklikste manier is om Streamlit Cloud te gebruik, wat gratis en maklik is om op te stel.


Maak eers die .streamlit/config.toml skrip oop en plak die volgende konfigurasies:


 [theme] primaryColor = "#F63366" backgroundColor = "#FFFFFF" secondaryBackgroundColor = "#F0F2F6" textColor = "#262730" font = "sans serif"


Hierdie is 'n paar tema-aanpassings wat uit persoonlike voorkeure kom. Skryf dan die README.md-lêer op (jy kan die inhoud daarvan van hierdie gehuisvesde lêer op GitHub kopieer).


Gaan uiteindelik na GitHub.com en skep 'n nuwe bewaarplek. Kopieer sy skakel en keer terug na jou werkgids:


 $ 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


Die bogenoemde opdragte inisialiseer Git, skep 'n aanvanklike commit en stoot alles na die bewaarplek (moenie vergeet om die repo-skakel met jou eie te vervang nie).


Nou moet jy inteken vir 'n gratis rekening by Streamlit Cloud . Koppel jou GitHub-rekening en kies die bewaarplek wat jou toepassing bevat.


Stel dan die programinstellings op:

  • Stel die Python-weergawe (bv. 3.9)
  • Stel die hooflêerpad na app.py
  • Voeg enige nodige geheime (soos OPENAI_API_KEY ) in die programinstellings by


Ten slotte, klik "Ontplooi"!


Die toepassing moet binne minute in werking wees. Die toepassing wat ek vir hierdie tutoriaal gebou het, kan by hierdie skakel gevind word. Probeer dit uit!

Die JOOL-stelsel in aksie

Gevolgtrekking

Hierdie tutoriaal kyk na die kragtige mengsel van Retrieval-Augmented Generation (RAG) en Streamlit wat 'n interaktiewe vraag-antwoordstelsel vorm gebaseer op dokumente. Dit neem die leser deur die hele proses, van die opstel van 'n omgewing en die verwerking van dokumente tot die bou van 'n JOOL-ketting en die implementering van 'n vriendelike webtoepassing.


Belangrike punte sluit in:

  • JOOL vir 'n slimmer (in die eksterne kennis sin) taalmodel
  • JOOL-kettings kan gebou word deur LangChain, OpenAI se modelle en derdeparty-gemeenskapsintegrasies te gebruik.
  • Die toepassing kan interaktief gemaak word met Streamlit en vir publieke gebruik ontplooi word.


Hierdie projek vorm die basis vir aansoeke wat meer gevorderd is. Dit kan op beduidende maniere uitgebrei word, soos die inkorporering van verskeie dokumenttipes, verbeterde herwinningsakkuraatheid en kenmerke soos dokumentopsomming. En tog, wat dit werklik dien, is as 'n demonstrasie van die potensiële krag van hierdie tegnologieë, individueel en gekombineer.