news 2026/4/18 15:54:12

引导 LLM 的响应以创建结构化输出

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
引导 LLM 的响应以创建结构化输出

原文:towardsdatascience.com/guiding-an-llms-response-to-create-structured-output-5dde0d3e426b

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/2ff653d8945615f8a4e4352606965b30.png

图片由Ricardo Gomez Angel在Unsplash提供

本文将教你如何使用 Python 中的验证库来结构化 LLM 响应,如 GPT-4 或 Llama 3。

这是一个非常相关的话题,鉴于提取结构化信息(例如,JSON 格式)对于数据挖掘任务来说是基本要求,在这些任务中,从非结构化格式(如自由文本)中提取精确信息。

此外,即使在最商业化的系统中,如 GPT,结构化响应格式也不可靠,这是由于 LLM 在生成输出标记时的随机性质。

我们将使用几个库,例如 Pydantic 和 Instructor 进行验证和模式建模,以及 OpenAI 和 ollama 进行 LLM 部分。提出的内容将适用于闭源模型,如 OpenAI 的 GPT 或 Anthropic,以及开源模型如 Llama 3。

通过阅读这篇文章,你将学习:

  • 什么是数据模型以及如何定义一个数据模型

  • 如何确保你的LLM 通过验证规则尊重输出格式

  • 如何使用Instructor 和 Pydantic 库

享受阅读!


我们为什么需要结构化输出?

即使 GPT-4 等 LLM 不遵循特定模式来结构化它们的响应,也能提供巨大的价值。然而,对于程序员和数据工作者来说,如果用户愿意,尊重可能的响应模式是很重要的。

从 GPT-3.5 的一个特定版本开始,OpenAI 在其completionsAPI 中添加了response_format参数——这允许用户定义不同的键,例如json_object,以引导模型向更适合输入提示的响应发展。

这里有一个例子:

fromopenaiimportOpenAI client=OpenAI()response=client.chat.completions.create(model="gpt-3.5-turbo-0125",response_format={"type":"json_object"},messages=[{"role":"system","content":"You are a helpful assistant designed to output JSON."},{"role":"user","content":"Who won the world series in 2020?"}])print(response.choices[0].message.content)>>>"content":"{"winner": "Los Angeles Dodgers"}"

然而,这种逻辑并不总是有效。事实上,OpenAI 在其文档中建议在提示中精确地写出“JSON”这个词,以引导 GPT 生成它。这是一个如此重要的提示,以至于当我们使用response_format={ "type": "json_object" }时,我们被迫在提示中某处写上它。

为什么 LLM 产生一致的 JSON 输出这么困难?

这是因为 LLM 实际上是机器,它们在给定输入提示的情况下更有可能返回紧随其后的下一个标记。实际上,除非在训练阶段明确引导模型去观察和理解这些格式,否则在“自然”中很难遇到这种模式。

新的 LLM 的 JSON 模式并不保证输出与特定模式匹配,只保证它是有效的,并且没有错误地解析。

因此,能够验证这些输出中的内容,并在它们与我们的数据模型不一致时引发异常和错误,仍然非常重要。

用例

我们将看到从简单问题到 LLM(如 GPT-4 或 Llama3)的信息提取示例,正如之前提到的。

我们可以问任何问题,但我们将询问模型关于足球世界杯历史上获胜者的相关问题。

尤其是我们想要提取

  • 最终日期

  • 本届赛事的主办国

  • 获胜团队

  • 最佳射手

我们将不会担心验证数据的准确性,而只是将 LLM 的文本响应适配到我们现在将看到的方案。

在本文中,我们将探讨这个例子,也许还会探索其他一些例子。

必需的依赖项

现在让我们看看运行此教程所需的依赖项。

显然,假设我们已经有了一个活跃的开发环境,我们将安装 Pydantic、Instructor、OpenAI 客户端和 ollama。

  • Pydantic:由于使用方便、效率高和与数据科学的关联性,它是社区中最著名的用于数据模型定义和验证的库

  • Instructor:实际上是一个针对 LLM 工作的 Pydantic 包装器,是允许你创建验证逻辑的库

  • OpenAI:著名的用于查询 GPT 和其他 OpenAI 模型的客户端

  • ollama:非常方便的界面,用于打开源代码 LLMs,如 llama3。在我们的开发环境中,我们发出开始命令

在我们的开发环境中,我们发出开始命令

pip install pydantic instructor openai ollama

由于我们还想测试开源模型,下一步是全局安装 ollama。你可以通过阅读这篇专门的文章来了解如何安装和使用 ollama

如何在 ollama 和 Python 中本地使用 LLM

现在,我们可以专注于开发。

数据模型定义

数据模型是一种逻辑模式,用于结构化数据。它们在许多场景中使用,从定义数据库中的表到验证输入数据。

在下面的帖子中,我已经介绍了一些使用 Pydantic 进行数据建模的例子,涉及数据科学和机器学习 👇

使用 Pydantic 改进你的数据模型

让我们从创建 Pydantic 数据模型开始:

