今天,我们很高兴宣布并展示一个开源聊天机器人,专门用于回答有关 LangChain 文档的问题。
主要链接
- 已部署的聊天机器人: chat.langchain.dev
- 在 HuggingFace Spaces 上部署的聊天机器人: huggingface.co/spaces/hwchase17/chat-langchain
- 开源仓库: github.com/hwchase17/chat-langchain
- Next.js 前端: github.com/zahidkhawaja/langchain-chat-nextjs
非常感谢 Zahid Khawaja 与我们合作完成此项目。如果您希望为一般网页提供此类功能,则应查看他的浏览器扩展程序: Lucid。
如果您喜欢这个并且想要为您的代码库提供更高级的版本,则应查看 ClerkieAI。他们的问答机器人已在我们的 Discord 上线。
概述
在过去几周,已经出现了一些与此类似的项目(下面会详细介绍)。但是,我们希望分享我们认为独特的见解,包括
- 文档摄取(什么是通用的,什么不是通用的)
- 如何在聊天机器人设置中使其工作(重要的用户体验)
- (提示工程)速度和性能之间的权衡
- (提示工程)如何使其引用来源
- (提示工程)输出格式
扩展粗体部分,我们认为聊天机器人界面是一个重要的用户体验(只需看看 ChatGPT 的成功!),我们认为其他实现中缺少这一点。
动机
将 LLM 与外部数据结合使用一直是 LangChain 的核心价值主张之一。我们制作的第一个演示之一是 Notion QA 机器人,而 Lucid 紧随其后,成为通过互联网实现此目的的一种方式。
我们希望启用问答的外部数据之一是我们的 文档。我们有一个 不断壮大的 Discord 社区,并希望为他们的所有问题提供快速准确的支持。因此,这似乎是内部利用我们自己技术的绝佳机会!我们联系了 ML 创作者和 LangChain 爱好者 Zahid,看看他是否愿意为此合作,令我们欣慰的是,他同意了!于是我们开始构建。
类似工作
出于某种原因,仅在过去一周就完成了许多类似的工作!几乎所有这些工作都遵循相同的流程,可以概括如下

摄取:
- 获取一组专有文档
- 将它们分成更小的块
- 为每个文档创建嵌入

查询:
- 为查询创建嵌入
- 在嵌入空间中找到最相似的文档
- 将这些文档与原始查询一起传递到语言模型以生成答案
其他一些好的例子包括:
- GitHub 支持机器人(由 Dagster 提供) - 就其试图解决的问题而言,可能与此最相似
- Dr. Doc Search - 与书籍(PDF)对话
- Simon Willison 论“语义搜索答案” - 对此处某些主题的良好解释
- Ask My Book - 由 Sahil Lavingia 提供,可能是引发当前热潮的示例
文档摄取
我们立即面临一个问题:从哪里获取数据。有两种选择:GitHub 中的文件,或从互联网上抓取。虽然 GitHub 中的某些文件格式良好,可以处理(markdown 和 rst),但我们也有一些不太适合处理的文件(.ipynb 文件,自动生成的文档)。因此,我们决定从互联网上抓取。
之后,我们面临着如何预处理 html 文件的问题。为此,我们最初以通用方式执行此操作(仅使用 Beautiful Soup 获取页面上的所有文本)。但是,在使用这些结果后,我们发现某些侧边栏内容会产生不良结果。作为一个视觉示例,在下图中,红色区域中的两个区域实际上并没有提供太多新信息。

