构建技术支持聊天机器人

,

在 Discourse 中添加一个 AI 聊天机器人非常简单(多亏了两个出色的插件)。但要添加一个能执行技术支持的聊天机器人,难度可就大多了!本文分享了我们在 support.suretyhome.com 设置技术支持聊天机器人的经验——我们的目标、遇到的问题、解决方案以及未来的规划。

我们的支持团队仅在正常工作时间提供服务,但客户希望全天候获得帮助。我们并非试图取代支持团队。我们的目标是通过一个聊天机器人来增强支持团队的能力,该机器人应:

  • 7×24 小时全天候可用,包括夜间和周末,就像我们的论坛一样
  • 即时响应,而我们的真人支持团队则需要稍长一些时间
  • 能够回答用户通过论坛搜索无法解决的问题

以下是我们的经验总结。

选择插件

有两个非常优秀的插件提供 AI 聊天机器人功能:

  1. Discourse AI
  2. Discourse Chatbot

Discourse AI 是 Discourse 开发团队推出的官方 AI 插件,它不仅包含聊天机器人,还提供其他 AI 功能。而 Discourse Chatbot 插件则专注于聊天机器人这一单一功能。它诞生于 Discourse AI 之前,致力于将这一件事做到极致。

起初,我们完全不知道该选用哪一个,于是我在这里提问以寻求建议。

我们得到了许多宝贵的帮助。最终我们选择了 Discourse Chatbot,因为它作为聊天机器人更加灵活,拥有更多可自定义的选项(“花哨功能”)。我们的使用场景有一些特定需求,目前看来用 Discourse AI 还难以实现。这两个插件都可以是绝佳的选择。哪一个适合你,取决于你论坛的具体需求。

初始设置

Discourse Chatbot 的初始设置可能有点复杂,因为你需要面对大量的选项和自定义设置。请仔细遵循 设置说明,并确保查看所有设置。

我们的目标是提供类似聊天的体验,因此我们只希望机器人在 Discourse 聊天中使用,而不希望在公开主题或私信中运行。我们需要采取的第一步是:

  • 设置 Discourse Chat(Discourse Chatbot 依赖于此)
  • 在 Discourse Chatbot 设置中,启用 chatbot permitted in chat(允许聊天机器人参与聊天)

提示工程(Prompt Engineering)

Discourse Chatbot 具有极高的可定制性。任何非设置项的内容都可在 Discourse 的“自定义 > 文本”中进行定制。这就是你进行所有提示工程的地方。在“自定义 > 文本”中搜索 chatbot.prompt,即可筛选出所有可自定义的提示文本。

为了让机器人按照我们的期望行为,我们需要编辑系统提示(system prompt)。但这里有两个系统提示,一个用于公开讨论,一个用于私密讨论。由于我们只在私密聊天频道中使用机器人,因此需要编辑 chatbot.prompt.system.rag.private

作为技术支持机器人,我们需要它比 LLM(大语言模型)默认表现更加保守和准确。我们的系统提示需要相对较长才能实现这一目标。在系统提示中,给 LLM 提供指令和背景信息,以回答以下问题:

  • 你的机器人是谁?它应该扮演什么角色?
  • 它需要了解哪些背景信息或上下文?
  • 它应该讨论哪些话题?哪些话题是绝对不能讨论的?
  • 它应该使用什么样的写作风格或语气?
  • 当用户感到沮丧时,它应该怎么做?

除了这些通用的提示工程外,系统提示也是解决测试中发现的问题的地方。如果你发现机器人犯了严重错误,也许可以通过在系统提示中添加指令来修复。但请注意,提示只是给 LLM 的建议,你并不是在编程,只是在请求它以某种方式行事。它可能不会听从。

温度(Temperature)与 Top P

另一个让机器人更加保守、减少胡编乱造的工具是温度设置。默认情况下,温度设置为 100,即最大温度的 50%。你可以进一步降低它,使机器人更加保守或确定性更强,减少犯错的可能性。但当温度设置得非常低(如 0)时,LLM 的表现就不那么令人印象深刻了。这是一个需要权衡的决策。

除了温度,还有 Top P 设置。你可能用不到它,但如果需要,它就在哪里。有关更多信息,请参考 OpenAI 文档。

Discourse Chatbot 中与此部分相关的设置是:

  • chatbot request temperature(聊天机器人请求温度)
  • chatbot request top p(聊天机器人请求 Top P)