frompydanticimportBaseModel,FieldfromtypingimportListimportdatetimeclassSoccerData(BaseModel):date_of_final:datetime.date=Field(...,description="Date of the final event")hosting_country:str=Field(...,description="The nation hosting the tournament")winner:str=Field(...,description="The soccer team that won the final cup")top_scorers:list=Field(...,description="A list of the top 3 scorers of the tournament")classSoccerDataset(BaseModel):reports:List[SoccerData]=[]

在这个脚本中,我们正在从 Pydantic 导入BaseModelField类,并使用它们来创建数据模型。实际上,我们正在构建最终结果必须具有的结构。

Pydantic 要求我们声明进入模型的数据类型。我们有datetime.date,例如,它强制date字段为日期而不是字符串。同时,top_scorers字段必须是列表,否则 Pydantic 将返回验证错误。

最后,我们创建一个数据模型,它收集了多个SoccerData模型的实例。这被称为SoccerDataset,并将由 Instructor 用于验证多个报告的存在,而不仅仅是单个报告。

创建系统提示

非常简单,我们将用英语写下模型必须执行的操作,通过提供示例来强调意图和结果的结构。

system_prompt="""You are an expert sports journalist. You'll be asked to create a small report on who won the soccer world cups in specific years. You'll report the date of the tournament's final, the top 3 scorers of the entire tournament, the winning team, and the nation hosting the tournament. Return a JSON object with the following fields: date_of_final, hosting_country, winner, top_scorers. If multiple years are inputted, separate the reports with a comma. Here's an example [ { "date_of_final": "1966", "hosting_country": "England", "winner": "England", "top_scorers": ["Player A", "Player B", "Player C"] }, { "date_of_final": ... "hosting_country": ... "winner": ... "top_scorers": ... }, ] Here's the years you'll need to report on: """

这个提示将用作系统提示,并简单地允许我们通过逗号分隔传递感兴趣的年份。

创建 Instructor 代码

在这里,我们将借助 Instructor 创建 JSON 验证和结构化的主要逻辑。它使用一个与 OpenAI 提供的通过 API 调用 GPT 的接口相似的接口。

首先,我们将使用一个名为query_gpt的函数来使用 OpenAI,该函数允许我们参数化我们的提示:

fromopenaiimportOpenAIimportinstructordefquery_gpt(prompt:str)->list:client=instructor.from_openai(OpenAI(api_key="..."))resp=client.chat.completions.create(model="gpt-3.5-turbo",response_model=SoccerDataset,messages=[{"role":"system","content":system_prompt},{"role":"user","content":prompt},],)returnresp.model_dump_json(indent=4)

让我们记住将我们的 OpenAI API 密钥传递给新创建的客户端。我们将使用 GPT-3.5-Turbo,将SoccerDataset作为response_model传递。当然,如果我们想使用当时最强大的模型,我们可以使用"gpt-4o"。

我们不使用SoccerData,而是使用SoccerDataset

如果我们使用前者,LLM 只会返回单个结果。

让我们把所有东西放在一起,并启动软件,将用户提示中的年份“2010, 2014 and 2018”作为输入,这是我们想要生成结构化报告的内容。

fromopenaiimportOpenAIimportinstructorfromtypingimportListfrompydanticimportBaseModel,FieldimportdatetimeclassSoccerData(BaseModel):date_of_final:datetime.date=Field(...,description="Date of the final event")hosting_country:str=Field(...,description="The nation hosting the tournament")winner:str=Field(...,description="The soccer team that won the final cup")top_scorers:list=Field(...,description="A list of the top 3 scorers of the tournament")classSoccerDataset(BaseModel):reports:List[SoccerData]=[]system_prompt="""You are an expert sports journalist. You'll be asked to create a small report on who won the soccer world cups in specific years. You'll report the date of the tournament's final, the top 3 scorers of the entire tournament, the winning team, and the nation hosting the tournament. Return a JSON object with the following fields: date_of_final, hosting_country, winner, top_scorers. If the query is invalid, return an empty report. If multiple years are inputted, separate the reports with a comma. Here's an example [ { "date_of_final": "1966", "hosting_country": "England", "winner": "England", "top_scorers": ["Player A", "Player B", "Player C"] }, { "date_of_final": ... "hosting_country": ... "winner": ... "top_scorers": ... }, ] Here's the years you'll need to report on: """defquery_gpt(prompt:str)->list:client=instructor.from_openai(OpenAI())resp=client.chat.completions.create(model="gpt-3.5-turbo",response_model=SoccerDataset,messages=[{"role":"system","content":system_prompt},{"role":"user","content":prompt},],)returnresp.model_dump_json(indent=4)if__name__=="__main__":resp=query_llm("2010, 2014, 2018")print(resp)

这是结果:

{"reports":[{"date_of_final":"2010-07-11","hosting_country":"South Africa","winner":"Spain","top_scorers":["Thomas Müller","David Villa","Wesley Sneijder"]},{"date_of_final":"2014-07-13","hosting_country":"Brazil","winner":"Germany","top_scorers":["James Rodríguez","Thomas Müller","Neymar"]},{"date_of_final":"2018-07-15","hosting_country":"Russia","winner":"France","top_scorers":["Harry Kane","Antoine Griezmann","Romelu Lukaku"]}]}

