上一课每次都发送全部聊天记录。聊得越久,请求就越大。这次我们只发送最近几轮,同时继续读取数据库里的阅读偏好。
1. 复制上一课文件
将上一课文件另存为:
C:\Users\Administrator\langchain-study\unit4_lesson14_trim_history.py保留原有代码,只改下面三处。
2. 添加导入
在文件顶部添加:
from langchain_core.messages.utils import trim_messages3. 修改chat函数末尾
找到原来调用模型的部分:
response = model.invoke([system_message, *state["messages"]]) return {"messages": [response]}替换为以下代码,注意保持在chat函数内部:
# 从完整历史中,选出最近几条消息 recent_messages = trim_messages( state["messages"], max_tokens=5, token_counter=len, strategy="last", start_on="human", allow_partial=False, ) print("完整历史消息数:", len(state["messages"])) print("本次发送的历史消息数:", len(recent_messages)) # 阅读偏好仍然通过 system_message 每次提供给模型 response = model.invoke([ system_message, *recent_messages, ]) return {"messages": [response]}这里几个参数的含义:
| 参数 | 含义 |
|---|---|
token_counter=len | 用列表长度计数,也就是按消息条数计数 |
max_tokens=5 | 配合上面的len,表示最多保留5 条消息,不是 5 个 token |
strategy="last" | 优先保留最新的消息 |
start_on="human" | 保留的对话从用户消息开始,避免从半截回答开始 |
allow_partial=False | 保留完整消息,不截断消息正文 |
一条用户问题和一条模型回答,共算2 条消息。官方参数说明
4. 测试四轮对话
把原来的questions列表换成:
questions = [ "请根据我的阅读偏好,推荐两本书。", "请介绍你刚才推荐的第一本书。", "请为这本书安排一个三天阅读计划。", "请把刚才的阅读计划压缩成三个要点。", ]保留原来的循环,因此每一轮仍然会输出问题和回答。
在已激活.venv的项目目录运行:
python unit4_lesson14_trim_history.py预期会看到这些计数——不包含额外添加的系统消息:
| 调用模型前 | 完整历史消息数 | 本次发送的历史消息数 |
|---|---|---|
| 第 1 轮 | 1 | 1 |
| 第 2 轮 | 3 | 3 |
| 第 3 轮 | 5 | 5 |
| 第 4 轮 | 7 | 5 |
第四轮调用模型前,有前三轮的 6 条消息,加上当前问题,共 7 条。裁剪后,第一轮的问题和回答不再发送。
这节最重要的区别
少发给模型,不等于删除历史。
我们没有修改state["messages"],只从中选出一个较短的列表。因此:
- 完整聊天记录仍由本课的内存检查点保留;程序退出后不会保留。
- 模型只看到选出的最近消息,以及额外添加的系统消息。
- 阅读偏好仍从 SQLite 数据库读取,每轮放进系统消息,不受这次历史裁剪影响。
先运行到第四轮,确认看到完整历史 7 条、本次发送 5 条,就说明这课的历史裁剪生效了。