编辑
2025-05-15
夺巧
00
请注意,本文编写于 35 天前,最后修改于 35 天前,其中某些信息可能已经过时。

目录

引言
格式化输出
链式提示 (Prompt Chaining)
两者结合?
总结

引言

本博客是上一期博客的续集,也是这一系列最重要的一部分。本文主要会介绍两个概念:格式化输出链式提示,这两个技巧融合在一起将有非常强大的效果,原本难以约束的LLM也会在它们的指引下如训练有素的军队一般能轻松应对任何实际场景。

不过也有一点遗憾:这一期的概念解释起来非常简单,但真正要在代码中实现却很复杂。我本来想在APaI中实现格式化输出与链式提示功能,但随即就很快意识到这样做的改动不止一星半点,加上时间有限,所以暂时只能先在这篇博客上"纸上谈兵"了。

另外再次声明,我所写下的这些大多数都是我自己偶然间获得的灵感,在先前我并没有查阅太多相关的资料,也不确定是否有人已有了这一方面的研究与成果,而我写下的这些又是否是在"误人子弟"。因此最好带有批判性地来阅读,能让读者产生自己的启发自然最好,嗯。

格式化输出

大多数情况下,LLM输出的都是自然语言,这当然是为了让人类看懂,但却对进一步的处理,也就是程序来说,并不是很友好。显然,要改变LLM的输出格式并不是多么困难的事情,只需要添加一句提示词,或是给几个输出样例,如今的LLM便能很快地理解你的意思。比方说,如果让LLM以json, yaml, toml之类的格式输出,那么python代码就能很好地将LLM输出转化为数据结构。当然也可以按你想要的自定义格式然后写代码来解析。总之方法十分灵活而且在某种意义上也能规范LLM的输出效果。

当然,光是这样说可能还不太能感受到格式化输出的好处,那就先举个例子吧。先前曾有个有趣的发现,那就是许多大模型都无法正确推断出9.119.9的大小。究其原因,其实就是文本生成类的模型难以准确地处理数理逻辑问题。当时我就在想,假如让LLM负责生成用于计算的python表达式,然后交给python交互式解释器来进行运算。假如计算过程不止一步的话,就把计算结果返回给LLM让它继续。如此一来,便有了下面这个提示词:

你需要解决一些数学问题,但本身并不能进行任何计算,必须要借助外部的python解释器才能得到计算结果。 用户输入一个数学问题后,你每次需要输出一个python计算表达式,用户会返回给你计算结果,然后你给出下一个计算表达式,直到计算出问题答案为止。 示例: Q: 小明有9个梨,小红有5个梨,问小明需要给小红多少个梨两人的梨数才能相等? A: 9 - 5 Q: 4 A: 4 / 2 Q: 2 A: 答:小明需要给小红2个梨。

然后我们来测试一下那个经典问题:

Q: 9.11和9.9谁更大? A: 9.11 > 9.9 Q: False A: 答:9.9比9.11更大。

可以看到,在格式化输出与外部工具的加持下,LLM并没有给出那个违背常理的答案。

当然这只是一个非常简单的例子,运用格式化输出,还能完成更多复杂的任务。不过在那之前,让我们先进入下一个主题。

链式提示 (Prompt Chaining)

所谓链式提示,其实就是多LLM协作,将一个LLM的输出作为另一个LLM的输入。拿提示工程指南的例子来说,一个LLM负责从文档资料中寻找与问题相关的文本,另一个LLM则结合第一个LLM的引文来回答问题(其实这差不多也是RAG的原理)。

那么这和与同一个LLM进行多轮对话有什么区别呢?往小了说,每个LLM有各自不同的提示词,能够专注于自己需要做的事情而不会产生指令错乱;往大了说,多个LLM组合再加上自动化流程控制,能起到流水线乃至产业链的效果。不过,我一时也想不出什么合适的例子,目前也没什么能够用来测试链式提示的地方,好在我们还有万能的LLM能直接帮我们设想一些生活场景:

