本节解决长对话里的上下文膨胀问题:用 SummarizationMiddleware 压缩旧消息,用 before_agent middleware 删除不该继续进入模型的消息。
Agent 的 state 里最核心的字段通常是 messages。对话越长,消息越多,成本、延迟和上下文污染都会上升。Module 3 的第一步就是学会控制消息历史。
| 问题 | 表现 | 本课方案 |
|---|---|---|
| 上下文过长 | token 超限或调用变慢 | SummarizationMiddleware 总结旧消息 |
| 无用工具消息 | 模型看到噪声输出后回答跑偏 | RemoveMessage 删除指定消息 |
| 多轮记忆混乱 | 旧信息和新问题互相干扰 | 只保留必要消息和摘要 |
不是所有历史消息都值得一直保留。需要保留的是“当前任务仍然需要的上下文”,而不是完整 transcript。
SummarizationMiddleware 会在消息达到触发条件后,用模型把旧消息压缩成摘要,同时保留最近几条原始消息。
from langchain.agents.middleware import SummarizationMiddleware
summarize_middleware = SummarizationMiddleware(
model=deepseek_model(),
trigger=("tokens", 100),
keep=("messages", 1),
)
agent = create_agent(
model=deepseek_model(),
checkpointer=InMemorySaver(),
middleware=[summarize_middleware],
)trigger=("tokens", 100) 表示 token 数超过阈值时触发总结;keep=("messages", 1) 表示保留最近 1 条消息原文。
Notebook 构造了一段虚构对话:月球首都、天气、矿工、工会罢工。最后再问“如果你是新总统会怎么回应”。这个问题需要旧上下文,但不一定需要完整逐字历史。
messages = [
HumanMessage(content="What is the capital of the moon?"),
AIMessage(content="The capital of the moon is Lunapolis."),
HumanMessage(content="What is the weather in Lunapolis?"),
AIMessage(content="Skies are clear, with a high of 120C and a low of -100C."),
HumanMessage(content="How many cheese miners live in Lunapolis?"),
AIMessage(content="There are 100,000 cheese miners living in Lunapolis."),
HumanMessage(content="Do you think the cheese miners' union will strike?"),
AIMessage(content="Yes, because they are unhappy with the new president."),
HumanMessage(content="If you were Lunapolis' new president how would you respond to the cheese miners' union?"),
]
response = agent.invoke(
{"messages": messages},
{"configurable": {"thread_id": "1"}},
)最后的问题依赖前文事实,但不依赖每一句原始表达。摘要可以保留关键事实,同时减少上下文长度。
第二个示例展示如何在模型调用前处理 state。@before_agent middleware 读取当前 messages,找出所有 ToolMessage,再返回 RemoveMessage 指令删除它们。
from langchain.messages import RemoveMessage
from langchain.agents.middleware import before_agent
@before_agent
def trim_messages(state: AgentState, runtime: Runtime) -> dict[str, Any] | None:
"""Remove all the tool messages from the state"""
messages = state["messages"]
tool_messages = [m for m in messages if isinstance(m, ToolMessage)]
return {"messages": [RemoveMessage(id=m.id) for m in tool_messages]}RemoveMessage(id=...) 不是普通文本消息,而是告诉 LangGraph 从 state 中移除指定 id 的消息。
Notebook 的设备故障排查示例里,工具消息包含“blorp-x7”“greeble”等噪声诊断文本。用户最后问温度时,如果模型看到所有原始工具输出,可能会被不可靠或无关的信息干扰。
messages = [
HumanMessage(content="My device won't turn on. What should I do?"),
ToolMessage(content="blorp-x7 initiating diagnostic ping…", tool_call_id="1"),
AIMessage(content="Is the device plugged in and turned on?"),
HumanMessage(content="Yes, it's plugged in and turned on."),
ToolMessage(content="temp=42C voltage=2.9v … greeble complete.", tool_call_id="2"),
AIMessage(content="Is the device showing any lights or indicators?"),
HumanMessage(content="What's the temperature of the device?"),
]
agent = create_agent(
model=deepseek_model(),
checkpointer=InMemorySaver(),
middleware=[trim_messages],
)可以删除低价值工具日志、敏感中间数据、格式很乱的 API 原始响应,只把必要结论保留给模型。