问题:LLM 不准确且信息过时

LLM 是在很久以前基于大量通用数据训练的。我们需要它拥有关于我们论坛最新、最具体的信息,并尽可能准确。解决方案是检索增强生成(RAG)。

RAG 会在回复用户之前搜索论坛以获取额外信息。作为技术支持机器人,我们不能仅依赖 LLM 的训练知识,而需要机器人在回复前搜索我们的论坛以获取技术信息。

为了进行 RAG,Discourse Chatbot 需要创建一个“嵌入”(embeddings)数据库,将每个论坛帖子表示为语义“特征”的向量。这需要启用,但我建议在你设置好嵌入策略后再启用它,我们将在下一节中介绍嵌入策略。

Discourse Chatbot 中与此部分相关的设置是:

  • chatbot bot type high trust(高信任度聊天机器人类型):RAG
  • chatbot bot type medium trust(中等信任度聊天机器人类型):RAG
  • chatbot bot type low trust(低信任度聊天机器人类型):RAG
  • chatbot embeddings enabled(启用嵌入):启用(在设置好嵌入策略后)

问题:许多论坛帖子并无用处

让机器人在回复前搜索论坛(RAG)固然很好,但这引入了一个新问题。论坛上的许多帖子并不太有用。有些帖子很有价值,你希望机器人能找到它们,但也有很多帖子只是闲聊、令人困惑,甚至完全错误。我们的解决方案是精心策划一个知识库(KB),只包含我们希望机器人找到的帖子。

在 Discourse Chatbot 中,这可以通过 categories embeddings strategy(类别嵌入策略)来实现。嵌入策略决定了聊天机器人在搜索论坛时可以访问哪些帖子。我们的方法是使用一个非公开类别作为聊天机器人的知识库,因此我们选择 categories 作为嵌入策略。知识库类别也必须在 embeddings categories(嵌入类别)设置中指定。

我们使用非公开类别(仅对工作人员可见),因为我们在该类别中复制了许多公开主题,不希望用户看到这些重复内容。将主题复制到私有类别会带来另外两个问题:

  1. 复制它们非常费力,而当主题更新或有人回复时,维护这些副本更是难上加难。
  2. 机器人搜索论坛时找到的主题并不是用户应该作为参考链接的公开主题。我们需要向 LLM 提供公开主题的链接。

为了解决这两个问题,我们创建了最小化的工具,帮助我们将从公开类别复制主题到私有的聊天机器人 KB 类别中,并在公开主题更新时保持 KB 副本的同步。如果你愿意采用与我们相同的方案,可以使用这些工具:

KB 工具将整个公开主题导入到私有 KB 类别中,作为一个仅包含一条新帖子的主题。它会将公开主题的所有帖子内容拼接在一起,并在每个公开帖子的内容前添加指向该帖子的链接。这样,当机器人在 RAG 搜索中找到该私有 KB 帖子时,它就能获取整个公开主题的内容,以及可以包含在回复中作为参考的公开帖子 URL。

Discourse Chatbot 中与此部分相关的设置是:

  • chatbot embeddings strategy(嵌入策略):categories
  • chatbot embeddings categories(嵌入类别):(你的私有 KB 类别)
  • chatbot forum search function include topic titles(论坛搜索函数包含主题标题):禁用(默认)

此外,你需要从论坛搜索提示中删除一些插值键,因为它们在帖子位于私有类别时并不相关:

  • chatbot.prompt.function.forum_search.answer.topic.each.post:删除 %{username} 和 %{date}
  • chatbot.prompt.function.forum_search.answer.topic.each.topic:删除 %{title} 和 %{url}

问题:LLM 不会进行足够的 RAG 搜索

现在我们有了一个包含精心策划帖子的私有类别,作为聊天机器人的知识库,一切应该都顺利了吧?错。我们遇到的下一个问题是,RAG 搜索功能被 LLM 使用的频率远远不够。

像 GPT-4 这样的 LLM 聪明到足以带来危险。它们常常“认为”自己已经知道答案,不需要寻求帮助,而实际上它们应该进行 RAG 搜索并在知识库中寻找答案。为了解决这个问题,我们使用工具选择(tool choice)选项,强制 LLM 调用函数。

