当前位置: 首页 > news >正文

【LangChain】对话历史管理

1 历史记录的剪裁 trimmed_messages

from langchain_core.messages import (
    AIMessage,
    HumanMessage,
    SystemMessage,
    trim_messages,
)
from langchain_openai import ChatOpenAI

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-mini"),
)

输出:

[AIMessage(content="Hmmm let me think.\n\nWhy, he's probably chasing after the last cup of coffee in the office!", additional_kwargs={}, response_metadata={}),
 HumanMessage(content='what do you call a speechless parrot', additional_kwargs={}, response_metadata={})]

这段代码演示了如何使用LangChain的消息管理功能控制对话长度,确保不超过大语言模型的Token限制。以下是逐部分解析:


1. 消息类型导入

from langchain_core.messages import (
    AIMessage,         # AI生成的消息
    HumanMessage,      # 用户输入的消息
    SystemMessage,     # 系统角色设定消息
    trim_messages      # 消息修剪函数
)
  • SystemMessage: 设定对话背景(如角色设定)
  • HumanMessage: 用户输入内容
  • AIMessage: AI的历史回复
  • trim_messages:核心工具函数,用于智能裁剪对话历史

2. 大语言模型初始化

from langchain_openai import ChatOpenAI
# 实际使用时需替换为有效模型名如"gpt-4"
token_counter = ChatOpenAI(model="gpt-4o-mini") 
  • 此处创建了一个用于计算Token的模型实例
  • ⚠️注意:gpt-4o-mini 可能是笔误,OpenAI官方模型应为 gpt-4-0125-preview

3. 对话历史构建

messages = [
    SystemMessage("you're a good assistant..."), # 系统角色设定
    HumanMessage("i wonder why..."),             # 用户第一次提问
    AIMessage('Well, I guess...'),               # AI第一次回复
    HumanMessage("and who is..."),               # 用户第二次提问
    AIMessage("Hmmm let me think..."),           # AI第二次回复
    HumanMessage("what do you call...")          # 待回答的新问题
]

对话结构可视化:

System: 你是一个爱讲笑话的助手
User: 为什么叫LangChain?
AI: 因为"WordRope"不好听...
User: Harrison在追什么?
AI: 可能在追最后一杯咖啡!
User: 怎么称呼不会说话的鹦鹉?(待回答)

4. 消息修剪执行

trimmed_messages = trim_messages(
    messages,
    max_tokens=45,              # 最大Token限制
    strategy="last",            # 修剪策略
    token_counter=token_counter # Token计数器
)
关键参数解析
参数作用典型值
max_tokens保留内容的最大Token数根据模型上限调整
strategy修剪策略:
- last:优先保留最新消息
- first:优先保留旧消息
“last”/“first”
token_counter指定Token计算方式(需匹配模型)模型实例
执行过程
  1. 从最新消息开始逆向计算Token总数
  2. 当累计Token超过45时,丢弃最旧的消息
  3. 保留能放入限额内的最长连续消息序列

假设每条消息Token消耗:

SystemMessage: 15 tokens  
HumanMessage1: 10  
AIMessage1: 12  
HumanMessage2: 8  
AIMessage2: 15  
HumanMessage3: 10  

总Tokens = 15+10+12+8+15+10 = 70 → 超过45

修剪过程(保留最后N条):

  1. 保留最后5条 → 10+15+8+15+10 = 58 ❌
  2. 保留最后4条 → 8+15+10+10 = 43 ✅

最终保留内容:

[
    HumanMessage("and who is..."), 
    AIMessage("Hmmm let me think..."),
    HumanMessage("what do you call...")
]

5. 典型应用场景

# 生成最终回复时使用修剪后的消息
llm = ChatOpenAI(model="gpt-4")
response = llm.invoke(trimmed_messages) 
  • 保证请求不超过模型的Token上限(如GPT-4的8,192 Tokens)
  • 维持尽可能长的对话记忆

6. 开发注意事项

  1. 策略选择

    • last:适合短期记忆重要的场景(如追问细节)
    • first:适合需要保留初始设定的场景(如角色扮演)
  2. Token计算

    • 不同模型的Token计算方式不同(如GPT-3与Claude)
    • 中文通常消耗更多Token(一个汉字≈1.5 Token)
  3. 最佳实践

    # 动态计算max_tokens(留出回答空间)
    max_tokens = model_max_limit - response_token_buffer
    
  4. 常见问题

    • 过度修剪导致丢失关键上下文
    • 未考虑特殊格式Token(如Markdown语法)
    • 混合多模型时Token计算方式不统一

通过这种消息管理机制,开发者可以在大模型有限的上下文窗口内,智能平衡历史记忆与当前问题的关联性。实际应用中常结合向量数据库实现长期记忆存储。

# 保留 system prompt

# 作用	默认值	典型场景
# 强制保留系统消息(即使超出Token限制)	False	需要始终保持角色设定/系统提示

trim_messages(
    messages,
    max_tokens=45,
    strategy="last",
    token_counter=ChatOpenAI(model="gpt-4o-mini"),
    include_system=True,
    allow_partial=True,
)

输出:
[SystemMessage(content="you're a good assistant, you always respond with a joke.", additional_kwargs={}, response_metadata={}),
 HumanMessage(content='what do you call a speechless parrot', additional_kwargs={}, response_metadata={})]

2 过滤带标签的历史记录 filter_messages

from langchain_core.messages import (
    AIMessage,
    HumanMessage,
    SystemMessage,
    filter_messages,
)

