從頭開始的RAG:概述 這些筆記本將帶你從頭開始建立RAG應用程式。 他們將朝著更廣泛理解RAG語言景觀的方向發展,如下所示: Enviornment !pip install langchain_community tiktoken langchain-openai langchainhub chromadb langchain LangSmith(監看llm系統) LangSmith 是一個用於建立生產級LLM應用程式的平台。 它可以讓您調試、測試、評估和監控基於任何LLM框架構建的鍊和智能代理,並與LangChain無縫集成,LangChain是使用LLMs進行構建的首選開源框架。 LangSmith 由 LangChain 開發,該公司是開源 LangChain 框架背後的公司。 LangSmith | 🦜️🛠️ LangSmith (langchain.com) LangChain的API key在哪裡?:LangSmith (langchain.com)(登入) → setting 基本環境設置 Tracing Quick Start | 🦜️🛠️ LangSmith (langchain.com) export LANGCHAIN_TRACING_V2=true export LANGCHAIN_API_KEY=###draft_code_symbol_lessthen###your-api-key> # The below examples use the OpenAI API, so you will need export OPENAI_API_KEY=###draft_code_symbol_lessthen###your-openai-api-key> or import os os.environ['LANGCHAIN_TRACING_V2'] = 'true' os.environ['LANGCHAIN_ENDPOINT'] = 'https://api.smith.langchain.com' os.environ['LANGCHAIN_API_KEY'] = ###draft_code_symbol_lessthen###your-api-key> Part 1. Overview LangChain有許多元件旨在幫助構建問答應用程式,以及更普遍的RAG應用程式。 熟悉這些內容後,我們將用些小篇幅構建一個簡單的Q&A 應用程式在文本數據源上。 在此過程中,我們將介紹一個典型的問答"架構",討論相關的LangChain元件,並突出顯示 有關更高級Q&A技術的其他資源。另外,也拭目以待 LangSmith 如何幫助我們跟蹤和瞭解我們的應用程式。 隨著我們應用程式的增長,LangSmith 將變得越來越有用。 可以的話讀者能先看這篇Reference:快速入門 |🦜️🔗 朗鏈 (langchain.com) 索引:用於從源引入數據並編製索引的管道。 檢索和生成:實際的RAG鏈,它帶走了使用者在運行時查詢並從索引中檢索相關數據,然後 將其傳遞給模型。 索引 載入:首先我們需要載入數據。為此,我們將使用 DocumentLoaders。 拆分:文本 拆分器將大塊分解成更小的塊。這對 索引數據並將其傳遞到模型中,因為大塊 更難搜索,並且不適合模型的有限上下文 窗。Documents 存儲:我們需要一個地方來存儲和索引我們的拆分,以便 以後可以搜索它們。這通常使用 VectorStore 和 Embeddings 模型來完成。 檢索和生成 檢索:給定使用者輸入,從中檢索相關拆分 使用 Retriever 進行存儲。 生成:ChatModel / LLM 使用生成答案 包含問題和檢索到的數據的提示 import bs4 from langchain import hub from langchain.text_splitter import RecursiveCharacterTextSplitter from langchain_community.document_loaders import WebBaseLoader from langchain_community.vectorstores import Chroma from langchain_core.output_parsers import StrOutputParser from langchain_core.runnables import RunnablePassthrough from langchain_openai import ChatOpenAI, OpenAIEmbeddings #### 索引 #### # 載入文件 loader = WebBaseLoader( web_paths=("https://lilianweng.github.io/posts/2023-06-23-agent/",), # 定義要加載的網頁路徑 bs_kwargs=dict( parse_only=bs4.SoupStrainer( class_=("post-content", "post-title", "post-header") # 只解析特定CSS類的內容 ) ), ) docs = loader.load() # 加載文檔 # 分割文本 text_splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=200) # 定義文本分割器的參數 splits = text_splitter.split_documents(docs) # 對文檔進行分割 # 嵌入 vectorstore = Chroma.from_documents(documents=splits, embedding=OpenAIEmbeddings()) # 從文檔創建向量存儲 retriever = vectorstore.as_retriever() # 將向量存儲轉換為檢索器 #### 檢索和生成 #### # 提示 prompt = hub.pull("rlm/rag-prompt") # 從LangChain Hub拉取RAG提示 # LLM llm = ChatOpenAI(model_name="gpt-3.5-turbo", temperature=0) # 初始化ChatGPT模型 # 後處理 def format_docs(docs): return "\n\n".join(doc.page_content for doc in docs) # 定義文檔格式化函數 # 鏈接 rag_chain = ( {"context": retriever | format_docs, "question": RunnablePassthrough()} # 使用檢索器獲取上下文,然後格式化文檔 | prompt # 應用提示 | llm # 使用LLM生成回答 | StrOutputParser() # 解析字符串輸出 ) # 提問 rag_chain.invoke("What is Task Decomposition?") # 調用鏈以解決特定問題 Part 2: Indexing # Documents question = "What kinds of pets do I like?" document = "My favorite pet is a cat." Count tokensconsidering~4 char / token 透過embedding來計算doc和que之間的關係和距離 參考資料: Text embedding models from langchain_openai import OpenAIEmbeddings embd = OpenAIEmbeddings() query_result = embd.embed_query(question) document_result = embd.embed_query(document) len(query_result) Cosine similarity is reccomended (1 indicates identical) for OpenAI embeddings. import numpy as np def cosine_similarity(vec1, vec2): dot_product = np.dot(vec1, vec2) norm_vec1 = np.linalg.norm(vec1) norm_vec2 = np.linalg.norm(vec2) return dot_product / (norm_vec1 * norm_vec2) similarity = cosine_similarity(query_result, document_result) print("Cosine Similarity:", similarity) Document Loaders #### INDEXING #### # Load blog import bs4 from langchain_community.document_loaders import WebBaseLoader loader = WebBaseLoader( web_paths=("https://lilianweng.github.io/posts/2023-06-23-agent/",), bs_kwargs=dict( parse_only=bs4.SoupStrainer( class_=("post-content", "post-title", "post-header") ) ), ) blog_docs = loader.load() 分割器 這個文本分割器是通用文本的推薦選擇。它由一個字符列表參數化。它會按順序嘗試在它們上分割,直到塊變得足夠小。默認列表是["\n\n", "\n", " ", ""]。這會試圖盡可能地保持所有段落(然後是句子,然後是單詞)在一起,因為這些似乎是最強的語義相關的文本片段。 # Split from langchain.text_splitter import RecursiveCharacterTextSplitter text_splitter = RecursiveCharacterTextSplitter.from_tiktoken_encoder( chunk_size=300, chunk_overlap=50) # Make splits splits = text_splitter.split_documents(blog_docs) Vectorstores # Index from langchain_openai import OpenAIEmbeddings from langchain_community.vectorstores import Chroma vectorstore = Chroma.from_documents(documents=splits, embedding=OpenAIEmbeddings()) retriever = vectorstore.as_retriever(search_kwargs={"k": 1}) 在上面這段code中,它展示了如何使用 langchain_openai 和 langchain_community 這兩個庫來創建一個向量存儲(vectorstore),然後從中創建一個檢索器(retriever)。這是在構建檢索增強生成(Retrieval-Augmented Generation, RAG)系統時的一個常見步驟。以下是對各個部分的詳細解釋: 1.從langchain_openai導入OpenAIEmbeddings: 這一步導入了OpenAI的嵌入模型,OpenAIEmbeddings是用來將文本轉換成嵌入向量的。嵌入向量是一種數學表示,能夠捕捉到文本的語義信息。這種轉換對於後續的文本相似性比較非常重要。 2.從langchain_community.vectorstores導入Chroma: Chroma是一個向量存儲庫,用於存儲文本的嵌入向量。它可以用來高效地執行相似性搜索,幫助找到與給定查詢最相似的文檔或文本片段。 3.創建向量存儲: 使用Chroma.from_documents方法,結合OpenAIEmbeddings將一系列文檔(splits)轉換為嵌入向量,並存儲在一個Chroma向量存儲中。這裡的documents=splits表示你已經有了一個分割後的文檔列表,這些文檔將被轉換成嵌入向量。 4.創建檢索器: 通過調用vectorstore.as_retriever方法,從向量存儲中創建一個檢索器。search_kwargs={"k": 1}參數指定了在執行搜索時返回的最相似文檔的數量為1。這意味著對於每個查詢,檢索器都會返回與之最相似的單個文檔或文本片段。 這段程式碼的目的是為了建立一個能夠快速檢索與查詢最相似內容的系統。通過使用OpenAI的嵌入模型來轉換文本,並利用Chroma進行高效的相似性搜索,這種系統可以在多種應用中發揮作用,比如問答系統、文檔檢索和推薦系統等。 docs = retriever.get_relevant_documents("What is Task Decomposition?") len(docs) Docs會跟query一起變成向量後送進向量資料庫, 再來會裝進類似python"字典"的資料結構, 可以做key:value 的索引,進而生成出最好的PromptTemplate版本,再送給前端的llm做最後的推理, 當然還會經過output parser還有Agent(Agent機制會再分開來講)等等… 解釋 以下這段code展示了如何使用LangChain和OpenAI庫來構建一個基於特定模板的提示(prompt),然後通過ChatGPT模型回答基於該提示的問題 from langchain_openai import ChatOpenAI from langchain.prompts import ChatPromptTemplate # 提示模板 template = """Answer the question based only on the following context: {context} Question: {question} """ # 定義一個提示模板,其中包含上下文(context)和問題(question) prompt = ChatPromptTemplate.from_template(template) # 從模板創建一個ChatPromptTemplate對象 prompt # 顯示創建的提示對象 # LLM(大型語言模型) llm = ChatOpenAI(model_name="gpt-3.5-turbo", temperature=0) # 初始化ChatOpenAI對象,指定使用的模型和溫度參數 # 鏈接 chain = prompt | llm # 將提示模板和大型語言模型連接成一個處理鏈 # 執行 chain.invoke({"context":docs,"question":"What is Task Decomposition?"}) # 使用提供的上下文和問題執行處理鏈 from langchain import hub prompt_hub_rag = hub.pull("rlm/rag-prompt") # 從LangChain Hub拉取一個名為"rlm/rag-prompt"的提示 prompt_hub_rag # 顯示拉取的提示 Summary 上面首先定義了一個提示模板,用於指導如何根據給定的上下文(context)和問題(question)產生回答。接著,它初始化了一個使用GPT-3.5 Turbo模型的ChatOpenAI對象,設置溫度參數為0以生成更確定性的回答。然後,通過連接提示模板和語言模型,創建了一個處理鏈(chain),最後使用此鏈來回答關於任務分解("What is Task Decomposition?")的問題。 此外,代碼還展示了如何從LangChain Hub拉取一個現成的提示("rlm/rag-prompt"),這顯示了如何使用LangChain平台來獲取和使用共享資源,以便於構建和優化自然語言處理應用。