子智能体派生¶
子智能体就是一个新的 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 时的默认策略:
Agent.inherit(parent)——共享显示层与工作区,克隆工具箱与命令注册表,会话独立,取消事件挂到父链;- 写入内置的子智能体系统提示;
- 若
param.name非空则改名; - 深度计数:
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() 时同样跑一遍扩展。由于工具箱是浅拷贝,
扩展若在原工具箱上注册过工具,子工具箱里已有同名工具,重复注册会冲突——
冲突会中止该扩展对子智能体的后续设置(降级为告警),但已经加上的钩子仍然生效。