messages = [
    SystemMessage("you are a good assistant", id="1"),
    HumanMessage("example input", id="2", name="example_user"),
    AIMessage("example output", id="3", name="example_assistant"),
    HumanMessage("real input", id="4", name="bob"),
    AIMessage("real output", id="5", name="alice"),
]

filter_messages(messages, include_types="human")

输出:

[HumanMessage(content='example input', additional_kwargs={}, response_metadata={}, name='example_user', id='2'),
 HumanMessage(content='real input', additional_kwargs={}, response_metadata={}, name='bob', id='4')]

filter_messages(messages, exclude_names=["example_user", "example_assistant"])

输出:
[SystemMessage(content='you are a good assistant', additional_kwargs={}, response_metadata={}, id='1'),
 HumanMessage(content='real input', additional_kwargs={}, response_metadata={}, name='bob', id='4'),
 AIMessage(content='real output', additional_kwargs={}, response_metadata={}, name='alice', id='5')]

filter_messages(messages, include_types=[HumanMessage, AIMessage], exclude_ids=["3"])

[HumanMessage(content='example input', additional_kwargs={}, response_metadata={}, name='example_user', id='2'),
 HumanMessage(content='real input', additional_kwargs={}, response_metadata={}, name='bob', id='4'),
 AIMessage(content='real output', additional_kwargs={}, response_metadata={}, name='alice', id='5')]
 

这段代码展示了如何使用 filter_messages 函数从对话历史中筛选特定类型的消息。以下是对代码的详细讲解及该函数的应用场景分析:


代码讲解

  1. 导入模块和函数

    from langchain_core.messages import AIMessage, HumanMessage, SystemMessage, filter_messages
    
    • 导入了三种消息类型:SystemMessage(系统消息)、HumanMessage(用户消息)、AIMessage(AI回复)。
    • filter_messages 是用于过滤消息列表的函数。
  2. 定义消息列表

    messages = [
        SystemMessage("you are a good assistant", id="1"),
        HumanMessage("example input", id="2", name="example_user"),
        AIMessage("example output", id="3", name="example_assistant"),
        HumanMessage("real input", id="4", name="bob"),
        AIMessage("real output", id="5", name="alice"),
    ]
    
    • 消息列表包含 5 条对话历史记录,每条消息都有 idname 属性用于标识来源。
  3. 调用过滤函数

    filtered = filter_messages(messages, include_types="human")
    
    • filter_messages 会根据 include_types 参数过滤消息。此处 include_types="human" 表示只保留 HumanMessage 类型的消息。
    • 输出结果filtered 将包含两条用户消息(id=2 和 id=4)。

filter_messages 的工作原理

  1. 按类型过滤

    • 根据消息的类型(SystemMessageHumanMessageAIMessage)进行筛选。
    • include_types 接受字符串或列表(如 ["human", "system"]),表示需要保留的类型。
    • 类型名称通常对应类名的小写形式(如 HumanMessage → "human")。
  2. 支持的参数

    • include_types: 包含的消息类型。
    • exclude_types: 排除的消息类型。
    • include_names/exclude_names: 根据 name 属性进一步过滤。

思考:你能想出这个功能的一个使用场景吗

应用场景

  1. 提取用户输入

    • 当需要单独分析用户的提问或指令时(如统计高频问题),过滤出所有 HumanMessage
  2. 构建对话上下文

    • 在聊天机器人中,可能只需将用户的历史输入(HumanMessage)和AI的回复(AIMessage)作为上下文,忽略系统消息。
  3. 数据清洗

    • 在训练模型时,可能需要移除系统提示或特定角色的消息,保留有效对话数据。
  4. 权限控制

    • 若某些消息类型仅供内部使用(如 SystemMessage),可通过过滤避免泄露给终端用户。
  5. 路由处理

    • 根据消息类型分发到不同处理模块(如用户消息转分析服务,AI消息转日志服务)。

示例扩展

若想同时保留用户和AI的消息:

filter_messages(messages, include_types=["human", "ai"])

若想排除名为 bob 的用户消息:

filter_messages(messages, include_types="human", exclude_names="bob")

通过 filter_messages,开发者可以灵活处理对话数据,适应不同业务场景的需求。

相关文章:

  • Celia智能助手2.0架构演进与性能突破
  • 网络安全中分区分域
  • 18.1 大模型时代的开源与数据协议:合规之路与技术突
  • 【网络安全】SSL重协商原理、过程、防范详解(含案例)
  • 如何将ConfigMap中的内容挂载为容器内的文件
  • 计算机视觉算法实战——医学影像分割(主页有源码)
  • 大模型笔记_大模型不是靠记忆回答问题
  • C++20 标准化有符号整数:迈向更可预测的整数运算
  • 《深度学习进阶》第9集:自监督学习与无监督学习
  • ES检索elasticsearch实现python库方法
  • 基于微信小程序的停车场管理系统的设计与实现
  • 【论文带读(3)】《Real-Time Flying Object Detection with YOLOv8》带读笔记翻译
  • R语言 | 在图形上标注P值的R包
  • QTcpSocket使用指南与实践
  • 30秒从零搭建机器人管理系统(Trae)
  • flutter集成华为推送(Android)
  • uploadlabs经验总结
  • 将 SSH 密钥添加到 macOS 的钥匙串中
  • 拉格朗日对偶性(Lagrangian Duality)详解
  • Windows创建密钥并登录linux服务器
  • 电子商务网站设计思路/在线h5免费制作网站
  • 呼和浩特网站运营公司/品牌网络营销成功案例
  • 网络建设图片/seo推广优化多少钱
  • 网站开发是打代码吗/国外电商平台有哪些
  • 网站建设模块需求/长沙关键词优化平台
  • 做网站需要多少钱 做/西安网站托管