trim_messages#
- langchain_core.messages.utils.trim_messages(messages: Sequence[MessageLikeRepresentation] | None = None, **kwargs: Any) list[BaseMessage] | Runnable[Sequence[MessageLikeRepresentation], list[BaseMessage]] [source]#
将消息修剪到低于令牌计数。
trim_messages 可用于将聊天记录的大小减少到指定的令牌数量或指定的消息数量。
无论哪种情况,如果将修剪后的聊天历史直接传递回聊天模型,生成的聊天历史通常应满足以下属性:
生成的聊天历史应该是有效的。大多数聊天模型期望聊天历史以 (1) 一个 HumanMessage 或 (2) 一个 SystemMessage 后跟一个 HumanMessage 开始。为了实现这一点,请设置 start_on=”human”。此外,通常一个 ToolMessage 只能出现在涉及工具调用的 AIMessage 之后。 请参阅以下链接以获取有关消息的更多信息: https://python.langchain.com/docs/concepts/#messages
它包括最近的聊天记录并丢弃旧的聊天记录。 要实现这一点,请设置 strategy=”last”。
通常,新的聊天历史记录应该包括SystemMessage,如果它在原始聊天历史记录中存在,因为SystemMessage包含对聊天模型的特殊指令。SystemMessage几乎总是历史记录中的第一条消息(如果存在)。要实现这一点,请设置include_system=True。
- Note The examples below show how to configure trim_messages to achieve
一种与上述属性一致的行为。
- Parameters:
messages (Union[Sequence[MessageLikeRepresentation], None]) – 要修剪的类似消息对象的序列。
max_tokens – 修剪后消息的最大令牌数。
token_counter – 用于计算BaseMessage或BaseMessage列表中令牌数量的函数或llm。如果传入的是BaseLanguageModel,则将使用BaseLanguageModel.get_num_tokens_from_messages()。设置为len以计算聊天历史记录中的消息数量。
strategy – 修剪策略。 - “first”: 保留消息的前 <= n_count 个标记。 - “last”: 保留消息的后 <= n_count 个标记。 默认值为“last”。
allow_partial – 是否在只能包含部分消息时拆分消息。如果
strategy="last"
则包含消息的最后部分内容。如果strategy="first"
则包含消息的第一部分内容。 默认值为 False。end_on – 结束的消息类型。如果指定,则在最后一次出现此类型之后的每条消息都将被忽略。如果
strategy=="last"
,则在我们尝试获取最后一个max_tokens
之前完成此操作。如果strategy=="first"
,则在我们获取第一个max_tokens
之后完成此操作。可以指定为字符串名称(例如“system”、“human”、“ai”等)或 BaseMessage 类(例如 SystemMessage、HumanMessage、AIMessage 等)。可以是单一类型或类型列表。默认值为 None。start_on – 开始的消息类型。仅在
strategy="last"
时指定。如果指定,则在此类型首次出现之前的所有消息都将被忽略。这在我们将初始消息修剪到最后max_tokens
之后进行。如果include_system=True
,则不适用于索引为0的SystemMessage。可以指定为字符串名称(例如“system”、“human”、“ai”等)或BaseMessage类(例如SystemMessage、HumanMessage、AIMessage等)。可以是单一类型或类型列表。 默认值为None。include_system – 是否保留索引0处的SystemMessage(如果存在)。 仅在
strategy="last"
时指定。 默认值为False。text_splitter – 用于分割消息字符串内容的函数或
langchain_text_splitters.TextSplitter
。仅在allow_partial=True
时使用。如果strategy="last"
,则包含来自部分消息的最后分割标记。如果strategy=="first"
,则包含来自部分消息的第一个分割标记。标记分割器假设分隔符被保留,因此分割内容可以直接连接以重新创建原始文本。默认情况下,按换行符分割。kwargs (Any)
- Returns:
修剪后的BaseMessages列表。
- Raises:
ValueError – 如果指定了两个不兼容的参数或指定了一个无法识别的
strategy
。- Return type:
联合[列表[BaseMessage], Runnable[序列[MessageLikeRepresentation], 列表[BaseMessage]]]
示例
根据令牌数量修剪聊天历史记录,如果存在则保留SystemMessage,并确保聊天历史记录以HumanMessage开头(或SystemMessage后跟HumanMessage)。
from typing import list from langchain_core.messages import ( AIMessage, HumanMessage, BaseMessage, SystemMessage, trim_messages, ) messages = [ SystemMessage("you're a good assistant, you always respond with a joke."), HumanMessage("i wonder why it's called langchain"), AIMessage( 'Well, I guess they thought "WordRope" and "SentenceString" just didn\'t have the same ring to it!' ), HumanMessage("and who is harrison chasing anyways"), AIMessage( "Hmmm let me think.\n\nWhy, he's probably chasing after the last cup of coffee in the office!" ), HumanMessage("what do you call a speechless parrot"), ] trim_messages( messages, max_tokens=45, strategy="last", token_counter=ChatOpenAI(model="gpt-4o"), # Most chat models expect that chat history starts with either: # (1) a HumanMessage or # (2) a SystemMessage followed by a HumanMessage start_on="human", # Usually, we want to keep the SystemMessage # if it's present in the original history. # The SystemMessage has special instructions for the model. include_system=True, allow_partial=False, )
[ SystemMessage(content="you're a good assistant, you always respond with a joke."), HumanMessage(content='what do you call a speechless parrot'), ]
根据消息数量修剪聊天历史记录,如果存在则保留SystemMessage,并确保聊天历史记录以HumanMessage开头(或SystemMessage后跟HumanMessage)。
- trim_messages(
消息, # 当len作为令牌计数函数传入时, # max_tokens将计算聊天历史中的消息数量。 max_tokens=4, strategy="last", # 传入len作为令牌计数函数将 # 计算聊天历史中的消息数量。 token_counter=len, # 大多数聊天模型期望聊天历史以以下之一开始: # (1) 一个HumanMessage 或 # (2) 一个SystemMessage 后跟一个HumanMessage start_on="human", # 通常,我们希望保留SystemMessage # 如果它存在于原始历史中。 # SystemMessage包含对模型的特殊指令。 include_system=True, allow_partial=False,
)
[ SystemMessage(content="you're a good assistant, you always respond with a joke."), HumanMessage(content='and who is harrison chasing anyways'), AIMessage(content="Hmmm let me think.\n\nWhy, he's probably chasing after the last cup of coffee in the office!"), HumanMessage(content='what do you call a speechless parrot'), ]
使用自定义的令牌计数函数来修剪聊天历史记录,该函数计算每条消息中的令牌数量。
messages = [ SystemMessage("This is a 4 token text. The full message is 10 tokens."), HumanMessage("This is a 4 token text. The full message is 10 tokens.", id="first"), AIMessage( [ {"type": "text", "text": "This is the FIRST 4 token block."}, {"type": "text", "text": "This is the SECOND 4 token block."}, ], id="second", ), HumanMessage("This is a 4 token text. The full message is 10 tokens.", id="third"), AIMessage("This is a 4 token text. The full message is 10 tokens.", id="fourth"), ] def dummy_token_counter(messages: list[BaseMessage]) -> int: # treat each message like it adds 3 default tokens at the beginning # of the message and at the end of the message. 3 + 4 + 3 = 10 tokens # per message. 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
- First 30 tokens, allowing partial messages:
trim_messages( messages, max_tokens=30, token_counter=dummy_token_counter, strategy="first", allow_partial=True, )
[ SystemMessage("This is a 4 token text. The full message is 10 tokens."), HumanMessage("This is a 4 token text. The full message is 10 tokens.", id="first"), AIMessage( [{"type": "text", "text": "This is the FIRST 4 token block."}], id="second"), ]
使用 trim_messages 的示例