学习资料:
本篇重点:
Checkpointer、Store、CompositeBackend、Memory、interrupt_on、Command(resume=...)、人工审批与状态恢复。
一、为什么 Agent 需要记忆和人工干预?
前面学习了 Deep Agents 的任务规划、SubAgent 和 Skills,分别解决了复杂任务拆解、任务委派与专业能力复用的问题。
但要让 Agent 真正用于长期、复杂的工作,还有两个问题:
1. Agent 如何记住过去?
例如,第一次对话告诉 Agent 项目的代码规范,下一次开启新对话时,希望它仍然按照相同规范工作。
这需要 Long-term Memory(长期记忆)。
2. Agent 如何安全地执行操作?
例如,Agent 自动修改代码、删除文件、发送邮件。如果完全自主执行,可能造成不可逆的结果。
这需要 Human-in-the-Loop(HITL,人在回路),在关键操作前引入人工审批。
两者共同解决的是 Agent 的状态连续性与执行可控性。
二、Memory:短期记忆与长期记忆
Deep Agents 将记忆分为两类:
| 对比 | 短期记忆 | 长期记忆 |
|---|---|---|
| 作用域 | Thread-scoped | Cross-thread |
| 核心机制 | Checkpointer | Store |
| 存储内容 | 对话历史、Todo、状态 | 用户偏好、项目知识 |
| 跨线程共享 | 默认不支持 | 支持 |
| 典型用途 | 恢复当前任务 | 积累长期知识 |
2.1 Checkpointer:短期状态管理
LangGraph 使用 Checkpointer 保存 Agent 的执行状态。
from langgraph.checkpoint.memory import MemorySaver
from deepagents import create_deep_agent
checkpointer = MemorySaver()
agent = create_deep_agent(
model=model,
checkpointer=checkpointer,
)
config = {
"configurable": {
"thread_id": "conversation-001"
}
}
同一个 thread_id 可以恢复之前保存的状态。
但如果切换新的 thread_id,不同线程之间的 State 默认相互隔离。
因此:
Checkpointer 解决的是线程内状态恢复,而不是跨线程知识共享。
此外,MemorySaver 只保存在内存中,程序重启后会丢失。生产环境通常需要持久化 Checkpointer,例如 PostgresSaver。
2.2 Store:跨线程长期记忆
长期记忆需要使用 Store,存储用户偏好、项目背景或 Agent 积累的经验。
Deep Agents 将记忆组织为文件,通过 Backend 决定文件实际保存在哪里。
其中最重要的是 CompositeBackend。
Agent 文件系统
|
v
CompositeBackend
|
+-- StateBackend
| 当前线程临时文件
|
+-- StoreBackend
跨线程共享记忆
核心配置如下:
from deepagents.backends import (
CompositeBackend,
StateBackend,
StoreBackend,
)
from langgraph.store.memory import InMemoryStore
store = InMemoryStore()
backend = CompositeBackend(
default=StateBackend(),
routes={
"/memories/": StoreBackend(
namespace=("user-001", "memories")
)
}
)
这样,普通文件保存在 State 中,而 /memories/ 路径下的文件会进入 Store。
对于 Agent 而言,都是 read_file、write_file 等文件操作,区别在于存储位置。
三、长期记忆如何加载和更新?
Deep Agents 提供 memory= 参数加载记忆文件。
agent = create_deep_agent(
model=model,
backend=backend,
store=store,
memory=[
"/memories/preferences.md",
"/memories/project.md"
],
)
这里需要区分:
-
memory=:指定启动时读取的已有文件。 -
edit_file:修改已有记忆。 -
write_file:写入文件,已有文件会被整体覆盖。
**memory=**只负责加载,不会自动创建或更新记忆文件。
因此,使用长期记忆通常需要三个步骤:
-
预先创建记忆文件并配置 Store。
-
通过
memory=将已有记忆加载到上下文。 -
通过提示词约定更新条件,再由 Agent 调用文件工具保存新信息。
3.1 namespace:记忆隔离
Store 使用 namespace 区分记忆的归属。
| 类型 | namespace 示例 | 用途 |
|---|---|---|
| Agent 级 | ("agent-001",) |
Agent 共享知识 |
| 用户级 | ("user-001",) |
个人偏好 |
| 组织级 | ("org-001",) |
团队规范 |
例如,科研 Agent 可以将个人代码习惯保存到用户级记忆,将团队实验规范保存到组织级记忆。
实际部署时,用户身份应来自可信的认证上下文,不能由 LLM 自行生成。
3.2 Memory Consolidation:记忆整合
长期运行后,记忆可能产生重复、冲突或过期内容。
常见策略包括:
-
Hot Path:对话过程中实时更新。
-
Background Consolidation:通过独立任务定期提取、去重和整理。
后台整合需要自行调度,并不是配置 Store 后就自动发生。
对于长期科研项目,可以将文献、实验进展与研究决策拆分保存,而不是全部堆积在一个文件中。
四、Human-in-the-Loop:为什么需要人工审批?
Agent 可以自主调用工具,但并不是所有操作都适合自动执行。
例如:
-
删除重要文件;
-
修改生产配置;
-
发送正式邮件;
-
执行数据库写入;
-
调用有成本或不可逆影响的外部 API。
Human-in-the-Loop 的核心思想是:
Agent 在执行敏感操作前暂停,由人类审核操作内容,再决定是否继续。
Deep Agents 通过 interrupt_on 配置工具审批。
from deepagents import create_deep_agent
from langgraph.checkpoint.memory import MemorySaver
agent = create_deep_agent(
model=model,
tools=[delete_file, send_email],
interrupt_on={
"delete_file": {
"allowed_decisions": [
"approve",
"edit",
"reject"
]
},
"send_email": {
"allowed_decisions": [
"approve",
"reject"
]
},
},
checkpointer=MemorySaver(),
)
这里的 delete_file 和 send_email 是需要提前定义的工具。
配置后,Agent 调用对应工具时会触发中断,而不是立即执行。
五、四种人工决策
HITL 支持四种决策:
| 决策 | 含义 | 应用场景 |
|---|---|---|
approve |
批准原始调用 | 确认删除文件 |
edit |
修改参数后执行 | 修改收件人 |
reject |
拒绝执行 | 取消部署 |
respond |
人工提供工具结果 | 回答 Agent 的问题 |
其中有一个重要区别:
reject 表示工具没有执行。
respond 则将人工输入作为成功的工具结果返回。
因此,对于删除、发送、部署等具有副作用的操作,应该使用 reject,而不是用 respond 表示拒绝。
六、Interrupt:中断与恢复机制
HITL 的关键不是弹出确认框,而是保存当前执行状态,并在收到人工决策后恢复工作流。
完整流程:
Agent 正常执行
|
v
准备调用敏感工具
|
v
interrupt() 触发中断
|
v
Checkpointer 保存状态
|
v
等待人工审批
|
+-- approve
+-- edit
+-- reject
+-- respond
|
v
Command(resume=...)
|
v
恢复 Agent 执行
6.1 代码示例
from langgraph.types import Command
config = {
"configurable": {
"thread_id": "task-001"
}
}
# 第一次执行
result = agent.invoke(
{
"messages": [{
"role": "user",
"content": "删除 temp.txt"
}]
},
config=config,
version="v2",
)
# 检查中断
if result.interrupts:
request = result.interrupts[0].value
print(request["action_requests"])
# 示例:假定人工已经确认批准
decisions = [{"type": "approve"}]
result = agent.invoke(
Command(
resume={"decisions": decisions}
),
config=config,
version="v2",
)
实际应用中,decisions 应来自用户的真实审批结果,不能自动填入 approve。
这个流程有三个关键要求:
1. 必须配置 Checkpointer。
否则没有可恢复的状态。
2. 恢复时必须使用相同的 thread_id。
否则无法找到对应的中断位置。
3. 多个工具同时中断时,决策必须与请求一一对应。
对于多个并行中断,可以通过中断 ID 绑定不同的恢复结果。
6.2 中断恢复不等于继续 Python 调用栈
LangGraph 恢复时会重新进入中断所在节点,而不是从原来的 Python 代码行继续运行。
因此,如果在同一个节点内先执行外部写操作,再调用 interrupt(),恢复时可能重复执行前面的操作。
这也是为什么有副作用的操作需要考虑幂等性(Idempotency)。
内置 interrupt_on 通过独立审批节点处理工具调用,可以减少这类问题。
七、按风险等级设计审批机制
并不是所有工具都需要审批,否则会降低 Agent 的执行效率。
比较合理的方案是分层管理:
| 风险等级 | 操作示例 | 建议策略 |
|---|---|---|
| 低 | 搜索、普通文件读取 | 自动执行 |
| 中 | 修改普通项目文件 | 按需审批 |
| 高 | 删除、发送、生产部署 | 强制审批 |
| 人工输入 | 询问用户偏好 | respond |
Deep Agents 还支持条件中断,通过 when 判断具体参数是否需要审批。
例如,只拦截工作区之外的文件写入操作,而允许 Agent 自动修改普通工作区文件。
此外,文件系统可以通过 FilesystemPermission 设置 mode="interrupt",对敏感路径提供额外保护。
八、自定义 Middleware 与 interrupt()
interrupt_on 主要负责工具调用审批,但有些场景并不涉及具体工具。
例如:
-
Agent 生成研究报告后等待人工审稿;
-
在进入下一阶段前确认研究方案;
-
对最终结果进行质量审核。
这时可以在自定义 Middleware 中直接调用 LangGraph 的 interrupt()。
通常更适合在 before_model、after_model 等 Node-style Hook 中设置中断。
而 wrap_model_call、wrap_tool_call 更适合模型重试、缓存、工具调用包装等逻辑,不建议作为常规人工审批边界。
九、长期记忆与 HITL 如何结合?
这两章看起来分别讨论记忆和审批,但底层都涉及状态管理。
需要注意,两者使用 Checkpointer 的目的不同:
-
长期记忆:Checkpointer 保存线程状态,Store 提供跨线程共享信息。
-
HITL:Checkpointer 保存暂停点,使人工审批后能够恢复执行。
可以构建如下工作流:
用户提出科研任务
|
v
加载长期项目记忆
|
v
Agent 制定研究计划
|
v
执行检索和分析
|
v
准备修改关键文件
|
v
人工审批
|
v
执行修改并验证结果
|
v
更新长期项目记忆
例如,在科研 Agent 中:
-
读取历史实验记录,避免重复分析。
-
自主执行数据检查和代码调试。
-
修改重要实验配置前请求人工确认。
-
完成任务后,将验证过的结果写入长期记忆。
这样既能利用历史知识提高效率,也能保留人类对重要决策的控制权。
十、学习总结
这两章让我进一步理解,Agent Harness 不只是让模型能够调用工具,还需要管理状态、权限和执行边界。
长期记忆解决"如何持续积累知识"。
通过 Store 和 Backend,将用户偏好、项目背景和研究经验从对话上下文中独立出来,实现跨线程复用。
Human-in-the-Loop 解决"如何控制自主执行"。
通过 interrupt_on、Checkpointer 和 Command(resume=...),让 Agent 在敏感操作前暂停,等待人工决策后再继续执行。
其中最值得关注的是三个设计思想:
-
状态与模型解耦:对话状态、长期记忆和暂停点由外部系统管理,不依赖模型永久记住信息。
-
持久化是可恢复执行的基础:没有 Checkpointer,就难以实现可靠的中断恢复。
-
自主性与可控性需要平衡:普通操作自动完成,高风险操作引入人工审批。
结合前面几章,Deep Agents 的整体架构逐渐完整:
-
Task Planning:任务拆解与进度管理。
-
SubAgent:任务委派和上下文隔离。
-
Skills:专业流程的封装与复用。
-
Long-term Memory:跨对话知识积累。
-
Human-in-the-Loop:人工审批与安全恢复。
这些机制共同支撑了一个能够长期运行、持续积累经验,同时保留人类控制权的 Agent 系统。
总结:Memory 让 Agent 记住过去,HITL 让人类控制关键决策,而 Checkpointer 是持续执行与可靠恢复的重要基础。
参考资料
标签: Deep Agents LangGraph Long-term Memory Human-in-the-Loop Checkpointer Agent Harness