下篇:LangChain Agent 智能体从入门到实战(二)—— 记忆管理、中间件与最佳实践

让 Agent 记住对话、压缩历史、接受人工审核,最终成为可靠的智能助手。

上篇我们学会了 Agent 的概念、工具定义和 MCP 协议接入。但目前的 Agent 每次调用都是独立的------它不记得上一轮对话说了什么。同时,长对话会导致消息列表无限膨胀,Token 消耗剧增。本篇将解决这些问题:通过 Checkpointer 实现记忆,通过 中间件 实现历史压缩和人工审核,并总结最佳实践与常见问题。


四、记忆管理:让 Agent 记住过去

4.1 失忆问题

默认情况下,Agent 每次 invoke 都是一次独立的"感知→推理→行动"过程,上一次的对话内容不会自动带入下一次。

python

复制代码
# 第1次调用
agent.invoke({"messages": [{"role": "user", "content": "我叫张三"}]})
# Agent: "你好张三!"

# 第2次调用(默认无记忆)
agent.invoke({"messages": [{"role": "user", "content": "我叫什么名字?"}]})
# Agent: "抱歉,我不知道你叫什么名字。"  ← 失忆了

4.2 Checkpointer 机制

LangChain 通过 Checkpointer 实现记忆。每次调用结束后,自动保存本次对话的所有消息;下次调用时,自动加载历史消息拼接到新输入前面。

使用方式只需两步:

python

复制代码
from langgraph.checkpoint.memory import InMemorySaver

# 1. 创建 checkpointer
checkpointer = InMemorySaver()

# 2. 创建 Agent 时传入
agent = create_agent(
    model=llm,
    tools=tools,
    checkpointer=checkpointer,   # ← 就这一行
)

4.3 Thread ID:多会话隔离

一个 Agent 通常服务多个用户,需要为每个用户维护独立的会话历史。通过 thread_id 区分:

python

复制代码
# 张三的对话(thread_id = "user_zhangsan")
agent.invoke(
    {"messages": [{"role": "user", "content": "我叫张三"}]},
    config={"configurable": {"thread_id": "user_zhangsan"}}
)

# 李四的对话(thread_id = "user_lisi")完全独立
agent.invoke(
    {"messages": [{"role": "user", "content": "我叫李四"}]},
    config={"configurable": {"thread_id": "user_lisi"}}
)

相同 thread_id 的调用共享历史,不同 thread_id 互不干扰。

4.4 完整示例

python

复制代码
import datetime
from langchain.agents import create_agent
from langchain.chat_models import init_chat_model
from langgraph.checkpoint.memory import InMemorySaver

llm = init_chat_model(model="gpt-4o-mini", model_provider="openai")
checkpointer = InMemorySaver()
agent = create_agent(model=llm, tools=[], checkpointer=checkpointer)

# 第1次调用
for chunk in agent.stream(
    {"messages": [{"role": "user", "content": "今天北京天气怎么样?"}]},
    config={"configurable": {"thread_id": "abc123"}}
):
    print(chunk, end="\n\n")

# 第2次调用(相同 thread_id → 有记忆)
for chunk in agent.stream(
    {"messages": [{"role": "user", "content": "我刚才问你什么了?"}]},
    config={"configurable": {"thread_id": "abc123"}}
):
    print(chunk, end="\n\n")
# Agent 会回答:"你刚才问了北京今天的天气"

# 第3次调用(不同 thread_id → 无记忆)
for chunk in agent.stream(
    {"messages": [{"role": "user", "content": "我刚才问你什么了?"}]},
    config={"configurable": {"thread_id": "xyz789"}}
):
    print(chunk, end="\n\n")
# Agent 会回答:"这是我们第一次对话,你还没有问过任何问题"

4.5 记忆的代价

随着对话轮次增加,保存的消息列表越来越长:

  • 第 1 轮:2 条消息

  • 第 10 轮:20 条消息

  • 第 100 轮:200+ 条消息

每次调用都要把所有历史消息发给 LLM,导致 Token 消耗剧增 ,甚至可能超出上下文窗口限制 。解决方案就是下一节的中间件