当我们检查一些我们得到的不良结果时,我们看到通常是这些信息被拉取并塞入上下文中。我们通过更改解析器以显式忽略这些区域来修复此问题。
尽管特定于此布局,但我们确实认为这突出了非常有意识地决定将哪些数据(或不将哪些数据)作为潜在上下文的重要性。虽然使用通用摄取工具并将其用作快速入门的方法可能很容易,但进行更深入的检查并修改您的流程可能对于获得更好的性能是必要的。
如何在聊天机器人设置中使其工作
如前所述,我们认为聊天机器人设置是一个重要的用户体验决策,它
- 人们觉得熟悉(参见 ChatGPT 的成功)
- 启用其他关键功能(下面会详细介绍)
我们希望确保包含的关键功能之一是我们没有看到在其他地方完成很多工作的功能是提问后续问题的能力。我们认为这对于任何基于聊天的界面都非常重要。大多数(所有?)实现主要忽略了这个问题,并将每个问题都视为独立的。当有人想以自然的方式提出后续问题,或参考先前问题或答案中的上下文片段时,这就会崩溃。
在 LangChain 中执行此操作的主要方法是使用对话链中的 memory。在最简单的实现中,先前的对话行会作为上下文添加到提示中。但是,这在我们的用例中有点崩溃,因为我们不仅关心将对话历史记录添加到提示中,而且还关心使用它来确定要获取哪些文档。如果提出后续问题,我们需要知道后续问题是关于什么的,以便获取相关文档。
因此,我们创建了一个新的链,其中包含以下步骤
- 给定对话历史记录和一个新问题,创建一个独立的、独立的问题
- 在正常的 向量数据库问答链 中使用该问题。

我们有点担心在向量数据库问答链中使用该问题会造成信息丢失并丢失一些上下文,但我们并没有真正观察到那里的任何负面影响。
在获得此解决方案的过程中,我们尝试了一些其他关于从向量数据库获取文档的方法,但效果都不如预期
- 嵌入聊天历史记录 + 问题在一起:一旦聊天历史记录变长,如果您试图更改主题,则会过度索引。
- 分别嵌入聊天和问题,然后组合结果:比上面更好,但仍然将太多关于先前主题的信息拉入上下文中
提示工程
在构建聊天机器人时,提供良好的用户体验至关重要。在我们的用例中,有几个重要的考虑因素
- 每个答案都应包含指向 文档 的官方来源。
- 如果答案包含代码,则应将其正确格式化为代码块。
- 聊天机器人应保持主题,并坦诚地表示不知道答案。
我们能够通过以下 提示 实现所有这些目标
You are an AI assistant for the open source library LangChain. The documentation is located at https://langchain.readthedocs.io.
You are given the following extracted parts of a long document and a question. Provide a conversational answer with a hyperlink to the documentation.
You should only use hyperlinks that are explicitly listed as a source in the context. Do NOT make up a hyperlink that is not listed.
If the question includes a request for code, provide a code block directly from the documentation.
If you don't know the answer, just say "Hmm, I'm not sure." Don't try to make up an answer.
If the question is not about LangChain, politely inform them that you are tuned to only answer questions about LangChain.
Question: {question}
=========
{context}
=========
Answer in Markdown:
在此提示中,我们要求以 Markdown 格式回复。这使我们能够以视觉上吸引人的格式呈现答案,并带有正确的标题、列表、链接和代码块。
这是它在我们的 Next.js 前端上的外观

每个答案中的超链接都是使用文档的基本 URL 构建的,我们在提示中预先提供了该基本 URL。由于我们在数据摄取流程中抓取了文档网站,因此我们能够识别答案的路径并将其与基本 URL 结合起来以创建可用的链接。
为了使聊天机器人尽可能准确,我们将温度保持在 0,并在提示中包含说明,以便在给出答案不明确的问题时说“嗯,我不确定。”。为了使对话保持主题,我们还在说明中拒绝与 LangChain 无关的问题。
在速度和性能之间取得平衡
- 我们开始使用较长的 Markdown 答案进行测试。虽然答案质量很好,但响应时间略长于预期。
- 我们花费了大量时间来改进提示并尝试不同的关键字和句子结构以提高性能。
- 一种方法是在提示末尾提及 Markdown 关键字。对于我们的用例,我们能够仅提及一次关键字即可返回格式化的输出。
- 在提示的开头附近提供基本 URL 提高了整体性能 - 它为模型提供了一个可用的参考,以创建要包含在答案中的最终 URL。
- 我们发现使用单数形式,例如“一个超链接”而不是“超链接”和“一个代码块”而不是“代码块”,可以缩短响应时间。
未来工作
我们才刚刚开始,我们欢迎来自 LangChain 社区的反馈。我们迫不及待地想在不久的将来推出新功能和改进!在 Twitter 上关注我们以获取更新,并查看 代码。
如果您喜欢这个并且想要为您的代码库提供更高级的版本,则应查看 ClerkieAI。他们的问答机器人已在我们的 Discord 上线。