實作使用Ollama、LangChain、Qdrant 的 RAG
前面幾篇已經對RAG 之前需要的步驟做過詳解了,本篇直接做RAG 的範例。
RAG 的實作有以下步驟:
- 準備建立向量資料庫時所使用的相同 embedding model
- 準備vector store 用來取得與問題相關的向量資料
- 使用LangChain 初始化與Ollama 的連線
- 使用LangChain 架構做RAG 的Prompt 與 Agent
- 透過LangChain 串起整個RAG 流程
from datetime import datetime
from langchain.agents import create_agent
from langchain_core.embeddings import Embeddings
from langchain_ollama import ChatOllama
from langchain_qdrant import QdrantVectorStore
from sentence_transformers import SentenceTransformer
from langchain.agents.middleware import dynamic_prompt, ModelRequest
# 1. 準備embedding model
embedding_model = SentenceTransformer('intfloat/multilingual-e5-large')
class E5Embedding(Embeddings):
def embed_query(self, text: str):
return embedding_model.encode(f"query: {text}", normalize_embeddings=True).tolist()
def embed_documents(self, texts):
return [
embedding_model.encode(f"passage: {t}", normalize_embeddings=True).tolist()
for t in texts
]
# 2. 準備vector_store
vector_store = QdrantVectorStore.from_existing_collection(
embedding=E5Embedding(),
collection_name="domain",
url=qdrant_url,
)
# 3. initial LLM with langChain
llm = ChatOllama(
model="llama3.1-tw",
base_url=ollama_url,
temperature= 0.8,
num_predict = 512
)
# 4. create agent
@dynamic_prompt
def prompt_with_context(request: ModelRequest) -> str:
"""Inject context into state messages."""
last_query = request.state["messages"][-1].text
retrieved_docs = vector_store.similarity_search(last_query, k=3)
docs_content = "\n\n".join(doc.page_content for doc in retrieved_docs)
print("\n================================ Retrieved Documents =================================")
print(docs_content)
now = datetime.now().strftime("%H:%M:%S")
print(f"[{now}]: retrieved finish\n")
return (
f"""你是一個專業的電商客服 AI 助理,使用繁體中文回答。
重要規則:
1. 仔細閱讀,理解,吸收參考資料
2. 利用參考資料,盡力回答使用者的問題.並嘗試提供解決方案
3. 用你自己的話總結和回答,不會直接拿參考資料來回答
利用以下的上下文回答問題:
{docs_content}
"""
)
agent = create_agent(llm, tools=[], middleware=[prompt_with_context])
# 5. call LLM
question = "賣家收到我的退貨了 甚麼時候才會退款給我?"
for step in agent.stream(
{"messages": [{"role": "user", "content": question}]},
stream_mode="values",
):
step["messages"][-1].pretty_print()
now = datetime.now().strftime("%H:%M:%S")
print(f"[{now}]: message end\n")
vector_store.client.close()從以下的輸出可以看到拿出檢索資料(Retrieved) 速度其實是蠻快的,呼叫與回傳幾乎是同一秒就完成。
筆者刻意將取出的檢索資料(Retrieved Documents) 顯示出來,用以判斷LLM 的是否有確實根據檢索資料回覆:
> ================================ Human Message =================================
> 賣家收到我的退貨了 甚麼時候才會退款給我?
> [14:12:35]: message end
>
> ================================ Retrieved Documents =================================
> 退款將統一使用日幣,將於賣家收到退貨商品後,三千個工作天內完成退款
> 買家收到商品後,發現有問題7天內可以無條件退貨退款
> 若退貨包裹遺失,請聯絡高速婆婆處理
> [14:12:35]: retrieved finish
>
> ================================== Ai Message ==================================
> 根據文中提到的資訊,「退款將統一使用日幣,將於賣家收到退貨商品後,三千個工作天內完成退款」,我們可以推斷出以下幾點:
> 1. 退貨的流程是:買家先通知賣家退貨,賣家收到貨物後再進行退款。
> 2. 退款的時間點是「賣家收到退貨商品後」,也就是在貨物寄回賣家之後。
> 3. 退款完成的時間為「三千個工作天內」,也就是在接獲退貨通知後,三千天(或更短的時間)內。
> 4. 退款金額與退貨商品金額相同,因為「統一使用日幣」。
> 因此,從買家通知賣家退貨開始,到賣家收到貨物並完成退款,大約需要3千天的時間。建議你可以再次確認一下退貨時的約定意見,或許還有其他細節要特別留意。
> [14:12:51]: message end
>
> Process finished with exit code 0到這邊恭喜我們🎉🎉🎉
終於完成了一個簡易的RAG 流程 ~~
references:
Build a RAG agent with LangChain - Docs by LangChain