专栏名称: 张铁蕾
老程序猿,全栈攻城狮,CTO,与你一起讨论技术干货和个人成长。
目录
相关文章推荐
51好读  ›  专栏  ›  张铁蕾

LangChain的OpenAI和ChatOpenAI,到底应该调用哪个?

张铁蕾  · 公众号  · 科技创业 科技自媒体  · 2024-12-29 09:25

主要观点总结

本文主要讨论了在使用LangChain连接LLM推理服务时,应调用OpenAI还是ChatOpenAI的问题。文章深入解释了两个接口的区别,涉及到completions和chat completions的区别,以及LLM推理时使用的chat template。文章还讨论了如何正确调用这些接口,特别是在使用vLLM等开源推理框架时需要注意的问题。

关键观点总结

关键观点1: OpenAI和ChatOpenAI的区别

OpenAI用于调用/v1/completions接口,提供续写能力;ChatOpenAI用于调用/v1/chat/completions接口,提供对话能力。这两个接口分别对应LLM的Base Model和Instruct Model。

关键观点2: chat template的重要性

chat template是用于将对话历史内容转化为free text的模板,对于Instruct Model的推理服务非常重要。

关键观点3: vLLM启动时的chat template获取方式

可以从模型文件夹中加载,也可以通过启动参数指定。如果没有正确加载chat template,可能会影响模型的回答准确度。

关键观点4: 如何正确调用LangChain的类

对于Base Model的推理服务,只能使用LangChain的OpenAI类;对于Instruct Model的推理服务,推荐使用ChatOpenAI类,也可以在调用OpenAI类之前手动拼接prompt。


正文

浮言易逝,唯有文字长存。

今天来聊一个非常具体的技术问题。

对于工程师来说,当我们使用LangChain来连接一个LLM推理服务时,多多少少会碰到一个疑问:到底应该调用 OpenAI 还是 ChatOpenAI ?我发现,每次解释这个问题时,都会费很多唇舌,所以干脆写下来供更多人参考。这背后其实涉及到两个关键问题:

  • completions 和 chat completions 两个接口的区别。

  • LLM推理时用到的chat template。

completions 和 chat completions

通过LangChain来调用LLM的时候,通常会引用下面这两个类:

from langchain_openai import OpenAI
from langchain_openai import ChatOpenAI

从这两个类的名字就不难猜出,它们是用来调用OpenAI提供的LLM接口的。具体来说,是这两个接口:

  • OpenAI 用来调用 /v1/completions 接口。

  • ChatOpenAI 用来调用 /v1/chat/completions 接口。

不过呢,由于OpenAI的影响力实在太大,很多其他闭源和开源的LLM,在提供推理服务的API时,也都不约而同地遵循了OpenAI的接口形式。这两个接口成了事实性的标准。

这两个接口的区别是什么呢?

  • /v1/completions 接口提供的是「续写」能力,也就是最基础的predict next token的能力。你提供一段prompt,它返回一段续写的文本。接口的输入和输出,都是文本。

  • /v1/chat/completions 接口提供的是对话能力。接口的输入是一个message list,输出是一个message。

Base Model 和 Instruct Model

一般来说,上一节提到的这两个接口,分别对应两类LLM模型:

  • Base Model : 仅经过预训练的基础模型,所以也称为Pretrained Model。它只能对输入的prompt进行续写。这一类模型在推理时只能提供 /v1/completions 接口。

  • Instruct Model : 在预训练之后经过进一步指令微调过的模型。只有这一类模型在推理时才能提供 /v1/chat/completions 接口。

以Llama 3模型系列为例:

在以上这个表格中,以“-Instruct”结尾命名的模型,就属于Instruct Model;反之就是Base Model。

OpenAI在早期推出API服务的时候,/v1/completions 和 /v1/chat/completions 这两个接口是都提供的。但随着时间的推移和技术的迭代,大部分AI应用场景都能用指令对话的形式来支持,所以后来OpenAI也就不再为最新的模型提供 /v1/completions 接口了。现在如果访问OpenAI关于 /v1/completions 接口的API reference文档页面[1],你会发现这个接口已经被标记为“Legacy”了。

结合前一节所讨论的,我们现在容易得出结论,如果我们想调用OpenAI的GPT模型,那么应该选择使用LangChain的 ChatOpenAI 这个类。

但是,如果我们使用开源推理框架(如vLLM[2])来为了开源模型在本地架设推理服务,那么通常来说,这个推理服务可能会同时支持/v1/completions 和 /v1/chat/completions 这两个接口。当我们使用LangChain进行调用的时候:

  • 如果加载的是一个Base Model,那么只有 /v1/completions 接口可用,/v1/chat/completions 接口是不可用的(或者说,调用它没有意义)。我们只能使用LangChain的 OpenAI 这个类进行调用。

  • 如果加载的是一个Instruct Model,那么理论上来说,我们应该调用 /v1/chat/completions 接口进行推理。也就是使用LangChain的 ChatOpenAI 这个类来进行调用。但是,由于对话消息在经过格式化之后,最终也是表达成一个文本串的,所以其实 /v1/completions 也是可用的,这时候就可以调用LangChain的 OpenAI 这个类。这个过程我们后面的章节再仔细展开。

关于chat template

根据前面的分析,我们知道了,调用 /v1/chat/completions 接口,我们需要使用LangChain的 ChatOpenAI 这个类,并且传入一个message list。下面是一段示例代码:

from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate

llm = ChatOpenAI(
openai_api_key="EMPTY",
openai_api_base="http://127.0.0.1:8000/v1",
model_name="llama3.2-1B-instruct"
)

prompt = ChatPromptTemplate.from_messages([
("system", "Your are a helpful assistant."),
("user", "Hello, how are you?"),
("assistant", "I'm doing well, thank you for asking."),
("user", "Can you tell me a joke?")
]
)

chain = prompt | llm

reponse = chain.invoke({})

在这段代码中,我们看到,传入给LLM的是一个有结构的对话历史列表。但是,不管是Base Model还是Instruct Model,模型最终接受的输入,应该是一段free text(再转成token)。那么问题来了,这个有结构的对话历史列表,是如何转成free text的呢?显然,这里需要一个模板(template),这就是所谓的chat template[3]。

对于前面示例代码中的 llama3.2-1B-instruct 模型,它所对应的chat template是下面这个样子的:

这是一个遵循Jinja格式的模版[4]。模板表达了各种message(包括system message,user message,assistant message以及其它类型的message)的渲染方式。

基于这个chat template,前面示例代码中的对话历史内容,最终输入到LLM时会转化成如下的free text(也就是prompt):

<|begin_of_text|><|start_header_id|>system<|end_header_id|>

Cutting Knowledge Date: December 2023
Today Date: 28 Dec 2024

Your are a helpful assistant.<|eot_id|><|start_header_id|>user<|end_header_id|>

Hello, how are you?<|eot_id|><|start_header_id|>assistant<|end_header_id|>

I'm doing well, thank you for asking.<|eot_id|><|start_header_id|>user<|end_header_id|>

Can you tell me a joke?<|eot_id|><|start_header_id|>assistant<|end_header_id|>


那么,这个chat template是从哪里来的呢?对于vLLM来说,它启动的时候,有两种方式可以获取到chat template:

  • 一种方式是从模型文件夹中加载。具体地说,chat template的内容存在于tokenizer_config.json文件中。







请到「今天看啥」查看全文