目录
1、会话存储类型
2、版本代码说明
3、对话缓存存储
3.1、示例代码
3.2、响应response说明
3.3、流式输出
3.4、添加提示词模板
3.5、指定回答语言
4、限制令牌数存储
4.1、trim_messages
4.1.1、自定义tokens计数器
4.1.2、自定义tokens计数器
4.2、完整chatbot代码
4.3、结合RunnableWithMessageHistory
5、对话摘要存储
5.1、示例代码
5.2、NotImplementedError
5.3.1、问题原因
5.3.2、解决方式
LLM大模型本身并不具备记忆、存储功能,是无状态的;
LangChain 提供了多种储存类型,本质上是将历史的对话内容暂时存储,并在下次一并发送给模型,但这种存储功能,增加了大模型在内容解析、性能资源的消耗。
本文基于Langchain-chat-0.2.x版本 + chatglm3-6b 模型部署使用
1、会话存储类型
- 对话缓存储存
即储存了当前为止所有的对话信息
- 对话缓存窗口储存
随着对话变得越来越长,所需的内存量也变得非常长。将大量的tokens发送到LLM的成本,也会变得更加昂贵。
对话缓存窗口储存只保留一个窗口大小的对话。它只使用最近的n次交互。这可以用于保持最近交互的滑动窗口,以便缓冲区不会过大。
- 对话令牌缓存储存
内存将限制保存的token数量。如果字符数量超出指定数目,它会切掉这个对话的早期部分 以保留与最近的交流相对应的字符数量,但不超过字符限制。
添加对话到Token缓存储存,限制token数量,进行测试
- 对话摘要缓存储存
对话摘要缓存储存,使用 LLM 对到目前为止历史对话自动总结摘要,并将其保存下来。
2、版本代码说明
传统的使用 ConversationChain 已经移除, 使用RunnableWithMessageHistory 代替。具体可以参考博文结尾的官方文档
LangChainDeprecationWarning: The class `ConversationChain` was deprecated in LangChain 0.2.7 and will be removed in 1.0. Use RunnableWithMessageHistory: https://api.python.langchain.com/en/latest/runnables/langchain_core.runnables.history.RunnableWithMessageHistory.html instead.
在新版的官方示例,基本上完全实现了 openai.OpenAI.chat.completions.create 所需的参数功能
详细示例说明可以通过包 langchain_openai.chat_models.base.py
内部的注释中提供了新旧两种代码,在实现对应功能上的说明
from langchain_openai import ChatOpenAI
llm = ChatOpenAI(
model="gpt-4o",
temperature=0,
max_tokens=None,
timeout=None,
max_retries=2,
# api_key="...",
# base_url="...",
# organization="...",
# other params...
)
**NOTE**: Any param which is not explicitly supported will be passed directly to the
``openai.OpenAI.chat.completions.create(...)`` API every time to the model is
invoked. For example:
.. code-block:: python
from langchain_openai import ChatOpenAI
import openai
ChatOpenAI(..., frequency_penalty=0.2).invoke(...)
# results in underlying API call of:
openai.OpenAI(..).chat.completions.create(..., frequency_penalty=0.2)
# which is also equivalent to:
ChatOpenAI(...).invoke(..., frequency_penalty=0.2)
Invoke:
.. code-block:: python
messages = [
(
"system",
"You are a helpful translator. Translate the user sentence to French.",
),
("human", "I love programming."),
]
llm.invoke(messages)
.. code-block:: python
AIMessage(
content="J'adore la programmation.",
response_metadata={
"token_usage": {
"completion_tokens": 5,
"prompt_tokens": 31,
"total_tokens": 36,
},
"model_name": "gpt-4o",
"system_fingerprint": "fp_43dfabdef1",
"finish_reason": "stop",
"logprobs": None,
},
id="run-012cffe2-5d3d-424d-83b5-51c6d4a593d1-0",
usage_metadata={"input_tokens": 31, "output_tokens": 5, "total_tokens": 36},
)
3、对话缓存存储
使用RunnableWithMessageHistory 类
1)通过创建全局对象store,存储所有历史记录
2)通过定义config中的id,区分不同用户的记录 config = {"configurable": {"session_id": "abc2"}},不同用户之间信息不共享
3.1、示例代码
from langchain_core.chat_history import (
BaseChatMessageHistory,
InMemoryChatMessageHistory,
)
from langchain_core.messages import HumanMessage
from langchain_core.runnables.history import RunnableWithMessageHistory
from langchain_core.runnables import ConfigurableField
from langchain_openai import ChatOpenAI
import os
store = {}
os.environ.setdefault("OPENAI_API_KEY", "EMPTY")
os.environ.setdefault("OPENAI_API_BASE", "http://192.168.1.1:20000/v1")
# 创建模型API对象
model = ChatOpenAI(model='chatglm3-6b',max_tokens=20).configurable_fields(
max_tokens=ConfigurableField(
id="output_token_number",
name="Max tokens in the output",
description="The maximum number of tokens in the output",
)
)
# 构建全局对象,存储所有历史记录
def get_session_history(session_id: str) -> BaseChatMessageHistory:
if session_id not in store:
store[session_id] = InMemoryChatMessageHistory()
return store[session_id]
with_message_history = RunnableWithMessageHistory(model, get_session_history)
# 设置session id
config = {"configurable": {"session_id": "abc2"}}
# 通过message_history 发送消息
response = with_message_history.invoke(
[HumanMessage(content="你好, 我叫宣晨光")],
config=config,
)
# response 对象 包含了 token的使用量、sessionId
# print(response)
print(response.content)
response = with_message_history.invoke(
[HumanMessage(content="你知道我的名字吗")],
config=config,
)
print(response.content)
你好,宣晨光先生!很高兴能为您服务。请问有什么我可以为您解答
当然知道,您叫宣晨光。请问有什么我可以帮助您的吗?
在打印store 对象时,我们可以看出,store 存储了所有消息的输入与输出,
因此在实现滑动窗口类型的存储会话,可以对 InMemoryChatMessageHistory中的messages数组做截取操作,从而保持数组固定大小。
3.2、响应response说明
content='你好,宣晨光先生!很高兴能为您服务。请问有什么我可以为您解答'
response_metadata=
{
'token_usage':
{'completion_tokens': 20, 'prompt_tokens': 14, 'total_tokens': 34},
'model_name': 'chatglm3-6b',
'system_fingerprint': None,
'finish_reason': 'stop',
'logprobs': None
}
id='run-d0c37edb-d83f-4852-b7fd-3f023ade9f9b-0'
usage_metadata={'input_tokens': 14, 'output_tokens': 20, 'total_tokens': 34}
3.3、流式输出
stream = model.stream("你知道我叫什么名字吗")
full = next(stream)
for chunk in stream:
full += chunk
print(full.content)
3.4、添加提示词模板
使用ChatPromptTemplate.from_messages 创建系统提示词
在invoke调用时,使用HumanMessage 传入用户输入
from langchain_core.chat_history import BaseChatMessageHistory, InMemoryChatMessageHistory
from langchain_core.messages import HumanMessage
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain_core.runnables import ConfigurableField, RunnableWithMessageHistory
from langchain_openai import ChatOpenAI
import os
os.environ.setdefault("OPENAI_API_KEY", "EMPTY")
os.environ.setdefault("OPENAI_API_BASE", "http://192.168.1.1:20000/v1")
model = ChatOpenAI(model='chatglm3-6b', max_tokens=1024).configurable_fields(
max_tokens=ConfigurableField(
id="output_token_number",
name="Max tokens in the output",
description="The maximum number of tokens in the output",
)
)
# 添加提示词模板,完善LLM回答
prompt = ChatPromptTemplate.from_messages(
[
(
"system",
"需要真实、准确的回答问题。不可以胡乱编造假的回答。如果是你不知道的问题,你可以回答不知道。",
),
# 占位符
MessagesPlaceholder(variable_name="messages"),
]
)
chain = prompt | model
# 未使用消息存储
response = chain.invoke({"messages": [HumanMessage(content="我是宣晨光")]})
print(response.content)
response = chain.invoke({"messages": [HumanMessage(content="你知道我的名字吗")]})
print(response.content)
print('\n')
# 添加历史消息记录
store = {}
def get_session_history(session_id: str) -> BaseChatMessageHistory:
if session_id not in store:
store[session_id] = InMemoryChatMessageHistory()
return store[session_id]
with_message_history = RunnableWithMessageHistory(chain, get_session_history)
# 定义用户session
config = {"configurable": {"session_id": "abc2"}}
response = with_message_history.invoke( [HumanMessage(content="我是宣晨光")], config=config,)
print(response.content)
response = with_message_history.invoke( [HumanMessage(content="你知道我的名字吗")], config=config,)
print(response.content)
3.5、指定回答语言
针对不同的用户,采取不同的提示词模板策略。
1)在使用ChatPromptTemplate.from_messages 定义占位符的参数 {language}
2)在RunnableWithMessageHistory 中定义 input_messages_key="messages",
3)在invoke发送消息时,重新构造入参对象,并传入指定的language参数
{"messages": [HumanMessage(content="我是宣晨光")], "language": "英语"},
from langchain_core.chat_history import BaseChatMessageHistory, InMemoryChatMessageHistory
from langchain_core.messages import HumanMessage
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain_core.runnables import ConfigurableField, RunnableWithMessageHistory
from langchain_openai import ChatOpenAI
import os
os.environ.setdefault("OPENAI_API_KEY", "EMPTY")
os.environ.setdefault("OPENAI_API_BASE", "http://192.168.1.1:20000/v1")
model = ChatOpenAI(model='chatglm3-6b', max_tokens=1024).configurable_fields(
max_tokens=ConfigurableField(
id="output_token_number",
name="Max tokens in the output",
description="The maximum number of tokens in the output",
)
)
# 添加提示词模板,完善LLM回答
prompt = ChatPromptTemplate.from_messages(
[
(
"system",
"回答必须使用指定的语言:{language} 回答问题。",
),
# 占位符
MessagesPlaceholder(variable_name="messages"),
]
)
chain = prompt | model
# 添加历史消息记录
store = {}
def get_session_history(session_id: str) -> BaseChatMessageHistory:
if session_id not in store:
store[session_id] = InMemoryChatMessageHistory()
return store[session_id]
with_message_history = RunnableWithMessageHistory(
chain,
get_session_history,
input_messages_key="messages",)
# 定义用户session
config = {"configurable": {"session_id": "abc2"}}
response = with_message_history.invoke(
{"messages": [HumanMessage(content="我是宣晨光")], "language": "英语"}, config=config )
print(response.content)
response = with_message_history.invoke(
{"messages": [HumanMessage(content="你知道我的名字?")], "language": "中文"}, config=config )
print(response.content)
4、限制令牌数存储
4.1、trim_messages
限定存储消息的最大tokens值
1)使用trim_messages 对 已发送的所有消息进行截断
2)最终的trimmer 则是最终存储的历史消息列表
3)有新的消息,需要 重新创建 trim_messages
参数说明:
- max_tokens=50
- token_counter=dummy_token_counter 自定义统计器
- start_on="human" #从第一条human消息开始,默认可以不指定
- strategy="last" #保留策略,first=从开始消息保存 last=保留最近的消息
- include_system #默认False
from typing import List
from langchain_core.messages import trim_messages, AIMessage, BaseMessage, HumanMessage, SystemMessage
messages = [
SystemMessage(content="你现在是一个优秀的助手"),
HumanMessage(content="你好,我叫宣晨光"),
AIMessage(content="你好!"),
HumanMessage(content="我喜欢看唱歌"),
AIMessage(content="很不错"),
HumanMessage(content="2 + 2 应该等于几"),
AIMessage(content="4"),
HumanMessage(content="谢谢"),
AIMessage(content="不客气"),
HumanMessage(content="你很高兴吗"),
AIMessage(content="是的!"),
]
def dummy_token_counter(messages: List[BaseMessage]) -> int:
# 对待每条消息,就像它在开始时添加了3个默认令牌
# 消息和消息的末尾。3 + 4 + 3 = 10个tokens
default_content_len = 4
default_msg_prefix_len = 3
default_msg_suffix_len = 3
count = 0
for msg in messages:
if isinstance(msg.content, str):
count += default_msg_prefix_len + default_content_len + default_msg_suffix_len
if isinstance(msg.content, list):
count += default_msg_prefix_len + len(msg.content) * default_content_len + default_msg_suffix_len
return count
trimmer = trim_messages(messages, max_tokens=50, token_counter=dummy_token_counter,start_on="human", strategy="last")
print(trimmer)
[HumanMessage(content='谢谢'), AIMessage(content='不客气'), HumanMessage(content='你很高兴吗'), AIMessage(content='是的!')]
4.1.1、自定义tokens计数器
def dummy_token_counter(messages: List[BaseMessage]) -> int:
# 对待每条消息,就像它在开始时添加了3个默认令牌
# 消息和消息的末尾。3 + 4 + 3 = 10个tokens
default_content_len = 4
default_msg_prefix_len = 3
default_msg_suffix_len = 3
count = 0
for msg in messages:
if isinstance(msg.content, str):
count += default_msg_prefix_len + default_content_len + default_msg_suffix_len
if isinstance(msg.content, list):
count += default_msg_prefix_len + len(msg.content) * default_content_len + default_msg_suffix_len
return count
4.1.2、自定义tokens计数器
from typing import List
# pip install tiktoken
import tiktoken
from langchain_core.messages import BaseMessage, ToolMessage
def str_token_counter(text: str) -> int:
enc = tiktoken.get_encoding("o200k_base")
return len(enc.encode(text))
def tiktoken_counter(messages: List[BaseMessage]) -> int:
"""Approximately reproduce https://github.com/openai/openai-cookbook/blob/main/examples/How_to_count_tokens_with_tiktoken.ipynb
For simplicity only supports str Message.contents.
"""
num_tokens = 3 # every reply is primed with <|start|>assistant<|message|>
tokens_per_message = 3
tokens_per_name = 1
for msg in messages:
if isinstance(msg, HumanMessage):
role = "user"
elif isinstance(msg, AIMessage):
role = "assistant"
elif isinstance(msg, ToolMessage):
role = "tool"
elif isinstance(msg, SystemMessage):
role = "system"
else:
raise ValueError(f"Unsupported messages type {msg.__class__}")
num_tokens += (
tokens_per_message
+ str_token_counter(role)
+ str_token_counter(msg.content)
)
if msg.name:
num_tokens += tokens_per_name + str_token_counter(msg.name)
return num_tokens
trim_messages(
messages,
max_tokens=45,
strategy="last",
token_counter=tiktoken_counter,
)
4.2、完整chatbot代码
通过限定 trim_messages中的max_tokens值,对最大tokens值存储能力的测试(100、150)
from operator import itemgetter
from typing import List
from langchain_core.chat_history import BaseChatMessageHistory, InMemoryChatMessageHistory
from langchain_core.messages import trim_messages, HumanMessage, SystemMessage, AIMessage, BaseMessage
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain_openai import ChatOpenAI
from langchain_core.runnables import RunnablePassthrough, RunnableWithMessageHistory
import os
os.environ.setdefault("OPENAI_API_KEY", "EMPTY")
os.environ.setdefault("OPENAI_API_BASE", "http://192.168.1.1:20000/v1")
model = ChatOpenAI(model='chatglm3-6b', max_tokens=1024)
# 添加提示词模板,完善LLM回答
prompt = ChatPromptTemplate.from_messages(
[
(
"system",
"你是一个回答准确的助手,必须使用指定的{language}语言回答问题",
),
# 占位符
MessagesPlaceholder(variable_name="messages"),
]
)
messages = [
SystemMessage(content="you're a good assistant"),
HumanMessage(content="hi! I'm bob"),
AIMessage(content="hi!"),
HumanMessage(content="I like vanilla ice cream"),
AIMessage(content="nice"),
HumanMessage(content="whats 2 + 2"),
AIMessage(content="4"),
HumanMessage(content="thanks"),
AIMessage(content="no problem!"),
HumanMessage(content="having fun?"),
AIMessage(content="yes!"),
]
def dummy_token_counter(messages: List[BaseMessage]) -> int:
# 对待每条消息,就像它在开始时添加了3个默认令牌
# 消息和消息的末尾。3 + 4 + 3 = 10个tokens
default_content_len = 4
default_msg_prefix_len = 3
default_msg_suffix_len = 3
count = 0
for msg in messages:
if isinstance(msg.content, str):
count += default_msg_prefix_len + default_content_len + default_msg_suffix_len
if isinstance(msg.content, list):
count += default_msg_prefix_len + len(msg.content) * default_content_len + default_msg_suffix_len
return count
trimmer = trim_messages(max_tokens=100, token_counter=dummy_token_counter,start_on="human", strategy="last")
chain = (
RunnablePassthrough.assign(messages=itemgetter("messages") | trimmer)
| prompt
| model
)
# max_tokens = 150
response = chain.invoke(
{
"messages": messages + [HumanMessage(content="你知道我的名字吗?")],
"language": "中文",
}
)
print(response.content)
# max_tokens = 100
response = chain.invoke(
{
"messages": messages + [HumanMessage(content="你知道,我上一次询问的数学问题是什么")],
"language": "中文",
}
)
print(response.content)
很抱歉,我只是一个计算机程序,无法知道你的个人信息。但是,如果你愿意告诉我,我很乐意与你交流并回答你的问题。
您问的是"2 + 2等于什么?"。我回答是"4"。
4.3、结合RunnableWithMessageHistory
1)全局对象store 记录了完整的历史对话
2)将多个函数通过管道命令组织形成的chain
chain = ( RunnablePassthrough.assign(messages=itemgetter("messages") | trimmer) | prompt | model )
3)第一次的 with_message_history.invoke 需要发送历史记录messages数组对象
4)max_tokens = 100,所以第一个问题LLM并不清楚。
from operator import itemgetter
from typing import List
from langchain_core.chat_history import BaseChatMessageHistory, InMemoryChatMessageHistory
from langchain_core.messages import trim_messages, HumanMessage, SystemMessage, AIMessage, BaseMessage
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain_openai import ChatOpenAI
from langchain_core.runnables import RunnablePassthrough, RunnableWithMessageHistory
import os
os.environ.setdefault("OPENAI_API_KEY", "EMPTY")
os.environ.setdefault("OPENAI_API_BASE", "http://192.168.1.1:20000/v1")
model = ChatOpenAI(model='chatglm3-6b', max_tokens=1024)
# 添加提示词模板,完善LLM回答
prompt = ChatPromptTemplate.from_messages(
[
(
"system",
"必须使用指定的国家语言:{language}回答问题",
),
# 占位符
MessagesPlaceholder(variable_name="messages"),
]
)
messages = [
SystemMessage(content="you're a good assistant"),
HumanMessage(content="hi! I'm bob"),
AIMessage(content="hi!"),
HumanMessage(content="I like vanilla ice cream"),
AIMessage(content="nice"),
HumanMessage(content="whats 2 + 2"),
AIMessage(content="4"),
HumanMessage(content="thanks"),
AIMessage(content="no problem!"),
HumanMessage(content="having fun?"),
AIMessage(content="yes!"),
]
def dummy_token_counter(messages: List[BaseMessage]) -> int:
# 对待每条消息,就像它在开始时添加了3个默认令牌
# 消息和消息的末尾。3 + 4 + 3 = 10个tokens
default_content_len = 4
default_msg_prefix_len = 3
default_msg_suffix_len = 3
count = 0
for msg in messages:
if isinstance(msg.content, str):
count += default_msg_prefix_len + default_content_len + default_msg_suffix_len
if isinstance(msg.content, list):
count += default_msg_prefix_len + len(msg.content) * default_content_len + default_msg_suffix_len
return count
trimmer = trim_messages(max_tokens=100, token_counter=dummy_token_counter,start_on="human", strategy="last")
chain = (
RunnablePassthrough.assign(messages=itemgetter("messages") | trimmer)
| prompt
| model
)
# 添加历史消息记录
store = {}
def get_session_history(session_id: str) -> BaseChatMessageHistory:
if session_id not in store:
store[session_id] = InMemoryChatMessageHistory()
return store[session_id]
with_message_history = RunnableWithMessageHistory(
chain,
get_session_history,
input_messages_key="messages",
)
config = {"configurable": {"session_id": "abc20"}}
# 第一步加载所有的历史消息
response = with_message_history.invoke(
{
"messages": messages + [HumanMessage(content="我的名字是?")],
"language": "中文",
},
config=config,
)
print(response.content)
response = with_message_history.invoke(
{
"messages": [HumanMessage(content="我问了什么数学题?")],
"language": "中文",
},
config=config,
)
print(response.content)
I'm sorry, I don't know your name. Can you tell me?
你问了 "2 + 2" 这个问题。
5、对话摘要存储
5.1、示例代码
多次的memory.save_context 只会在最后调用一次模型,生成新的摘要信息
from typing import List
from langchain_core.messages import BaseMessage
from langchain_openai import ChatOpenAI
from langchain.memory import ConversationSummaryBufferMemory
import os
from langchain_openai.chat_models.base import BaseChatOpenAI
os.environ.setdefault("OPENAI_API_KEY", "EMPTY")
os.environ.setdefault("OPENAI_API_BASE", "http://192.168.1.1:20000/v1")
class ChildChatOpenAI(ChatOpenAI):
def get_num_tokens_from_messages(self, messages: List[BaseMessage]) -> int:
model, encoding = self._get_encoding_model()
if model.startswith("cl100k_base"):
return super(BaseChatOpenAI, self).get_num_tokens_from_messages(messages)
else:
return super().get_num_tokens_from_messages(messages)
schedule = "在八点你和你的产品团队有一个会议。 \
你需要做一个PPT。 \
上午9点到12点你需要忙于LangChain。\
Langchain是一个有用的工具,因此你的项目进展的非常快。\
中午,在意大利餐厅与一位开车来的顾客共进午餐 \
走了一个多小时的路程与你见面,只为了解最新的 AI。 \
确保你带了笔记本电脑可以展示最新的 LLM 样例."
llm = ChildChatOpenAI(model='chatglm3-6b')
memory = ConversationSummaryBufferMemory(llm=llm, max_token_limit=100)
# 对每一组输入、输出存储
memory.save_context({"input": "你好,我叫皮皮鲁"}, {"output": "你好啊,我叫鲁西西"})
memory.save_context({"input": "很高兴和你成为朋友!"}, {"output": "是的,让我们一起去冒险吧!"})
memory.save_context({"input": "今天的日程安排是什么?"}, {"output": f"{schedule}"})
print(memory.load_memory_variables({})['history'])
System: 当前的对话如下:
人类:你好,我叫皮皮鲁。
AI:你好啊,我叫鲁西西。
人类:很高兴和你成为朋友!
AI:是的,让我们一起去冒险吧!
人类:今天的日程安排是什么?
AI:在八点你和你的产品团队有一个会议。 你需要做一个PPT。 上午9点到12点你需要忙于LangChain。LangChain是一个有用的工具,因此你的项目进展的非常快。中午,在意大利餐厅与一位开车来的顾客共进午餐 走了一个多小时的路程与你见面,只为了解最新的 AI。 确保你带了笔记本电脑可以展示最新的 LLM 样例。新的摘要:
今天,人类与AI讨论了许多话题,包括人工智能的潜力。AI认为人工智能可以人类发挥其全部潜力,并且帮助人类进步。此外,他们讨论了日程安排,包括一个与产品团队的会议,一个忙碌的LangChain项目,以及与一位开车来的顾客共进午餐的计划。
"POST /v1/chat/completions HTTP/1.1"
5.2、NotImplementedError
5.3.1、问题原因
在使用时,存在如下错误,不支持的模型计算tokens值
NotImplementedError: get_num_tokens_from_messages() is not presently implemented for model cl100k_base
代码位置
5.3.2、解决方式
1)使用类的继承方式,覆盖。定义子类class ChildChatOpenAI(ChatOpenAI) ,并重写get_num_tokens_from_messages函数
from langchain_core.messages import BaseMessage
from langchain_openai import ChatOpenAI
class ChildChatOpenAI(ChatOpenAI):
def get_num_tokens_from_messages(self, messages: List[BaseMessage]) -> int:
model, encoding = self._get_encoding_model()
# 此处的model名称并不是传入的chatglm3-6b
if model.startswith("cl100k_base"):
# 调用祖父类的函数
return super(BaseChatOpenAI, self).get_num_tokens_from_messages(messages)
else:
return super().get_num_tokens_from_messages(messages)
llm = ChildChatOpenAI(model='chatglm3-6b',temperature=0.0)
2)直接修改对应源码,对else部分,采用super 父类计算函数
return super().get_num_tokens_from_messages(messages)
# raise NotImplementedError(
# f"get_num_tokens_from_messages() is not presently implemented "
# f"for model {model}. See "
# "https://platform.openai.com/docs/guides/text-generation/managing-tokens" # noqa: E501
# " for information on how messages are converted to tokens."
# )
参考文档:
Migrating to LangChain v0.2 | 🦜️🔗 LangChain
面向开发者的LLM入门教程
RunnableWithMessageHistory — 🦜🔗 LangChain 0.2.12
LangChain-0.2.x 官方API文档