feat: scene skill generator — complete implementation
Adds a web-based UI for generating scene skill packages:
- Node.js HTTP server (zero npm dependencies) on port 3210
- HTML page with glass-morphism UI, dual-panel layout, settings modal
- LLM-powered scene-id/scene-name auto-extraction from directory contents
- Real-time SSE progress streaming during skill generation
- Spawns sg_scene_generate CLI with configurable parameters
- Windows-compatible startup scripts (serve.sh + serve.cmd)
- Rust integration tests for server files and HTML structure
Architecture:
Browser (HTML/JS) → Node.js server → LLM API + cargo run → sg_scene_generate
Files:
frontend/scene-generator/{server.js,config-loader.js,llm-client.js,generator-runner.js,sg_scene_generator.html,serve.sh,serve.cmd}
tests/{scene_generator_server_test.rs,scene_generator_html_test.rs,scene_generator_llm_test.js}
docs/superpowers/{plans,specs}/2026-04-16-scene-skill-generator*
🤖 Generated with [Qoder][https://qoder.com]
This commit is contained in:
@@ -0,0 +1,417 @@
|
||||
# Scene Skill Generator — Design Document
|
||||
|
||||
> **Date:** 2026-04-16
|
||||
> **Status:** Draft — awaiting review
|
||||
> **Author:** Qoder
|
||||
|
||||
---
|
||||
|
||||
## 1. Goal
|
||||
|
||||
提供一个可视化界面,让用户选择场景目录后,自动通过大模型提取 scene-id 和 scene-name,配置输出路径和 lessons 文件,一键调用 `sg_scene_generate` 生成完整的 skill 包,并实时查看生成日志。
|
||||
|
||||
---
|
||||
|
||||
## 2. Architecture
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ sg_scene_generator.html (浏览器) │
|
||||
│ ┌───────────────────┐ ┌───────────────────────────────┐ │
|
||||
│ │ 左侧:操作面板 │ │ 右侧:实时日志流 │ │
|
||||
│ │ │ │ │ │
|
||||
│ │ 📂 选择场景目录 │───────│ [状态卡片 + 实时滚动日志] │ │
|
||||
│ │ │ │ │ │
|
||||
│ │ 自动填充字段: │ │ 分析场景目录... │ │
|
||||
│ │ - scene-id │ │ 调用大模型提取场景信息... │ │
|
||||
│ │ - scene-name │ │ scene-id: tq-lineloss-report │ │
|
||||
│ │ │ │ scene-name: 台区线损报表 │ │
|
||||
│ │ 可编辑字段: │ │ 生成 skill 包... │ │
|
||||
│ │ - 输出根路径 │ │ 写入 SKILL.toml... │ │
|
||||
│ │ - lessons 路径 │ │ 写入 browser_script... │ │
|
||||
│ │ │ │ ✅ 生成完成 │ │
|
||||
│ │ [⚙ 设置] │ └───────────────────────────────┘ │
|
||||
│ │ [🚀 生成 Skill] │ │
|
||||
│ └───────────────────┘ │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
│
|
||||
│ 1. POST /analyze (选择目录后自动触发)
|
||||
│ → 发送目录路径 + 文件内容
|
||||
│ 2. SSE /generate (点击生成按钮后触发)
|
||||
│ → 推送实时进度
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ server.js (Node.js, 默认端口 3210) │
|
||||
│ │
|
||||
│ POST /analyze │
|
||||
│ 1. 读取 source-dir 下的关键文件 │
|
||||
│ - scene.toml (如果存在) │
|
||||
│ - *.js 脚本文件 │
|
||||
│ - SKILL.md / SKILL.toml (如果存在) │
|
||||
│ - 目录结构树 │
|
||||
│ 2. 构造 prompt,调用 LLM API │
|
||||
│ - baseUrl + apiKey + model 来自 sgclaw_config.json │
|
||||
│ 3. 返回 JSON: { sceneId, sceneName } │
|
||||
│ │
|
||||
│ POST /generate │
|
||||
│ 1. 接收 { sourceDir, sceneId, sceneName, outputRoot, lessons } │
|
||||
│ 2. spawn: cargo run --bin sg_scene_generate \ │
|
||||
│ --source-dir <sourceDir> \ │
|
||||
│ --scene-id <sceneId> \ │
|
||||
│ --scene-name <sceneName> \ │
|
||||
│ --output-root <outputRoot> \ │
|
||||
│ --lessons <lessons> │
|
||||
│ 3. 通过 SSE 实时推送 stdout/stderr │
|
||||
│ 4. 推送完成/失败事件 │
|
||||
│ │
|
||||
│ GET /health │
|
||||
│ → { status: "ok", pid: 12345 } │
|
||||
│ │
|
||||
│ GET / │
|
||||
│ → 服务 sg_scene_generator.html 静态文件 │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
│
|
||||
│ LLM API (OpenAI-compatible format)
|
||||
│ POST {baseUrl}/v1/chat/completions
|
||||
▼
|
||||
┌──────────────────────┐
|
||||
│ LLM (DeepSeek) │
|
||||
│ │
|
||||
│ System: 你是一个场景 │
|
||||
│ 信息提取助手... │
|
||||
│ User: 以下是场景目录 │
|
||||
│ 内容... 请提取 │
|
||||
│ scene-id 和 │
|
||||
│ scene-name │
|
||||
└──────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. File Map
|
||||
|
||||
### 新建文件
|
||||
|
||||
| 文件 | 说明 |
|
||||
|------|------|
|
||||
| `frontend/scene-generator/sg_scene_generator.html` | 主页面,内联 CSS + JS,复用 service-console 设计风格 |
|
||||
| `frontend/scene-generator/server.js` | Node.js 轻量 HTTP 服务器(零外部依赖) |
|
||||
| `frontend/scene-generator/serve.sh` | 一键启动脚本(Windows 兼容) |
|
||||
| `frontend/scene-generator/serve.cmd` | Windows 一键启动脚本 |
|
||||
| `frontend/scene-generator/config-loader.js` | 读取并解析 `sgclaw_config.json` |
|
||||
| `frontend/scene-generator/llm-client.js` | 封装 LLM API 调用(OpenAI-compatible 格式) |
|
||||
| `frontend/scene-generator/generator-runner.js` | 封装 `sg_scene_generate` 子进程调用 + SSE 推送 |
|
||||
|
||||
### 引用文件(不修改)
|
||||
|
||||
| 文件 | 用途 |
|
||||
|------|------|
|
||||
| `src/bin/sg_scene_generate.rs` | 被 server.js 通过 `cargo run` 调用 |
|
||||
| `src/generated_scene/generator.rs` | 理解生成逻辑和输出结构 |
|
||||
| `sgclaw_config.json` | 读取 LLM 连接配置(apiKey, baseUrl, model) |
|
||||
| `docs/superpowers/references/tq-lineloss-lessons-learned.toml` | 默认 lessons 路径 |
|
||||
| `frontend/service-console/sg_claw_service_console.html` | UI 风格参考 |
|
||||
|
||||
---
|
||||
|
||||
## 4. UI Design
|
||||
|
||||
### 4.1 整体布局
|
||||
|
||||
复用 service-console 的双栏布局:
|
||||
|
||||
- **外层容器 (`.shell`)**:圆角玻璃拟态面板,与 service-console 共享 CSS 变量
|
||||
- **顶部 (`.hero`)**:标题 "场景 Skill 生成器" + 简短说明
|
||||
- **内容区 (`.content`)**:`grid` 双栏,左侧操作面板 + 右侧日志流
|
||||
|
||||
### 4.2 左侧操作面板
|
||||
|
||||
#### 场景目录选择区
|
||||
|
||||
```
|
||||
📂 场景目录
|
||||
[ 粘贴或输入路径 ____________________________ ] [ 浏览 📁 ]
|
||||
当前:D:\data\ideaSpace\rust\sgClaw\claw-new\examples\generated_scene_platform\scenarios\tq-lineloss-report
|
||||
```
|
||||
|
||||
使用文本输入框 + "浏览" 按钮。点击 "浏览" 时,前端调用 `POST /browse`,由 Node.js 弹出系统目录选择对话框(通过 `electron` 风格的 `open-dialog` 不可行 — 改为**用户在输入框中粘贴/输入路径**,服务端通过 `fs.stat` 校验路径合法性)。
|
||||
|
||||
为简化实现,采用更务实的方案:
|
||||
- 主输入框:用户粘贴或手动输入场景目录的**绝对路径**
|
||||
- 输入路径后按回车或点击 "分析" 按钮,触发 `/analyze` 请求
|
||||
- 服务端通过 `fs.statSync(sourceDir).isDirectory()` 校验路径
|
||||
|
||||
**可选增强**:如果 Node.js 安装了 `electron`,可通过 `dialog.showOpenDialog` 弹出系统选择框,但这会增加依赖。默认不采用。
|
||||
|
||||
#### 自动提取结果(只读展示,可手动修正)
|
||||
|
||||
```
|
||||
scene-id
|
||||
tq-lineloss-report
|
||||
|
||||
scene-name
|
||||
台区线损报表
|
||||
```
|
||||
|
||||
分析中显示 loading 状态,分析失败时可手动输入。
|
||||
|
||||
#### 设置按钮
|
||||
|
||||
点击弹出模态框,包含以下字段:
|
||||
|
||||
| 字段 | 默认值 | 说明 |
|
||||
|------|--------|------|
|
||||
| 输出根路径 | `D:/data/ideaSpace/rust/sgClaw/claw-new/examples/generated_scene_platform` | skill 包输出根目录,实际输出到 `<output-root>/skills/<scene-id>/` |
|
||||
| Lessons 路径 | `D:/data/ideaSpace/rust/sgClaw/claw-new/docs/superpowers/references/tq-lineloss-lessons-learned.toml` | lessons TOML 文件路径 |
|
||||
| LLM 服务地址 | 来自 `sgclaw_config.json` 的 `baseUrl` | 可覆盖 |
|
||||
| LLM 模型 | 来自 `sgclaw_config.json` 的 `model` | 可覆盖 |
|
||||
| Node 服务端口 | `3210` | server.js 监听端口 |
|
||||
|
||||
#### 生成按钮
|
||||
|
||||
```
|
||||
[ 🚀 生成 Skill ] (disabled 直到选择了目录且提取完成)
|
||||
```
|
||||
|
||||
### 4.3 右侧日志流
|
||||
|
||||
与 service-console 一致的流式日志展示:
|
||||
|
||||
- **空状态**:显示提示 "选择场景目录开始生成"
|
||||
- **status 行**:关键阶段标记("开始分析", "提取完成", "开始生成", "生成成功")
|
||||
- **log 行**:cargo run 的 stdout 输出
|
||||
- **error 行**:stderr 输出或错误信息
|
||||
- **complete 行**:最终结果,包含生成的 skill 包路径
|
||||
|
||||
### 4.4 状态卡片
|
||||
|
||||
左侧面板顶部显示当前状态:
|
||||
|
||||
```
|
||||
[●] 就绪 / 分析中 / 生成中 / 完成 / 错误
|
||||
```
|
||||
|
||||
颜色编码:
|
||||
- 就绪:灰色
|
||||
- 分析中:橙色
|
||||
- 生成中:青色(accent)
|
||||
- 完成:绿色
|
||||
- 错误:红色
|
||||
|
||||
---
|
||||
|
||||
## 5. API Design
|
||||
|
||||
### 5.1 POST /analyze
|
||||
|
||||
**请求体:**
|
||||
|
||||
```json
|
||||
{
|
||||
"sourceDir": "D:/data/ideaSpace/rust/sgClaw/claw-new/examples/generated_scene_platform/scenarios/tq-lineloss-report"
|
||||
}
|
||||
```
|
||||
|
||||
服务端自行读取目录内容:
|
||||
- 校验路径是否存在且为目录
|
||||
- 读取 `scene.toml`(如果存在)
|
||||
- 读取 `*.js` 脚本文件
|
||||
- 读取 `SKILL.md` / `SKILL.toml`(如果存在)
|
||||
- 生成目录结构树
|
||||
|
||||
**响应:**
|
||||
|
||||
```json
|
||||
{
|
||||
"sceneId": "tq-lineloss-report",
|
||||
"sceneName": "台区线损报表"
|
||||
}
|
||||
```
|
||||
|
||||
**LLM Prompt 设计:**
|
||||
|
||||
```
|
||||
System: 你是一个场景信息提取助手。根据场景目录的内容,提取 scene-id 和 scene-name。
|
||||
|
||||
scene-id 规则:
|
||||
- 使用英文短横线连接,如 tq-lineloss-report
|
||||
- 全小写,有业务含义
|
||||
|
||||
scene-name 规则:
|
||||
- 使用中文,简短描述性名称
|
||||
- 如 "台区线损报表"、"知乎热榜导出"
|
||||
|
||||
User: 以下是场景目录的内容:
|
||||
|
||||
=== scene.toml ===
|
||||
[scene content here]
|
||||
|
||||
=== 脚本文件 ===
|
||||
[script content here]
|
||||
|
||||
=== 目录结构 ===
|
||||
[file tree here]
|
||||
|
||||
请以 JSON 格式返回:{"sceneId": "...", "sceneName": "..."}
|
||||
```
|
||||
|
||||
### 5.2 POST /generate (SSE)
|
||||
|
||||
**请求体:**
|
||||
|
||||
```json
|
||||
{
|
||||
"sourceDir": "/path/to/scenario/dir",
|
||||
"sceneId": "tq-lineloss-report",
|
||||
"sceneName": "台区线损报表",
|
||||
"outputRoot": "/path/to/output/root",
|
||||
"lessons": "/path/to/lessons.toml"
|
||||
}
|
||||
```
|
||||
|
||||
**SSE 事件流:**
|
||||
|
||||
```
|
||||
event: status
|
||||
data: {"message": "开始生成 skill 包..."}
|
||||
|
||||
event: status
|
||||
data: {"message": "调用 sg_scene_generate..."}
|
||||
|
||||
event: log
|
||||
data: {"message": "generated scene package: ..."}
|
||||
|
||||
event: complete
|
||||
data: {"success": true, "skillRoot": "/path/to/skills/tq-lineloss-report"}
|
||||
|
||||
或
|
||||
|
||||
event: error
|
||||
data: {"message": "生成失败: ..."}
|
||||
```
|
||||
|
||||
### 5.3 GET /health
|
||||
|
||||
**响应:**
|
||||
|
||||
```json
|
||||
{
|
||||
"status": "ok",
|
||||
"pid": 12345,
|
||||
"configLoaded": true,
|
||||
"configPath": "D:/data/ideaSpace/rust/sgClaw/sgclaw_config.json"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. Server Design (server.js)
|
||||
|
||||
### 6.1 模块结构
|
||||
|
||||
```
|
||||
server.js — HTTP 路由入口,SSE 连接管理
|
||||
config-loader.js — 读取 sgclaw_config.json,暴露 LLM 配置 + projectRoot
|
||||
llm-client.js — 调用 LLM API,返回 JSON 提取结果
|
||||
generator-runner.js — spawn 子进程,通过 SSE 推送输出
|
||||
```
|
||||
|
||||
### 6.1.1 projectRoot 配置
|
||||
|
||||
`cargo run --bin sg_scene_generate` 需要在项目根目录下执行。`projectRoot` 的确定优先级:
|
||||
|
||||
1. 环境变量 `SGCLAW_PROJECT_ROOT`(最高优先级)
|
||||
2. `sgclaw_config.json` 同级目录(常见情况:配置文件在项目根目录)
|
||||
3. 启动脚本所在目录
|
||||
|
||||
### 6.2 零依赖原则
|
||||
|
||||
仅使用 Node.js 内置模块:
|
||||
- `http` — HTTP 服务器
|
||||
- `fs` — 文件读取
|
||||
- `path` — 路径处理
|
||||
- `child_process` — 子进程调用
|
||||
- `events` — 事件发射
|
||||
|
||||
### 6.3 启动流程
|
||||
|
||||
```
|
||||
1. 读取 sgclaw_config.json (路径通过环境变量 SGCLAW_CONFIG_PATH 或默认 ../sgclaw_config.json)
|
||||
2. 验证必需字段: apiKey, baseUrl, model
|
||||
3. 启动 HTTP 服务器,监听 0.0.0.0:3210
|
||||
4. 打印启动信息,包含访问地址
|
||||
```
|
||||
|
||||
### 6.4 错误处理
|
||||
|
||||
| 场景 | 处理方式 |
|
||||
|------|----------|
|
||||
| sgclaw_config.json 不存在 | 启动失败,提示用户设置环境变量 |
|
||||
| LLM API 调用失败 | 返回 502 + 错误信息,前端允许手动输入 |
|
||||
| cargo run 失败 | SSE 推送 error 事件,显示 stderr |
|
||||
| source-dir 不存在 | 返回 400 |
|
||||
| 端口被占用 | 启动失败,提示更换端口 |
|
||||
|
||||
---
|
||||
|
||||
## 7. Security Considerations
|
||||
|
||||
1. **仅监听 localhost**:server.js 默认绑定 `127.0.0.1`,不暴露到外部网络
|
||||
2. **API Key 不暴露给前端**:LLM API 调用完全在 Node.js 服务端完成,前端不接触 API Key
|
||||
3. **路径校验**:`sourceDir` 和 `outputRoot` 需做基本路径合法性检查,防止路径遍历攻击
|
||||
4. **子进程超时**:`cargo run` 设置 5 分钟超时,防止挂起
|
||||
|
||||
---
|
||||
|
||||
## 8. Default Configuration
|
||||
|
||||
| 配置项 | 默认值 | 来源 |
|
||||
|--------|--------|------|
|
||||
| LLM apiKey | `sgclaw_config.json` 中的 `apiKey` | 启动时读取 |
|
||||
| LLM baseUrl | `sgclaw_config.json` 中的 `baseUrl` | 启动时读取 |
|
||||
| LLM model | `sgclaw_config.json` 中的 `model` | 启动时读取 |
|
||||
| 默认 lessons 路径 | `docs/superpowers/references/tq-lineloss-lessons-learned.toml` | 项目约定 |
|
||||
| 默认输出根路径 | `examples/generated_scene_platform` | 项目约定 |
|
||||
| Node 服务端口 | `3210` | 硬编码,可配置 |
|
||||
|
||||
---
|
||||
|
||||
## 9. User Flow
|
||||
|
||||
```
|
||||
1. 用户运行 bash serve.sh (或 node server.js)
|
||||
2. 浏览器打开 http://127.0.0.1:3210
|
||||
3. 页面加载,显示 "就绪" 状态
|
||||
4. 用户在 "场景目录" 输入框中粘贴或输入绝对路径
|
||||
5. 用户点击 "分析" 按钮(或输入框回车),触发 /analyze 请求
|
||||
6. server.js 读取目录内容,调用 LLM 提取 scene-id/name
|
||||
7. 页面自动填充 scene-id 和 scene-name 字段
|
||||
8. 用户确认/修改字段,点击 "设置" 检查输出路径和 lessons
|
||||
9. 用户点击 "生成 Skill"
|
||||
10. server.js 通过 SSE 推送实时进度
|
||||
11. 页面右侧日志流展示生成过程
|
||||
12. 生成完成,显示 skill 包路径
|
||||
13. 用户可前往输出目录查看生成的 skill
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 10. Windows Compatibility
|
||||
|
||||
由于目标平台是 Windows:
|
||||
|
||||
- `serve.sh` 同时提供 `serve.cmd` 替代方案
|
||||
- 路径分隔符统一使用 `/`(Node.js `path` 模块自动处理)
|
||||
- `cargo run` 命令在 Windows 上同样可用
|
||||
- 路径输入框支持 Windows 格式路径(如 `D:\data\ideaSpace\...`)
|
||||
- 服务端自动将 `\` 转换为 `/` 以兼容 Rust CLI 参数
|
||||
|
||||
---
|
||||
|
||||
## 11. Future Extensions (Not in Scope)
|
||||
|
||||
- 批量生成:一次选择多个场景目录
|
||||
- 生成后自动注册到 scene.toml manifest
|
||||
- 生成后自动运行 skill 测试
|
||||
- 历史记录:保存之前的生成记录
|
||||
- 生成参数模板:保存常用的输出路径/lessons 组合
|
||||
Reference in New Issue
Block a user