本电子书开源，欢迎 star 🌟，关注《LLM 应用开发实践笔记》

> **交流群** 创建了一个LLM应用开发交流群，有需要的可以选择加入
![](../images/group.png)


我的新书[《LangChain编程从入门到实践》](https://u.jd.com/V8pkqFY) 已经开售！推荐正在学习AI应用开发的朋友购买阅读！
[![LangChain编程从入门到实践](../../images/langchain-book.jpg "LangChain编程从入门到实践")](https://u.jd.com/V8pkqFY)

## LlamaIndex索引


### 列表索引

> 它对于综合跨多个数据源的信息的答案非常有用

列表索引是一种简单的数据结构，其中节点按顺序存储。在索引构建期间，文档文本被分块、转换为节点并存储在列表中。

在查询期间，如果没有指定其他查询参数，LlamaIndex只是将列表中的所有node加载到Response Synthesis模块中。

列表索引提供了许多查询列表索引的方法，从基于嵌入的查询中获取前k个邻居，或者添加一个关键字过滤器，如下所示:

![img](https://www.luxiangdong.com/images/llamaindex/5.png)

LlamaIndex为列表索引提供Embedding支持。除了每个节点存储文本之外，每个节点还可以选择存储Embedding。在查询期间，我们可以在调用LLM合成答案之前，使用Embeddings对节点进行最大相似度检索。

在**索引构建过程中，LlamaIndex不会生成Embedding，而是在查询时生成**，这种设计避免了在索引构建期间为所有文本块生成Embeddings，这可能会导致大量数据的开销。

### 向量存储索引

> 它是最常见且易于使用的，允许对大型数据语料库回答查询

默认情况下，`GPTVectorStoreIndex `使用内存中的` SimpleVectorStore`作为默认存储上下文的一部分初始化，**基于向量存储的索引在索引构建期间生成Embeddings**

![img](https://www.luxiangdong.com/images/llamaindex/7.png)

### 树状索引

> 它对总结一组文件很有用

树状索引是树结构索引，其中每个节点是子节点的摘要。在索引构建期间，树以自下而上的方式构建，直到我们最终得到一组根节点。树状索引从一组节点(成为该树中的叶节点)构建层次树。

查询树状索引涉及从根节点向下遍历到叶节点。默认情况下(`child_branch_factor=1 `)，查询在给定父节点的情况下选择一个子节点。如果`child_branch_factor=2`，则查询在每个级别选择两个子节点。

![img](https://www.luxiangdong.com/images/llamaindex/9.png)

为了在查询期间构建树状索引，我们需要将**retriver_mode**和**response_mode**添加到查询引擎，并将GPTTreeIndex中的build_tree参数设置为False

```python
index_light = GPTTreeIndex.from_documents(documents, build_tree=False)
query_engine = index_light.as_query_engine(
    retriever_mode="all_leaf",
    response_mode='tree_summarize',
)
query_engine.query("What is net operating income?")
```

### 关键词表索引

> 他对于将查询路由到不同的数据源非常有用

关键字表索引从每个Node提取关键字，并构建从每个关键字到该关键字对应的Node的映射。

在查询时，从查询中提取相关关键字，并将其与预提取的Node关键字进行匹配，获取相应的Node。提取的节点被传递到响应合成模块。

![img](https://www.luxiangdong.com/images/llamaindex/11.png)

- **`GPTKeywordTableIndex`**- 使用LLM从每个文档中提取关键字，这意味着它确实需要在构建期间调用LLM

- **`GPTSimpleKeywordTableIndex`**- 使用regex关键字提取器从每个文档中提取关键字，在构建期间不会调用LLM

### 可组合性图索引

> 它对于构建知识图谱很有用

使用LlamaIndex，您可以通过在现有索引之上构建索引来创建复合索引。该特性使您能够有效地索引完整的文档层次结构，并为GPT提供量身定制的知识。

通过利用可组合性，可以在多个级别定义索引，例如为单个文档定义低级索引，为文档组定义高级索引。考虑下面的例子:

- 可以为每个文档中的文本创建树索引。
- 生成一个列表索引，涵盖所有的树索引为整个文档集合。

通过一个场景来演示可组合性图索引的能力:

- 从多个文档创建树索引
- 从树索引生成摘要
- 接下来在3个树索引的顶部创建一个列表索引的图，因为列表索引适合于合成跨多个数据源组合信息的答案
- 最后查询图

```python
from llama_index.indices.composability import ComposableGraph

from langchain.chat_models import ChatOpenAI
from llama_index import LLMPredictor

# setting up vector indicies for each year
service_context = ServiceContext.from_defaults(chunk_size_limit=512)
index_set = {}
for year in years:
    storage_context = StorageContext.from_defaults()
    cur_index = GPTVectorStoreIndex.from_documents(
        documents=doc_set[year],
        service_context=service_context,
        storage_context=storage_context
    )
    index_set[year] = cur_index
    # store index in the local env, so you don't need to do it over again
    storage_context.persist(f'./storage_index/apple-10k/{year}')

# define an LLMPredictor set number of output tokens
llm_predictor = LLMPredictor(llm=ChatOpenAI(temperature=0, max_tokens=512, model_name='gpt-3.5-turbo'))
service_context = ServiceContext.from_defaults(llm_predictor=llm_predictor)
storage_context = StorageContext.from_defaults()\

## define a list index over the vector indicies 
## allow us to synthesize information across  each index
index_summary = [index_set[year].as_query_engine().query("Summary this document in 100 words").response for year in years]
graph = ComposableGraph.from_indices(
    GPTListIndex,
    [index_set[y] for y in years],
    index_summaries=index_summary,
    service_context=service_context,
    storage_context=storage_context
)

root_id = graph.root_id

#save to disk
storage_context.persist(f'./storage_index/apple-10k/root')

## querying graph
custom_query_engines = {
    index_set[year].index_id: index_set[year].as_query_engine() for year in years
}

query_engine = graph.as_query_engine(
    custom_query_engines=custom_query_engines
)

response = query_engine.query("Outline the financial statement of Q2 2023")
response.response
```

### Pandas索引和SQL索引

> 它对结构化数据很有用

```python
from llama_index.indices.struct_store import GPTPandasIndex
import pandas as pd

df = pd.read_csv("titanic_train.csv")

index = GPTPandasIndex(df=df)

query_engine = index.as_query_engine(
    verbose=True
)
response = query_engine.query(
    "What is the correlation between survival and age?",
)
```

### 文档摘要索引

> 这是一个全新的LlamaIndex数据结构，它是为了问答而制作的。

通常，大多数用户以以下方式开发基于LLM的QA系统:

1. 获取源文档并将其分成文本块。
2. 然后将文本块存储在矢量数据库中。
3. 在查询期间，通过使用相似度和/或关键字过滤器进行Embedding来检索文本块。
4. 执行整合后的响应。

然而，这种方法存在一些影响检索性能的局限性：

1. 文本块没有完整的全局上下文，这通常限制了问答过程的有效性。
2. 需要仔细调优top-k /相似性分数阈值，因为过小的值可能会导致错过相关上下文，而过大的值可能会增加不相关上下文的成本和延迟。
3. Embeddings可能并不总是为一个问题选择最合适的上下文，因为这个过程本质上是分别决定文本和上下文的。

为了增强检索结果，一些开发人员添加了关键字过滤器。然而，这种方法有其自身的挑战，例如通过手工或使用NLP关键字提取/主题标记模型为每个文档确定适当的关键字，以及从查询中推断正确的关键字。

![img](https://www.luxiangdong.com/images/llamaindex/14.png)

这就是 LlamaIndex 引入文档摘要索引的原因，它可以为每份文档提取非结构化文本摘要并编制索引，从而提高检索性能，超越现有方法。该索引比单一文本块包含更多信息，比关键字标签具有更多语义。它还允许灵活的检索，包括基于 LLM 和嵌入的方法。在构建期间，该索引接收文档并使用 LLM 从每个文档中提取摘要。在查询时，它会根据摘要使用以下方法检索相关文档：

- 基于 LLM 的检索：获取文档摘要集合并请求 LLM 识别相关文档+相关性得分
- 基于嵌入的检索：利用摘要嵌入相似性来检索相关文档，并对检索结果的数量施加顶k限制。

文档摘要索引的检索类为任何选定的文档检索所有节点，而不是在节点级返回相关块。

### 知识图谱索引

它通过在一组文档中提取知识三元组（主语、谓语、宾语）来建立索引。
在查询时，它既可以只使用知识图谱作为上下文进行查询，也可以利用每个实体的底层文本作为上下文进行查询。通过利用底层文本，我们可以针对文档内容提出更复杂的查询。

### 索引成本和索引时间

- 索引的成本是一个需要考虑的重要因素，在处理海量数据集时，这一点尤为重要。
- 索引速度，整个解决方案的运行准备时间。索引时间各不相同，但都是一次性的，通常 40 页的 PDF 文件大约需要 5 秒钟。试想一下，如果一个庞大的数据集超过 10 万页，可能需要几天时间，可以利用异步方法来缩短索引时间。