Skip to content

Plugin、Skill与App指令

追踪Plugin、Skill和App从能力发现、目录渲染、显式提及到完整指令注入的不同路径。

基于rust-v0.150.0
CodexRustContextPluginSkill

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、使用规则developerWorldState
Skill 全文当前 Turn 选中 SkillSKILL.md 主提示和 authorityuser当前 Turn
Plugin 通用说明Plugin 可用且模型允许Plugin 与 Skills/MCP/Apps 的关系developerWorldState
Plugin 显式提示用户明确提及 Plugin可见 MCP、App 与 Skill namespacedeveloper当前 Turn
App 通用说明connector 可访问且已启用connector 与 tool search 使用规则developerWorldState

模型看到 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

rust
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

rust
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

rust
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

rust
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

rust
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

rust
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 :: HostSkillsCatalogInWorldState
  • codex-rs/ext/skills/src/host_prompt.rs :: InjectedHostSkillPrompts
  • codex-rs/ext/skills/src/world_state_catalogs.rs :: CatalogContext::build_world_state_section
  • codex-rs/ext/skills/src/extension.rs :: TurnInputContributor
  • codex-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 后端一定可用。

bash
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