记录DirectProject客户端Skill自然语言触发能力缺口
Project CI / Native shell tests (pull_request) Has been cancelled
Project CI / Frontend tests (pull_request) Has been cancelled
Project CI / Backend tests (pull_request) Has been cancelled
Project CI / Repository checks (pull_request) Has been cancelled

新增当前AGC Skill导入、注入和显式使用方式说明

记录普通自然语言触发缺口与可复现测试步骤

整理Codex客户端分层和候选修复方向
This commit is contained in:
2026-09-01 05:48:59 +00:00
parent c039c4ebaf
commit fc82f1d3f2
@@ -0,0 +1,185 @@
# DirectProject 客户端 Skill 自然语言触发能力缺口
状态:待提 Issue,当前仅记录现状和候选方向,未修改代码。
## 1. 问题摘要
AGC 已经可以把用户导入的 Skill 安装到客户端,并在下一次 DirectProject 启动时注入真实 Codex app-server。使用 Codex 原生的 `$skill-name` 形式时,导入的 Skill 可以被读取并执行。
目前缺少的是:用户在普通自然语言中直接提到 Skill 名称时,AGC 没有把这次请求确定性地绑定为 Codex 的 Skill 输入项。结果是模型可能只看到用户提到了一个名称,并把“调用 Skill”误解成调用一个工具,返回“没有可调用接口”,而不是读取 `SKILL.md`
这不是 Skill 导入失败,也不是需要把 Skill 改造成 MCP 工具;是 AGC 客户端没有复用 Codex 官方客户端的 Skill 输入适配层。
## 2. 当前 AGC 如何使用 Skill
### 2.1 客户端导入
用户在客户端设置的“扩展”页面导入文件、目录或 zip。目录或 zip 中发现的每个 `SKILL.md` 会成为独立的 Skill 扩展项,可以单独启用、禁用、重命名和删除。
已识别的 Skill 导入后默认启用。扩展内容保存于 AGC 客户端扩展仓库,不直接写入用户全局 `CODEX_HOME`
### 2.2 DirectProject 启动时注入
创建或复用 DirectProject 的 Codex app-server 时,AGC 会:
1. 读取客户端扩展索引中 `enabled=true` 的 Skill
2. 在本次隔离运行的临时 HOME 下创建客户端 Skill root
3. 将每个 Skill 的运行时副本复制到该目录;
4. 对手动重命名的 Skill,只在运行时副本的 frontmatter 中使用当前名称;
5. 调用 Codex app-server 的 `skills/extraRoots/set`
6. 调用 `skills/list` 重新发现 Skill。
AGC 侧的运行时目录准备位于 [`client_extensions.rs`](../../apps/ai-game-creator-shell/src-tauri/src/client_extensions.rs) 的 `prepare_enabled_client_skill_root`app-server 的 Skill root 注册位于 [`codex_app_server.rs`](../../apps/ai-game-creator-shell/src-tauri/src/agent/codex_app_server.rs)。
Skill 配置从下一次 DirectProject 启动生效,当前已经运行的 Codex 会话不热更新。首页的 DirectHome 不作为客户端 Skill 的运行时入口。
### 2.3 当前可用的触发方式
Codex 原生显式触发方式是:
```text
$import-smoke-skill 请只回复一行结果。
```
这个方式已经在 AGC 中验证成功,测试 Skill 返回:
```text
SKILL_IMPORT_OK
```
这证明导入、启用、运行时 Skill root 注入、Codex 发现和 Skill 正文执行链路已经打通。
## 3. 当前缺少的能力
### 3.1 普通自然语言名称没有确定性绑定
以下输入目前不能稳定触发导入的 Skill:
```text
请调用 import-smoke-skill skill。
```
```text
请明确使用 import-smoke-skill。
```
实际结果是模型报告“当前没有可调用该 Skill 的工具接口”。这句话在“Skill 不是工具”这一点上没有错,但没有完成 Skill 指令的读取和应用。
### 3.2 AGC 没有发送结构化 Skill 输入项
Codex 官方 TUI 在识别 `$skill-name` 或用户从 Skill 提及菜单选择后,会向 `turn/start` 输入中追加类似内容:
```json
{
"type": "skill",
"name": "import-smoke-skill",
"path": ".../SKILL.md"
}
```
对应实现见 Codex 仓库中的 `codex-rs/tui/src/chatwidget/input_submission.rs`。随后 Codex core 会根据结构化输入加载 Skill 正文。
AGC 当前的 [`codex_app_server_turn_input`](../../apps/ai-game-creator-shell/src-tauri/src/agent/codex_app_server.rs) 只把用户消息整理成普通文本和图片输入,没有追加 `type: "skill"` 项。
### 3.3 “能发现”与“本轮已使用”没有明确的客户端绑定
AGC 启动时会设置 `skills/extraRoots` 并调用 `skills/list`,这只能证明 Skill 在当前 app-server 进程的发现范围内。它不等于本轮已经选中了某个 Skill,也不等于模型一定会读取该 Skill 的正文。
Codex 的 Skill 扩展会把可用 Skill 目录和使用规则放入模型上下文,但普通文本相关性判断主要交给模型;它不是 AGC 客户端已经完成的确定性绑定。
## 4. Codex 源码中的相关分层
### 4.1 模型侧使用规则
Codex 仓库中的 `codex-rs/ext/skills/src/catalog_prompt.rs` 会向模型说明:用户用 `$SkillName` 或普通文本点名 Skill,或者任务符合 Skill 描述时,应使用该 Skill。
这是一条模型行为指令,不是一个保证命中的 `if user_text.contains(...)` 路由器。普通自然语言是否命中,仍取决于模型能否看到对应目录、是否理解名称和描述,以及是否调用 `skills.read`
### 4.2 显式 Skill 选择
Codex 仓库中的 `codex-rs/ext/skills/src/selection.rs` 会处理结构化 `UserInput::Skill`,也会解析文本中的 `$skill-name`。选中后,core 在回合开始阶段读取并注入 Skill 正文。
### 4.3 “隐式 Skill”不是普通自然语言路由
源码中的 `implicit invocation` 主要用于命令或脚本访问某个 Skill 的资源目录时记录/识别对应 Skill,入口在 Codex 仓库的 `codex-rs/ext/skills/src/invocation.rs`。它不是一个根据任意用户自然语言自动选择 Skill 的通用分类器。
### 4.4 相关性选择实验不是当前确定性注入
Codex 的 `shadow_selection` / `skill_search` 相关实现会对任务和 Skill 描述做词法/相关性排序,但当前是 shadow 观测路径,不直接替当前回合注入 Skill,不能作为 AGC 当前缺口的现成解决方案。
## 5. 复现步骤
准备一个普通的 Skill 导入目录,目录中放置一个 `SKILL.md`。例如:
```markdown
---
name: import-smoke-skill
description: 用于验证客户端导入和加载 Skill 的最小测试 Skill。
---
当用户明确要求使用 `import-smoke-skill` 时,先单独输出一行 `SKILL_IMPORT_OK`,再继续回答用户的问题。
```
该目录和文件可以由 Issue 提交者在自己的测试环境中临时创建,不依赖仓库外的固定目录或本机产物。
复现:
1. 在客户端“扩展”中导入包含上述 `SKILL.md` 的目录;
2. 确认列表显示 `import-smoke-skill`、类型为 Skill、状态为“已启用”;
3. 新开一个 DirectProject 会话;
4. 依次发送:
```text
请调用 import-smoke-skill skill,如果找不到这个skill,不要伪造结果。
```
```text
请明确使用 import-smoke-skill,只回复一行结果。
```
```text
$import-smoke-skill 请只回复一行结果。
```
预期现象:前两条不会稳定加载 Skill,第三条返回 `SKILL_IMPORT_OK`。
## 6. 可能的修复方向
以下只是供 Issue 讨论的候选方向,不在本文锁定具体实现。
### 方向 A:客户端显式绑定用户点名的 Skill
AGC 在发送回合前,根据当前已启用 Skill 列表识别用户明确提到的名称,并把匹配项转换成 Codex 原生的 `type: "skill"` 输入项,同时保留原始用户文本。
优点是行为确定、与官方 TUI 的协议一致;需要讨论名称边界、重名处理和用户文本中普通单词与 Skill 名称的冲突。
### 方向 B:增加 Skill 提及选择入口
在聊天输入框提供 Skill 名称补全或选择入口。用户选择后,客户端保存/发送结构化 Skill 绑定,而不是要求用户手写 `$`。
优点是用户不需要了解 Codex 协议标记;需要额外的前端交互设计,且要决定是否在 DirectProject 聊天区增加入口。
### 方向 C:仅支持 Codex 原生 `$skill-name`
保持当前实现,只把 `$skill-name` 作为 Skill 的正式调用方式,并在测试和使用说明中明确这一点。
优点是改动最小;缺点是用户需要知道 Codex 的 Skill 提及语法,普通中文“请使用某 Skill”仍不会得到确定性支持。
### 方向 D:依赖模型根据 Skill 目录自行判断
让 Skill 目录和描述保持模型可见,由模型在普通任务中自行判断是否需要调用 `skills.read`。
这种方式可以覆盖“任务适合某 Skill”而不仅是精确名称,但结果受模型判断和上下文影响,不适合作为“用户明确点名后必须使用”的唯一保证。
## 7. Issue 建议关注点
提 Issue 时建议先确认产品契约,而不是直接指定实现:
- “用户普通中文提到已启用 Skill 名称”是否必须确定性生效;
- 是否接受用户使用 Codex 原生 `$skill-name`
- 是否需要聊天输入框提供 Skill 选择/补全;
- Skill 名称与 MCP Server 名称冲突时,客户端按哪类扩展解析;
- 没有找到匹配 Skill 时,是否只给出说明并继续普通对话;
- 是否需要在回合记录或状态 UI 中展示本轮实际注入了哪些 Skill。
本 Issue 不应扩大为:重新设计 Skill/MCP 导入格式、把 Skill 做成 MCP 工具、增加第三方内容安全扫描、增加脚本沙箱,或重做 Codex Plugin Runtime。