子 Agent 委托与自生成
子 Agent 委托与自生成
学习目标
本章聚焦 LibreChat 的子 Agent 系统。你将了解:
- 自生成(self-spawn)与显式子 Agent 两种模式的工作原理
- 循环安全检测与深度限制机制
- 子 Agent 上下文隔离策略
- 配置数量限制与展开约束
前置知识
下文假设你已理解上述概念,直接聚焦 LibreChat 的具体实现。
项目实践
两种子 Agent 模式
LibreChat 支持两种子 Agent 模式,通过 buildSubagentConfigs 函数递归展开:
1. 自生成(self-spawn)
{ self: true, type: 'self', name: 'assistant', description: 'Spawn assistant in an isolated context to handle a focused subtask.'}自生成复用父 Agent 的完整配置(模型、工具、指令),但创建隔离的图执行上下文。适合将复杂任务分解为多个独立子任务,每个子任务有自己的工具输出和上下文窗口。
2. 显式子 Agent
{ type: 'child-agent-id', name: 'code-reviewer', description: 'Delegate a subtask to the code-reviewer agent...', agentInputs: childInputs, // 子 Agent 的独立输入}显式子 Agent 有自己独立的配置(不同的模型、工具、指令)。通过 subagents.agent_ids 配置指定。
循环安全检测
buildSubagentConfigs 使用 ancestors 集合防止循环委托:
buildSubagentConfigs(agent, ..., ancestors, depth): nextAncestors = ancestors ∪ {agent.id}
for child in agent.subagentAgentConfigs: if child.id in ancestors: continue // 跳过,防止 A → B → A buildSubagentConfigs(child, ..., nextAncestors, depth + 1)深度限制:MAX_SUBAGENT_DEPTH 防止过深嵌套。每次递归递增 depth,超过限制抛出错误。
配置数量限制:MAX_SUBAGENT_RUN_CONFIGS 限制展开的总配置数。每个子 Agent(包括自生成和显式)都消耗一个配额。
子 Agent 上下文隔离
isSubagent: true 标志确保子 Agent 的工具上下文完全隔离:
| 维度 | 父 Agent | 子 Agent(isSubagent: true) |
|---|---|---|
defer_loading 覆盖 | 从历史提取 | 跳过 |
| 工具定义注入 | 从注册表添加 | 跳过 |
toolRegistry | 原始引用 | 深度克隆(Map + 每个 LCTool 浅拷贝) |
initialSummary | 传递 | 设为 undefined |
discoveredTools | 从历史提取 | 设为 undefined |
为什么需要深度克隆:父 Agent 在构建子 Agent 后,可能继续参与同一个图的其他路径(如 handoff 目标)。如果子 Agent 共享 toolRegistry 引用,父 Agent 后续对 defer_loading 的修改会泄漏到子 Agent 的上下文中。
递归展开与多级委托
buildSubagentConfigs 不仅展开当前 Agent 的直接子 Agent,还递归解析子 Agent 自己的 spawn targets:
A → B → C buildSubagentConfigs(A): → 构建 B 的 inputs → buildSubagentConfigs(B): // 递归 → 构建 C 的 inputs → B 没有子 Agent → 返回 → B.inputs.subagentConfigs = [C] → 返回 [self, B(C)]这使得多级委托(A → B → C)可以正确工作——B 的子 Agent C 在 B 的 agentInputs 中被配置。
问题与规避
问题 1:子 Agent 继承了父 Agent 的工具搜索状态
规避:isSubagent: true 时,跳过 defer_loading 覆盖、工具定义注入,并对 toolRegistry 进行深度克隆。
问题 2:A → A 自循环导致无限递归
规避:ancestors 集合在递归前包含当前 Agent ID,child.id === agent.id 的条件也被显式跳过。
问题 3:子 Agent 配置数超限
规避:countSubagentConfig 在每次添加子 Agent 配置时递增计数器,超过 MAX_SUBAGENT_RUN_CONFIGS 时抛出错误并记录详细日志(包含 rootAgentIds 和当前计数)。
设计取舍
自生成 vs 仅显式子 Agent
| 方案 | 优势 | 代价 |
|---|---|---|
| 支持自生成 | 用户无需预定义子 Agent,动态分解任务 | 需要确保隔离性,防止状态泄漏 |
| 仅显式 | 简单明确,每个子 Agent 有清晰定义 | 用户需要预定义所有子 Agent |
LibreChat 同时支持两种模式,满足不同场景需求。