# 抖音 / 小红书项目包插件化接入方案

Status: implementation-ready

本文是对当前仓库实现和真实项目包现状的落地说明，目标是把抖音、小红书这类云端 zip 项目接入为：

1. 专家页先选项目
2. 进入项目后只显示该项目自己的 skills
3. 会话、上下文、执行入口都保持项目隔离
4. 默认走项目级 `workspace-entry`

本文是对通用规范 [docs/remote-project-bundle-spec.zh-CN.md](D:\qjclaw\docs\remote-project-bundle-spec.zh-CN.md) 的补充，重点回答当前抖音包这种结构是否需要改、最小要改什么、客户端要怎么配合。

## 1. 当前事实

### 1.1 客户端当前已经具备的能力

当前仓库已经实现了这些主链路：

- `ProjectBundleService` 可以把云端 zip 下载、解压、物化到本地项目工作区
- `ProjectStoreService` 可以维护 `projects/<projectId>`、active project、project sessions、bound skills
- `ProjectExecutionRouter` 可以在 `workspace-entry`、`skill`、`chat-fallback` 之间路由
- `ProjectContextService` 会把当前项目的 `SOUL.md`、`USER.md`、`README.md`、bound skills 注入隔离上下文
- UI 已经有 `projects`、`currentProjectId`、`skills` 这些项目级展示能力

也就是说，客户端架构本身已经是“项目优先”，不是“全局 skills 池优先”。

### 1.2 真实抖音包现状

真实目录：

`D:\微信缓存\xwechat_files\wxid_wo77jy2n4p5q21_3789\msg\file\2026-04\douyin`

已确认的结构事实：

- 根目录就是一个完整项目根，不是单 skill 包
- 根目录存在 `skills/`
- 根目录存在 `AGENTS.md`
- 根目录存在 `SOUL.md`
- 根目录存在 `USER.md`
- 根目录存在 `openclaw.json`
- 根目录不存在 `project.json`
- 根目录不存在旧版 `plugin/openclaw.plugin.json`
- `skills/` 下已经有成组的 Douyin skills，例如：
  - `douyin-master`
  - `douyin-writer`
  - `douyin-visual`
  - `douyin-publisher`
  - `douyin-seedance-split`
  - `jimeng-omnihuman`
  - `seedance-2-0-video-generator`
  - `seedream-image-generator`

这说明该包已经具备“项目内容”和“项目内 skills”，缺的不是技能内容，而是客户端识别项目所需的最小项目清单。

### 1.3 小红书后续约束

后续小红书项目也按与当前抖音包相同的结构交付。  
因此本方案要求：

- 抖音包和小红书包都采用相同的项目根组织方式
- 两个包都通过各自的 `project.json` 声明项目身份和默认入口
- 不要求客户端从 `SKILL.md` 反推整个项目

## 2. 结论

基于当前代码现状，两个项目包的最小改动是：

- 抖音包补一个极简 `project.json`
- 小红书包补一个极简 `project.json`

除此之外：

- `skills/` 目录结构可以先不改
- `SKILL.md` 顶部元数据可以继续保留，不需要改成项目级配置来源
- `AGENTS.md`、`SOUL.md`、`USER.md` 可以继续作为项目上下文文件存在

不推荐的方案：

- 不推荐只靠扫描 `skills/*/SKILL.md` 顶部 metadata 去推断整个项目
- 不推荐把“项目识别”和“skill 识别”混成一层
- 不推荐让客户端为当前两个包额外做复杂的 `openclaw.json` 兼容推断作为主路径

原因很直接：

- 当前客户端主链路已经围绕 `project.json` 工作
- skill frontmatter 目前只适合做 skill 展示和 skill 路由，不适合承担项目入口声明
- 补一个 `project.json` 的成本远低于补一整套客户端兼容推断逻辑

## 3. 项目包交付约定

### 3.1 一包一项目

每个 zip 只承载一个项目：

- `douyin.zip` 对应 `douyin`
- `xhs.zip` 对应 `xhs`

客户端继续按当前实现处理：

- 云端配置返回多个 zip 下载项
- 客户端下载并解压每个 zip
- 每个 zip 物化成一个独立 project

### 3.2 根目录约定

zip 解压后，根目录直接是项目根。  
不要求再额外包一层无意义目录。

推荐最小结构：