我们可以强制进行本地论坛搜索,但我们发现仅仅强制调用函数就足够了,并且我们希望给 LLM 一定的自由,让它有时可以调用其他函数而不是进行论坛搜索。

Discourse Chatbot 中与此部分相关的设置是:

  • chatbot tool choice first iteration(首次迭代工具选择):force_a_function

问题:论坛搜索性能

通过强制调用函数,LLM 几乎每次回复都会可靠地进行 RAG 搜索。但我们仍然得到糟糕的结果。机器人无法在知识库中找到正确的帖子。由于我们的帖子中包含大量信息,存在很多“噪音”,干扰了搜索功能找到最佳帖子的能力。

例如,想象用户问机器人如何停止 HP LaserJet 打印机的蜂鸣声。LLM 可能会用“HP LaserJet stop beeping”作为查询进行论坛搜索。知识库中可能有一篇完美解决该问题的帖子,但其中“问题”部分(与查询高度匹配)仅占帖子文本的 2%。其余 98% 是故障排除步骤和答案。

Discourse Chatbot 的本地论坛搜索是一种语义搜索,它使用向量嵌入来查找与查询最相似的帖子(或少数几个帖子)。最佳帖子中的“问题”部分与查询非常相似,但它只占整体文本的 2%。其余 98% 的文本使得该帖子与查询的相似度降低,因此该帖子在该查询的搜索结果中排名不高。

我们解决这个问题的方法是在知识库主题中添加“诱饵帖子”(bait posts),这些帖子只包含与搜索查询相似的文本。 在上述例子中,我们可以添加一个仅包含“HP Laser Jet beeping”的诱饵帖子。本地论坛搜索函数很容易找到这个诱饵帖子,因为它与查询非常相似。然后,Discourse Chatbot 会将包含答案的真实帖子内容(而不是诱饵帖子)发送回 LLM。

由于我们的 KB 主题的所有内容都在第一条帖子中,我们可以利用 KB 主题中的回复作为诱饵帖子。在使用 KB 工具将主题导入知识库后,我们只需回复 KB 主题即可创建诱饵帖子。

Discourse Chatbot 中与此部分相关的设置是:

  • chatbot forum search function results content type(论坛搜索函数结果内容类型):topic
  • chatbot forum search function results topic max posts count strategy(论坛搜索函数结果主题最大帖子数策略):just_enough
  • chatbot forum search function results topic max posts count(论坛搜索函数结果主题最大帖子数):1

诱饵帖子提供了一种强大的机制来优化知识库以进行搜索,但你只能盲目地进行。你无法实时查看搜索过程,因此很难确切判断你的诱饵帖子如何影响搜索排名。为了解决这个问题,我们 创建了一个小程序,它执行与 Discourse Chatbot 相同的语义搜索,但在你的计算机本地运行,并显示所有详细信息,如相似度评分和排名。这使得创建能够真正提高搜索性能并优化知识库的诱饵帖子变得容易得多。

RAG、精心策划的知识库、强制 LLM 调用函数以及诱饵帖子的结合,最终造就了一个相当不错的技术支持聊天机器人!:boom: :tada: :partying_face:

LLM 幻觉生成 URL

虽然机器人在回答技术问题方面表现良好,但它仍然存在一个令人烦恼的幻觉问题。它经常在回复用户时幻觉生成 URL。这是 LLM 的一个已知问题,普遍观点是你只能接受它。但我们不希望我们的用户忍受这个问题。

由于我们的机器人提供技术支持,我们严重依赖 RAG 为 LLM 提供准确、最新的信息。我们强制它在每次回复前进行 RAG 搜索。我们依赖 LLM 来“理解”并与用户沟通,但我们几乎完全依赖知识库来提供回答用户问题所需的技术信息。我们可以利用这一点来阻止机器人幻觉生成 URL。

我们的解决方案是为机器人添加一个约束,使其只能在回复中包含来自论坛搜索结果的 URL。 如果 LLM 尝试包含一个不在论坛搜索结果中的 URL,Discourse Chatbot 会告知 LLM 该问题并要求它重试。这个简单的变通方法有效地消除了 URL 幻觉。

Discourse Chatbot 中与此部分相关的设置是:

  • chatbot url integrity check(URL 完整性检查):启用

问题:聊天机器人无法处理所有情况

有些支持问题聊天机器人根本无法处理,因为它们需要采取实际行动。例如,如果需要更改账户或退回产品(这需要退款和/或授权),机器人就无法做到。

