上周,OpenAI 发布了一个 ChatGPT 终端。它的市场宣传有几项重大改进,最值得注意的是价格降低了 10 倍,速度也快了很多。但它也带有一个全新的 API 终端。我们能够快速编写一个包装器,让用户像在 LangChain 中使用任何普通的 LLM 一样使用它,但这并没有充分利用新的基于消息的 API。在这篇博文中,我们将介绍新的 API 架构,以及我们如何调整 LangChain 以适应不仅是 ChatGPT,还有所有未来的基于聊天的模型。
主要链接
为什么需要新的抽象?
那么 OpenAI 发布了一个新的 API - 有什么大不了的?为什么甚至需要新的抽象?
虽然 ChatGPT 终端在底层使用了语言模型,但该 API 与现有的 GPT-3 终端截然不同。这有点简化,但现有的 GPT-3 终端的接口是
- 输入:文本
- 输出:文本
新的 ChatGPT 终端的接口是
- 输入:聊天消息列表
- 输出:聊天消息
这个新接口意味着我们对语言模型的输入/输出的一些抽象不再正确。例如,我们之前构建的所有提示策略都假设 PromptTemplate 的输出是一个字符串。然而,现在对于聊天模型,输入需要是消息列表。由于提示是许多 LangChain 实用程序和功能的核心,因此这是一个相当深入的更改。
为什么是聊天模型?
好的,现在出现了一种新型号。我们为什么要急于支持它?它真的与现有的 API 有那么大的不同,以至于值得新的抽象吗?
为了回答这个问题,值得思考一下为什么 OpenAI 选择以这种格式发布模型。这部分都是推测。此 API 与之前的 GPT-3 API 之间的主要区别在于,现在用户输入有了更多结构,以不同类型的消息形式出现。具体来说,ChatGPT API 允许区分“用户”、“助手”和“系统”消息。这种区分可能允许模型以不同的方式对待不同类型的消息,这在理论上可以提高构建在这些模型之上的应用程序的安全性。例如,针对 LLM 应用程序的一种常见攻击类型是 提示注入攻击,即最终用户指示应用程序执行它不打算执行的操作。通过现在将这种结构添加到提示中,理论上,最终应用程序可以将所有应用程序指令放在“系统”消息中,将所有最终用户输入放在“用户”消息中,然后可以训练模型始终“服从”系统消息,而不管用户消息可能告诉它什么。在实践中,似乎针对聊天模型的提示注入攻击仍然仍然存在,因此这可能无法完全解决这些问题。只有时间会证明一切。
上面“用户”与“助手”与“系统”消息的示例也说明了为什么这种格式与现有 API 有足够的差异,值得拥有自己的抽象。
还有更实际的原因。OpenAI 尚未发布与 ChatGPT 模型相对应的“正常” LLM 终端,因此如果人们想充分利用这些模型的速度/成本优势,他们需要使用 ChatGPT API。还有传言称,其他聊天风格的模型即将上市(Anthropic 的 Claude,Google 的 Bard,以及关于 Cohere 的对话模型的早期传言)。虽然尚不清楚这些模型将以何种格式提供,但假设它们也将具有基于聊天的 API 选项并非疯狂之举。
我们对新抽象的目标是什么?
在设计这些新抽象时,我们心中有三个主要目标
#1:允许用户充分利用新的聊天模型接口
如上所述,聊天模型的 API 与现有的 LLM API 非常不同。我们希望让用户利用这一点。
#2:允许“正常” LLM API 和基于聊天的 API 之间的提示互操作性
虽然“正常” LLM 与基于聊天的 API 的提示肯定存在差异,但我们希望尽可能无缝地(在合理范围内)将相同的提示用于任一类型的模型。实际上,我们优先考虑这一点,因为代码库中的大多数提示都针对 LLM 进行了优化,我们不希望它们在基于聊天的模型上出现严重错误。
#3:使抽象不仅仅是 OpenAI 通用的
我们不希望我们提出的任何抽象都特定于 OpenAI。如上所述,有传言称其他聊天风格的模型即将上市,我们希望确保我们能够无缝地支持它们。
这些新抽象是什么?
虽然看起来我们正在向代码库中引入过多的抽象,但它们的完成目标都是使添加到代码库中的任何内容都能尽可能好地与这些新模型配合使用。LangChain 的目标之一始终是允许语言模型提供商之间的互操作性,我们希望这有助于实现这一目标。
聊天消息
我们正在为不同类型的聊天消息添加抽象。
HumanMessage
:从人的角度发送的消息AIMessage
:从人正在交互的 AI 的角度发送的消息SystemMessage
:设置 AI 应遵循的目标的消息ChatMessage
:允许任意设置角色的消息
聊天模型
我们还在为聊天模型添加抽象。这些类型的模型期望的接口与底层的 ChatGPT API 非常相似 - 它接收聊天消息列表并返回聊天消息。
聊天消息模板
由于大多数应用程序不会硬编码要发送的消息,而是利用“PromptTemplates”(接收用户输入并返回提示)的概念,因此我们正在添加相应的聊天消息提示模板。
HumanMessagePromptTemplate
:一个类,它公开一个方法来接收用户输入并返回 HumanMessageAIMessagePromptTemplate
:一个类,它公开一个方法来接收用户输入并返回 AIMessageSystemMessagePromptTemplate
:一个类,它公开一个方法来接收用户输入并返回 SystemMessageChatMessagePromptTemplate
:一个类,它公开一个方法来接收用户输入并返回 ChatMessage
提示值
为了允许“正常” LLM API 和基于聊天的 API 之间的提示互操作性,我们正在添加 PromptValue
的概念。这是一个具有以下方法的类
to_string
:将 PromptValue 转换为字符串,用于“正常” LLM APIto_messages
:将 PromptValue 转换为消息列表,用于基于聊天的 API
为了配合这一点,我们还在 BaseLLM
和 BaseChatModel
类中添加了方法,这些方法接收 PromptValue
,将它们分别转换为 string
或 ChatMessages
,然后将它们传递给模型。
这些如何与我们的目标保持一致?
让我们确保这些抽象与我们设定的目标保持一致
#1:允许用户充分利用新的聊天模型接口
我们涵盖了 ChatGPT 接口中的所有功能。我们没有明确抽象某些内容(例如 name
参数),但这些内容仍然可用,如果它们变得更相关,我们可能会努力在未来添加更好的支持。
#2:允许“正常” LLM API 和基于聊天的 API 之间的提示互操作性
PromptValue
抽象应该允许这样做。我们有非常简单的方法将字符串转换为消息(使其成为单个 HumanMessage
)和消息转换为字符串(只需将整个列表强制转换为字符串)。这些方法(尤其是消息到字符串的方法)肯定可以改进。
#3:使抽象不仅仅是 OpenAI 通用的
我们相信,通过添加 Human/AI/System 消息的显式概念,而不是依赖带有 "role"
键的字典,我们已经为自己设置了模型 API 不可知性。这样,如果另一个模型提供商有不同的命名约定(例如,他们将 AI 称为 "AI"
而不是 OpenAI 的 "assistant"
),我们可以轻松地在模型包装器中映射到这些命名约定。
下一步
希望这有助于澄清我们围绕我们引入的抽象的一些想法。我们非常希望收到有关这些抽象的反馈。在不到一周的时间内就必须制定全新概念的抽象总是令人恐惧,因此我们非常感谢任何建议和意见。
我们还将努力继续添加更多围绕聊天模型的功能。例如,更好的消息到字符串的支持、更好的少样本示例支持以及更好的聊天记忆支持。
由于这次意外发布,我们还积累了大量的待办事项,因此感谢您的耐心,我们将努力筛选这些事项。
最后,非常感谢 Anthropic 团队(尤其是 Mike Lambert)思考了其中的一些抽象,并确保它们在 ChatGPT API 之外是通用的。当只有一个实例可以查看时,尝试概括抽象总是很困难,因此我们非常感谢他们的智慧。