用 CopilotKit 实现子代理编排:Supervisor 委派模式与实时委派日志实战 📅 发布时间:2026/9/12 2:53:11 👁 浏览次数: 用 CopilotKit 实现子代理编排Supervisor 委派模式与实时委派日志实战【免费下载链接】CopilotKitThe Frontend Stack for Agents Generative UI. React, Angular, Mobile, Slack, and more. Makers of the AG-UI Protocol项目地址: https://gitcode.com/GitHub_Trending/co/CopilotKitCopilotKit 的 Sub-Agents 演示展示了一种典型的「监督者Supervisor委派」多代理架构一个顶层 LLM 作为主管将research_agent研究、writing_agent写作、critique_agent评审三个专业子代理以工具tool的形式暴露出来并在每次委派时把记录追加到共享的代理状态agent state槽位中让前端以「实时委派日志」的形式逐条呈现整个工作流。读完本文你将掌握如何在 LangGraph 后端定义可委派的子代理、如何用Command一次性更新共享状态与消息以及如何用 CopilotKit 前端在聊天界面旁渲染实时日志。这个 Demo 演示了什么本 Demo源码位于 showcase/integrations/built-in-agent/src/app/demos/subagents的核心是带实时日志的多代理委派Supervisor LLM 编排三个以工具形式暴露的专职子代理每一次委派都通过共享代理状态实时流式进入 UI。它由三部分构成三个专职子代理research_agent收集事实、writing_agent起草文本、critique_agent评审草稿每一个都是完整的create_agent(...)拥有自己独立的系统提示词、独立的 LLM 调用上下文。子代理即工具Supervisor 通过tool包装调用它们每个包装器运行对应子代理并向共享的delegations状态槽位追加一条记录。实时委派日志左侧面板从代理状态中渲染delegations随着 Supervisor 向外分发任务而不断增长。从消息流的角度看这是一个经典的「research → write → critique」流水线研究代理产出事实要点写作代理基于要点产出草稿评审代理对草稿给出改进建议最终由 Supervisor 汇总成面向用户的最终回答。如何交互点击建议提示suggestion chip或直接输入自己的指令例如Produce a short blog post about the benefits of cold exposure training. Research first, then write, then critique.Explain how large language models handle tool calling. Research, write a paragraph, then critique.Summarize the current state of reusable rockets in 1 polished paragraph, with research and critique.在 Supervisor 执行 research → write → critique 的过程中右侧聊天区的SubAgentActivityCard会随工具调用实时出现左侧委派日志会持续填充。建议提示的具体实现位于 showcase/integrations/built-in-agent/src/app/demos/subagents/suggestions.ts页面骨架与左右分栏布局见 demo-layout.tsx。核心模式子代理即工具Sub-Agents-as-Tools这一节的架构理念是理解整个 Demo 的关键。Supervisor 本身也是一个 LLM 代理它没有直接调用子代理的魔法通道而是把三个子代理分别封装成工具函数注册在自己的工具列表里。Supervisor LLM 通过常规的 tool-calling 机制调用这些工具来委派工作每次工具调用都会同步运行对应的子代理记录委派并把子代理的输出以ToolMessage的形式交还给 Supervisor供其下一步决策读取。参考实现位于 showcase/integrations/langgraph-python/src/agents/subagents.py其顶部注释对该模式做了精确定义Each sub-agent is a fullcreate_agent(...)under the hood. Every delegation appends an entry to thedelegationsslot in shared agent state so the UI can render a live delegation log as the supervisor fans work out and collects results. This is the canonical LangGraph sub-agents-as-tools pattern, adapted to surface delegation events to the frontend via CopilotKits shared-state channel.三个子代理使用同一个模型gpt-5.4但各有独立的系统提示词、独立的内存与工具集——它们与 Supervisor 之间不共享记忆或工具Supervisor 只能看到子代理的返回值_research_agent create_agent( model_sub_model, tools[], system_prompt( You are a research sub-agent. Given a topic, produce a concise bulleted list of 3-5 key facts. No preamble, no closing. ), middleware[HeaderForwardingMiddleware()], ) _writing_agent create_agent( model_sub_model, tools[], system_prompt( You are a writing sub-agent. Given a brief and optional source facts, produce a polished 1-paragraph draft. Be clear and concrete. No preamble. ), middleware[HeaderForwardingMiddleware()], ) _critique_agent create_agent( model_sub_model, tools[], system_prompt( You are an editorial critique sub-agent. Given a draft, give 2-3 crisp, actionable critiques. No preamble. ), middleware[HeaderForwardingMiddleware()], )注意此处只挂载了HeaderForwardingMiddleware用于把入站请求的x-*头透传到子代理的出站 LLM 调用保证 aimock 录制回放匹配而刻意不挂载完整的CopilotKitMiddleware——因为 Supervisor 已经为整次运行承担了 App-Context 与前端工具注入的职责若在子代理上重复挂载会双重注入 prompt 状态。委派工具的返回一条 Command 做两件事每个委派工具的核心返回值是 LangGraph 的Command它在一次更新里同时完成两件事向共享状态追加委派记录 把子代理结果作为工具消息喂回对话流。_delegation_update辅助函数封装了这一逻辑def _delegation_update(sub_agent, task, result, tool_call_id) - Command: entry: Delegation { id: str(uuid.uuid4()), sub_agent: sub_agent, task: task, status: completed, result: result, } return Command( update{ delegations: [entry], messages: [ ToolMessage( contentresult, namesub_agent, idstr(uuid.uuid4()), tool_call_idtool_call_id, ) ], } )这里有两点值得展开delegations只追加新条目。因为AgentState.delegations上的 reducer 是operator.add列表拼接Command.update里绝不能回显已有的delegations否则每步都会把历史记录重复追加一遍。ToolMessage必须带上tool_call_id。它来自ToolRuntime.tool_call_id用于把子代理的返回结果正确关联到 Supervisor 发出的那次工具调用上Supervisor 才能在下一步读取并继续。三个工具research_agent、writing_agent、critique_agent都是tool包装内部调用_invoke_sub_agent(...)运行对应的create_agent实例再从消息列表中倒序找出本次任务产出的最终文本跳过只携带 tool_calls 的空AIMessage并兼容内容块列表形式的流式返回取不到时返回哨兵值sub-agent produced no output。防循环critique 单次上限Supervisor LLM 偶尔会在同一份草稿上反复调用critique_agent每次重跑产出近乎相同的输出。参考实现用模块级常量_MAX_CRITIQUE_ITERATIONS 1硬性限流工具内部读取runtime.state中已有的delegations统计critique_agent的调用次数达到上限后不再追加委派记录而是直接返回一条引导 Supervisor 收尾的ToolMessageif prior_critiques _MAX_CRITIQUE_ITERATIONS: skip_message ( Critique already produced for this run. Stop calling critique_agent and return your final answer to the user now. ) return Command(update{messages: [ToolMessage(...)]})选择返回 no-op 消息而非抛异常是因为抛错会表现为失败的工具调用从而打断 Supervisor 的最终总结。之所以不追加delegations条目是因为 UI 按一条委派一张卡片渲染一次 Supervisor 运行中评审卡片应当恰好只有一张。共享状态AgentState扩展AgentState继承自langchain.agents.AgentState只新增了一个字段delegationsclass AgentState(BaseAgentState): delegations: Annotated[list[Delegation], operator.add]Delegation是一个TypedDict字段为id、sub_agent限定取值research_agent/writing_agent/critique_agent、task、status当前恒为completed与result。使用operator.addreducer 是必要而非可选的当同一 Supervisor 步骤内存在并发子代理输出时LangGraph 会因「同一 step 收到多个更新值」抛出INVALID_CONCURRENT_GRAPH_UPDATECan receive only one value per step. Use an Annotated key to handle multiple values.reducer 负责把多路更新安全合并进单一列表。最终导出的 Supervisor 图把三个工具与状态 schema 一并传入create_agentgraph create_agent( modelChatOpenAI(modelgpt-5.4), tools[research_agent, writing_agent, critique_agent], middleware[CopilotKitMiddleware()], state_schemaAgentState, system_prompt(...), # 委派顺序说明 每个子代理至多调用一次 )系统提示词中明确了硬性编排规则非平凡请求一律按research_agent - writing_agent - critique_agent顺序委派每个子代理恰好调用一次评审返回后不得再调用任何子代理直接向用户给出融入评审意见的最终回答。前端实现CopilotKit 如何把状态变成实时日志挂载与订阅页面顶层用CopilotKitProvider 指定agentsubagents对应后端注册的代理 id内部组件通过useAgent订阅状态变更与运行状态CopilotKit runtimeUrl/api/copilotkit agentsubagents DemoContent / /CopilotKitconst { agent } useAgent({ agentId: subagents, updates: [UseAgentUpdate.OnStateChanged, UseAgentUpdate.OnRunStatusChanged], });UseAgentUpdate.OnStateChanged让agent.state在每次后端共享状态更新时刷新驱动委派日志增长UseAgentUpdate.OnRunStatusChanged让agent.isRunning实时反映 Supervisor 是否仍在运行驱动Supervisor running徽标与活跃子代理横幅。随后从状态中取出委派列表并推断当前活跃子代理const agentState agent.state as SubagentsAgentState | undefined; const delegations agentState?.delegations ?? []; const isRunning agent.isRunning; const activeSubAgent isRunning ? inferActiveSubAgent(delegations, agent.messages) : null;inferActiveSubAgent见 active-subagent.ts结合委派记录与消息流推断正在执行的子代理供顶部的 supervisor-activity-banner.tsx 展示正在执行哪个子代理、任务是什么。工具内联渲染聊天流中的活动卡片除侧边日志外useRenderTool为三个子代理工具分别注册了渲染器把Researcher is running task Y这样的活动以卡片形式内联进聊天流用户无需盯着侧栏也能看到进展。每个工具渲染器接收流式参数、最终结果与一个inProgress → executing → complete的状态机useRenderTool( { name: research_agent, parameters: z.object({ task: z.string() }), render: ({ parameters, status, result }) ( SubAgentActivityCard subAgentresearch_agent task{parameters?.task} status{status as SubAgentToolStatus} result{typeof result string ? result : undefined} / ), }, [], );writing_agent与critique_agent的注册方式完全一致见 page.tsx渲染组件实现在 subagent-activity-card.tsx。委派日志组件delegation-log.tsx 定义了对齐后端TypedDict的前端类型与日志 UIexport type SubAgentName | research_agent | writing_agent | critique_agent; export interface Delegation { id: string; sub_agent: SubAgentName; task: string; status: completed; result: string; }日志头部固定展示三个子代理角色指示芯片无论是否已委派都常驻可见并用data-fired标记哪些子代理已被调用标题栏显示实时增长的N calls计数与Supervisor running脉冲徽标正文区每条记录展示序号、角色徽章、任务描述与结果文本。空态提示文案为Ask the supervisor to complete a task. Every sub-agent it calls will appear here. 布局上左侧为日志、右侧为CopilotChat聊天面板见 demo-layout.tsx。内置代理built-in-agent中的等价实现在 showcase/integrations/built-in-agent 这个 TypeScript/React 集成里同一套子代理能力用tanstack/ai的toolDefinition重新实现subagent-tools.ts工具名刻意与 LangGraph 参考实现保持一致research_agent/writing_agent/critique_agent——这个对齐是承重的D5 fixtures 按 LangGraph 代理的工具名录制aimock 也按工具名匹配。三个角色各自成为一次带独立系统提示词的嵌套chat()调用同样不共享记忆与工具const subagentRoles [ { id: research_agent, systemPrompt: You are a research sub-agent. Given a topic, produce a concise bulleted list of 3-5 key facts. No preamble, no closing., }, { /* writing_agent ... */ }, { /* critique_agent ... */ }, ] as const; export function buildSubagentTools(parentAbortController: AbortController) { let critiqueCalls 0; // 每次运行闭包天然按 run 作用域隔离 return subagentRoles.map((role) toolDefinition({ name: role.id, description: Delegate a task to the ${role.id.replace(/_/g, )}., inputSchema: z.object({ task: z.string().describe(Task description for the ${role.id.replace(/_/g, )}), }), }).server(async ({ task }) { if (role.id critique_agent) { critiqueCalls 1; if (critiqueCalls MAX_CRITIQUE_ITERATIONS) { return { role: role.id, text: Critique already provided for this draft; skipping further review. }; } } const text await chat({ adapter: openaiText(gpt-5.4, { fetch: forwardingFetch }), messages: [{ role: user, content: task }], systemPrompts: [role.systemPrompt], abortController: parentAbortController, stream: false, }); return { role: role.id, text }; }), ); }三个实现细节值得留意AbortController 逐运行透传构造器接收父运行Supervisor run的AbortController子代理的chat()调用随父运行一起取消。若在模块导入期就构建工具每次都会持有独立的AbortController用户取消请求无法传导到飞行中的子代理调用造成孤儿异步任务、token 计费与悬挂 Promise。critique 计数用闭包而非模块级buildSubagentTools每次运行调用一次闭包变量天然按运行隔离若放到模块作用域上限会跨请求泄漏。MAX_CRITIQUE_ITERATIONS 1与参考实现的_MAX_CRITIQUE_ITERATIONS对齐防止 Supervisor 对同一草稿反复调用评审避免聊天中堆叠评审卡片、委派日志出现重复行。Supervisor 的系统提示词SUBAGENTS_PROMPT定义于 demo-prompts.ts是 LangGraph 参考实现提示词的移植保证三个子代理确实被调用、按合理顺序、各恰好一次委派日志本身由/delegations状态槽位驱动因此提示词无需操心 UI 细节。该代理在 api/copilotkit/route.ts 中注册subagents: createBuiltInAgent({ systemPrompt: SUBAGENTS_PROMPT }),并统一挂载在InMemoryAgentRunner上与页面端agentsubagents的声明一一对应。小结把这套模式移植到自己的项目从参考实现langgraph-python/src/agents/subagents.py与内置代理实现subagent-tools.ts中可以提炼出移植子代理编排的四步通用配方扩展共享状态在AgentState上新增delegations字段并用operator.add作为 reducer兼容并发委派写入把子代理封装成工具每个子代理是独立的create_agent(...)外层用toolLangGraph或toolDefinition(...).server(...)tanstack/ai包装工具内同步运行子代理并收集其最终文本一次更新完成两件事返回Command(update{delegations: [...], messages: [ToolMessage(...)]})既追加委派记录又把结果作为工具消息喂回 SupervisorToolMessage务必携带正确的tool_call_id前端订阅状态useAgent订阅OnStateChanged与OnRunStatusChanged读取agent.state.delegations渲染实时日志再用useRenderTool为每个子代理工具注册内联活动卡片。配套的防循环约束critique 单次上限与取消传播AbortController 透传虽非必需却是让真实 LLM 场景下日志干净、资源不泄漏的关键工程细节。更完整的 UI 与交互代码可直接参考 showcase/integrations/built-in-agent/src/app/demos/subagents 目录下的组件以及与前端字节对齐的 LangGraph 参考实现 showcase/integrations/langgraph-python/src/agents/subagents.py。【免费下载链接】CopilotKitThe Frontend Stack for Agents Generative UI. React, Angular, Mobile, Slack, and more. Makers of the AG-UI Protocol项目地址: https://gitcode.com/GitHub_Trending/co/CopilotKit创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考