跳转至

子智能体派生

子智能体就是一个新的 Agent.inherit(parent)。框架额外提供的是把它变成工具的那层工厂: 注册四个工具,模型自己决定什么时候派活、派给谁、什么时候收结果。

两种做法

自己动手——在普通工具函数里创建一个智能体即可,控制粒度最细:

from xun import Agent, ToolBox

def review(code: str) -> str:
    """Review a piece of code with a dedicated agent."""
    child = Agent.inherit(parent, share_display=False)
    return child.initialize().instruct(f"Review this code:\n{code}").execute().unwrap()

用内置策略——把派生注册成工具,模型按需调用:

from xun import Agent, ToolBox

agent = Agent(toolbox=ToolBox().with_subagent_provider()).initialize()

setup_agent(default_tools=True) 走的就是后者。

四个内置派生工具

工具 参数 返回 说明
agent_run task, name? Result[str, ErrorInfo] 阻塞执行,返回子智能体的最终输出
agent_spawn task, name? {"id": …} 后台执行,立即返回 ID
agent_wait identifier, timeout_seconds=300 Result[str, ErrorInfo] 或 {"status": "timeout", …} 等待并取出结果,取完即从池中移除;null 表示无限等
agent_list — [{"id": …, "status": "running" \| "finished"}] 列出池中任务,含已完成但未取的

没有 agent_kill

后台任务不能单独取消。子智能体注册在 before_finalize 上的收尾会 join 五秒, 超时未结束就 cancel();父侧取消则通过取消链传递,见 链式取消。

派生策略:agent_getter

with_subagent_provider(agent_getter=None) 的可选参数决定「子智能体长什么样」:

from xun import AgentGetterParam

def agent_getter(param: AgentGetterParam) -> Agent:
    # 必须返回**未初始化**的智能体,并兑现 param 里的要求(比如名字)
    child = Agent(toolbox=ToolBox().register(sqrt))
    child.name = param.name or "subagent"
    return child

agent = Agent(toolbox=ToolBox().with_subagent_provider(agent_getter)).initialize()

AgentGetterParam 只有两个字段:

字段 说明
tool_context 触发这次派生的 ToolCallContext,父智能体在 tool_context.agent 上
name 模型请求的名字,可为 None;getter 应尊重它

不传 getter 时的默认策略:

  1. Agent.inherit(parent)——共享显示层与工作区,克隆工具箱与命令注册表,会话独立,取消事件挂到父链;
  2. 写入内置的子智能体系统提示;
  3. 若 param.name 非空则改名;
  4. 深度计数:agent.state["__subagent_depth"] 逐层 +1,超过 SUBAGENT_MAX_DEPTH(3)就调用 toolbox.disable_subagent(),于是最深层的智能体不再有派生工具。
flowchart TD
    P["父 agent"] -->|"agent_spawn / agent_run"| G["agent_getter(param)"]
    G --> D["Agent.inherit(parent)"]
    D --> C["Agent[Uninit]"]
    C --> I["initialize():扩展重放 + 绑定显示层"]
    I --> T["线程中 execute()"]
    T --> Q["结果写回任务池"]
    Q --> W["agent_wait 取出 → Result"]

结果池与生命周期

后台任务由 AgentPool 记账,它是挂在 agent.state 上的 RuntimeEntry(键 __subagents), 所以派生的子智能体看到的池与父智能体一致:

动作 效果
agent_spawn 建线程跑,登记 AgentThread
agent_wait 每 0.5 秒轮询,同时检查父侧取消;取出后从池中移除
before_finalize 池收尾:join(5),仍未结束的 child.cancel()

自定义 getter 时请用 Agent.inherit 创建子智能体,否则取消链断开—— 要么显式把 parent.cancel_event 传下去。

child = Agent.inherit(param.tool_context.agent).system("……")   # 取消链自动继承

常见配置

想做的 怎么做
子智能体静默运行 Agent.inherit(parent, share_display=False),或给它 NullDisplay()
子智能体只用一套受限工具 自定义 getter,返回只注册了这些工具的 Agent
禁止继续派生 toolbox.disable_subagent()(默认策略在第四层自动这么做)
子智能体不装载扩展 child.config.enable_extensions = False
共享会话上下文 Agent.inherit(parent, copy_conversation=True)

扩展会重放

子智能体 initialize() 时同样跑一遍扩展。由于工具箱是浅拷贝, 扩展若在原工具箱上注册过工具,子工具箱里已有同名工具,重复注册会冲突—— 冲突会中止该扩展对子智能体的后续设置(降级为告警),但已经加上的钩子仍然生效。