五、中间件:在关键节点"拦截"处理

5.1 中间件概念

中间件是一种插入 Agent 执行流程中的"拦截器",可以在以下位置介入:

介入位置 时机 典型用途
before_model 消息发给 LLM 之前 压缩历史、注入额外上下文
after_model LLM 返回结果之后 记录日志、过滤敏感内容
wrap_tool 工具执行前后 人工审核、权限控制

使用方式:创建中间件实例,通过 middleware 参数传入 create_agent

python

复制代码
agent = create_agent(
    model=llm,
    tools=tools,
    middleware=[middleware_a, middleware_b],  # 列表,按顺序执行
)

5.2 消息压缩中间件(SummarizationMiddleware)

解决长对话中消息无限增长的问题。当消息量超过阈值时,自动用 LLM 将旧消息压缩成一段摘要。

python

复制代码
from langchain.agents.middleware import SummarizationMiddleware
from langchain_openai import ChatOpenAI

summary_middleware = SummarizationMiddleware(
    model=ChatOpenAI(model="gpt-4o-mini"),
    trigger=("messages", 100),  # 消息数量达到100条时触发压缩
)

压缩效果示意:

  • 压缩前 (100 条消息):[用户:你好,AI:你好,用户:天气?,AI:晴天,...,用户:最新问题]

  • 压缩后 (2 条消息):[系统:以下是摘要:用户询问了天气、订单状态...,用户:最新问题]

trigger 支持三种策略:

触发策略 写法 含义
按消息数量 ("messages", 100) 消息数达 100 条时压缩
按 Token 比例 ("fraction", 0.5) Token 数达上下文窗口 50% 时压缩
按 Token 绝对值 ("tokens", 3000) Token 数达 3000 时压缩

5.3 人工审核中间件(HumanInTheLoopMiddleware)

某些高风险操作(转账、删除数据、发送邮件)即使 LLM 决定执行,也需人工确认后再真正执行。

python

复制代码
from langchain.agents.middleware import HumanInTheLoopMiddleware

hitl_middleware = HumanInTheLoopMiddleware(
    interrupt_on={
        "transfer_money": True,   # 转账 → 需要审核
        "delete_record": True,    # 删除 → 需要审核
        "get_weather": False,     # 查天气 → 不需要审核
    }
)

执行流程

  1. Agent 决定调用 transfer_money

  2. 中间件拦截,暂停执行,返回"等待审核"状态;

  3. 人工通过界面查看并做出"批准"或"拒绝"决定;

  4. 将决定作为 Command(resume=...) 传回,Agent 继续执行或放弃。

完整示例代码(见文档 5.3 节),实际开发中需结合前端交互来实现审核界面。

5.4 中间件叠加使用

多个中间件可以同时使用,按列表顺序依次执行。例如:

python

复制代码
agent = create_agent(
    model=llm,
    tools=tools,
    middleware=[summary_middleware, hitl_middleware],
)

六、Agent 最佳实践

6.1 工具设计五原则

原则 为什么重要 正面示例 反面示例
单一职责 LLM 更容易理解功能明确的工具 get_weatherget_forecast 各做一件事 handle_all 一个工具做太多事
描述清晰 LLM 完全依赖描述决定何时用、怎么用 "获取指定城市今天的实时天气" "查天气"
参数具体 减少 LLM 猜测参数格式的可能 date: str = Field(description="格式YYYY-MM-DD") date: str(无格式说明)
错误友好 LLM 可根据错误信息调整策略 返回"城市'北精'无法识别,是否指'北京'?" 抛出 KeyError: '北精'
幂等安全 避免重试时产生副作用 get_user(id=123) 多次调用结果相同 create_order() 多次调用会创建多个订单

经验法则:如果一个工具的 docstring 超过 3 句话才能说清功能,说明它承担了太多职责,应该拆分。

6.2 系统提示词优化

好的系统提示词应包含:角色定位、工作流程、约束条件、输出格式。

好示例

text

复制代码
你是一个专业的数据分析助手。

工作流程:
1. 理解用户分析需求
2. 使用 search 工具获取数据
3. 使用 calculate 工具进行计算
4. 用简洁语言呈现结果

