宣布 LlamaCloud 正式发布(以及我们的 1900 万美元 A 轮融资!)
LlamaIndex

LlamaIndex 2023-11-15

宣布 LlamaIndex 0.9

我们的勤奋团队很高兴宣布我们最新的重大版本 LlamaIndex 0.9!您现在就可以获取它

pip install --upgrade llama_index

在 LlamaIndex v0.9 中,我们花费时间改进了用户体验的几个关键方面,包括 token 计数、文本分割等等!

作为其中的一部分,有一些新功能和现有用法上的微小变化,开发者应该了解。

  • 用于摄取和转换数据的新概念 IngestionPipline
  • 数据摄取和转换现在会自动缓存
  • 节点解析/文本分割/元数据提取模块的接口已更新
  • 默认 tokenizer 的更改,以及自定义 tokenizer
  • PyPi 的打包/安装更改(减少臃肿,新增安装选项)
  • 更可预测和一致的导入路径
  • 此外,测试版中包含:用于处理文本和图像的多模态 RAG 模块!

有问题或疑虑?您可以在 GitHub 上报告问题,或者在我们的 Discord 上提问

继续阅读以了解我们新功能和更改的更多详情。

IngestionPipeline — 纯粹用于数据摄取的新抽象

有时,您可能只希望从数据源摄取和嵌入节点,例如当您的应用程序允许用户上传新数据时。LlamaIndex V0.9 中引入了 IngestionPipepline 的概念。

一个 IngestionPipeline 使用了对输入数据应用的新概念 Transformations

那么 Transformation 是什么呢?它可以是一个

  • 文本分割器
  • 节点解析器
  • 元数据提取器
  • 嵌入模型

以下是基本用法模式的快速示例

from llama_index import Document
from llama_index.embeddings import OpenAIEmbedding
from llama_index.text_splitter import SentenceSplitter
from llama_index.extractors import TitleExtractor
from llama_index.ingestion import IngestionPipeline, IngestionCache

pipeline = IngestionPipeline(
    transformations=[
        SentenceSplitter(chunk_size=25, chunk_overlap=0),
        TitleExtractor(),
        OpenAIEmbedding(),
    ]
)
nodes = pipeline.run(documents=[Document.example()])

Transformation 缓存

每次运行同一个 IngestionPipeline 对象时,它会缓存输入节点 + transformations 的哈希值以及管道中每个 transformation 的输出。

在后续运行中,如果发生缓存命中,该 transformation 将被跳过,并转而使用缓存结果。这大大加快了重复运行的速度,并有助于在决定使用哪些 transformations 时缩短迭代时间。

以下是一个保存和加载本地缓存的示例

from llama_index import Document
from llama_index.embeddings import OpenAIEmbedding
from llama_index.text_splitter import SentenceSplitter
from llama_index.extractors import TitleExtractor
from llama_index.ingestion import IngestionPipeline, IngestionCache

pipeline = IngestionPipeline(
    transformations=[
        SentenceSplitter(chunk_size=25, chunk_overlap=0),
        TitleExtractor(),
        OpenAIEmbedding(),
    ]
)
# will only execute full pipeline once
nodes = pipeline.run(documents=[Document.example()])
nodes = pipeline.run(documents=[Document.example()])
# save and load
pipeline.cache.persist("./test_cache.json")
new_cache = IngestionCache.from_persist_path("./test_cache.json")
new_pipeline = IngestionPipeline(
    transformations=[
        SentenceSplitter(chunk_size=25, chunk_overlap=0),
        TitleExtractor(),
    ],
    cache=new_cache,
)
# will run instantly due to the cache
nodes = pipeline.run(documents=[Document.example()])

以下是使用 Redis 作为缓存、Qdrant 作为向量存储的另一个示例。运行此操作将直接将节点插入到您的向量存储中,并将每个 transformation 步骤缓存在 Redis 中。

from llama_index import Document
from llama_index.embeddings import OpenAIEmbedding
from llama_index.text_splitter import SentenceSplitter
from llama_index.extractors import TitleExtractor
from llama_index.ingestion import IngestionPipeline, IngestionCache
from llama_index.ingestion.cache import RedisCache
from llama_index.vector_stores.qdrant import QdrantVectorStore

import qdrant_client
client = qdrant_client.QdrantClient(location=":memory:")
vector_store = QdrantVectorStore(client=client, collection_name="test_store")
pipeline = IngestionPipeline(
    transformations=[
        SentenceSplitter(chunk_size=25, chunk_overlap=0),
        TitleExtractor(),
        OpenAIEmbedding(),
    ],
    cache=IngestionCache(cache=RedisCache(), collection="test_cache"),
    vector_store=vector_store,
)
# Ingest directly into a vector db
pipeline.run(documents=[Document.example()])
# Create your index
from llama_index import VectorStoreIndex
index = VectorStoreIndex.from_vector_store(vector_store)

自定义 Transformations

实现自定义 transformations 非常简单!让我们添加一个 transform,以便在调用 embeddings 之前从文本中删除特殊字符。

对 transformations 的唯一真正要求是它们必须接受一个节点列表并返回一个节点列表。

import re
from llama_index import Document
from llama_index.embeddings import OpenAIEmbedding
from llama_index.text_splitter import SentenceSplitter
from llama_index.ingestion import IngestionPipeline
from llama_index.schema import TransformComponent

