深入学LangChain官方文档(十九):Subagents、Skills 与 Custom Workflow——如何隔离上下文并组合复杂任务
深入学LangChain官方文档十九Subagents、Skills 与 Custom Workflow——如何隔离上下文并组合复杂任务本篇对应的官方文档Subagents说明主 Agent 如何把子 Agent 当作工具调用以及输入、输出和状态如何隔离。Skills说明专业能力如何按需加载以及“渐进披露”真正节省的是什么。Custom workflow说明何时需要用 LangGraph 固定顺序、分支、循环或并行。本篇讲解范围本篇只解决复杂任务的三种组合方式什么时候委派给 Subagent什么时候给同一个 Agent 加载 Skill什么时候把控制流写成 Custom Workflow。Router 与 Handoff 已在第 18 篇讲过前端如何承接长运行任务留给第 20 篇。一个企业研究助手收到这样的任务“分析近三个月的售后数据找出退款率上升的产品核对现行退款制度再给出一份可以交给管理层的报告。”表面上这只是一个更长的 prompt。实际上它至少包含资料检索、表格分析、制度核对、合规审校和报告合成五种工作。如果把所有工具、全部原始材料和每一步中间结果都塞给同一个 Agent问题不只是 token 变多。更危险的是Agent 会同时面对互相竞争的目标分析人员希望保留细节合规人员要求逐条核对写作者又要压缩成结论。上下文越满主线越容易被局部材料淹没。因此第 19 篇的核心问题不是“要创建几个 Agent”而是三句话谁决定下一步谁能看见哪些上下文哪些步骤必须按固定顺序发生。Subagent、Skill 与 Custom Workflow分别回答这三个问题的不同部分。一、复杂任务首先要拆责任不是拆 Agent先不要急着创建research_agent、analyst_agent和review_agent。把任务拆成 Agent 名称很容易得到一张看起来很完整、实际没有控制合同的架构图。更稳的做法是先写清每项工作的输入、输出和责任人。在本篇案例中可以先形成四份合同资料检索接收“研究问题与时间范围”返回“来源、摘录和检索空缺”表格分析接收“已授权的数据集与指标定义”返回“计算结果、异常点和计算口径”制度核对接收“候选结论与适用地区”返回“适用条款、限制和待人工确认项”主 Agent 接收三类结果判断证据是否足够再组织管理层报告。这四项工作不一定对应四个 Agent。一个确定性的指标计算函数可能比“数据分析 Agent”更可靠一份写作规范可能只需要 Skill只有那些需要独立推理、独立工具和干净上下文的工作才值得成为 Subagent。这也延续了前几篇的原则架构名称不能代替对象状态。任务从user request变成delegation input再变成subagent result最后变成final report。每一步都必须知道信息被谁压缩、遗漏和批准。二、Subagent 是工具化的专业工作者LangChain 官方文档中的 Subagents 架构有一个中心主 Agent通常称为 Supervisor。Supervisor 通过 Tool 调用子 Agent决定调用谁、传什么输入以及怎样组合返回结果。子 Agent 通常不直接接管用户对话而是把结果交还给主 Agent。这与第 18 篇的 Router 和 Handoff 有明确区别Router 通常完成一次分类或分发Handoff 把后续会话控制权交给另一个角色Supervisor 会跨多个回合保留主对话并在需要时反复调用不同 SubagentSubagent 完成的是一项被委派的工作不默认成为下一轮的直接对话者。下面这张图要观察的不是 Agent 数量而是调用方向用户目标始终留在 Supervisor专业工作者只通过 Tool 接收任务并把结果交回。在研究报告案例中主 Agent 保存用户目标、报告受众和最终交付标准。资料 Subagent 只接收当前研究问题表格 Subagent 只接收获准数据与指标合规 Subagent 只接收待核对结论。它们可以使用不同的工具和提示词但最终结果都回到主 Agent由主 Agent 判断能否合并。官方文档还强调Subagent 默认是无状态的每次调用从干净上下文开始过去对话记忆由主 Agent 维护。这正是上下文隔离的来源。若确实需要子 Agent 跨调用保留自己的持续历史可以进入 continuation 和独立 checkpointer 的设计但那已经改变了默认合同不能因为“它是 Agent”就假设它自动记得上次任务。三、上下文隔离发生在输入和输出两端很多人把上下文隔离理解成“给子 Agent 一个新的窗口”。窗口只是结果真正的工程动作是压缩输入和约束输出。输入端要回答“完成任务最少需要什么”。资料 Subagent 不需要看到用户地址、退款账户和整段客服聊天它只需要研究问题、时间范围、允许使用的来源。若主 Agent把全部消息历史原样转发Subagent 虽然拥有独立窗口却没有真正隔离噪声和敏感信息。输出端要回答“主 Agent 接下来凭什么做决定”。官方文档指出一个常见失败是子 Agent 做了大量工具调用和推理却没有把关键结果写进最终消息而 Supervisor 默认只拿到最终输出。解决办法不是把子 Agent 的全部消息历史搬回来而是要求它返回可判断的结果例如status完成、证据不足或失败findings可复核结论sources来源与时间uncertainties仍需确认的问题recommended_next_step建议主 Agent继续委派、追问还是停止。上下文隔离也不等于权限隔离。子 Agent 能看到什么、能调用什么工具仍由应用、Middleware 和后端权限系统控制。把一个高权限工具放进子 Agent不会因为它的窗口更干净就自动安全。同步与后台执行也要根据依赖关系选择。若主 Agent 必须拿到合规结论才能写报告就应该同步等待若资料收集是一个独立的长任务可以启动后台工作并返回 job ID之后再查询状态和结果。这里的“后台”不是 Pythonasync/await的同义词而是主对话是否在工作完成前继续向前。输入压缩不能只靠一句“请只传必要信息”。每个 Subagent 都应有自己的输入 schema研究任务需要主题、时间范围和来源限制数据任务需要数据集标识、指标定义和获准字段合规任务需要候选结论、地区和生效日期。主 Agent 先把对话状态转换成这个 schema再发起工具调用。这样做看似增加了一层转换实际上把“模型想起什么就传什么”变成了可以测试的接口。输出同样需要区分事实与建议。资料 Subagent 返回的来源和摘录是证据基于证据提出的趋势判断是分析建议管理层采取的动作则是建议。若三者混在一段自然语言中Supervisor 很难判断哪些内容可以直接引用、哪些必须再审查。即使不使用正式的 structured output也应在提示词和返回格式中保持这种分层。主 Agent 还要保存委派前的原始目标。子任务为了隔离上下文会主动压缩信息连续压缩几次后局部结果可能都正确却逐渐偏离用户真正需要的交付物。Supervisor 每次回收结果时都应拿它与原始目标、当前缺口和最终验收条件比较而不是只问“子 Agent 是否返回了内容”。四、用两种工具入口完成委派与结果回收Subagent 最常见的入口有两种。角色少而稳定时为每个子 Agent 建一个具名 Tool输入输出最清楚角色很多或由多个团队维护时可以建立统一task工具通过注册表按名称分发。下面把两种方式放在同一段代码中。代码重点不是模型配置而是任务怎样进入子 Agent以及 Supervisor 最终能拿到什么。fromenumimportEnumfromlangchain.agentsimportcreate_agentfromlangchain.toolsimporttool policy_agentcreate_agent(modelqwen3.7-plus,tools[],system_prompt(你只核对退款制度。最终消息必须包含适用条款、来源、限制和待确认项。),)data_agentcreate_agent(modelqwen3.7-plus,tools[],system_prompt(你只分析已经授权的数据摘要。最终消息必须说明指标口径、异常点和不确定性。),)# 将制度核对封装为具名工具适合少量且稳定的专业角色。tooldefreview_refund_policy(question:str)-str:核对退款制度并返回主 Agent 可复核的最终结果。resultpolicy_agent.invoke({messages:[{role:user,content:question}]})returnresult[messages][-1].contentclassAgentName(str,Enum):限制统一分发工具能够调用的子 Agent 名称。POLICYpolicyDATAdataSUBAGENTS{AgentName.POLICY:policy_agent,AgentName.DATA:data_agent,}# 使用统一入口调用注册表中的子 Agent适合角色较多的团队。tooldeftask(agent_name:AgentName,description:str)-str:启动一次无状态子任务并只回收子 Agent 的最终消息。workerSUBAGENTS[agent_name]resultworker.invoke({messages:[{role:user,content:description}]})returnresult[messages][-1].content supervisorcreate_agent(modelqwen3.7-plus,tools[review_refund_policy,task],system_prompt(你负责维护用户目标、委派专业任务并合成报告。证据不足或子任务失败时明确说明不得补写结论。),)具名工具把“什么时候调用”和“返回什么”直接写进工具描述适合角色少、输入差异大的系统。统一分发工具扩展更方便但agent_name、任务描述和输出合同必须更加稳定。官方文档给出的发现方式也有层级少量静态角色可列在 system prompt 或 Enum 中角色很多、注册表动态变化时再增加按需发现工具。无论使用哪种入口都不要把 Supervisor 写成“只负责转发的 Router”。它还要维护当前用户目标判断子结果是否满足下一步决定串行、并行还是再次委派并把多个结果压缩成一致答复。若两个子任务互不依赖主 Agent 可以在一轮中并行调用若合规审校依赖分析结果就必须等前一步完成。工具名称和描述也是路由合同。research_policy比helper更容易让主 Agent 判断用途“只核对已经生效的退款制度返回来源和限制”比“处理制度问题”更能减少误调用。角色发现、输入转换和输出回收构成一条连续机制主 Agent 先知道有哪些工作者再选择一个将当前状态压缩成任务最后把返回结果恢复为主流程可以判断的对象。只优化其中一步无法得到稳定委派。当注册表变大时也不要把几十个 Agent 的完整说明永久放进 system prompt。官方文档提供了从静态列举、Enum 约束到工具化发现的递进方式。小而稳定的列表优先显式动态注册表再按需搜索。发现机制本身也需要权限过滤不能让当前用户看见或调用本不属于其业务域的工作者。五、Skill 给同一个 Agent 按需加载专业行为Subagent 解决的是独立工作上下文Skill 解决的是同一个 Agent 如何按需获得专业说明。官方文档将 Skill 描述为以 prompt 为主的专业能力它可以引用脚本、模板和其他资源通过 Tool 调用完成渐进披露。假设研究助手既可能写管理层摘要也可能写合规复核表。如果把两套格式、措辞和检查清单永久塞进 system promptAgent 每次运行都要携带无关说明。Skill 可以先只暴露名称与用途等主 Agent 确认要写“管理层摘要”时再加载对应的完整模板和规则。渐进披露省下的是初始上下文并减少互相冲突的说明。它不创造新的执行身份也不天然提供独立状态。加载 Skill 之后仍然是同一个 Agent、同一条消息历史和同一组已授权工具。可以用一句最短判断区分 Tool 与 SkillTool 回答“系统能执行什么动作”例如查询数据库、读取文件、创建工单Skill 回答“Agent 应该怎样完成某类任务”例如按什么结构写分析、检查哪些风险、如何使用模板。Skill 可以告诉 Agent“生成结论前必须核对数据口径”但真正读取数据仍要调用受控 ToolSkill 也可以引用一个脚本但脚本能否运行、访问哪些文件仍受运行环境约束。把权限写进 Skill 文本只是提示不是安全控制。当专业工作需要自己的长推理、工具集或上下文隔离时用 Subagent当工作仍由当前 Agent 完成只是临时需要一套方法和资料时用 Skill。这条边界比“功能复杂不复杂”更稳定。Skill 还需要版本和适用范围。管理层报告模板可能每季度更新合规措辞可能按地区变化如果加载器只按一个模糊名称返回内容Agent 无法知道当前拿到的是哪一版。实际 Skill 目录至少应能回答名称、用途、版本、维护者、所需资源和禁止场景。加载记录也应进入运行轨迹便于追查某份报告为什么采用了某个结构。加载 Skill 之后还要验证任务是否仍在其范围内。比如“管理层摘要 Skill”可以规定先给结论、再给证据但它不能把证据不足改写成肯定结论。Skill 是行为指导不应覆盖主 Agent 的事实边界、工具返回状态和审批规则。若两个 Skill 的说明冲突应由更高层的系统合同决定优先级而不是让模型自行折中。六、Custom Workflow 固定不能被模型跳过的步骤Subagent 与 Skill 都允许 Agent 决定何时调用。若业务要求“先校验数据授权再并行完成分析与制度检索最后必须经过合规复核”顺序本身已经是产品合同不能只依赖模型临场规划。LangChain 官方的 Custom Workflow 使用 LangGraph 明确描述执行图。节点可以是普通函数、一次模型调用或包含工具的完整 Agent图可以包含顺序、条件分支、循环和并行。它的价值不是“比 Agent 更高级”而是把必须发生的控制流从 prompt 移到可检查的图结构。下面的最小工作流先做确定性授权检查再让分析 Agent 工作最后让 Supervisor 合成报告。为了突出结构示例省略真实数据读取和并行分支生产系统应把授权检查放在实际数据源之前。fromtypingimportTypedDictfromlanggraph.graphimportEND,START,StateGraphclassReportState(TypedDict):保存研究报告工作流中可检查的共享状态。request:strauthorized:boolanalysis:strreport:str# 用确定性逻辑检查请求是否具备数据访问资格。defcheck_access(state:ReportState)-dict:在任何分析开始前写入授权判断。return{authorized:已授权数据集instate[request]}# 根据授权结果决定继续分析还是直接结束。defroute_after_access(state:ReportState)-str:未授权时停止已授权时进入分析节点。returnanalyzeifstate[authorized]elseEND# 在图节点中调用专业 Agent并只保存最终分析结果。defanalyze(state:ReportState)-dict:让数据 Subagent 处理已通过授权检查的任务。resultdata_agent.invoke({messages:[{role:user,content:state[request]}]})return{analysis:result[messages][-1].content}# 让主 Agent 基于结构化状态生成最终交付报告。defcompose_report(state:ReportState)-dict:合成报告并保留上游分析结果作为证据。resultsupervisor.invoke({messages:[{role:user,content:(f原始任务{state[request]}\nf分析结果{state[analysis]}\n请生成管理层报告并标记不确定性。),}]})return{report:result[messages][-1].content}workflow(StateGraph(ReportState).add_node(check_access,check_access).add_node(analyze,analyze).add_node(compose_report,compose_report).add_edge(START,check_access).add_conditional_edges(check_access,route_after_access).add_edge(analyze,compose_report).add_edge(compose_report,END).compile())这段代码中授权检查是普通函数分析节点调用 Agent最终合成仍由 Supervisor 完成。三者共享ReportState但每个节点只读取和写入自己负责的字段。模型不能跳过check_access也不能在未授权分支中进入analyze。这正是确定性逻辑与 Agent 行为混合的意义。Custom Workflow 不是默认选择。流程只有一个 Agent、几个工具时显式图可能只增加维护成本标准 Subagent 或 Skill 已经能表达时也不必为了“可视化”重写成图。只有顺序、分支、循环、并行或审计点必须稳定标准模式又无法准确表达时才值得进入自定义工作流。图结构固定之后测试也获得了更清楚的落点。授权失败时应断言analyze从未执行分析失败时应断言报告不会被标记为可交付并行节点中任一结果缺失时应明确选择重试、部分交付还是人工复核。Agent 节点仍然具有非确定性但它能到达哪些节点、必须经过哪些检查、失败后落到哪里都可以由图合同约束。状态字段也要保持最小。把每个节点的完整消息历史都写进共享 state会让 Custom Workflow 重新变成一个巨大的上下文容器。更合适的做法是保存节点之间真正需要交换的结构化结果和引用把调试轨迹交给观测系统。共享 state 是业务协作面不是所有内部过程的备份盘。七、三种模式可以组合但控制权只能有一条主线把三种模式放在同一张图中重点是看它们分别改变哪一层Skill 改变当前 Agent 的行为Subagent 增加独立工作上下文Workflow 固定外层执行结构。一个完整研究系统可以同时使用三种模式Workflow 固定“授权检查—研究—合规复核—交付”的阶段研究节点内部由 Supervisor 委派资料和表格 Subagent写作节点按需加载“管理层摘要 Skill”。组合并没有问题问题在于每一层都不能重新发明控制权。可以用下面的顺序判断当前工作是否仍适合由一个 Agent 在同一上下文完成如果是优先单 Agent是否只缺一套按需方法、模板或领域说明如果是使用 Skill是否需要独立推理、独立工具和干净上下文并由主 Agent 回收结果如果是使用 Subagent是否存在模型不得跳过的顺序、条件、循环、并行或审计节点如果是使用 Custom Workflow专业角色是否要在后续轮次直接服务用户如果是应回到第 18 篇考虑 Handoff而不是把 Subagent 当作持续会话角色。最容易出错的组合是 Workflow 在调度、Supervisor 在调度、Subagent 内部又自由委派却没有统一的停止条件。层层“智能规划”不会自动得到更好的计划只会让一次用户请求在多个上下文中递归扩散。八、委派治理必须贯穿全程完成架构选型后还要沿“任务形成—权限授予—结果回收—再次委派”检查治理边界。下面这张图把这些检查点放回一次真实调用中。输入扩散。主 Agent 为了省事把整段历史交给所有 Subagent敏感数据和无关材料一起扩散。应为每类子任务定义最小输入并在调用前记录实际传递的字段。输出丢失。子 Agent 完成了工具调用却没有在最终消息中返回来源和结论。Supervisor 只能看到一句“已完成”无法继续判断。应使用固定输出合同并把“证据不足”作为正常状态而不是异常文本。权限放大。子 Agent 拥有比任务需要更多的工具或 Skill 文本暗示它可以绕过审批。权限必须在工具、运行时和服务端执行Skill 只负责指导行为。递归委派。Supervisor 调用 SubagentSubagent 又调用另一个同类 Supervisor任务不断拆分却没有交付。系统需要最大深度、最大调用次数、时间预算和明确的最终责任人。后台失联。长任务被放到后台后只返回 job ID却没有状态查询、结果获取和用户通知。后台执行至少需要“启动—查状态—取结果”三个入口不能把“异步”写成一句“稍后完成”。状态不可见。工具函数内部调用的 Subagent 不一定能被父图静态发现嵌套状态也不一定能通过父图的状态读取接口直接看到。若中断恢复和嵌套状态检查是核心需求应考虑把 Subagent 放进 Custom Workflow 的显式节点而不是隐藏在普通 Tool 内。治理记录至少应包含主任务 ID、调用者、子 Agent 名称、输入摘要、允许工具、开始与结束时间、状态、输出摘要、失败原因和是否继续委派。记录的目的不是保存完整思维过程而是让团队能回答“这条结论来自哪个工作者、看过哪些获准信息、失败后由谁处理”。总结隔离上下文不能隔离最终责任最后用一张演进图把前两篇和本篇连起来。读图时只追踪控制权谁决定当前去向、谁继续对话、谁完成隔离任务以及谁固定整体流程。现在重新回答开头的问题。复杂研究报告不应该默认由一个 Agent 携带所有材料也不应该默认拆成一群互相对话的 Agent。主 Agent 先保存用户目标和最终责任再把独立专业工作委派给 Subagent同一个 Agent 临时需要方法和模板时按需加载 Skill顺序、分支和审计点不能交给模型自由决定时再使用 Custom Workflow。最短记法是Subagent 隔离工作上下文Skill 按需加载专业行为Custom Workflow 固定执行结构无论使用哪一种最终责任都必须落回清晰的控制者。到这里后端已经能把复杂任务拆开并重新汇总。下一篇转向前端当任务持续运行、用户继续输入、网络暂时断开甚至回到历史消息重新分支时这条控制链怎样继续保持一致。