```text
<project-root>/
  project.json
  AGENTS.md
  SOUL.md
  USER.md
  README.md
  skills/
  output/
```

说明：

- `README.md` 强烈建议有，但不是本次唯一阻塞项
- `output/` 是否存在由项目自身决定，不影响客户端识别

### 3.3 `project.json` 的职责

`project.json` 在本方案里只承担项目级声明：

- 稳定项目 ID
- 专家页展示基础信息
- 项目平台
- 默认执行入口
- 项目绑定的 skills

它不负责：

- 描述每个 skill 的详细说明
- 取代 `SKILL.md`
- 取代 `AGENTS.md` / `SOUL.md` / `USER.md`

## 4. 项目包模板

### 4.1 抖音 `project.json`

放在抖音 zip 根目录：

```json
{
  "id": "douyin",
  "name": "douyin",
  "platform": "douyin",
  "projectType": "automation-project",
  "description": "抖音视频文案与视频生成专家",
  "version": "2026-04",
  "defaultEntry": {
    "id": "workspace-entry",
    "type": "workspace-entry",
    "requiresSkills": [],
    "capabilities": [
      "video-script",
      "storyboard",
      "video-generation",
      "publish"
    ],
    "intentAliases": [
      "抖音",
      "抖音视频",
      "视频文案",
      "分镜",
      "口播视频",
      "数字人视频",
      "生成视频"
    ]
  },
  "entries": [
    {
      "id": "workspace-entry",
      "type": "workspace-entry",
      "requiresSkills": [],
      "capabilities": [
        "video-script",
        "storyboard",
        "video-generation",
        "publish"
      ],
      "intentAliases": [
        "抖音",
        "抖音视频",
        "视频文案",
        "分镜",
        "口播视频",
        "数字人视频",
        "生成视频"
      ]
    }
  ],
  "boundSkillIds": [
    "douyin-intake-planner",
    "douyin-master",
    "douyin-publisher",
    "douyin-seedance-split",
    "douyin-visual",
    "douyin-writer",
    "jimeng-omnihuman",
    "seedance-2-0-video-generator",
    "seedream-image-generator"
  ],
  "ready": true
}
```

### 4.2 小红书 `project.json`

放在小红书 zip 根目录：

```json
{
  "id": "xhs",
  "name": "xhs",
  "platform": "xiaohongshu",
  "projectType": "automation-project",
  "description": "小红书内容生成与发布专家",
  "version": "2026-04",
  "defaultEntry": {
    "id": "workspace-entry",
    "type": "workspace-entry",
    "requiresSkills": [],
    "capabilities": [
      "topic-selection",
      "copywriting",
      "image-generation",
      "publish"
    ],
    "intentAliases": [
      "小红书",
      "笔记",
      "种草文案",
      "发笔记",
      "封面图",
      "生成配图"
    ]
  },
  "entries": [
    {
      "id": "workspace-entry",
      "type": "workspace-entry",
      "requiresSkills": [],
      "capabilities": [
        "topic-selection",
        "copywriting",
        "image-generation",
        "publish"
      ],
      "intentAliases": [
        "小红书",
        "笔记",
        "种草文案",
        "发笔记",
        "封面图",
        "生成配图"
      ]
    }
  ],
  "boundSkillIds": [
    "xhs",
    "xhs-pipeline"
  ],
  "ready": true
}
```

### 4.3 模板约束

两个项目包都必须满足：

- `id` 稳定且不可漂移
- `boundSkillIds` 必须与包内 `skills/<skillId>` 目录名一致
- `defaultEntry.type` 固定为 `"workspace-entry"`
- `entries` 至少显式声明一条 `workspace-entry`
- 不把真实用户密钥、长期 token、生产私密凭证固化进 zip 包

## 5. 客户端改造方案

### 5.1 主链路保持不变

本次不需要推翻现有项目隔离架构。  
客户端继续沿用当前实现：

1. 云端配置返回多个 zip
2. `ProjectBundleService` 下载并解压 zip
3. 每个 zip 物化到独立 `projects/<projectId>`
4. `ProjectStoreService` 把项目写入本地 inventory
5. 专家页按 project 展示
6. 进入项目后按当前 active project 读取该项目的 skills、sessions、context

### 5.2 `project-bundle.ts`

保持当前 bundle 同步逻辑，补两点明确约束：