class TextCleaner(TransformComponent):
  def __call__(self, nodes, **kwargs):
    for node in nodes:
      node.text = re.sub(r'[^0-9A-Za-z ]', "", node.text)
    return nodes
pipeline = IngestionPipeline(
    transformations=[
        SentenceSplitter(chunk_size=25, chunk_overlap=0),
        TextCleaner(),
        OpenAIEmbedding(),
    ],
)
nodes = pipeline.run(documents=[Document.example()])

节点解析/文本分割 — 平坦化和简化的接口

我们使解析和分割文本的接口变得更加简洁。

之前

from llama_index.node_parser import SimpleNodeParser
from llama_index.node_parser.extractors import (
	MetadataExtractor, TitleExtractor
) 
from llama_index.text_splitter import SentenceSplitter

node_parser = SimpleNodeParser(
  text_splitter=SentenceSplitter(chunk_size=512),
  metadata_extractor=MetadataExtractor(
  extractors=[TitleExtractor()]
 ),
)
nodes = node_parser.get_nodes_from_documents(documents)

之后

from llama_index.text_splitter import SentenceSplitter
from llama_index.extractors import TitleExtractor 

node_parser = SentenceSplitter(chunk_size=512)
extractor = TitleExtractor()

# use transforms directly
nodes = node_parser(documents)
nodes = extractor(nodes)

以前,LlamaIndex 中的 NodeParser 对象变得极其臃肿,包含了文本分割器和元数据提取器,这给用户更改这些组件带来了困扰,也给我们维护和开发它们带来了麻烦。

在 V0.9 中,我们将整个接口平坦化为一个单一的 TransformComponent 抽象,以便更轻松地设置、使用和自定义这些 transformations。

我们已尽力最小化对用户的影响,但需要注意的主要一点是 SimpleNodeParser 已被移除,其他节点解析器和文本分割器已被提升以拥有相同的功能,只是使用了不同的解析和分割技术。

任何旧的 SimpleNodeParser 导入都将重定向到最等效的模块 SentenceSplitter

此外,包装对象 MetadataExtractor 已被移除,改为直接使用提取器。

所有这些的完整文档可在下方找到

Tokenization 和 Token 计数 — 改进的默认设置和自定义

以前 LlamaIndex 的一个主要痛点是 tokenization。许多组件使用不可配置的 gpt2 tokenizer 进行 token 计数,这让使用非 OpenAI 模型或甚至 OpenAI 模型(如此处所示的一些临时修复)的用户感到头痛!

在 LlamaIndex V0.9 中,这个全局 tokenizer 现在可配置,并默认为 CL100K tokenizer,以匹配我们默认的 GPT-3.5 LLM。

对 tokenizer 的唯一要求是它必须是一个可调用函数,接受一个字符串并返回一个列表。

配置此项的一些示例如下

from llama_index import set_global_tokenizer

# tiktoken
import tiktoken
set_global_tokenizer(
  tiktoken.encoding_for_model("gpt-3.5-turbo").encode
)
# huggingface
from transformers import AutoTokenizer
set_global_tokenizer(
  AutoTokenizer.from_pretrained("HuggingFaceH4/zephyr-7b-beta").encode
)

此外,TokenCountingHandler 得到了升级,提供了更好的 token 计数功能,并且在可用时直接使用 API 响应中的 token 计数。

打包 — 减少臃肿

为了现代化 LlamaIndex 的打包方式,V0.9 也带来了安装方面的变化。

这里最大的变化是 LangChain 现在是一个可选包,默认情况下不会安装。

要将 LangChain 作为 llama-index 安装的一部分进行安装,您可以按照下面的示例操作。根据您的需求,还有其他安装选项,我们也欢迎未来对附加组件做出更多贡献。

# installs langchain
pip install llama-index[langchain]
 
# installs tools needed for running local models
pip install llama-index[local_models]

# installs tools needed for postgres
pip install llama-index[postgres]

# combinations!
pip isntall llama-index[local_models,postgres]

如果您之前在代码中导入了 langchain 模块,请相应地更新您的项目打包要求。

导入路径 — 更一致和可预测

我们对导入路径做了两项更改

  1. 我们移除了根级别中不常用的导入,以加快导入 llama_index 的速度
  2. 我们现在有了一个一致的策略,将“用户界面”概念在 level-1 模块中可导入。
from llama_index.llms import OpenAI, ...
from llama_index.embeddings import OpenAIEmbedding, ...
from llama_index.prompts import PromptTemplate, ...
from llama_index.readers import SimpleDirectoryReader, ...
from llama_index.text_splitter import SentenceSplitter, ...
from llama_index.extractors import TitleExtractor, ...
from llama_index.vector_stores import SimpleVectorStore, ...

我们仍然在根级别公开一些最常用的模块。

from llama_index import SimpleDirectoryReader, VectorStoreIndex, ...

多模态 RAG

鉴于最近 GPT-4V API 的发布,多模态用例比以往任何时候都更容易实现。

为了帮助用户使用这些功能,我们开始引入一些新模块,以支持多模态 RAG 的用例

  • 多模态 LLM(GPT-4V、Llava、Fuyu 等)
  • 用于联合图像-文本嵌入/检索的多模态嵌入(例如 clip)
  • 多模态 RAG,结合了索引和查询引擎

我们的文档中有一个关于多模态检索的完整指南

感谢大家的支持!

作为一个开源项目,没有我们数百位贡献者的支持,我们将无法存在。我们非常感谢他们以及全球数十万 LlamaIndex 用户的支持。Discord 上见!