微信扫码
添加专属顾问
我要投稿
掌握结构化输出技巧,提升应用效率。 核心内容: 1. 结构化输出概念及其在应用中的重要性 2. 获取结构化输出的两种方法:原生模型功能与提示工程 3. 提示工程的具体应用与技巧详解
结构化输出,简单来说,就是让大语言模型以一种清晰、可预测、有组织的格式返回信息,例如整齐的列表、带有标签的数据字段(键值对形式:key: value),甚至是简单的表格,而不是一长段对话式的文本。这一概念在实际应用中具有举足轻重的意义。
以构建一个利用人工智能分析客户评论的应用程序为例。当我们向模型询问“总结这条评论,并告诉我提到的产品和客户的总体情绪”时,模型可能给出这样的回复:“嗯,看起来客户简·D购买了‘MegaWidget 3000’,并且非常不满意。她提到产品只用了两天就坏了,整个体验让她感到非常沮丧。” 这样的回答对于人类来说很容易理解,但对于应用程序的代码而言,却充满挑战。代码需要从这段文本中费力地找出产品名称(“MegaWidget 3000”),判断情绪(“不满意” 还是 “沮丧” 才是主要情绪呢?),甚至可能还需要提取客户名称(如果有需要的话)。而且,如果模型下次使用了不同的表述,比如用 “感到失望” 代替 “不满意”,那么用于解析情绪的代码可能就会出错。
相比之下,结构化的输出则能避免这些问题。例如:
review_analysis:
product_name: MegaWidget 3000
sentiment: Negative
summary: 客户报告产品在两天后损坏,导致沮丧情绪。
这种结构化的格式将混乱的对话式文本转化为干净、有序的数据,使得代码能够可靠地处理。
通常,获取大语言模型结构化输出主要有两种途径:原生模型功能和提示工程。
随着人工智能模型的不断发展,一些模型增加了内置的请求结构化数据的方法。例如,谷歌的Gemini模型通常可以直接使用函数描述或特定的模式,有时还能与Pydantic等工具集成;OpenAI的模型(如GPT-4)提供了 “JSON模式” 或 “函数调用” 等功能,旨在将输出强制转换为特定的JSON结构。然而,这些原生功能虽然强大,但往往依赖于特定的模型。不同模型请求结构化数据的方式可能不同,这可能会使开发者锁定在特定的供应商,并且可能需要学习其特定的API或库,增加了使用的复杂性。
与依赖原生模型功能不同,提示工程是一种更为通用的方法。它的核心思想非常简单,就是在向模型发出的指令(即提示)中,直接明确地告诉模型你期望的输出格式。这种方法不依赖于特定的模型,几乎适用于大多数大语言模型。它充分利用了模型理解和遵循指令的核心能力,让开发者能够灵活地定义所需的结构,例如请求YAML或特定的JSON格式。此外,通过提示工程获取结构化输出,不需要预先学习复杂的模型特定API,降低了使用门槛,对于初学者来说是一种更为友好的选择。接下来,我们将重点介绍基于提示工程的三个必备技巧。
在请求大语言模型进行结构化输出时,选择合适的格式至关重要。JSON(JavaScript Object Notation)在Web开发和API中应用广泛,但它对于大语言模型来说,有时可能会带来一些问题,特别是在处理包含引号或多行文本的情况时。
JSON有严格的规则,字符串必须用双引号(")括起来。如果文本本身包含双引号,则必须使用反斜杠(\)进行转义,例如\"。同样,字符串中的换行符需要表示为\n。大语言模型在处理这些规则时常常会遇到困难,这主要源于它们处理文本的方式——分词(tokenization)。大语言模型会将文本分解为较小的部分(词元),这些词元可能是完整的单词、单词的一部分,或者是单个字符/符号。在分词过程中,像\这样的转义字符或\n这样的格式标记有时会被不恰当地拆分,模型也可能难以在其庞大的训练数据中学习何时以及如何正确应用这些复杂的上下文规则。由于这种底层的分词机制,大语言模型在一致地处理转义字符方面表现不佳。
例如,当要求模型提取一段对话 “Alice said: “Hello Bob. How are you?”” 并以JSON格式输出时,正确的格式应该是{"dialogue": "Alice said: \"Hello Bob.\nHow are you?\""}。但由于分词的挑战,要让模型每次都准确无误地生成这样的格式并不容易,很容易出现错误。
相比之下,YAML(YAML Ain’t Markup Language)在处理这类情况时具有明显的优势。YAML旨在更易于人类阅读,并且对于字符串,特别是多行字符串,具有更灵活的规则,这使得它对转义和格式错误的敏感度较低。同样以提取上述对话为例,用YAML格式表示则更加简洁:
speaker: Alice
dialogue: |
Alice said: "Hello Bob.
How are you?"
在这个例子中,不需要对引号进行转义,换行也显得很自然。这里使用了YAML的块标量风格(|)。
YAML提供了多种强大的处理多行字符串的方式。其中,字面量风格(|)会精确保留块中的换行符,源YAML中的每一行新行在结果字符串中都会变成一个换行符(\n)。例如:
literal_style: |
Line 1
Line 2
Line 4
其结果字符串为:"Line 1\nLine 2\n\nLine 4\n"(注意其中的双换行和最后的换行)。
折叠风格(>)则会将块内的大多数换行符折叠为空格,将文本视为一长行,仅为了可读性而进行换行。它会保留空白行(这些空白行在结果字符串中会变成\n)。例如:
folded_style: >
This is actually
just one long sentence,
folded for readability.
This starts a new paragraph.
其结果字符串为:"This is actually just one long sentence, folded for readability.\nThis starts a new paragraph.\n"(注意其中的空格和单个\n)。
此外,还可以通过添加 “咬尾指示符”(+、-)来进一步控制块标量末尾的换行符处理方式。默认情况下(无指示符,即|或>),如果有一个尾随换行符,会保留它,但会删除任何额外的尾随换行符;使用|+或>+表示保留所有尾随换行符;使用|-或>-则表示删除所有尾随换行符,包括最后一个(如果存在)。
在实际应用中,当提示大语言模型进行结构化输出,且输出内容可能包含复杂字符串时,我们应该明确要求使用YAML格式,并将输出包含在yaml …
块中。如果可能出现多行文本且格式很重要,还应在提示中指定多行风格(|或>)。一般情况下,除非有特定需求,否则不需要指定咬尾指示符。同时,即使YAML具有灵活性,也一定要在代码中对输出进行解析和验证,可以使用断言(assert)或其他模式检查方法,以确保数据的准确性和可用性。通过使用YAML,尤其是其多行处理能力,可以显著减少因JSON的严格规则和大语言模型的分词挑战而导致的格式错误。
在许多实际任务中,我们需要大语言模型从提供的列表中识别特定的项目,例如根据某些标准筛选或选择项目。一种常见的做法是要求模型返回它选择的项目的文本内容,但这种方法在处理真实世界中的文本时往往不可靠,因为真实文本可能包含各种格式问题和噪声。
假设我们有一批产品评论,希望利用大语言模型标记出其中看起来像垃圾邮件的评论(例如包含可疑链接或无意义内容的评论)。输入的评论列表可能如下所示:
review_list = [
"Great product, really loved it! Highly recommend.", # Index 0
"DONT BUY!! Its a scam! Visit my site -> www.getrichfast-totallylegit.biz", # Index 1
" Item arrived broken. Very disappointed :( ", # Index 2 (额外空格,表情符号)
"????? ?????? ?????? click here for prize >>> http://phish.ing/xxx", # Index 3 (乱码,链接)
"Works as expected. Good value for the price.", # Index 4
"¡¡¡ AMAZING DEAL just for YOU -> check my profile link !!!" # Index 5 (奇怪的标点符号,指令)
]
这个列表中的文本包含了各种变化,如标点符号、大小写、间距、符号,甚至可能存在拼写错误(这里虽未明确添加,但实际情况中可能出现)。
如果我们向大语言模型发出这样的提示:“Review the list below. Identify any reviews that appear to be spam or contain suspicious links. Return the full text of the reviews that should be removed.”,模型的返回结果可能会有所不同。它可能会完美地复制索引1的内容,也可能正确返回索引3的内容。但对于索引5,它可能会将“¡¡¡ AMAZING DEAL just for YOU -> check my profile link !!!” 规范化为“!!! AMAZING DEAL just for YOU -> check my profile link !!!”,或者在其他评论中微妙地改变间距或标点符号。如果我们的代码尝试根据模型返回的精确文本删除项目(例如,if llm_output_text in review_list:),那么即使模型正确识别了垃圾邮件评论,任何细微的变化都可能导致在原始列表中找不到该评论。这是因为大语言模型并非旨在完美复制可能有噪声的输入字符串,它们处理的是文本的含义并生成输出,有时会引入一些小的变化。
为了解决这个问题,我们可以改变提示的方式,要求模型输出应删除评论的索引(位置编号),而不是可能复杂多变的评论内容。我们可以这样重写提示指令:“Analyze the list of product reviews provided below, each marked with an index number (0 to 5). Identify any reviews that seem like spam or contain suspicious links/instructions. Output ONLY a list of the integer indexes corresponding to the reviews that should be removed.”,并在提示中包含编号列表:
# Product Reviews (Output indexes of spam/suspicious ones):
# 0: Great product, really loved it! Highly recommend.
# 1: DONT BUY!! Its a scam! Visit my site -> www.getrichfast-totallylegit.biz
# 2: Item arrived broken. Very disappointed :(
# 3: ????? ?????? ?????? click here for prize >>> http://phish.ing/xxx
# 4: Works as expected. Good value for the price.
# 5: ¡¡¡ AMAZING DEAL just for YOU -> check my profile link !!!
此时,大语言模型对于这个示例的预期输出应该是一个数字列表,并且很可能按照请求的YAML结构进行格式化(结合技巧一):
reviews_to_remove_indexes:
- 1
- 3
- 5
这样的输出具有诸多优点:它很简单,只是一个整数列表;整数不存在拼写错误、间距问题或标点符号变化,具有稳定性;验证也很容易,只需检查输出是否是一个包含在预期范围内(0 - 5)有效整数的列表;并且可以直接在代码中使用,通过遍历这些索引,无论原始评论内容多么混乱,都能够可靠地访问或删除原始评论列表中的相应评论。
因此,当要求大语言模型从提供的可能复杂或混乱的字符串列表中选择或识别项目时,我们应该在提示中为列表提供清晰的索引(或唯一、简单的标识符),并指示模型仅输出与所选项目对应的索引/标识符列表。同时,要验证输出是否是一个包含有效索引/标识符的列表。这种方法极大地提高了处理从嘈杂的真实世界文本输入中进行选择的任务的可靠性,避免了脆弱的字符串匹配,使用稳定的索引来确保任务的准确性。
第三个技巧可能乍一看有些违反直觉,即故意要求大语言模型在其结构化输出中添加 “额外” 的自然语言,我们通过YAML注释(#)来实现这一点。这样做不仅是为了提高输出的可读性,更重要的是为了提高结构化数据本身的准确性。
当我们要求大语言模型执行复杂任务(如分析多个评论并输出要删除的评论索引列表)并立即生成结构化数据时,模型有时可能会 “急于” 完成任务。在没有明确的步骤来整合其发现或在确定结构化格式之前对其选择进行推理的情况下,很容易出现错误。它可能会遗漏某个项目、包含错误的项目,或者在复杂的分类中出错。从分析直接跳到最终结构的过程可能不太可靠。
为了缓解这个问题,我们可以指示大语言模型在输出关键结构化数据之前,先生成一段自然语言注释来解释其推理过程。这不仅仅是为了让我们之后更容易理解输出内容(尽管这是一个额外的好处),更重要的是,它迫使大语言模型在最关键的时候进行一个 “思维链” 步骤。
在处理输入(例如评论列表)时,大语言模型首先进行分析。在输出索引列表之前,它必须先生成注释,总结为什么选择这些特定的索引。这使它回到自然语言推理模式,整合其发现。在明确阐述了推理之后,大语言模型现在更有准备输出正确的索引列表或准确的结构化值。生成注释就像是一个 “认知减速带”,它中断了直接跳到结构化输出的过程,促使模型进行片刻的思考,这通常会导致更准确的结果,特别是对于需要综合或判断的任务(如从列表中选择多个项目或进行细致的分类)。
让我们再次回顾垃圾邮件评论过滤任务(技巧二)。我们修改提示指令为:“Analyze the list of product reviews… Output ONLY a YAML block containing the key reviews_to_remove_indexes with a list of integers. Crucially, add a YAML comment line starting with # immediately before the reviews_to_remove_indexes list, briefly summarizing which reviews were identified as spam/suspicious and why.”。这时,大语言模型可能会生成如下输出:
# Identified reviews 1, 3, 5 as spam/suspicious due to external links, gibberish, or spammy language.
reviews_to_remove_indexes:
- 1
- 3
- 5
通过强制模型先生成 “# Identified reviews...” 这样的注释,我们增加了后续列表 [1, 3, 5] 准确的可能性,因为模型在输出数字之前必须用自然语言明确说明其选择的理由。
在实际应用中,为了利用嵌入推理来提高准确性,我们首先要确定那些大语言模型需要进行判断或综合的关键结构化输出(例如选定项目的列表、分类、总结字段)。然后,指示模型在这些特定字段之前添加YAML注释(# reasoning...),将其构建为在数据点之前需要总结其发现或理由的形式。这种方法在大语言模型不仅仅是提取简单事实,而是进行选择或将分析结果总结为结构化格式时最为有效。可以把它想象成要求大语言模型在完成结构化答案之前,先在注释中 “展示其初步工作”。这个嵌入推理的步骤是提高结构化输出可靠性和准确性的强大技术。
53AI,企业落地大模型首选服务商
产品:场景落地咨询+大模型应用平台+行业解决方案
承诺:免费场景POC验证,效果验证后签署服务协议。零风险落地应用大模型,已交付160+中大型企业
2025-05-27
AI 新闻小助手 100% 纯提示词实践
2025-05-26
驯服 AI 代理:Google研究员提出11 个让它更聪明的提示技巧
2025-05-25
Augment官方:11种提示词技巧,打造更出色的AI编程智能体
2025-05-24
我用Dify把飞书表格的「AI提示词库」打包成了MCP Server给AI使用和管理
2025-05-23
人力资源提示词:培训开班讲话稿
2025-05-21
让 AI 更懂你的需求!一文看懂如何在 Trae IDE 中巧用上下文
2025-05-19
90%的人写不好提示词,都是因为踩了这三个坑!
2025-05-19
智能即压缩。但prompt能力的关键,是敢于啰嗦。
2025-02-01
2024-09-18
2025-01-08
2024-08-23
2024-07-26
2025-01-17
2024-12-26
2024-08-23
2024-07-02
2024-10-17
2025-04-21
2025-03-31
2025-03-29
2025-03-17
2025-02-06
2025-01-10
2024-12-25
2024-11-20