我们公司主要帮餐饮店进行在线营销,并给他们一个一站式的平台来管理预约和优惠券等等。比如你在KLOOK或者Google上预约一个饭店,这个订单其实会进入我们的系统。
但不是所有的合作伙伴都和我们做了API对接,这种情况下,他们会把预约订单用邮件发给我们,我们再用程序解析邮件内容,把提取出来的订单信息保存在数据库里给业务部门处理。比如,KLOOK会发来这样的邮件:
Mac OS X的遗老遗少们,你们没看错,这个界面是Mavericks+Flavours皮肤
多数情况下,邮件里的信息都遵循特定的格式,所以处理起来并不会有什么问题。然而,一旦其中有非格式化的信息,系统就完全没办法处理了。比如,我们必须知道一共有几个人预约,但邮件中的出行人
的文本并不统一,比如它可能是以下任意一种:
如果其中还有儿童,那出行人
可能是这样的:
很乱对吧?
此外,里面有的部分代表儿童的年龄范围(比如儿童(0-11岁)
),而有的是套餐名称(比如儿童套餐(0-11岁)
),光看文字你未必知道两者区别。
还有如果预订客人中有儿童,那么我们必须知道他们几岁(餐厅的要求)。这部分就更乱了,因为内容是用户自由输入的,所以结果更是千奇百怪,比如:
最后,还有一些订单的出行人
里还有和人无关的信息:
更多的例子我就不举了,相信你已经知道问题在哪儿了:
订单的部分内容没有任何规律,我们却要把不同的信息分别提取出来。怎么办?
这恰好是语言模型最擅长的事情,GPT登场。
在系统中整合GPT的思路一般是这样的:
这是我最终采用的指令:
[Context]
Please parse the text in the [Email] section and return valid JSON data.
While outputting the `packages` in the JSON, if the text in the [Email] section matches any of the following known package names, put it to `packages`, otherwise ignore it.
Known package names:
#{GPT_PROMPT_PACKAGES.map { |p| "- #{p}" }.join("\n")}
[Examples]
Email:
出行人: 2 x 每人
0 - 7岁同行的儿童数量:
JSON: {"adults": 2, "children": {"number": 0, "ages": []}, "packages": []}
Email:
出行人: 3 x 成人
0 - 7岁同行的儿童数量:
JSON: {"adults": 3, "children": {"number": 0, "ages": []}, "packages": []}
Email:
出行人: 3 x 成人(12岁以上), 4 x 儿童(0-11岁)
如有儿童同行,请提供儿童的实际年龄/数量,以利座位安排:
3, 4, 4, 6
JSON: {"adults": 3, "children": {"number": 4, "ages": [3, 4, 4, 6]}}
Email:
出行人: 2 x 每人, 1 x 儿童(0-12岁)
0 - 7岁同行的儿童数量: 1个3岁
JSON: {"adults": 3, "children": {"number": 1, "ages": [3]}, "packages": []}
Email:
出行人: 6 x 成人(12岁以上), 1 x 包厢费, 1 x 0-12岁儿童套餐
JSON: {"adults": 6, "children": {"number": 1, "ages": [3]}, "packages": [{"name": "包厢费", "number": 1], ["name": "0-12岁儿童套餐", "number": 1]}
[Email]
出行人: #{mail['出行人']}
#{mail['其它信息']}
看起来很简单,但其中有两个地方其实很有意思。
第一个是实时学习(few-shot in-context learning)。我这次没有像以往一样在指令中详细定义JSON的格式,而是给GPT几个例子,让它学习什么样的输入对应什么样的输出,效果一样,但方便太多。
第二个,因为出行人
中既可能有套餐名,比如【小学生】寿喜烧黒毛和牛畅吃晚餐(7-11岁)
,也可能仅包含年龄信息,比如儿童(0-11岁)
,如果不是事先知道这一点,别说GPT,就是人也无法分辨这行字到底代表什么,所以我想了一个取巧的办法:把所有已知的套餐名都放到一个列表里,作为指令的一部分发给GPT,然后告诉它如果发现文字能匹配到,说明这行字是套餐,否则就是年龄信息。
写完了指令就简单了,无非就是在系统中调OpenAI的API然后处理返回。
这个功能发布有一阵子了,偶尔GPT会返回不想要的结果(几乎无法避免),但99%的时间都能正常运行,对我们来说足够用了。
这个指令其实还可以进一步改进,比如用压缩和YAML来节省成本,降低一些温度(temperature)来减少不确定性等等,因为投入产出比的缘故我们暂时没有做,以后再说吧。
更多技巧可以看我的这两篇小文:
也欢迎使用我做的若愚: