在LangChain的组件当中,Chat Models可以说跟LLMs占据着相同的“生态位”,它也是根据用户输入的提示内容,调用底层的大模型产生内容。与LLMs不同的是,LangChain的Chat Models主要有如下一些不同:
- 输入输出格式不同:LLMs通常接受单一的文本提示语,而Chat Models则接受一系列的Messages作为提示输入上下文,里面除了内容外还区分输入的不同角色。
- 应用的场景不同:LLMs通常用于文本处理,比如问答、总结和创作;而Chat Models更强调的是上下文的理解和跟踪对话,引入了更多的对话逻辑。
- 使用成本不同:针对Chat Models的使用,大模型提供商在内部结构上做了优化,这使得它的推理成本会比通用的LLMs更高。
Message类型
如前所述,Messages是Chat Models的主要输入类型,通过Messages我们可以给后端的大模型提供必要的角色和信息内容。在介绍Chat Models的使用前,先学习一下Messages的类型,便于后面的使用理解。
所有的Message都有两个属性:
- Role:role表示这条message是谁“说”的,不同类型的Message具有不同的role。
- Content:表示这条message的内容,即消息的文本。
根据这2个属性可以构造不同类型的Message。
Messages的主要类型见下表:
Message类型 | 作用说明 |
---|---|
HumanMessage | 表示“人”的消息,可以认为是使用者输入的信息,是对话的起点。 |
AIMessage | 表示模型的消息,是语言模型生成的响应消息,可以认为是对HumanMessage的回复。 |
SystemMessage | 表示给予语言模型的系统指令,告知大模型该以怎样的行为响应。一般用来设置语言模型的身份、任务和语气等。 并不是每个LangChain支持的大模型都支持这个类型。 |
FunctionMessage | 表示一个可执行函数或工具的调用结果。除了role和content,本类型消息还有一个name参数,用来传达用于生成此结果的函数名称。 |
ToolMessage | 与FunctionMessage类似,表示工具调用的结果。此类型的message携带tool_call_id参数表示生成该结果的工具id。 |
Chat Models基本使用
作为Runnable的子类,Chat Models的使用和LLMs大同小异,主要的不同就是构造Messages对象,如下面代码所示:
from langchain_community.chat_models import ChatOllama
from langchain_core.messages import SystemMessage, HumanMessage
# 构造Messages对象,提供系统信息(SystemMessage)和用户信息(HumanMessage)。
messages = [
SystemMessage(content="You're a helpful assistant"),
HumanMessage(content="What is the purpose of model regularization?"),
]
# 使用的是ChatOllama类,而非Ollama
chat = ChatOllama(model="llama3")
# 返回的是AIMessage对象
response = chat.invoke(messages)
# 打印AIMessage对象的content内容
print(response.content)
当传入messages给到Chat Model的invoke方法后,返回结果在AIMessage的response对象里。打印content的内容结果如下:
与前面介绍的LLMs直接返回str类型的结果不同,Chat Models输出的结果是Chat Message,因此,如果只想要大模型回答的结果内容,需要访问content字段。
流式传输
所有实现了Runnable接口的Chat Models都默认支持ainvoke/batch/abatch/stream/astream方法。流式传输支持返回结果的iterator,这样可以用迭代的方式输出结果:
比如上面的代码调用invoke部分改成:
for trunk in chat.stream(messages):
print(trunk.content, end="", flush=True)
最终结果一致,只是输出是迭代的流式输出。
自定义Chat Model
与自定义LLM类似,自定义Chat Model需要继承BaseChatModel,并实现方法:
- _generate:用于根据输入的提示语生成结果。
- _llm_type:返回属性字符串,用来唯一标志模型类型名称。
还有一些可选接口方法:
- _agenerate:_generate的异步版本。
- _stream / _astream:流式传输的同步/异步版本。
- _identifying_params:用于追踪目的的模型参数属性值。
下面定义一个自定义的Chat Model叫做MyCustomChatModel,为了简单示例,这里对任何输入都返回固定的字符串:
from typing import Any, List, Optional
from langchain_core.callbacks import (
CallbackManagerForLLMRun,
)
from langchain_core.language_models import BaseChatModel
from langchain_core.messages import BaseMessage, AIMessage
from langchain_core.outputs import ChatGeneration, ChatResult
class MyCustomChatModel(BaseChatModel):
def _generate(
self,
messages: List[BaseMessage],
stop: Optional[List[str]] = None,
run_manager: Optional[CallbackManagerForLLMRun] = None,
**kwargs: Any,
) -> ChatResult:
# 这里实现自己的生成逻辑,此处仅作举例所以输出固定字符串
tokens = "I don't want to answer any question from you"
message = AIMessage(
content=tokens,
additional_kwargs={}, # Used to add additional payload (e.g., function calling request)
response_metadata={ # Use for response metadata
"time_in_seconds": 3,
},
)
generation = ChatGeneration(message=message)
return ChatResult(generations=[generation])
@property
def _llm_type(self) -> str:
return "my_custom_chat_model"
# 使用自定义的Chat Model
chat = MyCustomChatModel()
response = chat.invoke("tell me a joke about bear")
print(response.content)
结果打印如下:
响应元数据(Response metadata)
在模型返回AIMessage中,还有一些额外的信息,模型提供商会将它们记录在AIMessage的response_metadata属性中。
使用很简单,如下直接调用ChatModel返回的AIMessage对象的response_metadata属性:
print(response.response_metadata)
在ChatOllama中返回如下(json格式化处理过):
{
"model": "llama3",
"created_at": "2024-06-19T13:02:40.297019706Z",
"message": {
"role": "assistant",
"content": ""
},
"done_reason": "stop",
"done": true,
"total_duration": 13890899022,
"load_duration": 7291480563,
"prompt_eval_count": 27,
"prompt_eval_duration": 124457000,
"eval_count": 355,
"eval_duration": 6327830000
}
不同提供商具体提供了哪些内容,需要查阅对应厂家的文档。通过response_metadata,有些厂家提供了Token使用的统计数据,有些提供了日志相关信息。
缓存(Caching)
具体使用和LLMs类似,具体使用方法可以参看《LangChain入门学习笔记(四)—— Model I/O之LLMs》的缓存部分。