注意事项:
- 计算结果保留2位小数
- 如果数据不足,主动告知用户
- 不要编造数据

差示例"你是一个AI助手,帮助用户解决问题。"(太模糊)

6.3 调试三层次

层次 方法 适用场景
第一层:开启日志 logging.basicConfig(level=logging.DEBUG) 快速定位调用参数
第二层:LangSmith 设置环境变量启用追踪 可视化完整执行轨迹
第三层:流式输出 stream 替代 invoke 观察每一步中间过程

经验:大多数 Agent 问题根源可归为三类:

  • 工具描述不够清晰 → LLM 选错工具

  • 系统提示词引导不足 → 执行顺序混乱

  • 工具返回值格式不规范 → LLM 无法解读结果

6.4 性能优化

问题 优化方法 效果
响应慢 只加载当前任务必需的工具 减少 LLM 决策时间
Token 消耗高 使用 SummarizationMiddleware 压缩历史消息
工具调用次数多 优化工具返回值,一次返回完整信息 减少循环轮次
吞吐量不足 使用 ainvoke 异步调用 支持并发处理

七、课程总结与常见问题

7.1 知识回顾

章节 核心问题 学到了什么
Agent 介绍 Agent 是什么?和 Chain 的区别? Agent = LLM + 自主决策 + 工具调用
工具定义 怎么让 Agent"做事"? @tool 定义,create_agent 组装
MCP 接入 怎么接入外部工具? MCP 协议统一接入,对 LLM 透明
记忆管理 怎么让 Agent 记住对话? Checkpointer + thread_id 实现多会话记忆
中间件 怎么压缩历史、人工审核? SummarizationMiddleware、HumanInTheLoopMiddleware

7.2 常见问题 Q&A

Q1:Chain 和 Agent 如何选择?

任务流程确定时用 Chain(简单可靠),任务需要动态判断时用 Agent(灵活但复杂)。两者可以结合------Agent 内部的某些子任务可以用 Chain 实现。

Q2:RAG 和 Agent 可以结合吗?

可以,而且非常常见。典型做法是把 RAG 检索封装成一个工具,Agent 在需要时自主调用它。例如,将向量数据库检索包装为 query_knowledge_base 工具,Agent 就可以在回答专业问题时自动检索相关知识。

Q3:如何优化 Agent 的响应速度?

四个方向:

  • 只加载必需的工具(减少 LLM 决策开销)

  • SummarizationMiddleware 压缩长对话(减少 Token)

  • 使用异步调用 ainvoke(支持并发)

  • 优化工具返回值格式(减少循环轮次)

Q4:什么时候需要 LangGraph?

当你需要多个 Agent 协作、复杂的条件分支和循环逻辑,或比 create_agent 更精细的流程控制时,就需要 LangGraph。它是 Agent 的进阶编排工具。

7.3 下一步建议

  • 动手实践:选择一个实际场景(如个人知识库问答、自动化数据分析),用本课知识构建一个 Agent。

  • 深入 LangGraph:学习状态图、条件边、多 Agent 协作,构建更复杂的工作流。

  • 关注 AI 生态:MCP 社区正在快速发展,关注社区动态可以快速扩展 Agent 的能力。

相关推荐
小林ixn1 小时前
从零实现AI流式对话:一篇搞懂SSE、ReadableStream与JSON截断处理
人工智能·agent·交互设计
Fnetlink11 小时前
Fnet 云网安 260717
网络·安全·网络安全
不才不才不不才1 小时前
Spring AI 实战(7):向量库怎么选?PgVector/Redis/Milvus 横向对比
java·人工智能·spring·ai
达达尼昂1 小时前
在 Claude Cowork 中用好 Claude Fable 5
android·人工智能·后端
qiuyi101 小时前
CRM产品对比测评:励拓云CRM vs 励销云CRM,名字相近但定位迥异
人工智能·eclipse·制造·tornado
tntxia1 小时前
PyTorch与其他框架区别分析
人工智能
方向研究2 小时前
金融游戏的训练价值
人工智能·游戏·金融