使用langchain做历史会话管理时报错,代码及报错如下:
from langchain_openai import ChatOpenAI
model = ChatOpenAI(
base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
api_key="sk-ba5d2f5624d144c98fb196f4013b0e3d",
model="qwen-plus",
)
from langchain_core.messages import HumanMessage, SystemMessage ,AIMessage
from langchain_core.messages import SystemMessage, trim_messages
trimmer = trim_messages(
max_tokens=65,
strategy="last",
token_counter=model,
include_system=True,
allow_partial=False,
start_on="human",
)
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!"),
]
trimmer.invoke(messages)
报错
NotImplementedError: get_num_tokens_from_messages() is not presently implemented for model qwen-plus. See https://platform.openai.com/docs/guides/text-generation/managing-tokens for information on how messages are converted to tokens.
原因
根本原因是:LangChain 的 ChatOpenAI 类在使用非 OpenAI 官方模型(比如阿里云的 Qwen 系列)时,默认无法准确计算 token 数量,因为 get_num_tokens_from_messages() 方法依赖于 OpenAI 的 tokenizer。而像 qwen-plus 这样的第三方兼容模型,并没有被 LangChain 内置支持用于 token 计算
解决方法
如果你使用的是较新版本的 LangChain(>=0.2),可以考虑直接使用 Tongyi 官方提供的 LangChain 集成,而不是通过 OpenAI 兼容模式
pip install langchain-community
pip install dashscope --upgrade
pip install transformers
修改后代码
tongyi_model = ChatTongyi(
base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
api_key="sk-ba5d2f5624d144c98fb196f4013b0e3d",
model="qwen-plus",
)
trimmer = trim_messages(
max_tokens=65,
strategy="last",
token_counter=tongyi_model,
include_system=True,
allow_partial=False,
start_on="human",
)
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!"),
]
trimmer.invoke(messages)
运行后,有警告
/home/runUser/anaconda3/envs/langchain/lib/python3.12/site-packages/langchain_core/language_models/base.py:328: UserWarning: Using fallback GPT-2 tokenizer for token counting. Token counts may be inaccurate for non-GPT-2 models. For accurate counts, use a model-specific method if available.
return len(self.get_token_ids(text))
表明 ChatTongyi 虽然被用作 token_counter,但其底层 没有实现准确的 token 计数方法(或未正确注册 Qwen 的 tokenizer),于是 LangChain 回退到 GPT-2 的 tokenizer(对中文和 Qwen 模型不准确)
ChatTongyi 的 get_num_tokens_from_messages 方法仍未被正确实现(或依赖未安装的 tokenizer 包),导致它无法准确计算 Qwen 系列模型的 token 数量,只能回退到不合适的 GPT-2 tokenizer。
🔍 验证:Qwen 模型使用的是 基于 tiktoken 的 cl100k_base 编码(和 GPT-3.5/4 相同),不是 GPT-2 的 gpt2 编码。用 GPT-2 tokenizer 会严重低估 token 数(尤其对英文标点、空格等处理不同)。
处理方法:
增加自定义的计数方法实现准确计数,调整后代码如下:
from langchain_core.messages import SystemMessage, trim_messages
import tiktoken
✅ 准确的 token 计数函数(适用于 Qwen / GPT-3.5 / GPT-4)
def count_tokens_in_messages(messages):
"""估算 messages 的总 token 数(参考 OpenAI 官方方式)"""
encoding = tiktoken.get_encoding("cl100k_base") # Qwen 使用此编码
tokens_per_message = 4 # 每条消息的固定开销(role + 分隔符等)
tokens_per_name = -1 # Qwen 不支持 name 字段,可忽略
num_tokens = 0
for message in messages:
num_tokens += tokens_per_message
num_tokens += len(encoding.encode(message.content))
num_tokens += len(encoding.encode(message.type)) # 'system', 'user', 'assistant'
# 注意:Qwen 不支持 'name',所以跳过
num_tokens += 3 # 每轮对话的额外开销(如 <|im_start|> 等)
return num_tokens
trimmer = trim_messages(
max_tokens=65,
strategy="last",
token_counter=count_tokens_in_messages,
include_system=True,
allow_partial=False,
start_on="human",
)
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!"),
]
trimmer.invoke(messages)
需要提醒的是Qwen的行为更“上下文驱动”而非“指令驱动”。它会优先模仿最近的语言风格,而不是严格遵行system规则
1689

被折叠的 条评论
为什么被折叠?



