跳转到内容

定义和自定义文档

文档可以通过数据加载器自动创建,也可以手动构建。

默认情况下,我们所有的数据加载器(包括LlamaHub上提供的那些)都通过load_data函数返回Document对象。

from llama_index.core import SimpleDirectoryReader
documents = SimpleDirectoryReader("./data").load_data()

您也可以选择手动构建文档。LlamaIndex 提供了 Document 结构体。

from llama_index.core import Document
text_list = [text1, text2, ...]
documents = [Document(text=t) for t in text_list]

为了加速原型设计和开发,您也可以使用一些默认文本来快速创建文档:

document = Document.example()

本节涵盖自定义 Document 对象的各种方法。由于 Document 对象是我们 TextNode 对象的子类,所有这些设置和细节同样适用于 TextNode 对象类。

文档还提供了包含有用元数据的机会。通过在每个文档上使用 metadata 字典,可以包含额外信息来帮助指导响应并追踪查询响应的来源。这些信息可以是任何内容,例如文件名或类别。如果您正在与向量数据库集成,请记住某些向量数据库要求键必须是字符串,值必须是扁平类型(即 strfloatint)。

在每个文档的 metadata 字典中设置的任何信息都将显示在从该文档创建的每个源节点的 metadata 中。此外,这些信息包含在节点中,使索引能够在查询和响应时利用它。默认情况下,元数据会被注入到文本中,用于嵌入和LLM模型调用。

有几种方法可以设置这个字典:

  1. 在文档构造函数中:
document = Document(
text="text",
metadata={"filename": "<doc_file_name>", "category": "<category>"},
)
  1. 文档创建后:
document.metadata = {"filename": "<doc_file_name>"}
  1. 使用 SimpleDirectoryReaderfile_metadata 钩子自动设置文件名。这将自动在每个文档上运行钩子来设置 metadata 字段:
from llama_index.core import SimpleDirectoryReader
filename_fn = lambda filename: {"file_name": filename}
# automatically sets the metadata of each document according to filename_fn
documents = SimpleDirectoryReader(
"./data", file_metadata=filename_fn
).load_data()

文档管理章节所述,doc_id用于实现索引中文档的高效刷新。使用SimpleDirectoryReader时,您可以将文档doc_id自动设置为每个文档的完整路径:

from llama_index.core import SimpleDirectoryReader
documents = SimpleDirectoryReader("./data", filename_as_id=True).load_data()
print([x.doc_id for x in documents])

你也可以直接设置任何 Documentdoc_id

document.doc_id = "My new document id!"

注意:ID也可以通过Document对象上的node_idid_属性设置,类似于TextNode对象。

上文提到的一个关键细节是,默认情况下,您设置的任何元数据都会包含在嵌入生成和LLM中。

通常,一个文档可能包含许多元数据键,但在响应合成过程中,您可能不希望所有键都对LLM可见。在上述示例中,我们可能不希望LLM读取文档的file_name。然而,file_name可能包含有助于生成更好嵌入的信息。这样做的一个关键优势是可以在不改变LLM最终读取内容的情况下,为检索任务优化嵌入表示。

我们可以像这样排除它:

document.excluded_llm_metadata_keys = ["file_name"]

然后,我们可以使用 get_content() 函数并指定 MetadataMode.LLM 来测试LLM实际最终会读取什么内容:

from llama_index.core.schema import MetadataMode
print(document.get_content(metadata_mode=MetadataMode.LLM))

类似于定制LLM可见的元数据,我们也可以定制嵌入模型可见的元数据。在这种情况下,如果您不希望特定文本影响嵌入结果,可以专门排除嵌入模型可见的元数据。

document.excluded_embed_metadata_keys = ["file_name"]

然后,我们可以使用 get_content() 函数并指定 MetadataMode.EMBED 来测试嵌入模型实际会读取的内容:

from llama_index.core.schema import MetadataMode
print(document.get_content(metadata_mode=MetadataMode.EMBED))

如您所知,当发送到LLM或嵌入模型时,元数据会被注入到每个文档/节点的实际文本中。默认情况下,此元数据的格式由三个属性控制:

  1. Document.metadata_seperator -> 默认值 = "\n"

在拼接元数据的所有键/值字段时,此字段控制每个键/值对之间的分隔符。

  1. Document.metadata_template -> 默认值 = "{key}: {value}"

此属性控制元数据中每个键/值对的格式化方式。两个变量 keyvalue 字符串键是必需的。

  1. Document.text_template -> 默认值 = {metadata_str}\n\n{content}

当您的元数据使用metadata_seperatormetadata_template转换为字符串后,此模板控制该元数据在与文档/节点文本内容结合时的显示形式。metadatacontent字符串键是必需的。

了解了这一切,让我们利用这些强大功能创建一个简短示例:

from llama_index.core import Document
from llama_index.core.schema import MetadataMode
document = Document(
text="This is a super-customized document",
metadata={
"file_name": "super_secret_document.txt",
"category": "finance",
"author": "LlamaIndex",
},
excluded_llm_metadata_keys=["file_name"],
metadata_seperator="::",
metadata_template="{key}=>{value}",
text_template="Metadata: {metadata_str}\n-----\nContent: {content}",
)
print(
"The LLM sees this: \n",
document.get_content(metadata_mode=MetadataMode.LLM),
)
print(
"The Embedding model sees this: \n",
document.get_content(metadata_mode=MetadataMode.EMBED),
)

我们有初步示例展示了使用大语言模型本身执行元数据提取。