主题
LLM 擅长理解数据,由此带来它最重要的用途之一:把日常人类语言(我们称之为非结构化数据)转换成具体、规整、可被计算机程序消费的格式。我们把这一过程的产物称为结构化数据。由于转换过程中往往会丢弃大量冗余内容,我们把这件事叫作抽取(extraction)。
LlamaIndex 结构化数据抽取的核心是 Pydantic 类:你用 Pydantic 定义一个数据结构,LlamaIndex 就会与 Pydantic 配合,把 LLM 的输出强制收敛到这个结构里。
什么是 Pydantic?
Pydantic 是一个被广泛使用的数据校验与转换库,它高度依赖 Python 的类型声明。那个项目的文档里有详尽的指南,这里我们只把最基本的内容过一遍。
要创建 Pydantic 类,继承 Pydantic 的 BaseModel 即可:
from pydantic import BaseModel
class User(BaseModel):
id: int
name: str = "Jane Doe"在这个例子里,你创建了一个含 id 和 name 两个字段的 User 类:id 声明为整数,name 声明为字符串且默认值是 Jane Doe。
通过嵌套这些模型,你可以构造更复杂的结构:
from typing import List, Optional
from pydantic import BaseModel
class Foo(BaseModel):
count: int
size: Optional[float] = None
class Bar(BaseModel):
apple: str = "x"
banana: str = "y"
class Spam(BaseModel):
foo: Foo
bars: List[Bar]这样 Spam 就有了一个 foo 和一个 bars。Foo 有一个 count 和一个可选的 size,而 bars 是一个列表,其中每个对象各有 apple 与 banana 属性。
把 Pydantic 对象转成 JSON Schema
Pydantic 支持把它的类转成符合通行标准的 JSON 序列化 schema 对象。比如上面的 User 类会序列化成这样:
{
"properties": {
"id": {
"title": "Id",
"type": "integer"
},
"name": {
"default": "Jane Doe",
"title": "Name",
"type": "string"
}
},
"required": ["id"],
"title": "User",
"type": "object"
}这个性质至关重要:这些 JSON 格式的 schema 常常会被交给 LLM,而 LLM 反过来拿它作为「该如何返回数据」的说明。
使用注解
如前所述,LLM 会把 Pydantic 生成的 JSON schema 当作返回数据的说明书。为了给它们提供更多帮助、提升返回数据的准确度,最好为对象和字段附上自然语言描述,说明它们各自的用途。Pydantic 在这方面的支持来自 docstring 和 Field。
后面的所有示例都会用到下面这组 Pydantic 类:
from datetime import datetime
class LineItem(BaseModel):
"""A line item in an invoice."""
item_name: str = Field(description="The name of this item")
price: float = Field(description="The price of this item")
class Invoice(BaseModel):
"""A representation of information from an invoice."""
invoice_id: str = Field(
description="A unique identifier for this invoice, often a number"
)
date: datetime = Field(description="The date this invoice was created")
line_items: list[LineItem] = Field(
description="A list of all the items in this invoice"
)它展开后是一个复杂得多的 JSON schema:
{
"$defs": {
"LineItem": {
"description": "A line item in an invoice.",
"properties": {
"item_name": {
"description": "The name of this item",
"title": "Item Name",
"type": "string"
},
"price": {
"description": "The price of this item",
"title": "Price",
"type": "number"
}
},
"required": ["item_name", "price"],
"title": "LineItem",
"type": "object"
}
},
"description": "A representation of information from an invoice.",
"properties": {
"invoice_id": {
"description": "A unique identifier for this invoice, often a number",
"title": "Invoice Id",
"type": "string"
},
"date": {
"description": "The date this invoice was created",
"format": "date-time",
"title": "Date",
"type": "string"
},
"line_items": {
"description": "A list of all the items in this invoice",
"items": {
"$ref": "#/$defs/LineItem"
},
"title": "Line Items",
"type": "array"
}
},
"required": ["invoice_id", "date", "line_items"],
"title": "Invoice",
"type": "object"
}现在你对 Pydantic 以及它生成的 schema 有了基本了解,就可以进入正题:在 LlamaIndex 里用 Pydantic 类做结构化数据抽取,从结构化 LLM开始。