RAG 程式筆記

這一行就代表了全部 RAG 的過程

index = VectorStoreIndex.from_documents(documents,show_progress=True)

會先進到 from_documents
llama_index\core\indices\base.py\BaseIndex\from_documents

然後會直接到此處建立儲存上下文的物件 storage_context

storage_context 現在是 None (預設), 如果之前有儲存此資料時才會需要在.from_documents()中添加 storage_context

# 建立 storage_context 物件
storage_context = storage_context or StorageContext.from_defaults() 

StorageContext.from_defaults() 位於 llama_index\core\storage

此物件可以放的參數是:

docstore:文件存儲
index_store:索引存儲
vector_store:向量存儲
graph_store:圖形存儲
image_store:映像存儲
persist_dir:持久目錄 (之前儲存的 index)

以上如果沒有放 都會是 None。

當 persist_dir 為 None , 其他存儲格式會是預設值

這邊先看 index_store 跟 vector_store

index_store = index_store or SimpleIndexStore()

SimpleIndexStore() 其實就是 simple key-value store 簡單鍵值存儲
llama_index\core\storage\index_store\SimpleIndexStore()

vector_stores = vector_stores or {DEFAULT_VECTOR_STORE: SimpleVectorStore()}

storage_context 最後回傳的資料為一個類別的實例

return cls(
            docstore=docstore,
            index_store=index_store,
            vector_stores=vector_stores,  # type: ignore
            graph_store=graph_store,
        )

接著設定 transformation 物件, 這是用來判斷要使用哪一種方式將文件切割成結點

transformations = transformations or transformations_from_settings_or_context( Settings, service_context )

transformations 初始值為 None。
service_context 初始值也為 None。
接著執行 transformations_from_settings_or_context(),
我們有放入 service_comtext 參數, 所以最終會回傳預設的設定:

return settings.transformations

而 settings.transformations 的由來是:

self._transformations = [self.node_parser]

node_parser 是 SentenceSplitter 物件

self._node_parser = SentenceSplitter()

剛才的回傳結果就是下面這個串列
[SentenceSplitter(include_metadata=True, include_prev_next_rel=True, callback_manager=<llama_index.core.callbacks.base.CallbackManager object at 0x0000015DC58807C0>, id_func=<function default_id_func at 0x0000015DC233D0D0>, chunk_size=1024, chunk_overlap=200, separator=' ', paragraph_separator='\n\n\n', secondary_chunking_regex='[^,.;。?!]+[,.;。?!]?')]
上述寫好了文件切個與行程節點的規則, 稍後會一個個步驟講解。

接著開始為 documents 做標記 id

for doc in documents:
    docstore.set_document_hash(doc.get_doc_id(), doc.hash)

而 doc.get_doc_id() 的功能就是取得 documents 的 id

def get_doc_id(self) -> str: """TODO: Deprecated: Get document ID.""" return self.id_

self.id_ 看起來就像是

'109f26d7-6f6f-4e3f-b16d-04122bdb3d65'

第二個參數 doc.hash:
主要是做哈希編碼

def hash(self) -> str: doc_identity = str(self.text) + str(self.metadata) return str(sha256(doc_identity.encode("utf-8", "surrogatepass")).hexdigest())

self.text 就是資料裡面所以的文字
'申請人\r\n本人、父、母、祖父、祖母、戶長、同居人或撫養人。\r\n如係...
self.metadata 就是資料本身的資訊
{'file_path': 'C:\\Users\\Admin\\Desktop\\data\\出生地登記.txt', 'file_name': '出生地登記...

doc.hash 最後回傳值為:
'7f8e0bce3caef174c6e24cb4e76f33799bc0b6a587eaffd3bfd7301f5623525a'

參數理解過後, 回到函數

def set_document_hash(self, doc_id: str, doc_hash: str) -> None:
        """Set the hash for a given doc_id."""
        metadata = {"doc_hash": doc_hash}
        self._kvstore.put(doc_id, metadata, collection=self._metadata_collection)

目的是建立一個 doc_hash 的字典, 最後會將 metadata 儲存在 _kvstore.put 裡面, kvstore 就是 SimpleKVStore, 用於儲存 metadata 的類別。

因為我們有多個文件, 所以會有多個 doc

for doc in documents:
    docstore.set_document_hash(doc.get_doc_id(), doc.hash)

跑完之後就可以儲存完所有 doc 的 metadata

接著我們進行下一步: run_transformations
目的是將切片文件轉換成節點。

nodes = run_transformations(
                documents,  # type: ignore
                transformations,
                show_progress=show_progress,
                **kwargs,
            )

我們先來看參數:

documents 就是我們的原始文件, transformations 就是切片的必要資料, show_progress 就是是否輸出切片及轉節點的進度。

show_progress 預設是 False。