Skip to content

结构化预测#

结构化预测能让你更精细地控制应用程序如何调用大语言模型(LLM)并使用Pydantic。我们将沿用之前的Invoice类,按照前例加载PDF文件,并继续使用OpenAI。不同于创建结构化LLM,我们将直接在LLM上调用structured_predict方法——这是每个LLM类都具备的方法。

结构化预测接收一个Pydantic类和一个提示模板作为参数,同时接收提示模板中所有变量的关键字参数。

from llama_index.core.prompts import PromptTemplate

prompt = PromptTemplate(
    "从以下文本中提取发票信息。如果找不到发票ID,请使用公司名称'{company_name}'和日期作为发票ID:{text}"
)

response = llm.structured_predict(
    Invoice, prompt, text=text, company_name="Uber"
)

如你所见,这种方式允许我们在Pydantic不足以正确解析数据时,为LLM提供额外的提示指引。此处的响应对象就是Pydantic对象本身。如需获取JSON格式输出:

json_output = response.model_dump_json()
print(json.dumps(json.loads(json_output), indent=2))
{
    "invoice_id": "Uber-2024-10-10",
    "date": "2024-10-10T19:49:00",
    "line_items": [
        {"item_name": "行程费用", "price": 12.18},
        {"item_name": "全民出行附加费", "price": 0.1},
        ...,
    ],
}

structured_predict针对不同使用场景提供了多个变体,包括异步版本(astructured_predict)和流式版本(stream_structured_predictastream_structured_predict)。

实现原理#

根据使用的LLM类型,structured_predict会采用两种不同的底层类来处理LLM调用和输出解析。

函数调用程序#

若LLM支持函数调用API,FunctionCallingProgram会: 1. 将Pydantic对象转换为工具 2. 强制LLM使用该工具进行响应 3. 返回生成的Pydantic对象

这种方式通常更可靠,会优先采用。但部分纯文本LLM需使用另一种方法。

文本补全程序#

对于纯文本LLM,LLMTextCompletionProgram会: 1. 将Pydantic模式输出为JSON格式 2. 向LLM发送模式和数据,并提示其返回符合该模式的响应 3. 对LLM返回的原始文本调用model_validate_json()进行验证

这种方式可靠性较低,但支持所有基于文本的LLM。

直接调用预测类#

实践中structured_predict适用于所有LLM,但如需更底层控制,可直接调用FunctionCallingProgramLLMTextCompletionProgram进行深度定制:

textCompletion = LLMTextCompletionProgram.from_defaults(
    output_cls=Invoice,
    llm=llm,
    prompt=PromptTemplate(
        "从以下文本中提取发票信息。如果找不到发票ID,请使用公司名称'{company_name}'和日期作为发票ID:{text}"
    ),
)

output = textCompletion(company_name="Uber", text=text)

上述代码与在不支持函数调用API的LLM上调用structured_predict效果相同,同样返回Pydantic对象。但可通过子类化PydanticOutputParser自定义解析逻辑:

from llama_index.core.output_parsers import PydanticOutputParser


class MyOutputParser(PydanticOutputParser):
    def get_pydantic_object(self, text: str):
        # 实现比默认更智能的解析逻辑
        return self.output_parser.model_validate_json(text)


textCompletion = LLMTextCompletionProgram.from_defaults(
    llm=llm,
    prompt=PromptTemplate(
        "从以下文本中提取发票信息。如果找不到发票ID,请使用公司名称'{company_name}'和日期作为发票ID:{text}"
    ),
    output_parser=MyOutputParser(output_cls=Invoice),
)

这对处理性能较弱的LLM特别有用。

最后我们将探讨更低层次的结构化数据提取调用,包括单次调用中提取多个结构化数据。