扩展机制与加载¶
本章讲扩展(插件):放进约定目录的文件,在每一个智能体初始化时自动运行,不需要改入口程序。
与之相对的是 API 用法:在调用处 tools=[...] 接线,只影响那一个智能体。
什么时候用扩展,什么时候直接用 API¶
| API 用法 | 扩展 | |
|---|---|---|
| 生效范围 | 你写代码的那一个智能体 | 每个 initialize() 的智能体,含派生的子智能体 |
| 需要改入口程序 | 是 | 否 |
| 入口程序能感知它 | 是 | 否,它只认目录 |
| 典型场景 | 单次任务、实验、应用内私有工具 | 团队共用的能力、跨会话常驻的集成 |
| 代码位置 | 你的程序里 | $XUN_HOME/extensions/ |
内置的 MCP、Skills、用量统计都是扩展,而不是核心功能——核心保持无关是刻意的。
例如 MCP 扩展自带标准库实现的客户端,核心不知道 MCP 存在;它需要的只是 ToolBox.register_raw、
ToolBox.pop、ToolBox.is_disabled 与 AgentConfig.extension_settings 这些通用接口。
因此「核心里没有某个集成」不等于做不到,通常只是还没人写成扩展。
扩展不是不可碰的黑盒
扩展能做的,你在同样位置也能做:setup_extension(ctx) 拿到的就是那个尚未初始化的智能体本身。
反过来也成立——需要扩展帮忙时,直接照着某个内置扩展抄结构往往最快。
发现与导入¶
flowchart TD
A["$XUN_HOME/extensions/"] --> B{"形态"}
B -- "<name>.py" --> C["单文件扩展"]
B -- "<name>/setup_extension.py" --> D["目录扩展<br/>其余文件可自由 import"]
C --> E["导入 + 读取 extension_attr"]
D --> E
E --> F{"版本门"}
F -- 超范围 --> G["skipped"]
F -- 兼容 --> H["进入待用列表"]
H --> I["每次 Agent.initialize()<br/>按名序运行 setup_extension(ctx)"]
规则细节:
- 扫描按目录项名字排序,进程内每个目录只扫一次并缓存;
ExtensionLoader.clear_scan_cache()可强制重扫。 - 以
.或__开头的条目忽略。目录里没有setup_extension.py会告警跳过;<name>.py与<name>/同时存在时,前者被后者遮蔽并告警。 - 扩展名必须是合法的模块标识符(目录扩展还要能作为 sys.modules 键),否则告警跳过。
- 入口名固定:模块级可调用对象
setup_extension(ctx),没有它告警跳过。 - 导入失败 → 状态
failed,只告警;启动继续。
XUN_HOME 未设置时是 ./.xun/extensions/。扩展代码里请走 ctx.data_dir() 与
config.extension_settings,不要把路径写死。
每个智能体重新运行一遍¶
Agent.initialize() 调用 extension_loader.apply(self),按名序把每个扩展作用到尚未初始化的智能体上。
Agent 的 extension_loader 字段可以注入(测试里给个临时目录的加载器即可)。
| 情况 | 行为 |
|---|---|
config.enable_extensions = false |
该智能体完全跳过扩展 |
| setup 抛异常 | 告警 + 状态 failed,启动继续 |
| 工具重名冲突(子智能体常见) | 冲突会中止该扩展对子智能体的后续设置,降级为告警;先加上的钩子仍生效 |
KeyboardInterrupt / CancelledError |
原样抛出,不算失败 |
因为每个智能体都会重放一遍,setup_extension 必须写成幂等:
重复 register 同名工具会抛冲突,昂贵资源(连服务端、读盘)要缓存在模块级。
改完扩展代码
扫描结果按目录缓存,进程启动后改文件不会自动生效——重启,或在测试里调用 clear_scan_cache()。
派生的子智能体同样会重放扩展。
状态与查看¶
ExtensionLoader.infos() 返回每个扩展的 ExtensionInfo(名字、描述、路径、状态、原因),
/extensions 把它渲染成表;Web 端对应 ShowExtensionsEvent。
| 状态 | 含义 |
|---|---|
uninitialized |
发现了,但还没跑过 setup |
loaded |
setup 成功跑完 |
skipped |
与当前 xun 版本不兼容,按声明跳过 |
failed |
导入失败或 setup 抛异常,原因见 reason |
安全边界¶
扩展是完整权限的代码
扩展按文件名字典序被导入,导入那一刻它就能执行任意逻辑(不必等 setup_extension)。
它运行在信任边界之内,像 shell 的 rc 文件一样全权,不是沙箱。
需要隔离请用 xunc 容器。config.enable_extensions 只是关掉运行,不是安全边界。
相关文档¶
- 写第一个扩展:快速开发
extension_attr与ExtensionContext的字段:Extension Attributes- 扩展自带命令行:扩展 CLI 入口 xune