一、项目概览
该项目演示了如何使用 deepagents构建一个生产级 AI Agent,核心能力包括:
| 工具调用 | 天气查询、数学计算、文件删除 |
| 子智能体 | 专门的深度研究助手 |
| 人工审批 | 高风险操作触发中断,等待用户确认 |
| 状态持久化 | 基于 MemorySaver 的检查点机制 |
| 自定义中间件 | 记录每一步的模型调用日志 |
| 长期记忆 | 基于文件的 Agent 记忆系统 |
技术依赖:
from deepagents import create_deep_agent, SubAgent
from langchain.agents.middleware import AgentMiddleware
from langgraph.checkpoint.memory import MemorySaver
from langgraph.types import Command, interrupt
from langchain_openai import ChatOpenAI
模型层使用阿里云 DashScope 提供的 Qwen-Plus,通过 OpenAI 兼容接口接入。
二、模型层:统一共享的 LLM 实例
QWEN_MODEL_NAME = "qwen-plus"
DASHSCOPE_API_KEY = '你的key'
DASHSCOPE_BASE_URL = "https://dashscope.aliyuncs.com/compatible-mode/v1"
shared_llm = ChatOpenAI(
model=QWEN_MODEL_NAME,
openai_api_key=DASHSCOPE_API_KEY,
openai_api_base=DASHSCOPE_BASE_URL,
temperature=0,
)
设计要点:
-
temperature=0 保证输出确定性,适合需要稳定推理的 Agent 场景。
-
所有组件(主 Agent、子 Agent、中间件)共用同一个 shared_llm 实例,避免重复初始化,节省资源。
-
使用 OpenAI 兼容协议访问 DashScope,这意味着你可以无缝替换为任何兼容的模型服务(如 DeepSeek、Moonshot 等)。
三、工具层:三类工具的完整实现
3.1 普通工具:天气查询
@tool
def get_current_weather(city: str) -> str:
"""获取指定城市的当前天气情况"""
weather_data = {
"北京": "晴朗,气温25°C,湿度40%",
"上海": "多云,气温22°C,湿度65%",
"深圳": "阵雨,气温28°C,湿度80%",
"成都": "阴天,气温20°C,湿度70%",
}
result = weather_data.get(city, f"{city}的天气数据暂不可用")
return f"🌤️ {city} 天气: {result}"
-
使用 @tool 装饰器将普通函数注册为 LangChain 工具,Agent 可直接调用。
-
函数文档字符串 """获取指定城市的当前天气情况""" 会被 Agent 用作工具描述,指导何时调用该工具。
-
这里使用静态字典模拟 API 调用,实际生产环境可替换为真实的天气 API。
3.2 安全计算工具
@tool
def calculate(expression: str) -> str:
"""执行基本的数学计算"""
try:
safe_dict = {
"__builtins__": {},
"abs": abs,
"round": round,
"min": min,
"max": max,
"sum": sum,
}
result = eval(expression, safe_dict)
return f"📊 计算结果: {result}"
except Exception as e:
return f"计算表达式 '{expression}' 时出错: {e}"
安全设计是关键:
-
覆盖 __builtins__ 为空字典,阻断 eval 执行任意代码的能力。
-
仅暴露五个无害的数学函数(abs、round、min、max、sum),防止代码注入攻击。
-
使用 try/except 包裹,避免非法表达式导致 Agent 崩溃。
3.3 高风险工具:文件删除
@tool
def delete_file(file_name: str) -> str:
"""删除指定的文件 (高风险操作,需要人工审批)"""
return f"待删除文件: {file_name}"
-
该工具本身不执行真正的删除操作,仅返回一个待确认的提示。
-
真正的删除逻辑由外部代码在审批通过后执行,形成"工具声明 → 中断 → 审批 → 执行"的安全链路。
四、子智能体:Research Assistant
research_agent = SubAgent(
name='research_assistant',
description='专门负责深度研究和信息收集的助手。擅长网络搜索、资料整理和报告生成。',
system_prompt='你是专业的研究助理。你会使用搜索工具查找最新信息…',
tools=[calculate],
model=shared_llm
)
SubAgent 的关键参数解析:
| name | 子智能体的唯一标识,主 Agent 通过 task 工具将其作为委托目标 |
| description | 描述子智能体的能力边界,帮助主 Agent 判断何时委托任务 |
| system_prompt | 子智能体的行为指令,定义其角色和专业领域 |
| tools | 子智能体可用的工具集(这里仅赋予计算能力) |
| model | 与主 Agent 共享同一个 LLM 实例 |
委托机制: 主 Agent 在遇到复杂研究任务时,会自动调用内置的 task 工具,将任务分派给 research_assistant 执行。这种"主-从"架构使得 Agent 系统可以按专业领域进行任务分流。
五、自定义中间件:LoggingMiddleware
class LogggingMiddleware(AgentMiddleware):
"""自定义中间件,记录每个步骤的输入和输出"""
def __init__(self):
self.step_count = 0
def before_model(self, state: dict, runtime: Any):
self.step_count += 1
print(f'步骤 {self.step_count}: agent正在处理请求…')
return state
def after_model(self, state: dict, runtime: Any):
print(f'步骤 {self.step_count}: agent处理完成…')
return state
中间件的生命周期:
用户输入 → before_model() → LLM 推理 → after_model() → 返回结果
↑ ↑
钩子1:记录开始 钩子2:记录结束
-
继承 AgentMiddleware 基类,重写 before_model 和 after_model 两个钩子方法。
-
before_model 在每次模型调用前触发,after_model 在模型返回后触发。
-
这里通过 step_count 计数器追踪 Agent 的执行步数,方便调试和性能分析。
-
runtime: Any 参数提供运行时的上下文信息,可以扩展为更复杂的日志记录(如耗时统计、token 用量等)。
六、长期记忆系统
MEMORY_FILE = './agent_memory.md'
if not os.path.exists(MEMORY_FILE):
with open(MEMORY_FILE, 'w', encoding='utf-8') as f:
f.write("""# 智能体长期记忆
## 用户偏好
– 用户更喜欢简洁的回答
– 用户对天气信息比较感兴趣
…
## 历史任务记录
– 2025-05-20: 帮助用户规划了一次商务旅行
…
## 学习到的知识
– 用户常用的城市:北京、上海、深圳
– 用户工作领域:技术研发
""")
工作机制:
-
create_deep_agent 的 memory=[MEMORY_FILE] 参数将文件路径注入 Agent,Agent 在每次对话时自动读取该文件作为上下文。
-
Markdown 格式便于人工编辑,也方便 Agent 解析结构化信息。
-
记忆内容按用户偏好、历史记录、学到的知识三个维度组织,形成持续进化的用户画像。
-
跨会话持久化:即使程序重启,记忆也不会丢失。
七、核心组装:create_enhanced_deep_agent()
def create_enhanced_deep_agent():
checkpointer = MemorySaver()
middleware_list = [
LogggingMiddleware()
]
agent = create_deep_agent(
model=shared_llm,
tools=[get_current_weather, calculate, delete_file],
system_prompt=DEEPAGENT_SYSTEMP_PROMPT,
subagents=[research_agent],
checkpointer=checkpointer,
interrupt_on={'delete_file': True},
memory=[MEMORY_FILE],
debug=True,
middleware=middleware_list
)
return agent, checkpointer
create_deep_agent 参数逐项解读:
| model | shared_llm | 统一的千问模型实例 |
| tools | [get_current_weather, calculate, delete_file] | 主 Agent 直接可用的工具 |
| system_prompt | DEEPAGENT_SYSTEMP_PROMPT | 定义 Agent 的角色和能力边界 |
| subagents | [research_agent] | 注册子智能体,激活 task 委托机制 |
| checkpointer | MemorySaver() | 提供状态检查点,支持中断恢复 |
| interrupt_on | {'delete_file': True} | 配置触发中断的工具名 |
| memory | [MEMORY_FILE] | 指定长期记忆文件路径 |
| debug | True | 启用调试模式,输出详细执行日志 |
| middleware | [LogggingMiddleware()] | 注册自定义中间件 |
interrupt_on 的工作原理: 当 Agent 调用 delete_file 工具时,deepagents 会在工具执行前自动暂停,生成一个中断点。开发者通过 agent.get_state(config) 检查 snapshot.next 来判断是否有待审批的中断。
八、中断与审批流程
def run_with_interrupt_demo(agent, checkpointer):
config = {
'configurable': {
"thread_id": "demo_thread_001",
"user_id": "test_user"
}
}
user_query = """请帮我完成以下三个任务:
1. 查询北京的天气
2. 计算 123 * 456 / 2 的结果
3. 删除文件 "old_report.txt"
对于最后一个任务,如果用户需要确认,请等待批准。"""
result = agent.invoke(
{"messages": [{"role": "user", "content": user_query}]},
config=config
)
snapshot = agent.get_state(config)
if snapshot.next:
user_decision = input("\\n⚠️ 是否批准删除操作?(approve/reject): ")
if user_decision.lower() == "approve":
resume_value = {"decisions": [{"type": "approve"}]}
result = agent.invoke(Command(resume=resume_value), config=config)
else:
resume_value = {"decisions": [{"type": "reject", "message": "用户拒绝了删除操作"}]}
result = agent.invoke(Command(resume=resume_value), config=config)
审批流程的状态机:
Agent 执行任务1(天气查询)→ 成功
↓
Agent 执行任务2(数学计算)→ 成功
↓
Agent 执行任务3(删除文件)→ 触发中断
↓
agent.get_state() → snapshot.next 非空
↓
用户输入 approve/reject
↓
agent.invoke(Command(resume=…)) → 恢复执行
↓
Agent 根据决策继续(执行删除 / 跳过)
关键机制:
-
thread_id 是会话的唯一标识,用于跨多次 invoke 调用维护同一会话的状态。
-
Command(resume=…) 是 LangGraph 的恢复指令,将用户的审批决定注入回 Agent 的执行流。
-
snapshot.next 返回 None 表示没有待处理的中断,Agent 已执行完毕。
九、状态恢复:跨会话持久化
def demo_state_recovery(agent, checkpointer):
config = {
"configurable": {
"thread_id": "demo_thread_001", # 使用相同的 thread_id
"user_id": "test_user"
}
}
state = agent.get_state(config)
if state.values:
print("🔄 从持久化状态恢复会话…")
print(f" 历史消息数量: {len(state.values.get('messages', []))}")
else:
print("📝 未找到历史状态,开始新会话")
-
相同的 thread_id 指向同一个持久化会话。
-
agent.get_state(config) 从 MemorySaver 中读取历史状态,包含完整的消息历史。
-
注意:这里的 MemorySaver 是内存存储,程序退出后状态会丢失。生产环境应替换为数据库支持的检查点(如 SqliteSaver 或 PostgresSaver)。
十、架构全景图
┌─────────────────────────────────────────────────────┐
│ DeepAgent │
│ │
│ ┌──────────────┐ ┌──────────────┐ │
│ │ Logging │ │ MemorySaver │ │
│ │ Middleware │ │ (Checkpoint)│ │
│ └──────┬───────┘ └──────┬───────┘ │
│ │ │ │
│ ┌──────▼─────────────────▼───────┐ │
│ │ 主 Agent │ │
│ │ (Qwen-Plus via DashScope) │ │
│ │ │ │
│ │ 工具: │ │
│ │ ├── get_current_weather │ │
│ │ ├── calculate │ │
│ │ └── delete_file (需审批) │ │
│ │ │ │
│ │ 中断机制: interrupt_on │ │
│ │ 记忆: agent_memory.md │ │
│ └──────────────┬──────────────────┘ │
│ │ task 委托 │
│ ┌──────────────▼──────────────────┐ │
│ │ 子 Agent │ │
│ │ research_assistant │ │
│ │ │ │
│ │ 工具: [calculate] │ │
│ │ 角色: 深度研究与信息收集 │ │
│ └─────────────────────────────────┘ │
└─────────────────────────────────────────────────────┘
十一、最佳实践总结
11.1 安全设计
-
工具安全:calculate 工具通过覆盖 __builtins__ 实现了受限的 eval,防止代码注入。
-
审批门禁:delete_file 工具不直接执行操作,而是通过 interrupt_on 触发人工审批,形成人机协作的安全闭环。
-
API 密钥管理:密钥硬编码仅为演示用途,生产环境应使用环境变量或密钥管理服务。
11.2 架构设计
-
单一模型实例:主 Agent 和子 Agent 共享同一个 shared_llm,避免资源浪费。
-
中间件可插拔:AgentMiddleware 的继承机制使得日志、监控、限流等功能可以独立开发和组合。
-
文件级记忆:使用 Markdown 文件存储长期记忆,兼顾可读性和可编辑性。
11.3 可扩展点
-
替换为持久化检查点:将 MemorySaver 替换为 SqliteSaver 或 PostgresSaver,支持程序重启后恢复。
-
增加子智能体:可注册多个不同专业领域的 SubAgent,如代码审查助手、数据分析助手等。
-
增强中间件:可在 before_model / after_model 中添加 token 计数、耗时统计、错误重试等逻辑。
-
丰富记忆系统:可将 agent_memory.md 替换为向量数据库,支持语义检索。
十二、完整执行流程
程序启动 → create_enhanced_deep_agent() 组装所有组件
用户提问 → agent.invoke() 开始执行
中间件 before_model() 记录步骤开始
LLM 分析任务,规划工具调用顺序
执行天气查询(无需审批)→ 返回结果
执行数学计算(无需审批)→ 返回结果
执行文件删除 → 触发中断,Agent 暂停
agent.get_state() 检测到中断点,提示用户审批
用户输入审批决定 → Command(resume=…) 恢复执行
Agent 根据审批结果继续或跳过
中间件 after_model() 记录步骤结束
返回最终结果给用户
本文基于 deepagents 示例代码拆解,涵盖了从工具定义到子智能体委托、从中间件到人工审批的完整链路。希望对正在构建 AI Agent 系统的开发者有所启发。



