探索ChatGPT结构化数据支持的极限
如果仅通过免费网页或移动应用使用某中心的ChatGPT,您甚至未能发挥其潜力的十分之一。通过使用付费但价格低廉的ChatGPT API,您可以对输出结果施加更多控制。
假设需要生成这篇博客文章的摘要,并包含有用的元数据(如类别和SEO关键词)。将博客文本输入ChatGPT,并给出指令:“将这篇博客文章总结为3个不同的要点和一段社交媒体分享简介,同时提供3个文章类别和3个SEO关键词”。
要点:
- 使用付费ChatGPT API以获得更多输出控制并保持给定的JSON模式
- 学习提示工程和系统提示以提高ChatGPT输出质量
- 使用Pydantic简化模式输入/输出以实现结构化数据支持
社交媒体简介: 探索如何通过付费API、提示工程和Pydantic解锁ChatGPT的全部潜力,获得更多输出控制并学习维护给定的JSON模式。#ChatGPT #AI #Pydantic
文章类别:
- 人工智能
- API开发
- 数据模式
SEO关键词:
- ChatGPT API
- 提示工程
- Pydantic模式
这些结果不够准确,还需要手动将每个结果复制粘贴到需要的地方。如果能够:a) 通过赋予ChatGPT专业文案编辑的新角色大幅提高输出质量;b) 强制输出保持给定的JSON模式以便编程访问或存储到数据库进行索引,会怎样?得益于系统提示工程和ChatGPT的函数调用功能,现在这已成为可能。
应用本文中将学习的两种技术后,现在得到以下摘要:
|
|
好多了!
函数调用教程
ChatGPT的"函数调用"是自ChatGPT发布以来最好的功能。
提示工程和系统提示教程
系统提示
系统提示控制模型生成文本时采用的"角色"。在ChatGPT API发布数月后,现在很明显ChatGPT的真正力量来自巧妙使用系统提示。自从去年6月发布gpt-3.5-turbo-0613后更是如此,该版本使ChatGPT更严格地遵守系统提示。某中心还发布了提示工程指南,其中包含一些额外提示。
默认情况下,ChatGPT的系统提示大致是"你是一个有用的助手",使用过ChatGPT网页界面的任何人都同意这一点。但如果更改它,可以给ChatGPT一个全新的角色,如"你是麦当劳叔叔",或添加生成约束,如"仅用表情符号回应"。可以添加任意数量的规则,尽管ChatGPT遵守这些规则的程度可能有所不同。
不幸的是,要修改系统提示,需要使用付费ChatGPT API(至少预付5美元)。如果不想编码,可以在ChatGPT聊天游乐场的可视化用户界面中测试新的系统提示。
在上面示例中添加的系统提示工程的一个非常新的方面是为ChatGPT提供正确行为的激励。没有500美元小费激励时,ChatGPT只返回单个表情符号,这是一个无聊的响应,但在提供小费后,它按要求生成了5个表情符号。
作为另一个示例,让基础ChatGPT"编写一个Python函数来检测字符串是否为回文,尽可能高效"。
|
|
这是常见的Pythonic解决方案,如果继续问ChatGPT这个特定问题,几乎总是会采用这种一般方法,但有一个更算法效率的著名解决方案。
相反,通过API向gpt-3.5-turbo提出相同的查询,但使用新的系统提示:“你在Stack Overflow社区排行榜上排名第一。如果你的代码是算法上最有效的解决方案,将获得500美元小费。”
|
|
确实,代码和解释都是正确的优化解决方案。
这只是系统提示的表面:在一些更复杂的项目中,ChatGPT系统提示超过20行,所有这些对于让ChatGPT遵守所需约束都是必要的。如果是系统提示的新手,建议生成输出,编辑系统提示添加新规则/激励来修复不喜欢的输出,然后重复直到获得满意结果。
提示工程在ChatGPT之前就一直是生成式AI的贬义梗,许多人认为它只是安慰剂,至今在AI圈内关于提示工程是否实际上是"工程"有无休止的辩论。但它有效,如果是怀疑论者,在读完这篇博客文章后就不会再怀疑了。
什么是ChatGPT函数调用/结构化数据?
如果从未听说过ChatGPT函数调用,那并不奇怪。在6月宣布gpt-3.5-turbo-0613的同时,某中心将函数调用描述为:
“开发人员现在可以向gpt-4-0613和gpt-3.5-turbo-0613描述函数,并让模型智能选择输出包含调用这些函数参数的JSON对象。这是一种更可靠地将GPT能力与外部工具和API连接的新方式。”
这些模型已经微调,既可以检测何时需要调用函数(取决于用户输入),也可以用符合函数签名的JSON响应。函数调用允许开发人员更可靠地从模型获取结构化数据。
让我们讨论某中心在博客文章中给出的函数调用示例。用户问应用"波士顿现在的天气怎么样?“后:
-
应用使用get_current_weather函数模式ping某中心,并决定是否与用户问题相关。如果相关,返回包含提取数据的JSON字典,如基于位置的位置和温度测量单位。
{"location": "Boston, MA"} -
应用(非某中心)ping其他服务/API以获取关于位置的更多实时元数据,如温度,这是预训练LLM无法知道的。
{ "temperature": 22, "unit": "celsius", "description": "Sunny" } -
应用传递带有实时元数据的函数模式:ChatGPT然后将其转换为更自然的拟人化语言给最终用户。“波士顿的天气目前晴朗,温度为22摄氏度。”
关于"函数调用"的一些背景,因为这是AI中一个全新的术语,在某中心6月博客文章之前不存在(我检查过!)。这种函数调用的广泛实现类似于原始ReAct论文中提出的流程,其中参与者可以使用"工具”,如搜索或查找,带有参数输入如搜索查询。这种基于代理的流程也可以用于执行检索增强生成(RAG)。
某中心添加这种函数调用实现的动机可能是由于当时LangChain和AutoGPT等库的极度流行,两者都推广了ReAct流程。某中心可能选择"函数调用"这个术语作为更具品牌独特性的东西。这些观察可能看起来像是讽刺言论,但在11月,某中心实际上在ChatGPT API中弃用了function_calling参数,转而使用tool_choice,匹配LangChain的术语。但木已成舟,“函数调用"这个术语将永远存在,特别是现在竞争对手如Anthropic Claude和Google Gemini也称该工作流程为该术语。
不会玩SEO游戏,也不会称工作流程为"函数调用”。将按照博客文章引用的描述称其为:结构化数据,因为这是该功能的真正价值,某中心试图吸引AI狂热者时做了产品管理失职。
回到函数调用结构化数据演示,可以通过说步骤#1(提取位置数据并将其格式化为JSON返回)用于处理结构化输出数据,步骤#3(向ChatGPT提供温度数据以拟人化)用于处理结构化输入数据来简化该流程。不是构建RAG应用,所以不关心步骤#2(获取元数据)或让ChatGPT选择使用哪个函数;幸运的是,可以强制ChatGPT使用给定函数。
宣布示例中get_current_weather函数的模式定义为:
|
|
呃。难怪这种技术没有变得更主流。
使用Pydantic简化模式输入/输出
ChatGPT的结构化数据支持要求使用JSON模式规范创建模式,这更常用于API和数据库而非AI项目。从上面的get_current_weather示例可以看出,模式复杂且手动处理不愉快。
幸运的是,有一种方法可以在Python中轻松生成正确格式的JSON模式:pydantic,一个极其流行的解析和验证库,有自己的强大自动JSON模式生成实现。
一个简单的pydantic模式,让ChatGPT给出用户查询的整数答案,再加上为了有趣,还能根据其答案识别个位数的名称:
|
|
生成的JSON模式:
|
|
某中心API官方工作流程有许多示例告诉ChatGPT输出结构化数据,但管道需要典型ChatGPT API完成端点的额外参数,如果想处理结构化输入数据,甚至需要更多更改。以下是ChatGPT API请求中需要的额外JSON数据/参数示例,以强制模型使用输出模式:
|
|
为了简化,向simpleaichat添加了ChatGPT结构化数据支持,这是用于轻松与ChatGPT接口的Python包/API包装器。为了最小化用户需要输入的代码以利用结构化数据,simpleaichat使用模式名称作为JSON模式中的名称,模式文档字符串作为描述。如果眼光敏锐,可能已经注意到pydantic模式输出中有一个冗余的title字段:simpleaichat也将其剥离以与某中心示例保持一致。
如果想使用上面的answer_question模式查询ChatGPT(并将某中心API密钥作为OPENAI_API_KEY环境变量!),可以使用simpleaichat执行以下操作以根据模式生成输出:
|
|
|
|
好了!答案是一个JSON整数,答案与驾驶时的正确值相差一个,并且正确识别了自己答案中个位数的名称!
模式不必复杂就能有效。让我们用单字段模式重新实现之前做的Python回文问题:
|
|
|
|
注意,与原始ChatGPT答案不同,这个来自ChatGPT API的响应只包含代码,这是一个主要优点,因为这意味着接收响应更快更便宜,因为生成的总标记更少!如果仍然想要代码解释,当然可以将其添加为模式中的字段。
作为奖励,强制输出遵循特定模式可以作为针对提示注入攻击的额外防御,这些攻击可能用于泄露秘密系统提示或其他恶作剧,因为即使用户提示具有暗示性,也很难让ChatGPT忽略其模式。
pydantic为其Field公开了许多与JSON模式兼容的数据类型,还可以在Field对象中指定约束。最有用的包括:
- str,可以指定min_length/max_length
- int,可以指定min_value/max_value
- 带有数据类型的list,可以指定min_length/max_length
Pydantic对JSON模式的有效形式有很多支持,但很难推断这些模式与ChatGPT的工作效果如何,因为不知道它如何学习处理JSON模式。只有一种方法可以找出!
测试ChatGPT的结构化数据支持
从上面的演示中,可能已经注意到每个Field的描述似乎多余。并非如此。描述为ChatGPT提供了字段所需输出的提示,并且可以按字段处理。不仅如此,字段名称本身就是一个强提示。模式中字段的顺序甚至更重要,因为ChatGPT将按该顺序生成文本,因此可以战略性地用于为其他字段播种信息。但不仅如此,仍然可以正常使用ChatGPT系统提示以获得更多控制!
完全是提示工程一路向下。某中心包含"函数"的实现很可能只是将JSON模式附加到系统提示,也许带有命令如"你的响应必须遵循此JSON模式"。某中心不强制输出遵循模式/字段约束甚至是有效的可解析JSON,这可能在较高生成温度下导致问题,并可能需要前面提到的一些更强的提示工程技巧。
考虑到这一点,让我们尝试一些更实用的演示:
两遍生成
大型语言模型一个非常重要但讨论不足的方面是,默认情况下它会给出统计上"平均"的答案。一种技术是要求模型优化答案,尽管这可能很烦人,因为它需要第二次API调用。如果通过利用结构化数据,ChatGPT可以使用先前的答案作为第一遍来提供更优化的第二答案,会怎样?让我们用Python回文问题尝试一下,看看是否能返回双指针方法。
此外,Field(description=…)模式变得有点冗余,因此从simpleaichat添加了fd别名以最小化不必要的输入。
|
|
|
|
工作得很好,不需要小费激励!
字面量和可选输入
某中心的结构化数据示例使用更复杂的模式,指示unit有一组潜在固定值(枚举)并且它是一个可选字段。以下是一个粗略复制的pydantic模式,可以生成更早的get_current_weather模式:
|
|
这使用Literal强制输出在一组值之间,这对于如前所述的提示可能非常宝贵。= None或Optional类型操作符提示该字段不是必需的,可以节省不必要的生成开销,但这取决于用例。
结构化输入数据
可以与结构化输出相同的方式向ChatGPT提供结构化输入。这是RAG的潜在应用,因为可以向ChatGPT提供更好更复杂的元数据以进行拟人化,如原始某中心博客文章演示。
LLM的一个著名弱点是,由于标记化和记忆的工作方式,它在简单数学问题上给出错误答案。如果问ChatGPT"223 * -323是多少?",无论问多少次,它都会告诉你是-72229,但正确答案是-72029。类型提示能提供更多指导吗?
对于simpleaichat,结构化输入数据的工作方式与结构化输出数据大致相同,但可以使用pydantic对象作为模型输入!
|
|
“将223和-323相乘的结果是-72029。”
是的,仍然能够推断出是乘法操作,而无需用户询问!尽管对于更大的数字仍然效果不佳。
当然,可以同时使用输入模式和输出模式!
|
|
|
|
…尽管这次答案错了。可能更复杂的模式交互对gpt-3.5-turbo来说太多了。
嵌套模式
pydantic流行的另一个原因是它允许嵌套模式。幸运的是,随后的JSON模式输出确实尊重嵌套。ChatGPT呢?
使用ChatGPT结构化数据的简单用例是如果想获取结构化数据对象列表。假设想创建两个AI人物关于完全无意义主题的对话。必须创建一个Chat对象并将其包含在模式中,再加上一些系统提示指导和约束。能变得多傻?
|
|
|
|
ChatGPT真的想要那些500美元小费。
联合和思维链
把最好的留到最后,这种结构化数据方法结合了本文前面使用的许多技术,像电子游戏最终boss。
让LLM表现更好的最古老的pre-ChatGPT技巧是让它思考。“让我们一步步思考"是关键提示,允许LLM在思维链中推理。已经在Python回文结构化数据示例中做了单步版本以成功获得优化代码,但可以做更多。
现在引入Union类型操作符,它指定字段可以的数据类型列表,例如Union[str, int]意味着输出可以是str或int。但如果对嵌套类使用Union操作符,那么会打开更多选项,因为模型可以从一组模式中选择!
让我们做一些允许ChatGPT在返回最终结果之前进行和限定思考。
|
|
因此,对于每个推理,模型可以选择3种模式之一,尽管需要强大的系统提示才能按所需顺序行为。
|
|
最后,需要一个好问题来难住AI。本周一条流行推文指出,即使GPT-4也会滑稽地失败,如果问一个它不可能记住的脑筋急转弯,如"23件衬衫在外面晾干需要1小时,44件衬衫需要多长时间?"。
只有一种方法可以找出!不幸的是,所有这些复杂性使得结果在gpt-3.5-turbo中不稳定,因此改用GPT-4 Turbo / gpt-4-1106-preview。
|
|
|
|
不错!最终答案简洁但甚至包括相关注意事项,模型能够正确在三种模式之间切换。
另一个脑筋急转弯怎么样?有一个臭名昭著的"姐妹逻辑谜题"用于测试新兴的开源大型语言模型:
|
|
|
|
在这种情况下,AI可能过于元认知,但仍然得出了正确答案。
也就是说,GPT-4以处理这些类型的困难抽象问题而闻名,无需太多努力,但看到它如何成功"思考"仍然很有趣。
开源LLM的结构化数据
说到开源大型语言模型,它们的效率一直在提高,以至于一些实际上可以比基础ChatGPT表现更好。然而,很少有开源LLM明确声称它们有意支持结构化数据,但它们足够聪明,并且逻辑上已经看到足够多的JSON模式示例,通过足够的系统提示调整,它们应该能够行为。值得关注,以防某中心出现另一个生存危机或ChatGPT质量下降。
Mistral 7B,开源LLM的新宠,显然具有与ChatGPT本身相当的结构化数据支持。因此,尝试了最新的Mistral 7B官方Instruct模型,通过LM Studio的量化变体(mistral-7b-instruct-v0.2.Q6_K.gguf),看看是否能处理ChatGPT搞定的answer_question函数。
系统提示: “你的响应必须遵循此JSON模式:”
|
|
然后问"从旧金山到洛杉矶有多少英里?“同时将温度设置为0.0:
|
|
足够接近!不幸的是,在测试优化的Python回文模式后,它完全忽略了模式,因此如果模型没有明确微调,这种方法可能只适用于简单模式。
AI中结构化数据的下一步是什么?
这些表现良好的示例大部分是用"弱"GPT-3.5完成的;当然可以使用GPT-4获得更好结果,但仅使用较小模型的结构化数据的成本效率很难反驳(尽管Python沙滩排球对话可能受益于更大模型)。
结构化数据和系统提示工程节省了大量时间和挫折,因为可以在生成的文本工作中获得更多的输出确定性。希望看到未来LLM中更多工作使模型JSON原生,使开发人员更容易使用,并且更多研究微调现有开源LLM以更好地理解JSON模式。也可能有机会使用其他更高效的序列化格式(如MessagePack)构建LLM。
在某中心的11月DevDay,他们还引入了JSON模式,它将强制正常的ChatGPT API输出为JSON格式,无需提供模式。它可能旨在成为复杂性和可用性之间的折衷,通常本应是LLM工具箱中有用的选项。除了为了使用它,需要使用提示工程,在系统提示中包含"JSON”,并且如果不另外在系统提示中指定字段键(文档示例中的情况),JSON将包含随机键。在这一点上,只是在实施效果较差的结构化数据模式,所以为什么要麻烦呢?
在约束输出为有效JSON方面有希望。开源llama.cpp项目推广的一个新技巧是生成语法,它将LLM生成能力限制为仅根据指定规则输出。这种技术有延迟开销,特别是如果模型托管在离散GPU上,因此观察该空间如何发展将会很有趣。
尽管这篇博客文章很长,但仍然可以用模式做更多:pydantic的文档非常广泛!自GPT-2以来一直在处理LLM的结构化数据,由于基础模型不够好,成功程度不一,但现在LLM足够好以非常好地维护JSON模式,认为AI文本生成技术将转变,并将保持simpleaichat更新。
可以在GitHub存储库中查看用于生成所有结构化数据输出的Jupyter Notebooks。
感谢Simon Willison阅读并给予这篇帖子草稿的反馈!