LlamaIndex 學習筆記 - 餵資料與 Vector DB
目錄
這筆記系列是建構在 LlamaIndex 官方文件的 starter example 的範例節錄/實作/追蹤, 前一篇在這
嘗試的原始碼在 https://github.com/unclefomotw/llamaindex-try/blob/main/src/rag_3.py
此嘗試有兩個重點
- 利用 LlamaIndex 做 data ingestion / ETL
- 運用外部的 vector database 儲存資料 (以 Qdrant 為例,但其他 DB 應該大同小異)
要安裝 pip install llama-index-vector-stores-qdrant
Offline: 餵資料 #
更新: 請參照
新的這篇,用 IngestionPipeline
首先,不一定需要用 LlamaIndex 做 ETL / Data ingestion,只要有個資料庫,並且已經把文件餵進去就好(可能欄位/設定/細節會需要調整,待查)
不過為了玩一下,我在這是拿空的資料庫 + LlamaIndex 的一些功能,把文本資料爬出來,並轉成文件餵進去
Vector DB - Qdrant #
隨便在本機裝個 Qdrant 試試。Qdrant 有 WebUI http://localhost:6333/ 觀察有哪些資料在裡面,剛裝起來是空的
Qdrant 的 python interface library 是 qdrant-client,不過安裝 llama-index-vector-stores-qdrant
會順便一起裝
利用 LlamaIndex 餵資料 #
def ingest_to_db():
# Crawl
documents = SimpleDirectoryReader(
input_dir=RAG_DATA_DIR, recursive=True, required_exts=[".md"]
).load_data()
# This is from qdrant, not llama-index
db_client = QdrantClient(host="localhost", port=6333)
# pass the DB client to the vector store
vector_store = QdrantVectorStore(
collection_name="rag_3",
client=db_client
)
storage_context = StorageContext.from_defaults(vector_store=vector_store)
# The effect: feed data into DB
VectorStoreIndex.from_documents(
documents,
storage_context=storage_context
)
- StorageContext: 所謂 context 是用來在函數之間傳遞資訊的,本身沒有作用,而是讓其他人拿得到「東西」。StorageContext 有儲存
vector_store
以及其他跟 store 相關的物件,讓其他人能存取 - 在 LlamaIndex 裡,“index” 並不是想像中建立反向索引、或有神秘演算法能快速查找資料的
- 要 look up 需要
.as_query_engine()
變出RetrieverQueryEngine
,用裡面的retriever
查
- 要 look up 需要
VectorStoreIndex.from_documents()
會把 documents 寫入資料庫 – 只要有 documents 以及vector_store
/storage_context
(後面解釋)- 所以實質的「索引」其實是 Qdrant 做的
乍看之下沒有「餵資料」,但其實看 http://localhost:6333/ Qdrant UI 是有的(後面解釋)
Online: 查詢資料庫,然後給 LLM 幻想 #
有了資料庫(無論怎麼餵進去的),可利用 VectorStoreIndex.from_vector_store()
聯繫資料庫變成 (llamaindex 的) index
前面餵完資料後,程式可以終止;現在查詢的程式可以重新跑,查詢是直接查 Qdrant 資料庫,本機沒有文件
# Declare the Qdrant VectorStore; assuming the data is ingested
db_client = QdrantClient(host="localhost", port=6333)
vector_store = QdrantVectorStore(
collection_name="rag_3",
client=db_client
)
# The index itself does NOT hold any nodes (see below)
index = VectorStoreIndex.from_vector_store(vector_store)
query_engine = index.as_query_engine()
# During inference, the major operations from the query_engine
# happen in its retriever, where the vector store's `query()` is called
response = query_engine.query("CFOP 要怎麼學?有建議的順序嗎?")
注意這邊 index 是從 .from_vector_store(vector_store)
來的; vector_store
是 llamaindex 的 QdrantVectorStore
連接 Qdrant 的 client
在 Offline 餵資料的 index 與這裡的 index 是不同的;或者說,不是因為前面用 VectorStoreIndex 餵了資料現在才能查詢,這兩個就是沒關連的
VectorStoreIndex #
詳見 https://github.com/unclefomotw/llamaindex-try/blob/main/src/rag_3.py
以 QdrantVectorStore 為例
- VectorStoreIndex 的
.from_documents(documents, storage_context)
會呼叫自身的build_index_from_nodes
→ 呼叫_build_index_from_nodes
- VectorStoreIndex 的
.from_vector_store(vector_store)
並不會傳文件 / nodes 給 index; 他單純只是把vector_store
(也就是 QdrantVectorStore) 記好- 之後其
.as_retriever()
是 VectorIndexRetriever;查詢的主要動作_retrieve()
是呼叫vector_store
(也就是 QdrantVectorStore) 的query()
,去打 Qdrant 的 API (見此) - 本機不需要儲存所有文件
- 之後其