基于DeepAgents实战:构建可扩展AI Agent系统的工程化指南
最近在尝试把一些重复性工作交给 AI Agent 自动化时我发现了一个很有意思的现象很多开发者包括我自己都曾陷入一个“工具陷阱”——我们花大量时间学习 LangChain、LangGraph 这些框架的 API 和概念但真正要落地一个能稳定运行、处理复杂逻辑的 Agent 项目时却常常卡在第一步如何把想法变成一个可运行、可调试、可扩展的代码结构。这就像你拿到了乐高积木的所有零件说明书却不知道从哪块开始拼才能搭出一座不会散架的城堡。直到我深入实践了DeepAgents这个项目它提供了一套近乎“保姆级”的工程化起点我才意识到Agent 开发的真正难点往往不在于理解某个框架的“是什么”而在于掌握从零到一构建一个健壮 Agent 系统的“怎么做”和“为什么这么做”。今天我们就以 DeepAgents 为蓝本抛开那些抽象的概念直接进入实战。我会手把手带你搭建一个 DeepAgents 项目并在这个过程中重点拆解三个核心问题Agent Skills 如何动态扩展、LangChain 与 LangGraph 在实际项目中究竟如何分工、以及如何设计一个能长期运行且状态可控的 Agent 系统。你会发现当把这些框架放到一个具体的项目上下文里时它们的价值和使用边界会清晰得多。1. 为什么从 DeepAgents 开始它解决的远不止“跑通一个 Demo”在接触 DeepAgents 之前你可能已经看过不少 LangChain 的“Hello World”示例几行代码调用一个 LLM完成一次问答。这能让你快速感受到能力但离一个真正的“Agent 项目”还差得很远。一个生产可用的 Agent 系统至少需要处理以下几个问题技能Skills的管理与加载Agent 需要调用工具如搜索、计算、读写文件。这些工具如何定义、如何被 Agent 发现和调用、如何安全地管理工作流Workflow与状态管理任务往往不是一步完成的需要多个步骤、有条件分支、有循环。如何定义这个流程每一步的状态如何传递和更新记忆Memory与上下文ContextAgent 如何记住之前的对话和操作长上下文如何有效利用而不至于让提示词Prompt爆炸项目结构与可维护性代码如何组织才能清晰地区分配置、工具定义、Agent 逻辑、工作流和主程序DeepAgents 的价值就在于它直接提供了一个回答了上述问题的、开箱即用的项目脚手架。它不是另一个框架而是基于 LangChain 和 LangGraph 的最佳实践集成。你克隆它的项目相当于直接拿到了一个“Agent 系统”的骨架你要做的不是从零砌砖而是在这个骨架上填充血肉——也就是你的具体业务逻辑。1.1 环境准备与项目初探先看到全貌我们首先把项目跑起来建立最直观的认知。获取项目通常你可以从 GitHub 找到 DeepAgents 的仓库。使用 Git 克隆到本地。git clone deepagents-repo-url cd deepagents依赖安装查看项目根目录的requirements.txt或pyproject.toml文件使用 pip 或 poetry 安装。pip install -r requirements.txt这里通常会包含langchain,langgraph,langchain-community等核心包以及项目自身的一些工具依赖。配置 API 密钥DeepAgents 需要连接大模型如 OpenAI, Anthropic 等。在项目根目录或config/目录下你会找到类似.env.example的模板文件。复制它并重命名为.env然后填入你的 API Key。OPENAI_API_KEYsk-你的密钥 # 或 ANTHROPIC_API_KEY你的密钥运行第一个示例项目一般会提供examples/目录。找一个最简单的示例运行比如python examples/basic_agent.py。这一步的目标不是理解所有代码而是确认环境无误并能看到 Agent 运行起来的输入输出日志。当你看到终端里 Agent 开始“思考”调用 LLM和“行动”调用工具时你对 Agent 的认知就从“概念”进入了“可交互的程序”阶段。接下来我们深入这个程序的内部。1.2 解剖项目结构理解“最佳实践”的布局DeepAgents 的目录结构本身就是一份重要的学习资料。一个典型的布局可能如下deepagents/ ├── agents/ # 存放不同 Agent 的定义 ├── skills/ # 核心存放所有可被 Agent 调用的技能工具 │ ├── __init__.py │ ├── web_search.py │ ├── calculator.py │ └── file_ops.py ├── workflows/ # 存放用 LangGraph 定义的工作流 ├── config/ # 配置文件和环境变量管理 ├── memory/ # 记忆存储和后端如向量数据库集成 ├── examples/ # 使用示例 ├── tests/ # 测试用例 └── main.py # 应用入口这个结构清晰地体现了关注点分离skills/管“能做什么”工具。agents/管“谁来做”赋予 LLM 调用工具的能力和身份。workflows/管“按什么顺序做”编排多个 Agent 或步骤。memory/管“记住什么”。config/管“用什么参数做”。为什么这个结构重要因为它解决了 Agent 项目初期最头疼的“代码往哪放”的问题。当你需要新增一个功能比如让 Agent 能发邮件你就很自然地知道要去skills/目录下创建一个email_sender.py。这种约束性对于保持项目长期可维护性至关重要。2. 核心实践一动态构建与管理 Agent SkillsSkills 是 Agent 的“手和脚”。DeepAgents 通常采用一种基于装饰器或类注册的机制来动态加载 Skills这比在代码里硬编码一长串工具列表要优雅和灵活得多。2.1 如何定义一个 Skill打开skills/目录下的任意一个文件比如calculator.py你会看到类似这样的结构from langchain.tools import tool from pydantic import Field, BaseModel # 定义输入参数的模型可选但推荐用于类型验证和说明 class CalculatorInput(BaseModel): a: float Field(description”第一个数字”) b: float Field(description”第二个数字”) operator: str Field(description”运算符支持 , -, *, /”) # 使用 tool 装饰器定义技能 tool(args_schemaCalculatorInput, return_directFalse) def calculator(a: float, b: float, operator: str) - str: “””执行简单的四则运算。当需要计算时使用此工具。””” try: if operator ‘’: result a b elif operator ‘-‘: result a – b elif operator ‘*’: result a * b elif operator ‘/’: if b 0: return “错误除数不能为零” result a / b else: return f”错误不支持的运算符 ‘{operator}’请使用 , -, *, /” return f”计算结果{a} {operator} {b} {result}” except Exception as e: return f”计算过程中发生错误{str(e)}”关键点解析tool装饰器这是 LangChain 提供的它将一个普通 Python 函数“包装”成一个 LangChain Tool 对象。args_schema参数指定了输入模型这能让 LLM 更清晰地理解需要提供哪些参数。文档字符串Docstring“””执行简单的四则运算…”””这部分极其重要LLM 主要靠这个描述来决定在什么情况下调用这个工具。描述要准确、简洁。错误处理在 Skill 内部进行健壮的错误处理并返回友好信息能防止整个 Agent 因为一个工具调用失败而崩溃。2.2 Skills 如何被动态加载DeepAgents 通常会在skills/__init__.py或一个专门的注册中心里自动发现并注册所有tool装饰的函数。其核心逻辑可能是遍历skills模块收集所有 Tool 对象。# skills/__init__.py 可能简化后的逻辑 import importlib import pkgutil from langchain.tools import BaseTool def get_all_skills(): all_tools [] # 遍历当前包下的所有模块 for _, module_name, _ in pkgutil.iter_modules(__path__): full_module_name f”{__name__}.{module_name}” module importlib.import_module(full_module_name) # 遍历模块中的属性找到 BaseTool 的实例或 tool 装饰的函数 for attr_name in dir(module): attr getattr(module, attr_name) if isinstance(attr, BaseTool): all_tools.append(attr) # 有时 tool 装饰后可能还不是 BaseTool需要进一步处理 elif callable(attr) and hasattr(attr, ‘_tool’): # 检查是否有 _tool 属性 all_tools.append(attr._tool) return all_tools # 导出一个工具列表 ALL_SKILLS get_all_skills()这样在创建 Agent 时你就可以直接from skills import ALL_SKILLS然后将这个列表传给 Agent。新增一个 Skill 只需要在skills/目录下新建一个.py文件并正确定义它就会被自动纳入系统。这种设计实现了“开闭原则”扩展功能时无需修改核心 Agent 的创建代码。2.3 与 MCPModel Context Protocol的思考在热搜词里看到了 “skills和mcp区别”。这里简单厘清一下Skills (在 DeepAgents/LangChain 语境下)指的是 Agent 可以调用的、具体的、封装好的函数工具如计算器、搜索、读写文件。它们是实现层面、项目内的。MCP (Model Context Protocol)是 Anthropic 提出的一种协议旨在标准化服务器提供工具、数据源与客户端如 Claude 桌面应用之间的通信方式。它更偏向于架构层面解决的是如何让 LLM 动态发现和调用外部服务的问题。你可以把 DeepAgents 的skills/目录看作一个本地化、项目内的“工具服务器”而 MCP 设想的是一个标准化、可远程访问的工具服务生态。对于单个项目DeepAgents 的方式足够且更简单直接如果你需要构建一个让多个客户端如不同的 AI 应用都能调用的中心化工具服务那么研究 MCP 会更有价值。3. 核心实践二用 LangGraph 构建可控的工作流与状态机当你的 Agent 需要完成多步骤任务并且步骤之间有依赖、循环或分支时就该 LangGraph 上场了。DeepAgents 的workflows/目录就是 LangGraph 的用武之地。3.1 LangChain 与 LangGraph 的分工这是另一个常见困惑点从热搜词 “langchain和langgraph的区别” 也能看出。LangChain核心价值在于“连接”和“组装”。它提供了LLM、Prompt、Memory、Chains链和Tools等基础组件以及将它们快速组合成一条线性处理管线的能力。Agent在 LangChain 中是一种特殊的 Chain它引入了“根据 LLM 输出决定下一步调用哪个 Tool”的循环。LangGraph核心价值在于“编排”和“状态管理”。它允许你定义任意复杂的有向图工作流节点可以是调用 LLM、执行工具、运行条件判断等边定义了执行流向。它显式地管理一个全局的State所有节点都读写这个状态非常适合多 Agent 协作、复杂决策流程。简单比喻LangChain 像给你提供了乐高积木组件和拼装直线轨道的方法Chain。LangGraph 则给了你一张设计图和白板让你可以拼出带岔路、环路和多个站台的复杂铁路系统Graph并且能清楚地追踪一辆火车状态在整个系统中的位置。在 DeepAgents 中agents/下的单个 Agent 可能用 LangChain 的create_react_agent等方式构建。而当需要协调多个这样的 Agent或者为一个 Agent 设计包含“规划-执行-检查-重试”的复杂循环时就会在workflows/下使用 LangGraph 来定义。3.2 设计一个简单的工作流以“研究报告生成”为例假设我们要构建一个 Workflow先让一个“规划师” Agent 分解任务再让一个“研究员” Agent 搜集信息最后让一个“写作者” Agent 汇总成报告。在workflows/research_report.py中from typing import TypedDict, Annotated import operator from langgraph.graph import StateGraph, END from langgraph.graph.message import add_messages from langchain_core.messages import HumanMessage # 1. 定义状态State class ReportState(TypedDict): # 消息历史用于记录对话 messages: Annotated[list, add_messages] # 规划步骤产生的任务列表 task_list: list[str] # 研究员收集到的信息 research_findings: list[str] # 最终报告 final_report: str # 2. 定义节点函数 def planner_node(state: ReportState): “””规划节点分析用户请求拆解成具体任务列表。””” # 这里会调用规划师 Agent planner_agent get_planner_agent() # 假设这个函数返回一个配置好的 Agent task_list planner_agent.invoke({“input”: state[“messages”][-1].content}) # 更新状态 return {“task_list”: task_list} def researcher_node(state: ReportState): “””研究节点针对每个任务调用搜索工具收集信息。””” research_findings [] for task in state[“task_list”]: # 这里会调用研究员 Agent它拥有搜索技能 researcher_agent get_researcher_agent() finding researcher_agent.invoke({“task”: task}) research_findings.append(finding) return {“research_findings”: research_findings} def writer_node(state: ReportState): “””写作节点基于收集的信息撰写最终报告。””” writer_agent get_writer_agent() report writer_agent.invoke({ “task_list”: state[“task_list”], “findings”: state[“research_findings”] }) return {“final_report”: report} # 3. 构建图 workflow StateGraph(ReportState) # 添加节点 workflow.add_node(“planner”, planner_node) workflow.add_node(“researcher”, researcher_node) workflow.add_node(“writer”, writer_node) # 设置边执行顺序 workflow.set_entry_point(“planner”) workflow.add_edge(“planner”, “researcher”) workflow.add_edge(“researcher”, “writer”) workflow.add_edge(“writer”, END) # 编译图 app workflow.compile()这个例子展示了 LangGraph 的核心模式定义状态 - 定义操作节点的函数 - 用边连接节点形成流程图。State是整个工作流的共享内存每个节点只关心自己读写哪部分状态。3.3 处理循环与条件分支实现“检查与重试”更强大的功能是循环。比如我们可以在“写作”节点后加一个“评审”节点如果报告不合格就跳回“研究员”节点重新搜集信息。def reviewer_node(state: ReportState): “””评审节点评估报告质量。””” # 调用一个评审 Agent 或简单的规则判断 is_qualified review_report(state[“final_report”]) # 关键返回一个 “next” 字段指示下一步去哪个节点 if is_qualified: return {“next”: “end”} # 结束 else: # 可以更新任务列表指明需要补充研究什么 new_tasks generate_followup_tasks(state[“final_report”]) return {“next”: “researcher”, “task_list”: new_tasks} # 在构建图时需要处理条件边 workflow.add_node(“reviewer”, reviewer_node) workflow.add_edge(“writer”, “reviewer”) # 条件边根据 reviewer_node 返回状态中的 “next” 字段决定流向 workflow.add_conditional_edges( “reviewer”, # 这是一个路由函数根据状态决定下一个节点 lambda state: state.get(“next”, “end”), { “researcher”: “researcher”, “end”: END } )这就是 LangGraph 的威力它让你能以代码清晰定义出那些用自然语言描述都显得复杂的 Agent 协作流程。State的设计 (State如何设计是热搜词) 是整个工作流设计的核心你需要仔细规划哪些信息需要跨节点共享。4. 从搭建到生产工程化考量与避坑指南跟着 DeepAgents 的示例跑通流程只是第一步。要将其用于更严肃的场景以下几个工程化考量点必须提前规划。4.1 记忆Memory与上下文管理LangChain 提供了多种记忆后端如ConversationBufferMemory、ConversationSummaryMemory等。在 DeepAgents 项目中通常会在创建 Agent 时注入 memory。关键决策点记忆放在哪是每个 Agent 独立记忆还是整个 Workflow 共享一个记忆对于多步工作流通常将记忆作为State的一部分如我们之前定义的messages字段在整个流程中传递。长上下文问题如果对话或收集的信息很长直接塞进 Prompt 会耗尽 Token 且效果下降。考虑使用ConversationSummaryMemory定期总结历史。使用向量存储RAG将历史信息存入向量库在需要时进行检索召回。这通常涉及memory/目录下的向量数据库集成代码。记忆持久化当 Agent 重启后如何恢复之前的对话状态这需要将记忆对象序列化存储到数据库或文件中。4.2 错误处理与稳定性Agent 在自动调用外部工具时失败是常态。工具层容错如前所述在每个 Skill 函数内部做好异常捕获返回结构化错误信息。Agent 层重试为 Agent 配置重试逻辑。LangChain 的某些 Agent 执行器支持max_iterations和handle_parsing_errors等参数。工作流层监控在 LangGraph 的工作流中可以在关键节点后添加“检查节点”验证上一步的输出是否合理不合理则触发重试或人工干预流程。日志记录这是最重要的。确保 Agent 的每一步思考LLM 调用、每一次工具调用、每一次状态变更都有清晰的日志。这不仅是调试的需要也是理解 Agent 行为、进行后续优化的基础。DeepAgents 项目可能已经集成了日志但你需要确认其详细程度是否满足你的需求。4.3 性能与成本优化并发与异步对于批量处理任务研究 LangChain 或 LangGraph 的异步接口。避免在循环中同步调用这会导致极慢的速度。缓存对相同的 Prompt 或工具调用结果进行缓存可以显著降低 LLM API 调用成本和延迟。考虑使用LangChain的Cache组件。模型选择不是所有步骤都需要最强大、最贵的模型。在 Workflow 中可以为“规划”、“创意”类节点使用大模型如 GPT-4为“格式化”、“简单分类”类节点使用小模型如 GPT-3.5-Turbo或本地模型以平衡效果与成本。4.4 测试与评估如何知道你的 Agent 系统工作得好不好单元测试为每个独立的 Skill 编写单元测试确保其功能正确。集成测试针对完整的 Workflow构建一批有标准答案的测试用例定期运行监控其输出质量和稳定性。评估指标根据任务类型定义评估指标如任务完成率、步骤数、人工审核通过率等。这需要设计评估流程可能结合人工和自动化。5. 总结DeepAgents 带来的思维转变通过 DeepAgents 的实战我们得到的远不止一个可运行的项目模板。它更重要的价值是提供了一种构建 AI Agent 应用的工程化思维技能优先将能力封装成独立的、可测试的、文档清晰的 Skills这是构建复杂 Agent 的基石。状态驱动用 LangGraph 的State来显式管理整个工作流的数据流让复杂的多步逻辑变得可视化和可调试。关注分离遵循清晰的项目结构区分配置、技能、Agent、工作流和记忆让代码随着功能增长而保持可维护性。迭代思维不要指望一次性设计出完美的 Agent。从一个小而准的 Workflow 开始通过日志观察其行为不断调整 Prompt、优化 Skills、重构 State逐步迭代出鲁棒的解决方案。回到开头的问题LangChain 和 LangGraph 本身并不难学难的是如何将它们有机地组合起来解决真实世界的问题。DeepAgents 这个“保姆级”项目恰恰降低了这个“从知识到实践”的鸿沟。它告诉你一个合格的 Agent 系统应该长什么样以及如何从那里开始构建属于你自己的、智能的自动化工作流。下一步我建议你不要停留在运行示例。尝试基于这个骨架改造一个你日常工作中最重复、最枯燥的任务。比如一个自动整理会议纪要并生成待办事项的 Agent或者一个监控日志并自动生成日报的 Agent。在真实的需求驱动下你会对 Skills 的设计、State 的规划和错误的处理有更深的理解。这才是学习 Agent 技术最有效的路径。