这也取决于问题的复杂性。许多问题都很直接——如果用户用正确的查询搜索论坛,他们本可以找到答案。在这种情况下,机器人在搜索论坛、找到答案并将其呈现给用户方面表现得相当不错。但当问题需要调查或复杂分析时,机器人通常会给出一个没有太多价值的通用答案。

在这两种机器人无法处理问题的情况下,我们需要它将聊天升级给我们的真人支持团队。 Discourse Chatbot 提供了一个名为 escalate to staff(升级给工作人员)的函数,供 LLM 执行此操作。

Discourse Chatbot 中与此部分相关的设置是:

  • chatbot escalate to staff function(升级给工作人员函数):启用
  • chatbot escalate to staff groups(升级给工作人员群组):(你希望升级到的群组)

我们已经在强制 LLM 在回复前调用函数,但现在的问题是调用哪个函数。LLM 应该调用 local forum search(本地论坛搜索)函数(RAG)还是 escalate to staff(升级给工作人员)函数?每次用户发送消息时,它都必须做出这个决定。大多数时候,我们希望它调用 local forum search。只有当它无法处理问题、注意到用户感到沮丧,或用户明确要求时,我们才希望它 escalate to staff

我们使用提示来引导 LLM 如何决定调用哪个函数。你可以在“自定义 > 文本”下编辑的提示文本包括:

  • chatbot.prompt.system.rag.private
  • chatbot.prompt.function.forum_search.description
  • chatbot.prompt.function.escalate_to_staff.description

问题:无法监控聊天

当用户实际使用聊天机器人时,查看对话并留意错误答案或改进机会是非常有帮助的。但 Discourse 没有为管理员提供阅读聊天的功能,这是合理的,因为聊天通常是人与人之间的私密对话。然而,与支持机器人的聊天并不是私密对话,如果我们无法审查它们,就无法不断改进机器人。

好消息是,Discourse 为管理员提供了一种将论坛整个聊天历史记录导出为 CSV 文件的方法。

为了解决这个问题,我们创建了一个小程序,将聊天历史 CSV 文件转换为多个 HTML 文件,每个与机器人聊过的用户对应一个文件。 我们无法实时监控机器人使用情况,但通过此解决方案,我们可以定期导出聊天历史,将其转换为 HTML 文件,然后审查它们以改进我们的机器人。

总结

在解决了上述每个问题并充分策划知识库后,我们终于可以让用户开始使用聊天机器人了。

到目前为止,结果喜忧参半。当我们看到人们在夜间和周末使用聊天机器人并获得问题解答时,这令人兴奋。当我们看到人们尝试使用聊天机器人却体验不佳时,这令人警醒。这通常意味着我们的知识库中缺少某些内容或不够清晰。偶尔我们会遇到需要改进系统提示的问题。有时很明显存在混淆,我们需要改进向用户展示聊天机器人的方式。

我们发现大约一半的聊天需要升级给工作人员。其中约一半的情况是机器人根本不可能处理该问题。另一半(约占聊天的 25%)是机器人本可以解决问题但失败了的情况,这些是改进的机会。

在未升级给工作人员的聊天中,很难判断用户是否真正解决了问题,还是只是放弃离开了。当机器人给出错误答案时,需要改进的内容显而易见。但当它给出合理、良好的答案时,除非用户告诉我们,否则并不总是能清楚他们是否完全理解并解决了问题。

总体而言,我们对 Surety 支持聊天机器人的这一首次迭代感到满意,并期待 LLM 的进步以及我们知识库随时间推移变得更好。目前,策划知识库是我们最大的任务。

自我们开始这个项目以来,Discourse Chatbot 已添加了对混合搜索(语义搜索和文本搜索)的支持,因此我们可能很快就会尝试使用它。

感谢 @merefield 为 Discourse Chatbot 插件所做的辛勤工作!与它合作非常有趣,并证明它完全能够胜任这项任务。

如果其他人决定为自己的论坛构建一个技术支持聊天机器人,请随时联系我!能与其他人合作、交流想法将非常棒。随着有趣的事情发生、变更的实施或我们学到新东西,我会再次更新。

14 个赞

供参考,这将根据以下内容在 Discourse AI 中实现:

我同意,这对于 RAG 来说是一项非常重要的功能,LLM 很懒惰,经常拒绝搜索。

6 个赞