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]
310 lines
11 KiB
Markdown
310 lines
11 KiB
Markdown
# 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` — 当前生成器
|