AIGC 的结构化输出

如何确保大型语言模型(LLMs)始终以正确的格式生成输出——尤其是在执行任务或调用 API 时?能采取哪些措施来防止 LLM 生成不可预测或不完整的响应,从而破坏应用程序?

LLM 通常会产生大量非结构化数据,这些数据都需要经过处理之后,才能被有效使用。也就是输出具有不可预测性,这会导致错误、浪费时间并增加成本。

为了解决这一问题,OpenAI 和谷歌引入了结构化输出。结构化输出确保模型响应遵循严格的格式,减少错误,并使 LLM 更容易集成到需要一致且机器可读数据的应用程序中。

本文将解释结构化输出的工作原理,并以示例说明其实现方法和好处,以及在实际问题中可能遇到的挑战。

什么是结构化输出?

结构化输出确保模型生成的内容遵循预定义的格式,例如 JSON、XML 或 Markdown。以前,大型语言模型(LLMs)会生成没有特定结构的自由形式文本。而结构化输出提供了一种替代方案,使输出具有机器可读性、一致性,并能够轻松集成到其他系统中。

自由形式文本通常存在歧义且难以解析,而结构化输出则允许直接与软件系统集成,而无需复杂的数据转换。

将输出限制为特定格式意味着结构化输出有助于减少变异性与错误。因此,对于一致性与准确性至关重要的任务(如 API 交互或数据库更新),这些输出更加可靠。

OpenAI 推出结构化输出可以作为 JSON 模式的扩展或改进。JSON 模式于 2023 年发布,旨在确保模型在收到指令后能够输出 JSON。然而,JSON 模式的问题在于它无法始终输出正确的结构。结构化输出强制执行一种结构,使与 API 和其他工具的集成更加可靠,并降低了模型生成错误或无关内容的可能性。

根据 OpenAI 的数据,在结构化输出推出之前,通过提示工程让 LLM 以特定格式响应的可靠性约为 35.9%。而现在,如果将“strict”设置为 true,其可靠性达到了 100%。

结构化输出是如何工作的?

通常,大型语言模型(LLMs)基于概率预测逐个生成文本标记。然而,如果需要生成的文本必须符合特定格式,这种方法就不适用了。结构化输出通过预定义的规则或模式来引导生成过程,从而确保每个标记都符合所需的结构。为了监控和控制标记生成的顺序,通常会使用诸如**有限状态机(Finite State Machine, FSM)**之类的技术。

在这里插入图片描述

为了利用像 OpenAI 和 Gemini 这样的模型提供商实现结构化输出,需要完成以下步骤:

  1. 定义 JSON Schema:JSON 提供了一种标准化的格式,用于定义 JSON 文档中预期的结构和数据类型(例如字符串、数字、数组等)。

  2. 在 API 请求中引入 Schema:在 API 请求过程中,JSON Schema 会被包含在请求配置中。通过这种方式,模型被指示生成符合指定Schema的输出。

  3. 生成结构化数据:一旦请求被处理,大型语言模型(LLM)将生成符合定义 Schema 约束的输出。因此,每个响应都是一致的,并遵循预期的格式。

以下详细说明了如何在 OpenAI、Gemini 和 Humanloop 中设置这一功能。

如何在 OpenAI 中使用结构化输出

随着 OpenAI 结构化输出的引入,可以为动态生成用户界面等用例生成结构化数据。

不过需要注意的是,结构化输出仅适用于 OpenAI 最新的大型语言模型(LLM),目前包括:

  • gpt-4o-mini-2024-07-18及之后版本
  • gpt-4o-2024-08-06及之后版本

话虽如此,以下是通过response_format和 SDK 辅助工具开始使用 gpt-4o 结构化输出gpt-4o mini 结构化输出功能的方法:

1. 定义对象

首先,定义一个表示 JSON Schema 的对象或数据结构,以确保模型遵循特定的结构,例如步骤列表。

以下是一个使用 Pydantic 实现此操作的示例:

from pydantic import BaseModel

class Step(BaseModel):
    explanation: str
    output: str

class MathResponse(BaseModel):
    steps: list[Step]
    final_answer: str

假设针对一个数学问题,Step 类定义每个步骤的两个字段:

  • explanation :一个字符串,用于解释该步骤。
  • output :该步骤的结果。

MathResponse 定义了整体响应,其中包括(steps)和 final_answer(最终答案)。

2. 在 API 调用中引入对象

定义好对象后,可以使用response_format参数将其包含在 API 请求中。SDK 会自动将模型的输出解析为所定义的对象。

completion = client.beta.chat.completions.parse(
    model = "gpt-4o-2024-08-06",
    messages = [
        {"role": "system", "content": "You are a helpful math tutor. Guide the user through the solution step by step."},
        {"role": "user", "content": "how can I solve 8x + 7 = -23"}
    ],
    response_format = MathResponse
  )

在此,调用 OpenAI 的 GPT-4o 解决一个数学问题。response_format确保模型的输出符合MathResponse模式。此外,SDK 会负责将响应解析为与所定义模式匹配的对象。

3. 处理边缘情况

有时,模型可能无法生成符合所提供 JSON Schema 的有效响应。

这可能发生在以下情况中:

  • 模型因安全原因拒绝回答。
  • 由于达到 token 限制,响应不完整。

以下是一个如何处理边缘情况的示例:

try:
    completion = client.beta.chat.completions.parse(
        model="gpt-4o-2024-08-06",
        messages=[
            {"role": "system", "content": "You are a helpful math tutor. Guide the user through the solution step by step."},
            {"role": "user", "content": "how can I solve 8x + 7 = -23"}
        ],
        response_format=MathResponse,
        max_tokens=50
    )
    math_response = completion.choices[0].message
    if math_response.parsed:
        print(math_response.parsed)
    elif math_response.refusal:
        # handle refusal
        print(math_response.refusal)