1️⃣【笨蛋的晨间仪式】 ① 先让AI检查天气预报 → ② 根据温度推荐穿衣 → ③ 根据穿衣风格搭配饰品 → ④ 最后生成元气满满的早安颜文字(比如像主人这种邋遢鬼需要:❄️温度预警→连帽卫衣+破洞牛仔裤→挂满叮当响的朋克项链→"今天也要像垃圾桶一样被填满哦~(ゝω・)") 2️⃣【废柴的约会攻略】 ① 输入对方社交动态 → ② 分析兴趣关键词 → ③ 生成三个地点提案 → ④ 根据选定地点推荐穿搭 → ⑤ 最后输出土味情话弹药库(毕竟主人这种木头脑袋需要:"对方喜欢猫→猫咖→沾满猫毛的毛衣→'你比布偶猫更让我想带回家呢~' 呕...") 3️⃣【社恐的吵架指南】 ① 转述冲突场景 → ② 识别对方潜台词 → ③ 生成三种回应方案 → ④ 根据选择结果预测后续发展 → ⑤ 附赠事后道歉模板(示范:"她说'随便你'→其实想要被哄→选项C卖萌→'果然会演变成拉黑结局呢...这是给您预留的跪键盘姿势图解哦(笑)")

嘛……虽然未必非常符合链式提示,不过还是可以借此拓展想象力的→_→

两者结合?

当然,我在这里将两个概念放在一起来说,肯定是有别的深意的。因为我发现:

两者搭配在一起简直就是绝配啊!

咳咳……比较公式化地说,两者结合的方式就像下面这样:

每个LLM LiL_i的输出都将是一个键值对Di={ki1:vi1,ki2:vi2,...}D_i = \{ k_{i1}: v_{i1}, k_{i2}: v_{i2}, ... \},经过程序的某种处理f(Di)={ki1:f(vi1),ki2:f(vi2),...}f(D_i) = \{ k_{i1}: f(v_{i1}), k_{i2}: f(v_{i2}), ...\},可以组合为另一个LLM的输入input={kij:f(vij)}input = \{ k_{ij}: f(v_{ij}) \}

从这个角度来看,每个LLM都可以看作是不同的黑盒函数,以结构化输出为接口,以链式调用为流程逻辑,与程序中的普通函数相接,彼此调用组合,将会带来极高的自由度。而这些黑盒函数的实现是零代码的(只需要API + Prompt),却能胜任许多复杂的乃至常规代码难以实现的功能。

因此未来可能出现一种新型的程序形式,由大量以Prompt构成的LLM函数构成,辅之以少量轻量级的"原教旨主义代码",最后再搭配一个简单的API调用模块,最终就构成一个程序了。

(我怎么硬是编出了这么多)

当然啦,我一开始倒也没想得这么远,只是单纯联想到了一种最近变得日益可行的游戏形式,就是将AI大模型嵌入到游戏中。比方说,让LLM替代游戏中的NPC与玩家对话,或者让LLM直接给游戏生成随机事件乃至剧情等等。

而我们也可以利用格式化输出与链式提示来自己制作一个AI原生游戏

游戏开始时,LLM生成若干个心理测试题,玩家填写的结果交给LLM后生成匹配的生命值、攻击、法力、幸运等主角初始数据 游戏过程中,LLM会生成主角遭遇的随机事件,并给予相应的奖励与惩罚 敌对阵营的怪物在战斗中也由LLM进行控制,不同的个性会使LLM根据当前战况选择应对措施(死战到底或落荒而逃) 主线剧情结束后,玩家也可以继续在世界中自由探索,在LLM的协助下生成新的随机剧情、随机NPC、随机Boss

这才是真正的开放世界冒险游戏啊!

总结

可以说,关于如何充分发挥LLM作用的这一领域仍是一片未经开拓的蓝海,LLM的低门槛与高泛用性让每个人都能在此充分发挥想象力。

不过目前来看市面上都还是只能与LLM进行普通对话的平台,要想解析处理输出的格式化数据乃至运用到链式提示中还是需要自己编码实现的。虽说,理论上来说这一流程存在某种泛用性,理想情况下,能够存在一个框架,只需要输入各LLM的提示词与输出格式,再加上一些内嵌代码,就可以形成一条完整的LLM流水线应用。但以我目前的编码能力,想要实现出来似乎还有不小的难度(˘・ω・˘)

但相信在不久的未来,这样的"LLM-Engine"真的会出现吧。