![cover](https://img-blog.csdnimg.cn/direct/760ad334acd14d73bc3c0ad786e34d7b.jpeg)
【可能是全网最丝滑的LangChain教程】九、LangChain进阶之Chat Model
本文是关于 LangChain 中 Chat Model 组件的详细解析,介绍了 Chat Model 组件是什么,与 LLM 组件有什么不同,以及如何使用 Chat Model。
系列文章地址
【可能是全网最丝滑的LangChain教程】一、LangChain介绍-CSDN博客
【可能是全网最丝滑的LangChain教程】二、LangChain安装-CSDN博客
【可能是全网最丝滑的LangChain教程】三、快速入门LLM Chain-CSDN博客
【可能是全网最丝滑的LangChain教程】四、快速入门Retrieval Chain-CSDN博客
【可能是全网最丝滑的LangChain教程】五、快速入门Conversation Retrieval Chain-CSDN博客
【可能是全网最丝滑的LangChain教程】六、快速入门Agent-CSDN博客
【可能是全网最丝滑的LangChain教程】七、LCEL表达式语言-CSDN博客
【可能是全网最丝滑的LangChain教程】八、LangChain进阶之LLM
01 Model介绍
任何语言模型应用程序的核心元素都是…模型(Model)。LangChain 提供了与任何语言模型进行交互的构建块(building blocks)。
LangChain 的 Model I/O 组件包括:两种不同类型的模型 - LLM(【可能是全网最丝滑的LangChain教程】八、LangChain进阶之LLM) 和 Chat model;格式化模型输入 Prompt;输出解析器 Output parser。
本文将重点介绍:Model I/O子组件—>Chat Model
02 Chat Model介绍
Chat Model 是 LangChain的核心组件,同时 Chat Model 也是语言模型的一种。
它使用 Chat Message 作为输入,并将 Chat Message作 为输出返回(而不是使用纯文本)。LangChain 与许多模型提供商(OpenAI、Cohere、Hugging Face等)都有良好的集成,并公开了一个标准接口来与这些模型进行交互。
LangChain 允许我们在同步、异步、批处理和流模式下使用模型,并且 LangChain还提供其他功能(例如:缓存)等。
我们可以看下 LangChain 中目前已经支持的 Chat Model 有哪些,我格式化打印了下直接子类和间接子类。
class BaseChatModel
class SimpleChatModel
class FakeListChatModel
class FakeChatModel
class ChatMaritalk
class FakeListChatModel
class FakeMessagesListChatModel
class GenericFakeChatModel
class ParrotFakeChatModel
class ChatOpenAI
class AzureChatOpenAI
class ChatOpenAI
class AzureChatOpenAI
class ChatAnyscale
class ChatEverlyAI
class ChatKonko
class PromptLayerChatOpenAI
class ChatAnthropic
class BedrockChat
class ChatBaichuan
class ChatCohere
class ChatMlflow
class ChatDatabricks
class ChatDeepInfra
class ChatFireworks
class ChatFriendli
class ChatGooglePalm
class ChatHuggingFace
class ChatHunyuan
class ChatJavelinAIGateway
class ChatKinetica
class ChatLiteLLM
class ChatLiteLLMRouter
class ChatMLflowAIGateway
class ChatOllama
class ChatPerplexity
class ChatSparkLLM
class ChatTongyi
class ChatVertexAI
class ChatYandexGPT
class ChatYuan2
class ChatZhipuAI
class ErnieBotChat
class FakeMessagesListChatModel
class GPTRouter
class GigaChat
class HumanInputChatModel
class JinaChat
class LlamaEdgeChatService
class MiniMaxChat
class PaiEasChatEndpoint
class QianfanChatEndpoint
class VolcEngineMaasChat
class ChatPremAI
class ChatLlamaAPI
从上面的打印结果可以看出,基本目前国内国外的大模型都是支持的。比如:BaiChuan(百川)、Tongyi(通义)、Hunyuan(混元)、Zhipu(智谱)…等等国产模型都是支持的。
03 Chat Model案例实操
聊天模型是语言模型的变体。虽然聊天模型在后台使用语言模型,但它们使用的方式略有不同。他们没有使用“文本输入,文本输出”的 API,而是使用“聊天消息”作为输入和输出。
1 安装依赖
pip install -qU langchain-openai
2 初始化Chat Model
from langchain_openai import ChatOpenAI
# xxxxxx是你们自己传递的参数
chat_model = ChatOpenAI(xxxxxx)
3 设置Message
聊天模型界面基于消息而不是原始文本。LangChain 目前支持的消息类型是AIMessage、HumanMessage、SystemMessage、FunctionMessage和ChatMessage——ChatMessage 可以采用任意角色参数。大多数时候,开发者只会处理 HumanMessage、AIMessage 和 SystemMessage。
聊天模型实现了 Runnable 接口,这是 LangChain 表达式语言 (LCEL) 的基本构建块。这意味着它们支持 invoke、ainvoke、stream、astream、batch、abatch、astream_log 调用。
聊天模型接受 List[BaseMessage] 作为输入,或者接受一个可以转换成Message的输入,包括 str(转换为 HumanMessage)和 PromptValue。
这里我们设置了 SytemMessage 和 HumanMessage。其中 SytemMessage一般用于定义模型的角色和技能,目的是为了回答更加准确;HumanMessage 设置真正的用户输入,也就是你的问题。
from langchain_core.messages import HumanMessage, SystemMessage
messages = [
SystemMessage(content="你是一个顶级的历史学家,尤其华夏历史。"),
HumanMessage(content="请用一句话总结秦始皇的家庭情况。")
]
4 执行
# 一般执行
chat.invoke(messages)
# 流式输出
for chunk in chat.stream(messages):
print(chunk.content, end="", flush=True)
# 批量执行
chat.batch([messages])
# 异步执行
await chat.ainvoke(messages)
# 异步流式执行
async for chunk in chat.astream(messages):
print(chunk.content, end="", flush=True)
# 异步流式执行,并输出执行日志
async for chunk in chat.astream_log(messages):
print(chunk)`
输出:
AIMessage(content='秦始皇是秦庄襄王嬴异人的儿子,母亲是赵姬,他在秦国宫廷中度过了自己的童年。 ')
# 实际上这里给到模型的输入具体有如下消息:
# {
# "prompts": [
# "System: 你是一个顶级的历史学家,尤其华夏历史。\nHuman: 请用一句话总结秦# 始皇的家庭情况。"
# ]
# }
04 Message介绍
ChatModel 将 Message 列表作为输入并返回 Message。有几种不同类型的 Message。所有消息都具有 role 和 content 属性。LangChain 为不同的角色提供了不同的消息类。content 属性描述消息的内容。
- content 可以是一个字符串(99% 的情况我们试用这个)
- content 也可以是一个字典列表(用于多模态输入,其中字典包含有关该输入类型和输入位置的信息)
此外,Message 还具有 additional_kwargs 属性。可以传递有关 Message 的其他信息。主要用于特定程序要求而不是常规的输入参数。最著名的例子是 OpenAI 的 function_call。
1 HumanMessage(常用)
这表示来自用户的消息。通常仅由content(内容)组成。
2 AIMessage(常用)
这表示来自模型的消息。这可能包含 additional_kwargs。例如:如果使用 OpenAI 工具调用,则为 tool_calls信息。
3 SystemMessage(常用)
这表示一条系统消息,它告诉模型如何操作。这通常只包含内容。并非每个模型都支持这个消息。
4 FunctionMessage
这表示函数调用的结果。除了 role 和 content 之外,此消息还有一个 name 参数,该参数传达为生成此结果而调用的函数的名称。一般我们在function_call的调用中能看到这个消息。
5 ToolMessage
这表示工具调用的结果。这与 FunctionMessage 不同,这是为了在调用 OpenAI 的 function 和 tool 而使用的消息类型。除了 role 和 content 之外,此消息还有一个 tool_call_id 参数,该参数将调用的 ID 传达给相应的调用工具。
05 Tool/Function Calling
LangChain 中的 Tool Calling 与 Function Calling 基本是一回事,Tool Calling 内部做了处理,让 Chat Model 能同时调用多个函数(或者叫 tool)。主要功能就是让模型通过使用用户自定义的一些工具或者叫函数来响应用户的输入。模型为工具提供运行参数,而实际运行该工具(或不运行)取决于用户——例如:如果你想从非结构化文本中提取与某个模式匹配的输出,你可以为模型提供一个“提取”工具,该工具使用与所需模式匹配的参数,然后将生成的输出视为最终结果。
工具调用包括名称、参数字典和一个可选标识符。参数字典是结构化的{argument\_name: argument\_value}
。
许多 LLM 提供商,包括 Anthropic、Cohere、Google、Mistral、OpenAI 等,都支持工具调用功能的变体。这些功能通常允许 LLM 的请求工具,并获取工具的响应。例如:给定一个搜索引擎工具,LLM 可能会通过首先向搜索引擎发出调用来处理查询。然后 LLM 接收工具的输出并处理,最后将最终结果返回给用户。LangChain包含一套内置工具,并支持多种方法来定义您自己的自定义工具。工具调用对于构建使用工具的链和代理,以及更好地从模型中获取结构化输出非常有用。
LangChain 实现了用于定义 tool、传递 tool 给LLM以及调用 tool 的标准接口。之所以有标准接口,目的就是为了适配市面上各种模型的工具调用能力。
因为 Tool Calling 或者说 Function Calling 只在 Chat Model 模式下才生效,所以这里提一下,这部分后续会单独开一篇文章来专门讲述。
06 Structured Output
顾名思义,结构化输出。
让 LLM 返回结构化输出通常至关重要。这是因为 LLM 的输出通常用于需要特定参数的下游应用程序。为此,必须让 LLM 可靠地返回结构化输出。
如何实现结构化输出呢?LangChain 支持的方式有如下几种:
-
Prompting:我们主动要求 LLM 以所需格式(JSON、XML)返回输出。这很好用,因为它适用于所有 LLM。同时,也并不好用,因为不能保证所有 LLM 都能以正确的格式返回输出。
-
Function calling:当 LLM 经过微调支持函数调用后,LLM 将可以调用的函作为额外参数传递给模型 API。函数名称和描述应被视为提示的一部分,因为是执行指定函数,而函数方法体又是用户自己定义的,所以可以正确返回之地你个格式的输出。
-
Tool Calling:一种类似于函数调用的技术,但它允许 LLM 同时调用多个函数。
-
Json mode:当我们需要 LLM 返回Json格式输出的时候,可以用这个。
因为不同的 LLM 结构化输出的方式可能不同,所以 LangChain 添加了一个通用接口:.with_structured_output
。
同理,因为 Structured Output 只在 Chat Model 模式下才生效,所以这里提一下,这部分后续会单独开一篇文章来专门讲述。
07 缓存支持
Chat Model 缓存与 LLM 缓存使用基本一致,这里不再赘述,大家可以看这篇文章了解缓存使用(【可能是全网最丝滑的LangChain教程】八、LangChain进阶之LLM)
08 自定义Chat Model
既然 LLM 可以自定义,必然 Chat Model 也可以自定义。这部分实际上和自定义 LLM 非常相似…
自定义 Chat Model 必须实现两个方法:
- _generate:用户根据用户输入消息,执行具体逻辑,然后返回输出消息
- _llm_type:获取此聊天模型使用的语言模型的类型。仅用于日志记录。
我们在自定义 LLM 的代码上,稍作修改就能得到自己的自定义 Chat Model。具体代码如下:
python
复制代码
from langchain_core.messages import AIMessageChunk
from typing import Any, Dict, Iterator, List, Optional
from langchain_core.callbacks.manager import CallbackManagerForLLMRun
from langchain_core.language_models import BaseChatModel
class CustomChatModel(BaseChatModel):
"""
自定义一个Chat Model,在用户输入的基础上加上前缀:oh,我的天哪~
"""
def _generate(
self,
messages: List[BaseMessage],
stop: Optional[List[str]] = None,
run_manager: Optional[CallbackManagerForLLMRun] = None,
**kwargs: Any,
) -> ChatResult:
"""
根据用户输入,执行具体逻辑,然后返回输出
"""
last_message = messages[-1]
ai_message = AIMessage(content="oh,我的天哪~" + last_message.content)
chat_generation = ChatGeneration(message = ai_message)
return ChatResult(generations=[chat_generation])
def _stream(
self,
messages: List[BaseMessage],
stop: Optional[List[str]] = None,
run_manager: Optional[CallbackManagerForLLMRun] = None,
**kwargs: Any,
) -> Iterator[ChatGenerationChunk]:
"""
根据用户输入,执行具体逻辑,然后流式返回输出
"""
last_message = messages[-1]
tokens = last_message.content
for token in "oh,我的天哪~" + tokens:
chunk = ChatGenerationChunk(message=AIMessageChunk(content=token))
if run_manager:
run_manager.on_llm_new_token(chunk.message.content, chunk=chunk)
yield chunk
# 这里还可以添加一些其他的额外信息
# ......
@property
def _identifying_params(self) -> Dict[str, Any]:
"""返回模型自定义参数字典"""
return {
"model_name": "Custommm",
"model_version":"1.0.0"
}
@property
def _llm_type(self) -> str:
"""获取此聊天模型使用的语言模型的类型。仅用于日志记录。"""
return "custom"
使用方式也和其他 Chat Model 一样,我们打印一点信息看看效果。
chat_model = CustomChatModel()
chat_model.invoke([HumanMessage(content="你好呀!")])
# AIMessage(content='oh,我的天哪~~你好呀!', id='run-e5984b0b-a18f-49f0-b82f-fd3049b37495-0')
chat_model.batch(["hello", "goodbye"])
# [AIMessage(content='oh,我的天哪~~hello', id='run-15cc053a-0832-4540-a660-5864fb7d33c1-0'), AIMessage(content='oh,我的天哪~~goodbye', id='run-6c83c667-a5e1-4522-9ad9-a6377b44522d-0')]
for chunk in chat\_model.stream("cat"):
print (chunk.content, end="|")
# o|h|,|我|的|天|哪|~~|c|a|t|
async for chunk in chat_model.astream("cat"):
print(chunk.content, end="|")
# o|h|,|我|的|天|哪|~~|c|a|t|
async for event in chat_model.astream_events("cat", version="v1"):
print(event)
# {'event': 'on_chat_model_start', 'run_id': '0a6a9864-2e9b-46ee-a9b4-6079b58199bc', 'name': 'CustomChatModel', 'tags': [], 'metadata': {}, 'data': {'input': 'cat'}}
# ......
# {'event': 'on_chat_model_end', 'name': 'CustomChatModel', 'run_id': '0a6a9864-2e9b-46ee-a9b4-6079b58199bc', 'tags': [], 'metadata': {}, 'data': {'output': AIMessageChunk(content='oh,我的天哪~~cat', id='run-0a6a9864-2e9b-46ee-a9b4-6079b58199bc')}}
09 Response metadata 与 Tracking token usage
相比于 LLM,Chat Model支持获取响应元数据。
许多模型会在在其Chat Model 模式下的响应中包含一些元数据。可以通过 AIMessage.response_metadata: Dict
属性访问此元数据。根据模型提供程序和模型配置,这可能包含令牌计数、logprob 等信息。
具体使用如下(这里以OpenAI为例):
from langchain_openai import ChatOpenAI
llm = ChatOpenAI()
msg = llm.invoke([("human", "What's the oldest known example of cuneiform")])
msg.response_metadata
# {'token_usage': {'completion_tokens': 164,
# 'prompt_tokens': 17,
# 'total_tokens': 181},
# 'model_name': 'gpt-4-turbo',
# 'system_fingerprint': 'fp_76f018034d',
# 'finish_reason': 'stop',
# 'logprobs': None}
从结果中,我们可以看到 token 的使用情况。
10 Model介绍
做个总结吧,这篇主要是对Model I/O中的 Chat Model 组件进行详细解析。
我们介绍了 Chat Model 组件是什么,与 LLM 组件有什么不同;我们查看了目前 LangChain 框架中支持的所有 Chat Model;我们知道如何使用 Chat Model;我们还能自定义自己的 Chat Model;我们知道 Chat Model 与 LLM 一样都能设置缓存;我们知道所谓的Tool/Function Calling与Structured Output只能在 Chat Model 组件下使用;我们还能通过查看元数据信息,了解 Chat Model 的执行token使用情况等等。
以上内容依据官方文档编写,官方地址:官方文档
如果还不知道LangChain是什么,如何用的小伙伴们,可以看下我之前的文章。
大家要是随便关注下公众号,那就太好了。
Love & Peace~
更多推荐
所有评论(0)