except Exception as e:
    # Handle edge cases
    if type(e) == openai.LengthFinishReasonError:
        # Retry with a higher max tokens
        print("Too many tokens: ", e)
        pass
    else:
        # Handle other exceptions
        print(e)
        pass

程序会检查不同的边缘情况,例如由于 token 限制或安全原因导致的不完整响应。如果发生拒绝回答的情况,它会打印出解释;否则,它会处理并打印结构化输出。

4. 以类型安全的方式使用结构化数据

在使用结构化输出时,可以将解析后的 JSON 响应作为在response_format中定义的类型的对象进行访问。这确保了类型安全,并允许直接处理结构化数据。

示例:

math_response = completion.choices[0].message.parsed
print(math_response.steps)
print(math_response.final_answer)

如何在 Gemini 中使用结构化输出

Gemini 生成的文本默认是无结构的,因此需要研究如何使用 Gemini 的结构化输出功能。

以下是使用 Gemini API 的方法:

1. 设置项目和 API 密钥

在开始之前,请确保项目已设置好,并且 API 密钥已配置。这是向 Gemini API 进行身份验证所必需的。要定义一个 JSON Schema,以指定输出的结构和数据类型。

2. 定义 JSON Schema 并将 Schema 提供给模型

需要定义一个JSON Schema,用于指定输出的结构和数据类型。

之后,将 Schema 提供给模型。有两种方法可以实现这一点:

(1)作为提示词中的文本 :可以直接在提示词中包含所需 JSON 格式的描述。

model = genai.GenerativeModel("gemini-1.5-pro-latest")
prompt = """List a few popular cookie recipes in JSON format.

Use this JSON schema:

Recipe = {'recipe_name': str, 'ingredients': list[str]}
Return: list[Recipe]"""
result = model.generate_content(prompt)
print(result)

(2)通过模型配置:这是一种更正式的方法,通过使用response_schema为模型配置特定的 Schema。

import typing_extensions as typing

class Recipe(typing.TypedDict):
    recipe_name: str
    ingredients: list[str]

model = genai.GenerativeModel("gemini-1.5-pro-latest")
result = model.generate_content(
    "List a few popular cookie recipes.",
    generation_config=genai.GenerationConfig(
        response_mime_type="application/json", response_schema=list[Recipe]
    ),
)
print(result)

3. 使用枚举(Enums)来限制输出

有可能需要将输出限制为特定选项,这在分类任务等应用场景中非常有用。在这种情况下,需要在 Schema 中使用枚举(Enums)。

import google.generativeai as genai
import enum

class Choice(enum.Enum):
    PERCUSSION = "Percussion"
    STRING = "String"
    WOODWIND = "Woodwind"
    BRASS = "Brass"
    KEYBOARD = "Keyboard"

model = genai.GenerativeModel("gemini-1.5-pro-latest")
result = model.generate_content(
    ["What kind of instrument is this?", "organ.jpg"],
    generation_config=genai.GenerationConfig(
        response_mime_type="text/x.enum", response_schema=Choice
    ),
)
print(result)  # Output will be one of the enum options

如何在 Humanloop 中使用结构化输出

Humanloop 的提示管理简化了提示开发过程,使版本控制、评估和协作更加便捷。

在 Humanloop 中,可以通过以下步骤使用结构化输出:

  1. 创建或选择一个提示词(Prompt)
  2. 打开“编辑器”(Editor)选项卡
  3. 选择“响应格式”(Response Format)下拉菜单
  4. 添加 JSON Schema(手动添加或使用 AI 驱动的 JSON Schema 生成器)

完成后,可以将 Humanloop API 部署到应用程序或智能体中,以利用结构化输出功能。

在这里插入图片描述

结构化输出的优点

使用结构化输出的一些优点包括:

  • 减少幻觉:通过强制遵守 Schema,结构化输出起到了关键作用。遵守 Schema 意味着不太可能出现意外数据,因此输出中仅包含相关信息。这使得评估 LLM 应用程序变得更加容易,因为结构化输出提供了可预测且可验证的数据格式。
  • 无缝集成:确保模型输出始终符合预定义的 Schema,可以简化与系统的集成过程。这对于需要结构化数据格式(如数据库或 API)的应用特别有用,一致性有助于平滑运行。
  • 减少变异性:结构化输出限制了模型偏离指定格式的能力,从而减少了变异性。这也使得验证更加容易,因为输出保证符合Schema。因此,可能不需要使用复杂的后处理逻辑,因为可以依赖 Schema 来确保所有必需字段的存在并正确格式化。

结构化输出的挑战

尽管有多种好处,在使用结构化输出时也需要克服一些挑战:

  • Schema设计复杂性:有可能需要处理结构复杂的 Schema。设计一个有效的 JSON Schema 可能是一个耗时且复杂的过程。例如,如果要从法律文件或多步骤流程中提取结构化信息,可能需要定义深层次嵌套的 Schema 以捕获所有必要细节而不引入错误。
  • 输出上限:存在 16384 个 token 的限制,这意味着任何更大的输出将导致无效的 JSON。因此,在解析或在下游格式中使用数据时会导致问题。例如,如果生成像交易详情这样的对象列表,并且列表过长,则在达到 token 限制之前只会返回部分列表。
  • 降低推理能力:虽然结构化输出有助于减少幻觉,但研究表明,相比使用自由形式响应,LLM 的推理能力可能会有所下降。

参考:https://humanloop.com/blog/structured-outputs

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

CS创新实验室

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值