Plugin、Skill与App指令
Plugin、Skill 和 App 都会影响模型上下文,但它们不是同一种“扩展提示”。Skill 有可用目录与显式全文两层; Plugin 有通用使用规则和显式能力摘要;App 通用说明则由 connector 可用性决定。rust-v0.150.0 还把 Skill 目录拆成 Host、Executor、Orchestrator 三个 authority,资源必须由拥有它的 provider 读取。
本文只研究能力已经被发现后如何进入一次模型请求。Skill 目录发现、Plugin 安装和 MCP transport 分别由其他 专题解释。阅读前可先看 指令优先级与拼装顺序 和 Context变更语义。
1. 注入入口
| 路径 | 触发条件 | 内容 | 角色 | 生命周期 |
|---|---|---|---|---|
| Skill 目录 | catalog 可见且配置允许 | 名称、描述、locator、使用规则 | developer | WorldState |
| Skill 全文 | 当前 Turn 选中 Skill | SKILL.md 主提示和 authority | user | 当前 Turn |
| Plugin 通用说明 | Plugin 可用且模型允许 | Plugin 与 Skills/MCP/Apps 的关系 | developer | WorldState |
| Plugin 显式提示 | 用户明确提及 Plugin | 可见 MCP、App 与 Skill namespace | developer | 当前 Turn |
| App 通用说明 | connector 可访问且已启用 | connector 与 tool search 使用规则 | developer | WorldState |
模型看到 Skill 名称不等于完整主提示已经加载;Plugin 被列为可用也不表示它的全部 MCP server 和 App 已经 连接成功。
2. Skill目录
AvailableSkillsInstructions 是 developer-role fragment。目录不再只有 host filesystem 路径: SkillSourceKind 决定 locator 的含义,Host 使用 file,Executor 使用 executor package,Orchestrator 使用 orchestrator package,Custom 使用 provider 自定义资源。
源码位置:codex-rs/ext/skills/src/fragments.rs :: AvailableSkillsInstructions
impl ContextualUserFragment for AvailableSkillsInstructions {
fn role(&self) -> &'static str {
"developer"
}
fn content_kind(&self) -> ContentItemKind {
ContentItemKind("skills.catalog".to_string())
}
fn markers(&self) -> (&'static str, &'static str) {
(SKILLS_INSTRUCTIONS_OPEN_TAG, SKILLS_INSTRUCTIONS_CLOSE_TAG)
}
}源码位置:codex-rs/ext/skills/src/render.rs :: SkillLine::new
let locator = match &entry.authority.kind {
SkillSourceKind::Executor | SkillSourceKind::Orchestrator => entry.id.0.as_str(),
SkillSourceKind::Host | SkillSourceKind::Custom(_) => entry.rendered_path(),
};
let locator_kind = match &entry.authority.kind {
SkillSourceKind::Host => "file",
SkillSourceKind::Executor => "executor package",
SkillSourceKind::Orchestrator => "orchestrator package",
SkillSourceKind::Custom(_) => "custom resource",
};locator 不是展示装饰。模型后续读取 executor/orchestrator Skill 时必须把 package 交回同一 provider,不能把 skill://... 转换成本地路径。
3. Catalog分层
WorldState contributor 并行发现 Executor、Orchestrator 和 Host catalog。每个来源拥有独立 status、缓存和 WorldState section;渲染阶段再按总预算生成目录。
源码位置:codex-rs/ext/skills/src/world_state_catalogs.rs :: CatalogContext::discover_catalogs
let (executor, orchestrator, host) = futures::join!(
self.discover_executor_catalog(query.clone()),
self.discover_orchestrator_catalog(query),
self.discover_host_catalog(),
);
CatalogContributions {
executor,
orchestrator,
host,
}Executor catalog 以 selected capability root 为 authority,并绑定执行环境 filesystem;Orchestrator catalog 通过 MCP resources 分页读取;Host catalog 来自 HostSkillsSnapshot。成功 catalog 会进入 thread/turn extension data,供显式 mention 和后续 resource read 使用。
4. 目录预算
目录只保留 model-visible entry。预算足够时保留全部描述;只能容纳 minimum line 时 round-robin 分配描述字符; 连 minimum line 都放不下时省略 entry,并生成 omission 状态,而不是把 Markdown 字符串任意截断。
源码位置:codex-rs/ext/skills/src/render.rs :: allocate_skill_lines
if full_cost <= budget.limit() {
return skill_lines
.iter()
.map(|line| SkillLineAllocation::DescriptionChars(line.description_char_count()))
.collect();
}
if minimum_cost <= budget.limit() {
return allocate_description_chars(
budget,
skill_lines,
budget.limit().saturating_sub(minimum_cost),
)
.into_iter()
.map(SkillLineAllocation::DescriptionChars)
.collect();
}Host、Executor、Orchestrator 可以同时出现在目录中。alias 布局只有在预算压力下信息保留更好时才采用,且不同 authority 仍保留不同 locator kind。
5. Skill全文
显式选择后,SkillsExtension 通过对应 provider 读取主提示,生成 user-role SkillInstructions。无法直接暴露 为本地文件的资源会附带 authority、package 和 main_resource。
源码位置:codex-rs/ext/skills/src/fragments.rs :: SkillInstructions
impl ContextualUserFragment for SkillInstructions {
fn role(&self) -> &'static str {
"user"
}
fn content_kind(&self) -> ContentItemKind {
ContentItemKind("skills.selected_skill_instructions".to_string())
}
fn body(&self) -> String {
let resource_access = self.resource_access.as_ref().map(|access| {
serde_json::json!({
"authority": access.authority,
"package": access.package,
"main_resource": access.main_resource,
})
});
// name、path、resource access和contents共同形成fragment。
}
}源码位置:codex-rs/ext/skills/src/extension.rs :: SkillsExtension::read_main_prompt
读取成功后限制名称、路径和主提示长度;读取失败发 warning 并继续其他 Skill。Host Skill 记录规范化路径, 避免 core fallback 再次注入同一 SKILL.md。
6. Plugin提示
Plugin 通用说明只解释 Plugin 是 Skills、MCP 和 Apps 的能力组合。用户显式提及 Plugin 时, build_plugin_injections 才汇总当前可见的非 Apps MCP server、enabled App 和 Skill namespace。
源码位置:codex-rs/core/src/plugins/injection.rs :: build_plugin_injections
let available_mcp_servers = mcp_tools
.iter()
.filter(|tool| {
tool.server_name != CODEX_APPS_MCP_SERVER_NAME
&& tool.plugin_display_names.contains(&plugin.display_name)
})
.map(|tool| tool.server_name.clone())
.collect::<BTreeSet<_>>();
let available_apps = available_connectors
.iter()
.filter(|connector| {
connector.is_enabled
&& connector.plugin_display_names.contains(&plugin.display_name)
})
.map(connector_display_label)
.collect::<BTreeSet<_>>();显式提示被限制为 4 KiB;没有可见 Skill、MCP 或 App 时 renderer 返回 None。它只是当前 Turn 的能力导航, 不是 Plugin catalog 的持久化副本。
7. App说明
AppsInstructions 是 developer fragment,解释 connector 链接格式、隐式触发、Apps MCP 和 lazy tool loading。 只有配置允许、Apps 开启、至少一个 connector 可访问且 enabled,并且模型要求 Apps usage instructions 时才注入。
源码位置:codex-rs/core/src/session/world_state.rs :: AppsInstructionsState, PluginsInstructionsState
Apps 与 Plugins 使用独立 bool snapshot:Apps 由 connector policy 决定,Plugins 由 plugins_available() 和模型 开关决定。WorldState 从 unavailable 变为 available 时注入;保持 available 不重复;retained history 已存在时不 重新发送。
8. 重复抑制
HostSkillsCatalogInWorldState 和 InjectedHostSkillPrompts 已迁移到 ext/skills,不再位于删除的 core-skills crate。前者表示 Host catalog 已由 extension 投影到 WorldState;后者记录当前 Turn 已注入或被 非 Host authority 同名 Skill 覆盖的 Host Skill 路径。
相关源码:
codex-rs/ext/skills/src/state.rs :: HostSkillsCatalogInWorldStatecodex-rs/ext/skills/src/host_prompt.rs :: InjectedHostSkillPromptscodex-rs/ext/skills/src/world_state_catalogs.rs :: CatalogContext::build_world_state_sectioncodex-rs/ext/skills/src/extension.rs :: TurnInputContributorcodex-rs/core/src/session/turn.rs :: InjectedHostSkillPrompts consumer
路径同时保存原值和规范化值,覆盖 URI 与 host-native path 的表达差异。选择 Executor/Orchestrator 同名 Skill 时,也会记录对应 Host path,避免 fallback 又加载 Host 版本。
9. 失败边界
| 失败或边界 | 行为 | 模型可见结果 |
|---|---|---|
| 某来源 catalog 不可用 | 独立 status 为 unavailable | 其他来源仍可渲染 |
| Orchestrator Skills 禁用 | status 为 disabled | 可生成明确 WorldState 更新 |
| Skill 描述超预算 | 缩短描述或省略 entry | 部分目录和 omission 信息 |
| Skill 主提示过长 | UTF-8 边界截断 | fragment存在并发 warning |
| Skill resource 读取失败 | warning,继续其他 entry | 失败 Skill 无全文 |
| Plugin 无可见能力 | renderer 返回 None | 无显式 hint |
| Plugin hint 超 4 KiB | 字符边界截断 | 有限能力摘要 |
| App 不可访问或未启用 | available 为 false | 无 App 通用说明 |
这些路径不能合并成一个“扩展加载失败”。排查时先确定缺的是哪个 authority 的 catalog、某个 Skill 全文、Plugin 显式摘要,还是 App 通用说明。
补充注入顺序图,展示 catalog、预算分配和历史去重如何共同决定最终 prompt。
10. 测试路径
预算测试验证混合 catalog 在压力下仍保持 authority-aware locator;WorldState 测试验证 Apps/Plugins retained fragment;Plugin 测试验证 4 KiB 边界。
源码位置:codex-rs/ext/skills/src/render_tests.rs :: mixed_catalogs_keep_absolute_authority_aware_rendering_under_budget_pressure
源码位置:codex-rs/core/src/context/world_state/apps_instructions_tests.rs :: persisted_guidance_is_restored_only_when_missing_from_history
源码位置:codex-rs/core/src/plugins/render_tests.rs :: explicit_plugin_instructions_are_bounded
这些测试证明目录布局、差分恢复和提示上限,不证明远端 provider、MCP server 或 connector 后端一定可用。
rg -n "SkillSourceKind|SkillProvider|HostSkillsCatalogInWorldState|InjectedHostSkillPrompts" \
codex-rs/ext/skills/src codex-rs/core/src
cargo test -p codex-skills-extension mixed_catalogs_keep_absolute_authority_aware_rendering_under_budget_pressure
cargo test -p codex-core persisted_guidance_is_restored_only_when_missing_from_history
cargo test -p codex-core explicit_plugin_instructions_are_bounded