LangChain中的深度智能体:规划、子智能体与银行网关
深度智能体如何将规划功能、文件系统及子智能体整合到工具循环中,它与ReAct有何不同,以及如何通过网关在领域智能体之间分配工具。
来源: Beyond Simple Agents: Engineering DeepAgents for Enterprise with LangChain
许多团队能够让智能体在笔记本电脑上运行。但能使其在实际任务中持续有效运行的团队则少得多,而正是这一差距让深度智能体有了用武之地。这并非一个流行术语,而是对现实限制的回应:当任务变得冗长、复杂或多步骤时,单纯的工具调用循环就会失效。
简单循环的局限
最基础的智能体就是在循环中运行并调用工具的LLM。这类智能体通常较为浅层,无法规划长期任务,也无法完成复杂的多步骤工作。它处理单个问题尚可,但一遇到规模更大的任务就会陷入困境。
Deep Research、Manus和Claude Code这类产品之所以能够克服这一难题,得益于四个要素:规划工具、子智能体、文件系统以及详细的提示词。deepagents项目源于试图理解Claude Code具备如此强通用性的原因,并进一步拓展这一能力。LangChain将这类智能体称为“深度智能体”,因为它们能够规划复杂任务并在较长时间内逐步完成。实际上,这就是LangChain实现类似Claude Code的自主智能体的途径,这类智能体能够处理真正的多步骤任务,而不仅仅是单一的查询问题。
deepagents 是一个独立的库,由 LangChain 的核心智能体构建模块开发而成,并在 LangGraph 运行时上运行,该运行时具备稳定的执行能力、流式处理功能以及人工干预支持。用一个恰当的比喻来说,它就像一种智能体工具箱:拥有与其他框架相同的工具调用机制,同时还内置了额外的工具与功能。
四个核心要素
规划功能。 内置的 write_todos 工具使智能体能够将大型任务拆分为多个独立步骤,跟踪进度,并在获得新信息时调整计划。
用于上下文管理的文件系统。诸如 ls、read_file、write_file 和 edit_file 这样的工具能让智能体将庞大的上下文存储到内存或磁盘中,从而避免上下文窗口溢出,并能处理任意大小的工具输出结果。当对话内容变得过长时,旧消息会自动被汇总。
子智能体。内置的任务工具可以生成拥有独立上下文的专用子智能体,这样主智能体就不会被冗余信息占据,而辅助智能体则可以专注处理某项子任务。
长格式系统提示词。从 Claude Code 重新生成的提示词内容较长,其中包含大量关于工具使用的说明以及针对特定情境的少样本示例。没有这些提示词,这些智能体的能力将会大打折扣。提示词的作用依然至关重要。
它在 LangChain 架构中的位置
共有三层架构,每一层都建立在下一层之上。LangGraph是智能体运行时环境,适用于整合不同的工作流与智能体。LangChain则是智能体框架,便于使用核心循环机制,并从零开始编写提示词与工具。Deep Agents则是一种集成框架,专为需要内置规划功能、文件系统及类似附加功能的自主运行型长时智能体设计。该框架建立在LangChain的智能体抽象层之上,而LangChain本身又基于LangGraph。
银行业示例
设想一家银行提供了五种领域专用工具:根据邮政编码查找自动取款机、列出某地区的投资顾问名单、获取该银行投资组合中的公司信息、提供退休计划详情,以及查找各地的分行位置。这些工具各自都能出色地完成单一的特定任务。但当用户同时提出涉及多项功能的请求时,问题就出现了:
请您列出XYZ银行的自动取款机网点及其投资组合中的公司名称,同时告知其在芝加哥的投资经理所在位置。
要回答这个问题,就需要在同一个请求中识别多种意图,选择并协调合适的工具,在不同的数据领域之间保持上下文一致性,最终将所有信息整合成一条连贯的回复。那些为单步骤或线性流程设计的架构往往难以应对此类任务,而这正是深度智能体存在的意义。
处理流程如下:
- 规划。 主协调器会读取这个复杂问题,然后使用
write_todos函数将其分解为三项任务:获取自动取款机网点信息、获取投资组合中的公司列表,以及查找芝加哥的投资经理位置。
get_atm_centers(zipcode=...)、get_trusted_portfolio_companies()以及get_investment_officers(city="Chicago")。一个基本的工具调用代理会在同一个上下文中依次处理这三种任务。它可能会忘记之前的结果,搞不清自己正在回答哪个问题,或者达到上下文限制。而深度代理则会将各项任务分开处理,并通过待办列表跟踪进展,因此即便某个调用失败或仅返回部分数据,协调器也能掌握当前状况。
其结构相当简单:
from deepagents import create_deep_agent
agent = create_deep_agent(
tools=[
get_atm_centers, # your ATM tool
get_investment_officers, # your officers tool
get_portfolio_companies, # your portfolio tool
get_retirement_plans, # your retirement tool
get_bank_locations, # your locations tool
],
system_prompt="You are XYZ Bank's AI assistant. Always answer completely and cite sources."
)
result = agent.invoke({"messages": [{"role": "user", "content": "Can you give me a list of ATM centers for XYZ bank and also list the name of companies XYZ has in its investment portfolio? Also can you give me the location of XYZ investment agents in Chicago?"}]})
代理负责决定调用哪些工具、按何种顺序调用以及如何整合返回的结果。你无需自行编写路由逻辑,这正是该框架的真正价值所在。
当工具缺失时针对深度代理采用ReAct机制
以同样的请求为例,再添加一个没有对应工具的第四个问题:适合学生的信用卡选项列表。以下是每种设计对应的处理方式。
深度智能体会在采取行动前检查其工具注册表并生成待办列表,因此一开始就能发现没有可用于处理信用卡的相关工具。它会为三项需要完成的任务启动子智能体,这些子智能体并行运行并正常完成工作,随后将该信用卡相关项标记为“无可用工具”,并返回一个综合答案。该答案完整涵盖了三个问题,同时明确告知用户目前没有配置信用卡处理工具,可能需要添加。整个流程不会出错,部分成功的结果也会被清晰地汇报出来。
ReAct即“推理加行动”的缩写,它遵循严格的“思考、行动、观察”循环,一步一个步骤地推进,不会提前制定计划。这导致了三个问题:
- 它的处理是顺序进行的,先处理自动取款机相关事务,再处理投资组合相关事务,最后处理工作人员相关事务。信用卡相关问题要等到所有其他任务都完成后才会出现。
ReAct适用于那些任务简短、定义明确且所有工具都已知存在的场景。而深度代理则适用于更为复杂的现实世界,其中问题往往是复合型的,工具可能缺失,但人们仍希望利用现有资源尽可能地创造价值。
设计问题:是否需要一个拥有所有工具的代理?
这是否意味着我们应该编写大量工具并通过一个代理来统一调用它们?并非如此。让一个代理承载50种甚至更多工具是一种反模式。更好的架构是采用代理的层级结构。想象这样一个代理,其配置中列出了数百种工具,从网络搜索和电子邮件到航班预订以及火箭发射,同时还带有“自行解决”的指令。它有时或许能起作用,偶尔会通过预订航班、办理税务和订购披萨来回应货物延迟的投诉,但没人希望如此。
领域所有权
基本原则是,每个领域代理负责管理一小组相互关联的工具,理想情况下为3到5种,且都来自同一业务领域。网关代理从不直接操作这些工具,它仅负责解析用户意图,将请求路由到相应的领域代理,然后再整合各代理的回复结果。这与微服务中采用的职责分离理念如出一辙。
在银行业务中,合理的划分方式如下:
- 分支机构和自动取款机相关模块,具备
get_atm_centers和get_bank_locations功能,用于处理实体网点信息:邮政编码、城市、营业时间等。 - 投资相关模块,具备
get_portfolio、get_officers和get_retirement_plans功能,负责资金管理,涵盖金融法规、从业人员资质以及退休计划类型等信息。 - 产品相关模块,一旦有信用卡功能则具备
get_credit_cards功能,同时还涵盖贷款和储蓄账户功能,用于管理产品目录。当某项功能缺失时,仅该模块会报告缺失情况,其他模块则继续正常运行。
代码结构也与此划分方式一致:
from deepagents import create_deep_agent
# Each domain agent is self-contained
branch_agent = create_deep_agent(
tools=[get_atm_centers, get_bank_locations],
system_prompt="You are the Branch & ATM specialist for XYZ Bank..."
)
investments_agent = create_deep_agent(
tools=[get_portfolio, get_officers, get_retirement_plans],
system_prompt="You are the Investments specialist for XYZ Bank..."
)
products_agent = create_deep_agent(
tools=[get_credit_cards], # sparse now, grows over time
system_prompt="You are the Products specialist for XYZ Bank..."
)
# Gateway knows nothing about tools — only about agents
gateway = create_deep_agent(
tools=[
branch_agent.as_tool(name="branch_atm_agent"),
investments_agent.as_tool(name="investments_agent"),
products_agent.as_tool(name="products_agent"),
],
system_prompt="""You are the XYZ Bank assistant gateway.
Parse the user's intent, delegate to the right domain agent(s),
and synthesize a unified response. Never answer directly — always route."""
)
为何能实现扩展
随着银行功能不断增加,网关及其他领域代理保持不变。只需将工具添加到相应的领域代理中,或创建新的工具如贷款相关功能或欺诈检测功能,并将其注册到网关即可。所有变更都局限于某个特定领域。
可以将其比作工具的“森林”。拥有30种工具的代理无法区分不同领域,因此在回答关于自动取款机开放时间的问题时,也可能调用get_retirement_plans函数,仅仅因为这两个功能都在其处理范围内。而领域代理通过系统提示和工具集为每个代理赋予明确的身份,从而避免此类问题。明确自身职能的代理能够在所属领域内做出更准确的判断。
一个实用的测试:如果你在争论某个工具应归属于代理A还是代理B,那么要么这个工具的功能过于复杂需要拆分,要么就需要第三个领域。其边界应像判断一个问题该交给分支经理还是投资顾问那样显而易见。
未解之谜
还有两件事有待解答:一是深度代理是否在很大程度上取决于底层模型的选择,二是能否利用它们构建完整的端到端解决方案。第二个问题的答案是肯定的,接下来的自然步骤就是实际构建这个银行场景。