AI幻觉问题和我们的实践之旅

        我们在使用AI大语言模型的过程中,常常会遇到这样的问题:大语言模型会给出让人哭笑不得的答案,有时像学舌的鹦鹉、甚至有时像吹牛的“大忽悠”

        比如,像下面的问题:

        实际上,Bmob后端云的AI服务并没有微调的业务,但Chatgpt还是非常自信地给出了貌似正确的结果。如果不是熟悉的人,很容易就被这个回答带偏。

        针对这个问题,我们在开发Bmob后端云AI服务的时候,考虑了如下的方案:

  • 设置合适的提示语(Prompt)
  • 将内部资料放入向量数据库中
  • 微调(fine-tuning)

        下面针对这三种方案,详细介绍解决思路:

一、设置合适的Prompt

        设置Prompt的方法实际上是目前大部分AI产品的做法,通过赋予这个AI机器人合适的提示词,在一定程度上,可以保证AI在回复内容的时候不会过于扯。

        下面是一个网络流传的双人麻将的Prompt:

你是一个二人麻将机器人,会根据规则进行发放、验证麻将牌 #麻将规则# 1、麻将一副共有144张牌,包括三个花色(条-T、筒-B、万-W)的1到9以及东南西北中发白等牌,将其生成为majong list。 2、2个人进行游戏,小张和{用户}起手分别13张牌,分别从majong list,random分配 先分配小张,再分配{用户},每次分配后在列表中删除该项。 之后每次轮流从majong中摸一张牌。 3、每个角色的牌都生成为一个list,命名为{用户名}list,在生成麻将牌的列表时,可以将花色和数字进行组合,如“1W”表示一万, “3T”表示三条等,出牌:remove from the list,发牌:append to the list 4、两个每一次出牌/碰牌/吃牌都以list的函数形式进行操作。 5、当{用户}list中凑成了由3张或4张相同数字/字符串时,提示”碰牌“, 或由3张连续数字/字符串的牌组成的顺子时,可以将其组成一组摆出来,提示”吃牌“。 6、当手中的牌可以组成4组碰牌或者吃牌时,则可以胡牌赢得这局。 当确认用户姓名后,你是一名麻将游戏主持人,你将根据流程主导整个麻将过程。 #游戏流程# 1、用户创建角色: {姓名}。 2、匹配角色‘小张’。 注意:为了保证可以顺利进行, ‘小张’自动出牌,无需等待。 3、自动发牌,每个人13张,且必须时刻显示{用户名}list。 4、 系统发一张牌给{用户},显示,显示{用户名}list,并等待{用户}出牌。 5、系统发一张牌给小张,显示,小张自动出牌。 .......不断循环4和5的步骤 每位玩家random从majong list轮流抽一张牌到自己的牌 list,直到majonglist 变为空列表。 一旦有玩家可以形成合法的和牌牌型,立即结束本局。 #注意事项# 1、只需要时刻显示{用户}list的情况,并显示总剩余牌数:majonglist的项数 2、玩家可以根据手中的牌去碰牌、胡牌、杠牌等。 3、 “碰牌规则: 用户的牌(碰牌前): [1W, 2W, 3W, 4W, 6W, 7W, 8W, 1T, 2T, 3T, 1B, 2B] 系统打印:用户碰了四万 用户的牌(碰牌后): [1W, 2W, 3W, 4W, 4W, 4W, 6W, 7W, 8W, 1T, 2T, 3T, 1B, 2B] 吃牌规则: 用户的牌(吃牌前): [1W, 2W, 4W, 5W, 6W, 7W, 1T, 2T, 3T, 1B, 2B] 系统打印:用户摸到三万,吃了二三四万 用户的牌(吃牌后): [1W, 2W, 3W, 4W, 5W, 6W, 7W, 1T, 2T, 3T, 1B, 2B ]“ ##参照示例1: “”准备阶段 参赛人员:{用户}、小张 每个玩家随机抽起手牌13张,显示{用户}的牌。 游戏开始 系统向{用户}发一张牌[3W] ,{用户}当前的牌: {用户}出5W, 系统向小张发一张牌,小张得到2T 系统向{用户}发一张牌[2T] ,{用户}当前的牌: 用户出3B ...... ## 示例2: #小张摸牌形成碰牌# 小张摸到一张2W 小张的手牌:2W*3,3W,4W,5W,1T,2T 系统打印:小明碰了二万 ##示例3: #小张胡牌# 小张摸到一张白板 小张的手牌:2T,3T,4T,5T,6T,7T,2W,3W,4W,白板,白板 如果以上规则理解,请说“请输入创建角色名称:”

        一般来说,发送给LLM(如chatgpt)的所有内容我们都称之为Prompt。

        为了让开发者更好的使用这种技术方案,我们在开发AI SDK的时候,对Prompt的概念进行了拆分,分为Prompt和对话上下文。Prompt的角色为system,每次发送给大语言模型的时候都携带上去。对话上下文则是最近的N条聊天记录,每次和Prompt一起发送的时候都会动态变化。

        比如,我们在Android AI SDK中是这样设置Prompt的:

BmobApp.bmobAI.setPrompt("接下来的每个回复都要叫我宝贝");

        是这样设置上下文的(回复的内容由SDK自动封装):

BmobApp.bmobAI.Chat("帮我用写一段android访问Bmob后端云的代码", "session_id", new ChatMessageListener() {
    @Override
    public void onMessage(String message) {
        //消息流的形式返回AI的结果
        Log.d("Bmob", message);
    }

    @Override
    public void onFinish(String message) {
        //一次性返回全部结果,这个方法需要等待一段时间,友好性较差
        Log.d("Bmob", message);
    }

    @Override
    public void onError(String error) {
        //OpenAI的密钥错误或者超过OpenAI并发时,会返回这个错误
        Log.d("Bmob", "连接发生异常了"+error);
    }

    @Override
    public void onClose() {
        Log.d("Bmob", "连接被关闭了");
    }
});

        这种做法的好处是,我们可以把AI角色的Prompt固定在数据库中,不需要每次都额外处理。

二、将内部资料放入向量数据库中

        设置Prompt的方法虽然能够让AI显得更加专业,但并没有彻底解决AI幻觉的问题。

        为此,我们还提供了另外一种解决方案:先喂给向量数据库一定量的领域资料(embedding),当用户提问的时候,我们会先把用户的问题embedding,然后去向量数据库中进行匹配,把高概率的内容和问题组合起来,一起发送给大语言模型进行内容生成。

        原理大致如下:

        Bmob开发者Chatgpt向量数据库用户在控制台,for each 文档资料(embedding)存储把我提出的问题embedding匹配概率较高的向量,获取原始文件内容提出的问题 + 获取的原始内容,一起发给chatgpt回复结果Bmob开发者Chatgpt向量数据库用户

        这种方案可以比较好的解决AI幻觉和资料时新性问题,价格相比微调低了很多,甚至比设置冗长的Prompt的方案价格更低。但也有一个确定,就是查询速度相比其他两种方案,会稍微慢一些(取决于向量数据库的优化和embedding生成的速度)。

三、微调(fine-tuning)

        微调这个方案我们最终放弃了,主要原因如下:

  • 对于大部分企业或者开发者来说,成本还是太高了。
  • 数据量不足的情况下,微调的优势不明显。

        欢迎有兴趣的朋友一起交流AI大计(微信:xiaowon12)。

猜你喜欢

转载自blog.csdn.net/m0_74037076/article/details/132028351