太棒了。GPT-3.5-Turbo 完美地遵循了我们的提示,Instructor 验证了字段,创建了一个与数据模型一致的结构。事实上,输出不是一个字符串,而是一个 Python 字典的列表,这是 LLM 如 GPT 通常会返回的。

现在,让我们尝试插入一个没有意义的输入。

if__name__=="__main__":print(query_gpt("hi, how are you?"))>>>{"reports":[]}

LLM 正确地返回了一个空报告,因为我们就是这样通过系统提示要求它处理无效查询的。

使用 Instructor 使用开源模板

我们已经看到了如何在 Instructor 中使用 GPT 来获得结构化的 JSON 输出。现在让我们看看如何使用 ollama 来使用开源模板如 llama3

记住,您需要通过 ollama 下载 llama3 才能使用它。

使用ollama pull llama3命令下载它!

让我们创建一个名为query_llama的新函数。

defquery_llama(prompt:str)->list:client=instructor.from_openai(OpenAI(base_url="http://localhost:11434/v1",api_key="ollama",# valore richiesto, ma non influente),mode=instructor.Mode.JSON,)resp=client.chat.completions.create(model="llama3",messages=[{"role":"system","content":system_prompt},{"role":"user","content":prompt}],response_model=SoccerDataset,)returnresp.model_dump_json(indent=4)

与 GPT 代码有一些不同。让我们看看它们。

  • ollama 是通过与 GPT 相同的接口调用的,但需要更改基础 URL 指针(base_url)和 API 密钥,这些是必需的,但不是正确操作所必需的(不要问我为什么)

  • 您需要通过模式参数解释 JSON 模式。让我们运行新函数

让我们运行这个函数

if__name__=="__main__":print(query_llama("2010, 2014, 2018"))

下面是结果:

{"reports":[{"date_of_final":"2010-07-11","hosting_country":"South Africa","winner":"Spain","top_scorers":["Thomas Müller","Wolfram Toloi","Landon Donovan"]},{"date_of_final":"2014-07-13","hosting_country":"Brazil","winner":"Germany","top_scorers":["James Rodríguez","Miroslav Klose","Thomas Müller"]},{"date_of_final":"2018-07-15","hosting_country":"Russia","winner":"France","top_scorers":["Harry Kane","Kylian Mbappé","Antoine Griezmann"]}]}

我们有一个包含正确 JSON 的列表!所有这些都是在 Llama 3 本地完成的。

正如我之前提到的,验证发生在结构上,而不是内容上。实际上,内容与 GPT 生成的内容不同。

让我们看看标记符有何不同。也许通过迭代提示并明确指出我们希望接收哪些标记符,可以得到正确的列表。

结论

我们已经看到了如何使用 Pydantic、Instructor 和 ollama 来驱动 LLM 的输出到一个结构化格式,例如 JSON。

记住,在这个过程中模型实际上是受到引导的,因此它不是确定性的。由于 LLMs 的非确定性本质,可能会出现 JSON 不被尊重的情况。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/18 3:47:08

Python+Vue的毕业设计选题系统的设计与实现 Pycharm django flask

这里写目录标题项目介绍项目展示详细视频演示感兴趣的可以先收藏起来,还有大家在毕设选题(免费咨询指导选题),项目以及论文编写等相关问题都可以给我留言咨询,希望帮助更多的人技术栈文章下方名片联系我即可~解决的思路…

作者头像 李华
网站建设 2026/4/18 7:30:54

Python+Vue的洪涝灾害应急信息管理系统设计与实现 Pycharm django flask

这里写目录标题项目介绍项目展示详细视频演示感兴趣的可以先收藏起来,还有大家在毕设选题(免费咨询指导选题),项目以及论文编写等相关问题都可以给我留言咨询,希望帮助更多的人技术栈文章下方名片联系我即可~解决的思路…

作者头像 李华
网站建设 2026/4/17 18:25:17

LobeChat产品质量反馈分析系统

LobeChat产品质量反馈分析系统 在当今AI驱动的产品迭代浪潮中,用户的声音比以往任何时候都更加重要。然而,传统的反馈收集方式——如问卷、工单系统或社交媒体监听——往往存在响应滞后、信息碎片化和难以量化的问题。面对海量非结构化的自然语言反馈&a…

作者头像 李华
网站建设 2026/4/18 7:38:03

LobeChat直播话术生成器设计构想

LobeChat直播话术生成器设计构想 在一场高密度、快节奏的直播带货中,主播往往需要同时应对产品讲解、用户提问、促销引导和情绪调动等多重任务。稍有迟疑,就可能错失转化窗口。然而,即便是经验丰富的主播,也难以做到对每款产品的参…

作者头像 李华
网站建设 2026/4/18 12:24:30

[PM]项目管理方法案例研究

项目管理方法案例研究案例1:丰田汽车 - 精益生产/六西格玛(亚洲-日本)项目类型:工业制造项目丰田生产系统(TPS)是精益制造的鼻祖,结合六西格玛方法持续优化生产流程。其核心是"准时制生产&…

作者头像 李华