结构化预测#
结构化预测能让你更精细地控制应用程序如何调用大语言模型(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_predict
、astream_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,但如需更底层控制,可直接调用FunctionCallingProgram
和LLMTextCompletionProgram
进行深度定制:
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特别有用。
最后我们将探讨更低层次的结构化数据提取调用,包括单次调用中提取多个结构化数据。