- 安装后校验项目根必须存在 `project.json`
- 若缺失，给出明确错误，而不是静默降级

推荐行为：

- zip 缺少 `project.json` 时，bundle sync 标记为 error
- 错误内容包含 zip 文件名和项目根路径

不建议在这里新增“扫描 `openclaw.json` 自动生成项目”的主路径逻辑。  
这类兼容逻辑只应作为临时兜底，不应成为标准交付方式。

### 5.3 `project-store.ts`

这里是本次最值得补强的小改造点。

建议增加：

- `boundSkillIds` 与实际 `skills/` 子目录交叉校验
- 若 `boundSkillIds` 中有不存在的 skill，则：
  - 从最终项目 skill 列表中过滤掉
  - 记录日志
  - 不让整个项目失效

建议保持：

- `listProjectSkills(projectId)` 只返回当前项目绑定的 skills
- session 仍保持 `project:<projectId>:<uuid>` 格式

### 5.4 `project-execution-router.ts`

这里不需要大改。  
当前路由规则继续成立：

- 未显式选 skill 时，优先走 `workspace-entry`
- 发布/自动执行类请求，优先走项目级入口
- 只有项目内部需要精确 skill 分流时，才交给 skill router

这和抖音 / 小红书两个项目包的定位是一致的：

- 一级入口是项目
- skill 是项目内能力，不是项目本身

### 5.5 UI / 专家页

UI 也不需要推翻，只需要明确展示层次：

- 专家页展示 project 列表，不展示全局混合 skill 列表
- 点击项目卡片后切换 active project
- 进入项目后，只显示当前项目的 skills
- 当前项目的 sessions 也只属于当前 project

需要坚持的交互约束：

- 抖音项目不显示小红书 skills
- 小红书项目不显示抖音 skills
- 不允许跨项目复用 session skill 绑定

## 6. 为什么不走“只识别 skills 就够了”

“只识别各自 `skills/` 目录就可以跑起来”这句话只对一半：

- 对“项目内 skill 展示和 skill 路由”成立
- 对“项目本身如何被专家页识别、如何确定默认入口”不成立

当前代码里：

- project 识别靠 `project.json` 和项目级元数据
- skill 识别靠 `skills/*/SKILL.md`

这两层不能混成一层。  
如果只靠 `SKILL.md` 反推项目，客户端需要新增：

- 从多个 skill 中推断项目 ID
- 从多个 skill 中推断默认入口
- 处理主 skill / 辅助 skill / 通用 skill 歧义
- 处理 skill 缺失时项目是否仍然可见

这比每个包补一个 `project.json` 明显更复杂，也更不稳定。

## 7. 验收标准

至少完成以下验证：

1. 云端配置同时下发 `douyin.zip` 与 `xhs.zip`
2. 客户端本地出现两个独立项目
3. 专家页出现两个项目卡片
4. 选中 `douyin` 后，只能看到 douyin skills
5. 选中 `xhs` 后，只能看到 xhs skills
6. 在 `douyin` 项目内发“生成一个苹果抖音视频文案”，默认走 `workspace-entry`
7. 在 `xhs` 项目内发“小红书笔记文案”，默认走 `workspace-entry`
8. 切换项目不会污染对方的 session skill 绑定
9. `boundSkillIds` 配错时，客户端给出可诊断错误，不崩
10. 移除某个 zip 后，对应项目能被清理，另一个项目不受影响

## 8. 推荐实施顺序

### 第一步：项目包侧

先做最小包改动：

- 给抖音包补 `project.json`
- 给小红书包补 `project.json`
- 校对 `boundSkillIds`

### 第二步：客户端侧

补小幅硬化：

- `project-bundle.ts` 增加 `project.json` 缺失校验
- `project-store.ts` 增加 `boundSkillIds` 与实际目录交叉校验
- UI 确认专家页使用 project 维度展示，不混显示全局 skills

### 第三步：联调验收

用真实云端配置跑完整链路：

- cloud config
- zip sync
- 专家页选项目
- 进入项目后发送自然语言
- 命中 `workspace-entry`

## 9. 最终决策

对当前这两个项目包，标准交付方式定为：

- 一包一项目
- 根目录保留现有结构
- 各自补一个极简 `project.json`
- 客户端继续沿用现有项目隔离架构

本期不把“只靠 `skills/*/SKILL.md` metadata 自动反推整个项目”作为主目标。
