前言
当只有一个主agent的时候,如果涉及多次工具调用,且工具入参、结果都很长的情况,就会占用大量的上下文,导致上下文爆炸。而主agent可能只需关注最终结果概要,那么我们有必要使用子agent。
为什么需要子agent
上下文膨胀
假设主agent需要完成一个研究报告,其中一个子任务是“搜索langgraph的技术文档”,这个子任务可能涉及:
- 5次网络搜索调用
- 每次返回3000+token搜索结果
- 多次write_file保存中间结果
- 多次read_file回顾和整理
这些中间过程产生了大量的工具调用记录,全部堆在主agent的上下文。虽然自动卸载机制可以缓解一部分,但主agent根本不需要知道这些细节——它只需要最终的研究概要。
上下文隔离
这个是子agent的核心设计动机:上下文隔离
工作方式很简单:
- 主agent通过task工具创建一个子agent
- 子agent在独立的上下文执行任务(中间调用记录不进入主agent对话,共用backend的时候文件仍可共享)
- 子agent完成后,只把最终结果给到主agent
- 主agent的上下文保持干净
可以打个比方:主agent就是项目经理,子agent是某个模块负责人,项目经理不会去管所有的细节,只需要知道某个模块关键的结论要点即可。
什么时候用子agent
- 需要多次搜索和整理的研究任务
- 需要特殊指令和专业工具的任务——子agent可以有自己的工具和系统提示词
- 需要不同模型能力的任务——子agent可以使用不同的模型
- 需要高层协调的复杂任务——主agent专注协调,子agent专注执行
不需要子agent的情况:
单步简单查询——反而导致开销大于收益
需要主agent看到子agent完整过程——主agent只能收到最终结果
定义子agent:字典方式
- name,必填,一标识
- description,必填,描述子agent能力,通过该属性决策使用哪个子agent
- system_prompt,必填,不继承,需子agent自己定义
- tools,可选,默认继承,指定后替换
- model,可选,默认继承,
- middleware,可选,不继承,
- interrupt_on,可选,默认继承
- skills,可选,不继承
- response_format,可选,不继承
- permissions,可选,默认继承
general-purpose子agent:默认的万能助手
即使不定义任何子agent,deepagents也会默认带一个general-purpose子agent,它很特殊,会继承主agent的system_prompt、tools、model、skills。主agent显示启用todo时,也会继承同一个TodoMiddleware
它的主要作用就是实现上下文隔离,它和主agent有相同的能力,但是单独的上下文工作,这样主agent不需要接受子任务中10次搜索带来的上下文膨胀,只需要收到一份最终的摘要。
禁用general-purpose子agent
如果你不想让主agent拥有task工具,可以禁用子agent机制
fromdeepagentsimportcreate_deep_agentfromdeepagents.profilesimportGeneralPurposeSubagentProfile,HarnessProfile,register_harness_profile# 两步缺一不可# 为指定模型注册 Harness Profile。register_harness_profile(key="openai:zai-org/GLM-5.2",profile=HarnessProfile(general_purpose_subagent=GeneralPurposeSubagentProfile(enabled=False)),)agent=create_deep_agent(model=model,subagents=[],# 不传任何同步子 Agent)如上是禁用了general-purpose子agent,还是子agent机制都禁用了??本意其实是前者
覆盖general-purpose子agent
agent=create_deep_agent(model=model,# 主 Agent 复用应用的默认模型tools=[internet_search],subagents=[{"name":"general-purpose",# 覆盖默认"description":"通用助手,处理各种委派任务","system_prompt":"你是一个通用助手。","tools":[internet_search],"model":ChatOpenAI(# 子 Agent 用更强的模型model="zai-org/GLM-5.2",api_key=os.environ["SILICONFLOW_API_KEY"],base_url="https://api.siliconflow.cn/v1",),},],)通过同名子agent进行覆盖
CompliedSubAgent:继承langgraph工作流
对于更复杂的场景,可以用一个预构建的langgraph图作为子agent,这适用于需要多步骤、有分支逻辑的工作流
fromdeepagentsimportcreate_deep_agent,CompiledSubAgentfromlangchain.agentsimportcreate_agent# 用 LangChain 创建一个自定义 Agent 图custom_graph=create_agent(model=model,tools=[statistical_analysis,generate_chart],system_prompt="你是数据分析专家,擅长统计分析和可视化。",)# 包装为 CompiledSubAgentdata_subagent=CompiledSubAgent(name="data-analyzer",description="执行复杂的数据分析任务,包括统计分析和图表生成",runnable=custom_graph,# 传入编译好的 LangGraph 图)agent=create_deep_agent(model=model,subagents=[data_subagent],)字典、CompliedSubAgent怎么选
- 大多数情况,使用字典方式,简单直观、配置灵活
- 子agent需要复杂多步骤的工作流,使用CompliedSubAgent
- 子agent已有现成的langgraph图,直接复用CompliedSubAgent
多agent协作
importosfromlangchain_openaiimportChatOpenAIfromdeepagentsimportcreate_deep_agentfromlangchain.agents.middlewareimportTodoListMiddleware model=ChatOpenAI(# 主 Agent 负责协调多个子 Agent,建议使用能力较强、支持工具调用的模型model="zai-org/GLM-5.2",api_key=os.environ["SILICONFLOW_API_KEY"],base_url="https://api.siliconflow.cn/v1",)subagents=[{"name":"data-collector","description":"从多个来源收集原始数据,包括网络搜索和 API 调用","system_prompt":"你是数据收集专家。搜索并整理相关数据,返回结构化的数据摘要。","tools":[internet_search,api_call],},{"name":"data-analyzer","description":"对收集到的数据进行统计分析,提取关键洞察","system_prompt":"你是数据分析专家。分析数据并提取 3-5 个关键发现,控制在 300 字以内。","tools":[statistical_analysis],},{"name":"report-writer","description":"根据分析结果撰写专业报告","system_prompt":"你是技术写作专家。根据提供的分析结果撰写清晰、专业的报告。","tools":[format_document],},]agent=create_deep_agent(model=model,middleware=[TodoListMiddleware()],system_prompt="""你是一位项目协调者。面对复杂任务时: 1. 先用 write_todos 制定计划 2. 将数据收集委派给>,subagents=subagents,)工作流程:
1. 主agent使用显式启用的write_todos制定计划
2. task工具创建子agent进行数据收集
3. task工具创建子agent进行数据分析
4. task工具创建子agent进行报告编写
5. 主agent整合输出
每一步的子agent都在独立的上下文工作
结构化输出
默认情况下,主agent收到的是子agent最后一条消息的自由文本。
通过response_format字段,可以让子agent返回json,方便主agent程序化处理。
frompydanticimportBaseModel,Fieldfromdeepagentsimportcreate_deep_agentclassResearchFindings(BaseModel):summary:str=Field(description="研究摘要")confidence:float=Field(description="置信度 0-1")sources:list[str]=Field(description="信息来源 URL 列表")research_subagent={"name":"researcher","description":"研究特定主题并返回结构化发现","system_prompt":"深入研究给定主题,返回你的发现。","tools":[internet_search],"response_format":ResearchFindings,# 需要 deepagents>=0.5.3}agent=create_deep_agent(model=model,subagents=[research_subagent])# 主 Agent 的 ToolMessage 将收到:# '{"summary": "...", "confidence": 0.87, "sources": ["https://..."]}'子agent最佳实践
1、子agent要描述具体,这样才可以让主agent找到合适的子agent进行任务分配
2、System Prompt要详细,包括输出格式要求、字数限制,以保证返回给主agent结果的内容质量
3、工具集精简,只传递需要的工具
4、不同子agent根据任务不同使用不同的模型
5、返回内容要精简,在system Prompt中明确要求子agent只返回核心内容,避免将大量内容返回主agent,否则失去了上下文隔离的初衷
常见问题排查
子agent没被调用,主agent自己做了所有工作
原因在于主agent无法通过子agent的description判断应该委派给谁
解决:
1、子agent description详细具体
2、主agent system Prompt明确指示委派
agent=create_deep_agent(...,system_prompt="""...你的指令... 重要:面对复杂任务时,使用 task() 工具委派给对应的子 Agent,保持自身上下文干净。""",)上下文依然膨胀
原因,子agent返回了大量原始数据
解决:
1、在子agent system Prompt中强制要求简洁返回,限制字数
2、让子agent将大量数据写入文件,只返回信息摘要
调用了错误的子agent
原因,多个子agent的描述过于相似,主agent无法区分
解决:
在子agent description中明确各个子agent的使用场景,以便区分
结语
本文我们主要学习了deepagents的子agent机制
1、核心动机:上下文隔离
2、子agent的定义
3、最佳事件与常见问题与解决
参考链接:https://datawhalechina.github.io/deepagents-in-action/chapters/ch05-subagents/