### [AI / ML領域相關學習筆記入口頁面](https://hackmd.io/@YungHuiHsu/BySsb5dfp)
#### [Deeplearning.ai GenAI/LLM系列課程筆記](https://learn.deeplearning.ai/)
##### GenAI
- [Large Language Models with Semantic Search。大型語言模型與語義搜索 ](https://hackmd.io/@YungHuiHsu/rku-vjhZT)
- [LangChain for LLM Application Development。使用LangChain進行LLM應用開發](https://hackmd.io/1r4pzdfFRwOIRrhtF9iFKQ)
- [Finetuning Large Language Models。微調大型語言模型](https://hackmd.io/@YungHuiHsu/HJ6AT8XG6)
##### RAG
- [Preprocessing Unstructured Data for LLM Applications。大型語言模型(LLM)應用的非結構化資料前處理](https://hackmd.io/@YungHuiHsu/BJDAbgpgR)
- [Building and Evaluating Advanced RAG。建立與評估進階RAG](https://hackmd.io/@YungHuiHsu/rkqGpCDca)
- [[GenAI][RAG] Multi-Modal Retrieval-Augmented Generation and Evaluaion。多模態的RAG與評估
](https://hackmd.io/@YungHuiHsu/B1LJcOlfA)
##### AI Agents
- [AI Agents in LangGraph](https://hackmd.io/@YungHuiHsu/BJTKpkEHC)
##### Framework
- [LlamaIndex - Data Storage Architecture Design and Base Components<br>LlamaIndex - 資料儲存的架構設計與基礎元件](https://hackmd.io/@YungHuiHsu/HJ6iqpmPC)
- [Llamaindex 與 Langchain的多模態檢索實作](https://hackmd.io/@YungHuiHsu/S1f8gEBD0)
---
# LlamaIndex - Data Storage Architecture Design and Base Components<br>LlamaIndex - 資料儲存的架構設計與基礎元件
## Retrieval Augmented Generation (RAG)
:::info
大型語言模型(LLMs)訓練於大量資料上,但它們並沒有訓練私有資料。RAG 通過將個人/公司的私有資料添加到 LLMs 已經能夠訪問的資料中來解決這個問題
![image](https://hackmd.io/_uploads/BkjqxEVw0.png =600x)
:::
### 檢索增強生成 (Retrieval Augmented Generation, RAG)
- RAG 的階段
![image](https://hackmd.io/_uploads/rJtKgNVD0.png =600x)
RAG 有五個關鍵階段,這些階段將成為構建的大多數大型應用程序的一部分 :
1. **加載(Loading)**:
- 將資料從存儲位置(無論是文本文件、PDF、其他網站、資料庫或 API)加載到管道中
2. **索引(Indexing)**:
- 創建一個資料結構以便於查詢資料。對於 LLMs 來說,這幾乎總是意味著創建向量嵌入(vector embeddings),這些向量嵌入是資料含義的數值表示,還有許多其他元資料(metadata)策略,以便能夠準確地找到上下文相關的資料
3. **存儲(Storing)**:
- 一旦資料被索引,幾乎總是希望存儲索引以及其他元資料,以避免重新索引
4. **查詢(Querying)**:
- 對於任何給定的索引策略,有很多方法可以利用 LLMs 和 LlamaIndex 資料結構進行查詢,包括子查詢、多步查詢和混合策略
5. **評估(Evaluation)**:
- 任何管道中的關鍵步驟是檢查它相對於其他策略的效果,或者當進行更改時進行檢查。評估提供了關於查詢回應的準確性、忠實性和速度的客觀衡量標準
以下補充幾個LlamaIndex設計上比較令人困惑卻很重要的基礎元件
## Storing
![image](https://hackmd.io/_uploads/rJtvWAXwR.png =600x)
LlamaIndex 提供了一個高階介面,用於導入、索引和查詢外部資料
在底層,LlamaIndex 還支持可替換的存儲組件,允你自定義以下內容:
- **Document stores**:儲存導入的文件(即 Node 對象)的地方。
- **Index stores**:儲存索引元資料的地方。
- **Vector stores**:儲存嵌入向量的地方。
- **Graph stores**:儲存知識圖譜的地方(即 KnowledgeGraphIndex)。
- **Chat Stores**:儲存和組織聊天訊息的地方。
Document/Index stores 依賴於一個通用的 Key-Value 存儲抽象
LlamaIndex 支持將資料持久化到由 [fsspec](https://filesystem-spec.readthedocs.io/en/latest/index.html) 支持的任何存儲後端。
- 利用`StorageContext` object 來存放
```python=
from llama_index.core.storage.docstore import SimpleDocumentStore
from llama_index.core.storage.index_store import SimpleIndexStore
from llama_index.core.vector_stores import SimpleVectorStore
from llama_index.core import StorageContext
# create storage context using default stores
storage_context = StorageContext.from_defaults(
docstore=SimpleDocumentStore(),
vector_store=SimpleVectorStore(),
index_store=SimpleIndexStore(),
)
```
### Langchain的VectorStore
![image](https://hackmd.io/_uploads/rJwQKmHv0.png =400x)
Vector store workflow from doc to store. Source: deeplearning.ai
## [Indexing](https://docs.llamaindex.ai/en/stable/module_guides/indexing/index_guide/)
:::success
索引(Index)是一種資料結構,它允許快速檢索與用戶查詢相關的上下文。對於 LlamaIndex 來說,它是增強生成(Retrieval-Augmented Generation,RAG)使用案例的核心基礎。
從高層次來看,索引是從文檔(Documents)構建而來的。它們被用來構建查詢引擎(Query Engines)和聊天引擎(Chat Engines),從而實現對資料的問答和聊天功能。
在底層,索引將資料存儲在節點對象(Node objects)中(這些節點表示原始文檔的塊),並提供一個檢索器(Retriever)接口,該接口支持額外的配置和自動化。
> An Index is a data structure that allows us to quickly retrieve relevant context for a user query.
:::
- **Node**:對應於`Document`中的一段文本。LlamaIndex 會接收 `Document` 對象,並在內部解析/分塊成 Node 對象。
- **Response Synthesis**: 在給定檢索到的 Node 後生成回應。
![image](https://hackmd.io/_uploads/SyTfXyEDC.png =400x)
## [Loading](https://docs.llamaindex.ai/en/stable/module_guides/loading/)
### [Node](https://docs.llamaindex.ai/en/stable/module_guides/loading/documents_and_nodes/root.html#nodes)
:::success
:pencil2:在LlamaIndex中,`Node`即是用來儲存被切塊向量(chunked Embedding)的資料結構
> a “chunk” of a source Document, whether that is a text chunk, an image, or other. Similar to Documents, they contain metadata and relationship information with other nodes.
:::
![image](https://hackmd.io/_uploads/Sye7ksmsT.png =600x)
>[2023.09。Raphael Mansuy。LlamaIndex. Chunk, Index, Query: How LlamaIndex Unlocks Custom LLMs](https://medium.com/@raphael.mansuy/llamaindex-chunk-index-query-how-llamaindex-unlocks-custom-llms-329d543a06b7)
- **文件(Document)**
- 是任何資料源的泛用容器,例如PDF文件、API輸出或從資料庫檢索的資料。
> a generic container around any data source - for instance, a PDF, an API output, or retrieved data from a database.
- 文件可以手動構建,也可以通過資料加載器自動創建
- 默認情況下,文件存儲文本以及一些其他屬性,如元資料(`metadata`)和關係(`relationships`)等
- `metadata`是可以附加到文本的註解字典
- `relationships`是包含與其他文件/節點關係的字典
```python=
from llama_index import Document, VectorStoreIndex
text_list = [text1, text2, ...]
documents = [Document(text=t) for t in text_list]
# build index
index = VectorStoreIndex.from_documents(documents)
```
- **節點(Node)**
- 代表源文件的一個「塊」(Chunked Embedding),無論是被切塊的文本或其他資料(例如圖像)及其相應的向量嵌入
- Node不僅僅是用於儲存向量嵌入,它還承載了文檔處理流程中的語義信息、結構化資料以及文檔間的相互關係
- 節點在LlamaIndex中是一等公民(first-class citizen),可以直接定義節點及其所有屬性,也可以選擇通過`NodeParser`類將源文件「解析」成節點
- 默認情況下,每個從文件衍生的(derived)節點都會繼承該文件的相同元資料(例如,文件中的「file_name」字段會傳播到每個節點)
- 原始文件中的元資料會被複製到節點中
> "By default every Node derived from a Document will inherit the same metadata from that Document (e.g. a 'file_name' filed in the Document is propagated to every Node)"
```python=
from llama_index.node_parser import SentenceSplitter
# load documents
...
# parse nodes
parser = SentenceSplitter()
nodes = parser.get_nodes_from_documents(documents)
# build index
index = VectorStoreIndex(nodes)
```
因此,文件到節點的關係可以理解為從一個泛用的資料容器到其具體內容片段的細化(Refine)。這種細化使得對文件的處理可以更加靈活和高效,特別是在涉及到對大型文檔進行索引、檢索和分析時。節點的概念允許LlamaIndex將文件的不同部分獨立處理,並在需要時重用文件的元資料和關係信息
- TextNode與Document間的繼承關係
- **繼承關係**:`Document` 類是從 `TextNode` 類繼承來的。由於 `TextNode` 本身就是 `BaseNode` 的子類,專門用於處理含有文本內容的節點,`Document` 類進一步專化了 `TextNode`,用於表達一份文檔。
- **功能擴展**:`Document` 類繼承了 `TextNode` 的所有屬性和方法(如文本內容、元資料處理等),並且可以添加新的屬性和方法,或者覆寫現有方法,以滿足特殊需求。例如,`Document` 類可能添加了一個唯一的文檔標識符屬性(`id_`),以便在索引和檢索時區分不同的文檔。
- **使用場景**:`Document` 類的設計使其成為處理和表示文檔資料的理想結構。除了包含文檔的內容和元資料,還可能包括一些特定於文檔的方法和邏輯,例如與其他格式或系統的轉換方法。
```python=
# Node classes for indexes
class BaseNode(BaseComponent):
"""Base node Object.
Generic abstract interface for retrievable nodes
"""
class TextNode(BaseNode):
class Document(TextNode):
"""Generic interface for a data document.
This document connects to data sources.
"""
```
```mermaid
classDiagram
direction LR
BaseNode <-- TextNode
TextNode <-- Document
class BaseNode{
<<abstract>>
+String text
+Dict metadata
+get_content()
+get_metadata_str()
}
class TextNode{
+String start_char_idx
+String end_char_idx
+String text_template
+String metadata_template
+String metadata_seperator
+get_node_info()
+get_text()
}
class Document{
+String id_
+to_langchain_format()
+from_langchain_format()
+to_haystack_format()
+from_haystack_format()
+to_embedchain_format()
+from_embedchain_format()
+to_semantic_kernel_format()
+from_semantic_kernel_format()
}
```
- `VectorStoreIndex`
```python=
VectorStoreIndex(
nodes: Optional[Sequence[llama_index.core.schema.BaseNode]] = None,
...
```
- `nodes` 參數是一個可選的序列,它可以包含零到多個 `llama_index.core.schema.BaseNode` 類型的物件。在 `llama-index` 的核心架構中,`BaseNode` 是一個基礎類別,代表系統中的一個基本節點,這種節點可能包含文本內容、元資料等信息。
- **使用目的**:此參數主要用於在初始化或設置某些組件時提供一系列節點作為輸入。這些節點可以代表不同形式的文本資料,例如文章、段落、句子等,每個節點都攜帶了用於後續處理的相關信息。
- **靈活適用性**:通過接收一系列 `BaseNode` 類型的物件作為輸入,`llama-index` 能夠處理多種類型的文本資料,不論是單一文件、文件集合,還是文件的特定部分。這種設計提供了極大的靈活性,使得 `llama-index` 能夠適應多樣化的文本處理需求。
## [ServiceContext](https://docs.llamaindex.ai/en/latest/module_guides/supporting_modules/service_context.html)
:::success
:pencil2: `ServiceContext`是LlamaIndex中用於集中配置LLM、嵌入模型和其他處理組件的設定對象
:::
:::warning
v0.10.0(2024.0213)起service context 廢棄使用
在[Building and Evaluating Advanced RAG。建立與評估進階RAG](https://hackmd.io/@YungHuiHsu/rkqGpCDca)課程內容仍是舊版
[Deprecated ServiceContext。Updating to v0.10.0](https://docs.llamaindex.ai/en/stable/getting_started/v0_10_0_migration.html)
```python=?
# from llama_index import ServiceContext, set_global_service_context
# service_context = ServiceContext.from_defaults(
# llm=llm, embed_model=embed_model, chunk_size=512)
# set_global_service_context(service_context)
from llama_index.core import Settings
Settings.llm = llm
Settings.embed_model = embed_model
Setting.chunk_size = 512
```
:::
在LlamaIndex管Pipeline各階段中,`ServiceContext`彙集所需的各種資源和設定。這樣的設計可以方便地重用和修改這些設定,而不必在管線的每個部分都進行個別配置。這在實際操作中顯示為一個簡化的過程,例如在使用VectorStoreIndex.from_documents這樣的方法時,可以通過一行代碼就完成文件的攝取、分塊、嵌入和索引的過程
- `ServiceContext` code example
參數說明:
* `llm`:用於生成自然語言回應查詢的LLM。如果未提供,則默認使用OpenAI的gpt-3.5-turbo。如果未設置OpenAI密鑰,則默認使用Llama.cpp的llama2-chat-13B
* `prompt_helper`:
* 功能主要是輔助處理**與LLM相關的文本**,例如處理文本塊以適應(fit)LLM的上下文窗口大小限制(截斷)和重新打包文本塊,並非直接參與(Ingestion)、分塊(Chunking)、嵌入(Embedding)和索引(Indexing)的過程。
* `embed_model`:用於生成文本向量表示的嵌入模型()
* `node_parser`:將文檔轉換為節點(儲存embedding)的解析器
* `callback_manager`:在事件上調用其處理程序的回調管理器對象。提供基本的日誌記錄和跟蹤能力
:::spoiler
```python=
from llama_index import (
ServiceContext,
OpenAIEmbedding,
PromptHelper,
)
from llama_index.llms import OpenAI
from llama_index.text_splitter import SentenceSplitter
llm = OpenAI(model="text-davinci-003",
temperature=0,
max_tokens=256)
embed_model = OpenAIEmbedding()
text_splitter = SentenceSplitter(chunk_size=1024,
chunk_overlap=20)
prompt_helper = PromptHelper(context_window=4096,
num_output=256,
chunk_overlap_ratio=0.1,
chunk_size_limit=None,
)
service_context = ServiceContext.from_defaults(llm=llm,
embed_model=embed_model,
text_splitter=text_splitter,
prompt_helper=prompt_helper,
)
```
:::
# Data Schema for Llamaindex
- [llama-index-core/llama_index/core/schema.py](https://github.com/run-llama/llama_index/blob/main/llama-index-core/llama_index/core/schema.py)
```mermaid
classDiagram
direction LR
BaseNode <|-- TextNode
TextNode <|-- ImageNode
TextNode <|-- Document
Document <|-- ImageDocument
ImageNode <|-- ImageDocument
class BaseNode {
+id_: str
+embedding: List
+metadata: Dict
+excluded_embed_metadata_keys: List
+excluded_llm_metadata_keys: List
+relationships: Dict
}
class TextNode {
+text: str
+start_char_idx: int
+end_char_idx: int
+text_template: str
+metadata_template: str
+metadata_separator: str
}
class ImageNode {
+image: str
+image_path: str
+image_url: str
+image_mimetype: str
+text_embedding: List
}
class Document {
+id_: str
+doc_id: str
}
```
> 注意,這是用物件導向的class 繼承關係呈現
> `TextNode`與`ImageNode`在同位階
> `Document`與`ImageDocument`在同位階
> ![image](https://hackmd.io/_uploads/H1pywuYPA.png =400x)
```python=
class BaseNode(BaseComponent):
"""Base node Object.
Generic abstract interface for retrievable nodes
"""
"""
metadata fields
- injected as part of the text shown to LLMs as context
- injected as part of the text for generating embeddings
- used by vector DBs for metadata filtering
"""
class ImageNode(TextNode):
"""Node with image."""
# TODO: store reference instead of actual image
# base64 encoded image str
class Document(TextNode):
"""Generic interface for a data document.
This document connects to data sources.
"""
class ImageDocument(Document, ImageNode):
"""Data document containing an image."""
```
#### DocumentStore、VectorStore、IndexStore
```mermaid
classDiagram
class StorageContext {
+docstore: BaseDocumentStore
+index_store: BaseIndexStore
+vector_stores: Dict
+graph_store: GraphStore
}
class SimpleDocumentStore {
+simple_kvstore: SimpleKVStore
+namespace: str
+batch_size: int
}
class SimpleIndexStore {
+simple_kvstore: SimpleKVStore
}
class SimpleVectorStore {
+stores_text: bool
+_data: SimpleVectorStoreData
+_fs: fsspec.AbstractFileSystem
}
StorageContext --> SimpleDocumentStore : uses
StorageContext --> SimpleIndexStore : uses
StorageContext --> SimpleVectorStore : uses
```
```python=
class SimpleVectorStore(VectorStore):
"""Simple Vector Store.
In this vector store, embeddings are stored within a simple, in-memory dictionary.
Args:
simple_vector_store_data_dict (Optional[dict]): data dict
containing the embeddings and doc_ids. See SimpleVectorStoreData
for more details.
"""
class SimpleDocumentStore(KVDocumentStore):
"""Simple Document (Node) store.
An in-memory store for Document and Node objects.
Args:
simple_kvstore (SimpleKVStore): simple key-value store
namespace (str): namespace for the docstore
"""
class SimpleIndexStore(KVIndexStore):
"""Simple in-memory Index store.
Args:
simple_kvstore (SimpleKVStore): simple key-value store
"""
```