docs: add multi-scene-kind generator design spec
Design for extending sg_scene_generate to support multiple scene kinds (report_collection, monitoring) with manual type selection in Web UI, relaxing the requirement for meta tags in third-party scene directories. 🤖 Generated with [Qoder][https://qoder.com]
This commit is contained in:
@@ -0,0 +1,309 @@
|
||||
# Multi-Scene-Kind Generator Design
|
||||
|
||||
> **Status:** Draft
|
||||
> **Date:** 2026-04-16
|
||||
> **Author:** Qoder
|
||||
|
||||
## Problem Statement
|
||||
|
||||
`sg_scene_generate` 当前只支持 `report_collection` 类型的场景,强制要求场景目录的 `index.html` 包含 `sgclaw-scene-kind` 和 `sgclaw-tool-kind` meta 标签。
|
||||
|
||||
**现实情况**:
|
||||
- 400+ 第三方场景目录**没有** meta 标签
|
||||
- 场景类型不单一:既有**报表收集类**(查询数据导出 Excel),也有**监测类**(定时检查状态、监控告警)
|
||||
- 不可能要求所有第三方场景添加 meta 标签
|
||||
|
||||
**结果**:当前 `sg_scene_generate` 对真实场景目录完全无法使用。
|
||||
|
||||
## Goal
|
||||
|
||||
扩展 `sg_scene_generate` 支持多种场景类型,让用户在 Web UI 上**手动选择场景类型**,而不是依赖场景目录中的 meta 标签。
|
||||
|
||||
## Non-Goals
|
||||
|
||||
- 不实现 LLM 自动识别场景类型(后续增强)
|
||||
- 不实现运行时自动推断场景类型
|
||||
- 不修改 `registry.rs` 的运行时校验逻辑(V1 仍只支持已注册的类型)
|
||||
- 不实现完整的监测类参数解析器(生成简化模板,用户手动编辑)
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ Web UI (HTML) │
|
||||
│ ┌─────────────┐ ┌──────────────┐ ┌──────────────────┐ │
|
||||
│ │ 场景路径输入 │ │ 场景类型下拉框│ │ 分析/生成按钮 │ │
|
||||
│ └─────────────┘ └──────────────┘ └──────────────────┘ │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ Node.js Server │
|
||||
│ /analyze → LLM 提取 scene-id, scene-name │
|
||||
│ /generate → 调用 cargo run,传递 --scene-kind 参数 │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ sg_scene_generate (Rust CLI) │
|
||||
│ --scene-kind report_collection | monitoring │
|
||||
│ │
|
||||
│ analyzer.rs → 放宽 meta 校验,接受用户指定类型 │
|
||||
│ generator.rs → 根据类型选择不同模板 │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## Implementation Details
|
||||
|
||||
### 1. analyzer.rs — 放宽校验逻辑
|
||||
|
||||
**当前行为**:
|
||||
- 强制要求 `sgclaw-scene-kind` meta 标签 = `report_collection`
|
||||
- 强制要求 `sgclaw-tool-kind` meta 标签 = `browser_script`
|
||||
- 缺失则报错退出
|
||||
|
||||
**新行为**:
|
||||
- meta 标签**可选**
|
||||
- 如果缺失,使用用户通过 `--scene-kind` 参数指定的类型
|
||||
- 如果用户未指定,默认为 `report_collection`
|
||||
- `sgclaw-tool-kind` 默认为 `browser_script`(V1 只支持这一种)
|
||||
|
||||
**枚举扩展**:
|
||||
```rust
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum SceneKind {
|
||||
ReportCollection,
|
||||
Monitoring,
|
||||
}
|
||||
|
||||
impl SceneKind {
|
||||
pub fn from_str(s: &str) -> Option<Self> {
|
||||
match s {
|
||||
"report_collection" => Some(Self::ReportCollection),
|
||||
"monitoring" => Some(Self::Monitoring),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**函数签名变更**:
|
||||
```rust
|
||||
// 改动前
|
||||
pub fn analyze_scene_source(source_dir: &Path) -> Result<SceneSourceAnalysis, AnalyzeSceneError>;
|
||||
|
||||
// 改动后
|
||||
pub fn analyze_scene_source(
|
||||
source_dir: &Path,
|
||||
scene_kind_hint: Option<SceneKind>,
|
||||
) -> Result<SceneSourceAnalysis, AnalyzeSceneError>;
|
||||
```
|
||||
|
||||
### 2. generator.rs — 多模板支持
|
||||
|
||||
**函数签名变更**:
|
||||
```rust
|
||||
// 改动前
|
||||
fn scene_toml(request: &GenerateSceneRequest, tool_name: &str, expected_domain: &str, target_url: &str) -> String;
|
||||
|
||||
// 改动后
|
||||
fn scene_toml(request: &GenerateSceneRequest, analysis: &SceneSourceAnalysis, tool_name: &str) -> String;
|
||||
```
|
||||
|
||||
**模板路由**:
|
||||
```rust
|
||||
fn scene_toml(request: &GenerateSceneRequest, analysis: &SceneSourceAnalysis, tool_name: &str) -> String {
|
||||
match analysis.scene_kind {
|
||||
SceneKind::ReportCollection => scene_toml_report_collection(request, analysis, tool_name),
|
||||
SceneKind::Monitoring => scene_toml_monitoring(request, analysis, tool_name),
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3. sg_scene_generate.rs — 新增 CLI 参数
|
||||
|
||||
**新增参数**:
|
||||
```
|
||||
--scene-kind <report_collection|monitoring>
|
||||
场景类型,默认 report_collection
|
||||
- report_collection: 报表收集类(查询数据导出报表)
|
||||
- monitoring: 监测类(定时检查状态、监控告警)
|
||||
```
|
||||
|
||||
**用法示例**:
|
||||
```bash
|
||||
# 报表类(默认)
|
||||
cargo run --bin sg_scene_generate -- \
|
||||
--source-dir "D:/desk/场景/营销报表" \
|
||||
--scene-id marketing-report \
|
||||
--scene-name "营销报表" \
|
||||
--output-root "./out" \
|
||||
--lessons "docs/superpowers/references/tq-lineloss-lessons-learned.toml"
|
||||
|
||||
# 监测类
|
||||
cargo run --bin sg_scene_generate -- \
|
||||
--source-dir "D:/desk/场景/设备监测" \
|
||||
--scene-id device-monitor \
|
||||
--scene-name "设备监测" \
|
||||
--scene-kind monitoring \
|
||||
--output-root "./out" \
|
||||
--lessons "docs/superpowers/references/tq-lineloss-lessons-learned.toml"
|
||||
```
|
||||
|
||||
### 4. 监测类模板设计
|
||||
|
||||
监测类场景差异较大,生成**简化模板**,用户后续手动编辑:
|
||||
|
||||
```toml
|
||||
[scene]
|
||||
id = "<scene-id>"
|
||||
skill = "<scene-id>"
|
||||
tool = "monitor_<scene-id>"
|
||||
kind = "browser_script"
|
||||
version = "0.1.0"
|
||||
category = "monitoring"
|
||||
|
||||
[manifest]
|
||||
schema_version = "1"
|
||||
|
||||
[bootstrap]
|
||||
expected_domain = "<从 analyzer 提取或空>"
|
||||
target_url = "<从 analyzer 提取或空>"
|
||||
requires_target_page = true
|
||||
|
||||
[deterministic]
|
||||
suffix = "。。。"
|
||||
include_keywords = ["<scene-name>"]
|
||||
exclude_keywords = []
|
||||
|
||||
# 参数部分留空,用户手动编辑
|
||||
# [[params]]
|
||||
# name = "xxx"
|
||||
# resolver = "literal_passthrough"
|
||||
|
||||
[artifact]
|
||||
type = "monitoring-status"
|
||||
success_status = ["ok", "running"]
|
||||
failure_status = ["error", "timeout"]
|
||||
|
||||
# 后处理留空,用户手动编辑
|
||||
```
|
||||
|
||||
### 5. Web UI 改动
|
||||
|
||||
**新增控件**:
|
||||
```html
|
||||
<div class="form-group">
|
||||
<label>场景类型</label>
|
||||
<select id="sceneKind">
|
||||
<option value="report_collection" selected>报表收集类</option>
|
||||
<option value="monitoring">监测类</option>
|
||||
</select>
|
||||
<span class="hint">报表类:查询数据导出 Excel;监测类:定时检查状态</span>
|
||||
</div>
|
||||
```
|
||||
|
||||
**JavaScript 改动**:
|
||||
```javascript
|
||||
// generate() 函数增加 sceneKind 参数
|
||||
const sceneKind = document.getElementById('sceneKind').value;
|
||||
const response = await fetch('/generate', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
sourceDir,
|
||||
sceneId,
|
||||
sceneName,
|
||||
sceneKind, // 新增
|
||||
outputRoot,
|
||||
lessons
|
||||
})
|
||||
});
|
||||
```
|
||||
|
||||
### 6. Node.js Server 改动
|
||||
|
||||
**/generate 接口**:
|
||||
```javascript
|
||||
async function handleGenerate(req, res) {
|
||||
const { sourceDir, sceneId, sceneName, sceneKind, outputRoot, lessons } = body;
|
||||
// ...
|
||||
const args = [
|
||||
"run", "--bin", "sg_scene_generate", "--",
|
||||
"--source-dir", normalize(sourceDir),
|
||||
"--scene-id", sceneId,
|
||||
"--scene-name", sceneName,
|
||||
"--scene-kind", sceneKind || "report_collection", // 新增
|
||||
"--output-root", normalize(outputRoot),
|
||||
"--lessons", normalize(lessons),
|
||||
];
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
## Compatibility Matrix
|
||||
|
||||
| 场景目录 | meta 标签 | 用户选择 | 最终类型 |
|
||||
|---------|----------|---------|---------|
|
||||
| 有 meta | `report_collection` | 未选择 | `report_collection` |
|
||||
| 有 meta | `report_collection` | `report_collection` | `report_collection` |
|
||||
| 有 meta | `report_collection` | `monitoring` | `monitoring`(用户优先) |
|
||||
| 无 meta | 无 | 未选择 | `report_collection`(默认) |
|
||||
| 无 meta | 无 | `monitoring` | `monitoring` |
|
||||
|
||||
**用户选择优先于 meta 标签**:因为用户比静态 meta 标签更了解场景的实际用途。
|
||||
|
||||
## File Changes Summary
|
||||
|
||||
| 文件 | 改动类型 | 改动量 |
|
||||
|------|---------|-------|
|
||||
| `src/generated_scene/analyzer.rs` | 修改 | ~30 行 |
|
||||
| `src/generated_scene/generator.rs` | 修改 | ~50 行 |
|
||||
| `src/bin/sg_scene_generate.rs` | 修改 | ~20 行 |
|
||||
| `frontend/scene-generator/sg_scene_generator.html` | 修改 | ~15 行 |
|
||||
| `frontend/scene-generator/server.js` | 修改 | ~5 行 |
|
||||
| `frontend/scene-generator/generator-runner.js` | 修改 | ~5 行 |
|
||||
| `tests/scene_generator_test.rs` | 修改 | ~30 行 |
|
||||
| `tests/fixtures/generated_scene/monitoring/index.html` | 新增 | ~20 行 |
|
||||
|
||||
**总计**:~175 行改动,8 个文件。
|
||||
|
||||
## Testing Strategy
|
||||
|
||||
### 单元测试
|
||||
|
||||
1. `analyzer_accepts_missing_meta_with_hint` — 缺失 meta 标签时,使用 hint 参数
|
||||
2. `analyzer_uses_meta_when_present` — 有 meta 标签时,使用 meta 值
|
||||
3. `generator_emits_report_collection_template` — 报表类模板正确
|
||||
4. `generator_emits_monitoring_template` — 监测类模板正确
|
||||
|
||||
### 集成测试
|
||||
|
||||
1. 无 meta 标签的场景目录 + `--scene-kind report_collection` → 生成成功
|
||||
2. 无 meta 标签的场景目录 + `--scene-kind monitoring` → 生成成功
|
||||
3. Web UI 选择监测类 → 生成的 scene.toml 包含 `category = "monitoring"`
|
||||
|
||||
### 手动验证
|
||||
|
||||
1. 真实场景目录 `D:\desk\智能体资料\场景\营销2.0零度户报表数据生成` → 选择报表类 → 生成成功
|
||||
2. 真实监测类场景 → 选择监测类 → 生成成功
|
||||
|
||||
## Risks and Mitigations
|
||||
|
||||
| 风险 | 影响 | 缓解措施 |
|
||||
|------|------|---------|
|
||||
| 监测类模板过于简化 | 用户需要大量手动编辑 | 文档说明 + 后续迭代优化 |
|
||||
| 用户选错类型 | 生成错误模板 | UI 上提供清晰说明 |
|
||||
| registry.rs 不支持 monitoring | 生成的包无法注册 | V1 只生成,运行时支持后续迭代 |
|
||||
|
||||
## Open Questions
|
||||
|
||||
1. 监测类场景的 `artifact.type` 应该是什么?当前设计为 `monitoring-status`,是否合适?
|
||||
2. 监测类是否需要新的 resolver 类型?
|
||||
3. 是否需要在前端 UI 显示更多类型说明?
|
||||
|
||||
## References
|
||||
|
||||
- `docs/superpowers/plans/2026-04-15-generated-scene-skill-platform-plan.md` — 原实现计划
|
||||
- `src/generated_scene/analyzer.rs` — 当前分析器
|
||||
- `src/generated_scene/generator.rs` — 当前生成器
|
||||
Reference in New Issue
Block a user