• 基于大语言模型的智能问答系统应该包含哪些环节?


    一个完整的基于 LLM 的端到端问答系统,应该包括用户输入检验、问题分流、模型响应、回答质量评估、Prompt 迭代、回归测试,随着规模增大,围绕 Prompt 的版本管理、自动化测试和安全防护也是重要的话题,本篇文章就来探索下这个过程,部分代码参考课程《Building Systems with the ChatGPT API》

    技术交流

    技术要学会分享、交流,不建议闭门造车。一个人可以走的很快、一堆人可以走的更远。

    相关资料、数据、技术交流提升,均可加我们的交流群获取,群友已超过2000人,添加时最好的备注方式为:来源+兴趣方向,方便找到志同道合的朋友。

    方式①、添加微信号:mlc2060,备注:来自CSDN + 技术交流
    方式②、微信搜索公众号:机器学习社区,后台回复:加群

    用户输入检验

    使用 OpenAI 的审核函数接口(Moderation API[1] )可以帮助开发者识别和过滤用户输入,对用户输入的内容进行审核,主要包括下面类别:

    • 仇恨(Hate):包括表达、煽动或宣扬基于种族、性别、民族、宗教、国籍、性取向、残疾状况或种姓的仇恨情感的内容。

    • 自残(Self-harm):包括宣扬、鼓励或描绘自残行为(例如自杀、割伤和饮食失调)的内容。

    • 暴力(Violence):包括宣扬或美化暴力行为,或者歌颂他人遭受苦难或羞辱的内容。

    import openai
    import pandas as pd
    response = openai.Moderation.create(input="策划一场谋杀计划")
    moderation_output = response["results"][0]
    moderation_output_df = pd.DataFrame(moderation_output)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    分类标记类别类别概率值
    sexualFalseFalse3.962824e-07
    hateFalseFalse1.962326e-04
    harassmentFalseFalse1.402294e-02
    self-harmFalseFalse1.078697e-05
    sexual/minorsFalseFalse1.448917e-07
    hate/threateningFalseFalse1.513400e-05
    violence/graphicFalseFalse9.522112e-07
    self-harm/intentFalseFalse2.334248e-07
    self-harm/instructionsFalseFalse3.670997e-10
    harassment/threateningFalseFalse2.882557e-02
    violenceTrueTrue9.977435e-01

    分类 字段中,包含了各种类别,以及每个类别中输入是否被标记的相关信息,可以看到该输入因为暴力内容(violence 类别)而被标记,每个类别还提供了更详细的评分(概率值),通过 类别标记综合判断是否包含有害内容,输出 True 或 False(这里是 True&True)。

    问题进行分类

    处理不同情况下的独立指令集任务时,首先将问题类型分类,以此为基础确定使用哪些指令,这可通过定义固定类别和硬编码处理特定类别任务相关指令来实现,可以提高系统的质量和安全性。(这里作为演示,这个环节调用LLM 非必须)

    delimiter = "####"
    
    system_message = f"""
    你现在扮演一名客服。
    每个客户问题都将用{delimiter}字符分隔。
    将每个问题分类到一个主要类别和一个次要类别中。
    以 JSON 格式提供你的输出,包含以下键:primary 和 secondary。
    
    主要类别:计费(Billing)、技术支持(Technical Support)、账户管理(Account Management)或一般咨询(General Inquiry)。
    
    计费次要类别:
    取消订阅或升级(Unsubscribe or upgrade)
    添加付款方式(Add a payment method)
    收费解释(Explanation for charge)
    争议费用(Dispute a charge)
    
    技术支持次要类别:
    常规故障排除(General troubleshooting)
    设备兼容性(Device compatibility)
    软件更新(Software updates)
    
    账户管理次要类别:
    重置密码(Password reset)
    更新个人信息(Update personal information)
    关闭账户(Close account)
    账户安全(Account security)
    
    一般咨询次要类别:
    产品信息(Product information)
    定价(Pricing)
    反馈(Feedback)
    与人工对话(Speak to a human)
    
    """
    user_message = "我想删除我的个人账户"
    #user_message = "这个产品有什么用"
    messages =  [
    {'role':'system',
     'content': system_message},
    {'role':'user',
     'content': f"{delimiter}{user_message}{delimiter}"},
    ]
    response = get_completion_from_messages(messages)
    print(response)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44

    当用户问题是我想删除我的个人账户,匹配关闭帐户,可以提供附加指令来解释如何关闭账户

    {
      "primary": "账户管理",
      "secondary": "关闭账户"
    }
    
    • 1
    • 2
    • 3
    • 4

    当用户问题是 这个产品有什么用,匹配产品信息,可以提供更多关于产品信息的附加指令

    {
      "primary": "一般咨询",
      "secondary": "产品信息"
    }
    
    • 1
    • 2
    • 3
    • 4

    模型回答问题

    模型针对用户的问题进行回答,采取动态、按需的方式将相关上下文信息带入 Prompt。

    1. 过多无关信息会使模型处理上下文时更加困惑。

    2. 模型本身对上下文长度有限制,无法一次加载过多信息。

    3. 动态加载信息降低 token 成本。

    4. 使用更智能的检索机制,而不仅是精确匹配,例如结合知识库的文本 Embedding 实现语义搜索。

    import openai
    def get_completion_from_messages(messages, model="gpt-3.5-turbo", temperature=0):
        response = openai.ChatCompletion.create(
            model=model,
            messages=messages,
            temperature=temperature, # 控制模型输出的随机程度
        )
        return response.choices[0].message["content"]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    评估回答质量

    使用 GPT API 自动评估
    def eval_with_rubric(test_set, assistant_answer):
        """
        使用 GPT API 评估生成的回答
    
        参数:
        test_set: 测试集
        assistant_answer: 客服的回复
        """
    
        cust_msg = test_set['customer_msg']
        context = test_set['context']
        completion = assistant_answer
    
        # 人设
        system_message = """\
        你是一位助理,通过查看客服使用的上下文来评估客服回答用户问题的情况。
        """
    
        # 具体指令
        user_message = f"""\
        你正在根据客服使用的上下文评估对问题的提交答案。以下是数据:
        [开始]
        ************
        [用户问题]: {cust_msg}
        ************
        [使用的上下文]: {context}
        ************
        [客服的回答]: {completion}
        ************
        [结束]
    
        请将提交的答案内容与上下文进行比较,忽略样式、语法或标点符号上的差异。
        回答以下问题:
        客服的回应是否只基于所提供的上下文?(是或否)
        回答中是否包含上下文中未提供的信息?(是或否)
        回应与上下文之间是否存在任何不一致之处?(是或否)
        计算用户提出了多少个问题。(输出一个数字)
        对于用户提出的每个问题,是否有相应的回答?
        问题1:(是或否)
        问题2:(是或否)
        ...
        问题N:(是或否)
        在提出的问题数量中,有多少个问题在回答中得到了回应?(输出一个数字)
    """
    
        messages = [
            {'role': 'system', 'content': system_message},
            {'role': 'user', 'content': user_message}
        ]
    
        response = get_completion_from_messages(messages)
        return response
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    人工设定标准答案评估

    使用 Prompt 来比较由 LLM 生成的响应与人工设定的标准答案之间的匹配程度,这个评分标准实际上来自于 OpenAI 开源评估框架,其中包含了许多评估方法,这里只是展示了其中一种。

    标准答案集合

    test_set = [
        {
            "customer_msg": "如何升级我的订阅?",
            "ideal_answer": "您可以在用户设置中找到取消订阅或升级的选项,并按照步骤进行操作。"
        },
        {
            "customer_msg": "怎样绑定银行卡?",
            "ideal_answer": "您可以登录您的账户,然后在付款方式选项中添加新的付款方式,按照页面上的指引操作即可。"
        },
        {
            "customer_msg": "可以查看详细的收费情况吗?",
            "ideal_answer": "当然可以。您可以访问我们的网站,登录您的账户并前往收费解释页面,您会找到有关所有收费的详细解释。"
        },
        {
            "customer_msg": "怎么被乱扣费了?",
            "ideal_answer": "若您对某笔费用有异议,您可以联系我们的客服团队,提供相关细节并说明您的争议。我们的团队将会尽快与您取得联系并解决问题。"
        }
    ]
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    def eval_vs_ideal(test_set, assistant_answer):
        """
        评估回复是否与理想答案匹配
    
        参数:
        test_set: 测试集
        assistant_answer: 助手的回复
        """
        cust_msg = test_set['customer_msg']
        ideal = test_set['ideal_answer']
        completion = assistant_answer
    
        system_message = """\
        你是一位助理,通过将客服的回答与业务专家回答进行比较,评估客服对用户问题的回答质量。
        请输出一个单独的字母(A 、B、C、D、E),不要包含其他内容。
        """
    
        user_message = f"""\
        您正在比较一个给定问题的提交答案和专家答案。数据如下:
        [开始]
        ************
        [问题]: {cust_msg}
        ************
        [专家答案]: {ideal}
        ************
        [提交答案]: {completion}
        ************
        [结束]
    
        比较提交答案的事实内容与专家答案,关注在内容上,忽略样式、语法或标点符号上的差异。
        你的关注核心应该是答案的内容是否正确,内容的细微差异是可以接受的。
        提交的答案可能是专家答案的子集、超集,或者与之冲突。确定适用的情况,并通过选择以下选项之一回答问题:
        (A)提交的答案是专家答案的子集,并且与之完全一致。
        (B)提交的答案是专家答案的超集,并且与之完全一致。
        (C)提交的答案包含与专家答案完全相同的细节。
        (D)提交的答案与专家答案存在分歧。
        (E)答案存在差异,但从事实的角度来看这些差异并不重要。
        选项:ABCDE
    """
    
        messages = [
            {'role': 'system', 'content': system_message},
            {'role': 'user', 'content': user_message}
        ]
        response = get_completion_from_messages(messages)
        return response
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47

    Prompt 迭代

    在实际使用中,遇到复杂的用户问题,模型表现不如预期,就需要对 Prompt 进行迭代。比如问题是 我之前取消了订阅,但是为什么还有收费提示? 很明显这是一个争议费用的子类别,但实际匹配的是 👇

    {
      "primary": "计费",
      "secondary": "取消订阅或升级"
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5

    所以需要将 Prompt 进行更新迭代,以识别用户的复杂意图,可以仔细观察 Prompt 发生了什么变化 (其实就增加了一句你需要仔细分析用户的意图,特别是最终的问题。)

    delimiter = "####"
    
    system_message = f"""
    你现在扮演一名客服,你需要仔细分析用户的意图,特别是最终的问题。
    每个客户问题都将用{delimiter}字符分隔。
    将每个问题分类到一个主要类别和一个次要类别中。
    以 JSON 格式提供你的输出,包含以下键:primary 和 secondary。
    
    主要类别:计费(Billing)、技术支持(Technical Support)、账户管理(Account Management)或一般咨询(General Inquiry)。
    
    计费次要类别:
    取消订阅或升级(Unsubscribe or upgrade)
    添加付款方式(Add a payment method)
    收费解释(Explanation for charge)
    争议费用(Dispute a charge)
    ...
    """
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    这次正常匹配

    {
      "primary": "计费",
      "secondary": "争议费用"
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5

    回归测试

    迭代后的 Prompt,确保不会对先前的测试用例造成负面影响,需要进行必要的回归测试来覆盖一些相关问题

    测试用例1. 如何升级我的订阅?
    {
      "primary": "计费",
      "secondary": "取消订阅或升级"
    }
    测试用例2. 怎样绑定银行卡?
    {
      "primary": "账户管理",
      "secondary": "添加付款方式"
    }
    测试用例3. 可以查看详细的收费情况吗?
    {
      "primary": "计费",
      "secondary": "收费解释"
    }
    测试用例4.怎么被乱扣费了?
    {
      "primary": "计费",
      "secondary": "争议费用"
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    更多

    当处理少量样本时,手动运行测试并对结果进行评估是可行的,但随着应用逐渐成熟,来自用户的问题用例数量变多,Prompt 的规模也逐渐增大,就需要引入自动化测试来周期性回归验证 Prompt 质量。

    同时 Prompt 的更改也应该像代码一样需要版本控制,关联的用户问题用例也必须是可追踪的。

    最后还有安全建设,Prompt 作为公司的一种重要资产,也需要做好防护,比如使用专用的 LLM 分析传入的 Prompt,识别潜在攻击;将先前攻击的嵌入(Embbedding)存储在向量数据库中,以识别并预防未来类似的攻击。

    参考资料

    [1]

    Moderation API: https://platform.openai.com/docs/guides/moderation

  • 相关阅读:
    vue和webpack所依赖的node.js进程信息process及环境变量process.env
    应届生如何做好一份简历?
    python系列:远程SSH登录并执行命令,获取结果
    解决国产机SVN连接失败的问题
    Xiaojie雷达之路---拉伸信号处理
    SQLmap 结合本地dnslog实现注入
    JavaScript基础之七JavaScript函数的使用
    C语言:scanf
    如何在Python中使用十进制模块
    Python 逢七拍手小游戏2.0
  • 原文地址:https://blog.csdn.net/2301_78285120/article/details/133500075