refactor: 统一Hands系统常量到单个源文件 refactor: 更新Hands中文名称和描述 fix: 修复技能市场在连接状态变化时重新加载 fix: 修复身份变更提案的错误处理逻辑 docs: 更新多个功能文档的验证状态和实现位置 docs: 更新Hands系统文档 test: 添加测试文件验证工作区路径
22 KiB
Pipeline DSL 架构设计
目标: 在 ZCLAW 中实现 OpenMagic 类教育功能,支持多步骤自动化工作流 原则: 用户只看到 Pipeline,Hands/Skills 作为内部实现被隐藏
1. 背景
1.1 问题
当前 Hands + Skills 架构存在以下局限:
- 流程编排: Skills 是静态提示模板,无法定义执行流程
- 状态管理: 复杂场景需要跨多个步骤传递状态
- 可扩展性: 不同行业需要不同的生成流程
- 前端绑定: Hands 动作无法直接映射到 UI 组件
1.2 需求
- 输入课题 → 自动生成大纲 → 并行生成场景 → 渲染预览 → 导出文件
- 支持垂直扩展(不同学科)和水平扩展(跨行业)
- 内部播放预览 + 文件导出(PPTX/HTML/PDF)
2. 架构设计
2.1 分层架构
┌─────────────────────────────────────────────────────────┐
│ User Interface │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────┐ │
│ │ Pipeline List│ │ Pipeline Run│ │ Result Preview │ │
│ └─────────────┘ └─────────────┘ └─────────────────┘ │
└─────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ Pipeline Engine (新增) │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────┐ │
│ │ DSL Parser │ │ Executor │ │ State Manager │ │
│ │ YAML/TOML │ │ DAG Runner │ │ Context Store │ │
│ └─────────────┘ └─────────────┘ └─────────────────┘ │
└─────────────────────────────────────────────────────────┘
│
┌───────────────┼───────────────┐
▼ ▼ ▼
┌─────────────────┐ ┌─────────────┐ ┌─────────────────┐
│ Skills (隐藏) │ │ Hands (隐藏)│ │ Exporters │
│ prompt templates│ │ executors │ │ pptx/html/pdf │
└─────────────────┘ └─────────────┘ └─────────────────┘
2.2 核心组件
A. Pipeline DSL (声明式配置)
# pipelines/education/classroom.yaml
apiVersion: zclaw/v1
kind: Pipeline
metadata:
name: classroom-generator
displayName: 互动课堂生成器
category: education
description: 输入课题,自动生成完整互动课堂
spec:
inputs:
- name: topic
type: string
required: true
label: 课题名称
placeholder: 例如:牛顿第二定律
- name: difficulty
type: select
options: [初级, 中级, 高级]
default: 中级
- name: export_formats
type: multi-select
options: [pptx, html, pdf]
default: [pptx, html]
steps:
# Step 1: 解析输入
- id: parse_input
action: llm.generate
template: skills/classroom-generator/parse.md
input:
topic: ${inputs.topic}
difficulty: ${inputs.difficulty}
output: parsed_data
# Step 2: 生成大纲
- id: generate_outline
action: llm.generate
template: skills/classroom-generator/outline.md
input: ${steps.parse_input.output}
output: outline
# Step 3: 并行生成场景
- id: generate_scenes
action: parallel
each: ${steps.generate_outline.output.items}
max_workers: 4
step:
action: llm.generate
template: skills/classroom-generator/scene.md
input:
item: ${item}
index: ${index}
output: scenes[]
# Step 4: 渲染课堂
- id: render
action: classroom.render
input:
outline: ${steps.generate_outline.output}
scenes: ${steps.generate_scenes.output}
output: classroom_data
# Step 5: 导出文件
- id: export
action: file.export
formats: ${inputs.export_formats}
input: ${steps.render.output}
output: export_paths
outputs:
classroom_id: ${steps.render.output.id}
preview_url: /classroom/${steps.render.output.id}
export_files: ${steps.export.output}
B. Pipeline Executor (执行引擎)
// crates/zclaw-pipeline/src/executor.rs
pub struct PipelineExecutor {
skill_registry: Arc<SkillRegistry>,
hand_registry: Arc<HandRegistry>,
llm_driver: Arc<dyn LlmDriver>,
state: PipelineState,
}
impl PipelineExecutor {
pub async fn execute(&self, pipeline: &Pipeline, inputs: Value) -> Result<PipelineOutput> {
let mut context = ExecutionContext::new(inputs);
for step in &pipeline.spec.steps {
match &step.action {
Action::LlmGenerate { template, input } => {
let prompt = self.render_template(template, &context)?;
let response = self.llm_driver.generate(prompt).await?;
context.set_output(&step.id, response);
}
Action::Parallel { each, step, max_workers } => {
let items = context.resolve(each)?;
let results = self.execute_parallel(step, items, *max_workers).await?;
context.set_output(&step.id, results);
}
Action::ClassroomRender { input } => {
let data = context.resolve(input)?;
let classroom = self.render_classroom(data).await?;
context.set_output(&step.id, classroom);
}
Action::FileExport { formats, input } => {
let paths = self.export_files(formats, input).await?;
context.set_output(&step.id, paths);
}
}
}
Ok(context.extract_outputs(&pipeline.outputs))
}
}
C. State Manager (状态管理)
// crates/zclaw-pipeline/src/state.rs
pub struct ExecutionContext {
inputs: Value,
steps_output: HashMap<String, Value>,
variables: HashMap<String, Value>,
}
impl ExecutionContext {
// 解析表达式 ${steps.generate_outline.output.items}
pub fn resolve(&self, expr: &str) -> Result<Value> {
// 支持的表达式:
// ${inputs.topic} - 输入参数
// ${steps.xxx.output} - 步骤输出
// ${item} - 循环变量
// ${index} - 循环索引
}
pub fn set_output(&mut self, step_id: &str, value: Value) {
self.steps_output.insert(step_id.to_string(), value);
}
}
3. 文件结构
3.1 新增 Crate
crates/zclaw-pipeline/
├── Cargo.toml
├── src/
│ ├── lib.rs
│ ├── parser.rs # YAML/TOML DSL 解析
│ ├── executor.rs # 执行引擎
│ ├── state.rs # 状态管理
│ ├── actions/ # 内置动作
│ │ ├── mod.rs
│ │ ├── llm.rs # LLM 调用
│ │ ├── parallel.rs # 并行执行
│ │ ├── render.rs # 渲染
│ │ └── export.rs # 导出
│ └── types.rs # 类型定义
3.2 Pipeline 配置目录
pipelines/
├── education/ # 教育类
│ ├── classroom.yaml # 互动课堂
│ ├── quiz-generator.yaml # 测验生成
│ └── flashcard.yaml # 闪卡生成
├── marketing/ # 营销类
│ ├── campaign.yaml # 营销方案
│ └── content-calendar.yaml # 内容日历
├── legal/ # 法律类
│ └── contract-review.yaml # 合同审查
└── _templates/ # 模板
└── base.yaml
3.3 隐藏 Skills/Hands
Skills 和 Hands 文件保留,但:
- 不在 UI 中直接展示
- 只通过 Pipeline DSL 引用
- 作为内部实现细节
4. 前端集成
4.0 设计原则
- Pipeline 页面 - 类似 Hands 页面,展示所有可用 Pipeline
- 双触发入口 - 页面直接触发 + 对话中 Agent 引导触发
- Agent 智能推荐 - 识别用户意图,推荐合适的 Pipeline
4.1 Pipeline 页面 (新增)
页面布局 (参考 Hands 页面)
┌─────────────────────────────────────────────────────────────┐
│ Pipelines [+ 新建] │
├─────────────────────────────────────────────────────────────┤
│ [全部] [教育] [营销] [法律] [研发] [更多...] │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────┐ ┌─────────────────┐ │
│ │ 📚 互动课堂生成 │ │ 📝 测验生成器 │ │
│ │ │ │ │ │
│ │ 输入课题,自动 │ │ 根据内容生成 │ │
│ │ 生成完整课堂 │ │ 选择题/填空题 │ │
│ │ │ │ │ │
│ │ [开始生成] │ │ [开始生成] │ │
│ └─────────────────┘ └─────────────────┘ │
│ │
│ ┌─────────────────┐ ┌─────────────────┐ │
│ │ 📊 营销方案生成 │ │ ⚖️ 合同审查 │ │
│ │ │ │ │ │
│ │ 输入产品信息, │ │ 上传合同文档, │ │
│ │ 生成营销策略 │ │ 自动审查风险点 │ │
│ │ │ │ │ │
│ │ [开始生成] │ │ [开始生成] │ │
│ └─────────────────┘ └─────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
Pipeline 卡片组件
interface PipelineCard {
id: string;
icon: string; // emoji 或图标
displayName: string;
description: string;
category: string;
tags: string[];
usage_count: number; // 使用次数(用于排序推荐)
last_used?: Date;
}
Pipeline 运行对话框
┌─────────────────────────────────────────┐
│ 互动课堂生成器 [×] │
├─────────────────────────────────────────┤
│ │
│ 课题名称 * │
│ ┌───────────────────────────────────┐ │
│ │ 牛顿第二定律 │ │
│ └───────────────────────────────────┘ │
│ │
│ 难度等级 │
│ ○ 初级 ● 中级 ○ 高级 │
│ │
│ 导出格式 │
│ ☑ PPTX ☑ HTML ☐ PDF │
│ │
│ [取消] [开始生成] │
└─────────────────────────────────────────┘
4.2 对话中触发 (Agent 引导)
Agent 系统提示词注入
// 在 Agent 系统提示词中注入 Pipeline 信息
const systemPrompt = `
你是一个智能助手。用户可能会描述一些任务需求,你可以推荐使用合适的 Pipeline 工具。
可用的 Pipeline 工具:
${pipelines.map(p => `- ${p.displayName}: ${p.description}`).join('\n')}
当用户的需求匹配某个 Pipeline 时:
1. 简要说明该 Pipeline 能做什么
2. 询问用户是否要使用
3. 如果用户同意,调用 run_pipeline 工具启动
示例:
用户: "帮我做一个关于光合作用的课件"
你: "我可以使用【互动课堂生成器】为你自动生成完整课件,包括幻灯片、讲解和测验。需要开始吗?"
`;
Agent 推荐行为模式
主动推荐策略(需求模糊时也推荐):
// Agent 识别潜在需求的模式
const intentPatterns = [
// 教育类
{ keywords: ["准备.*课", "备课", "教案", "教学"], pipeline: "classroom-generator", confidence: 0.7 },
{ keywords: ["学生.*测验", "出题", "考试"], pipeline: "quiz-generator", confidence: 0.8 },
// 营销类
{ keywords: ["推广.*方案", "营销计划", "宣传"], pipeline: "marketing-campaign", confidence: 0.75 },
{ keywords: ["内容.*日历", "发布.*计划"], pipeline: "content-calendar", confidence: 0.7 },
// 法律类
{ keywords: ["合同.*审查", "条款.*风险", "法律.*检查"], pipeline: "contract-review", confidence: 0.8 },
];
// Agent 行为:
// 1. 监听用户对话内容
// 2. 匹配潜在需求模式
// 3. 当 confidence > 0.6 时,主动询问
// 4. 用户确认后启动 Pipeline
对话触发流程
场景 1: 需求明确
用户: 帮我做一个关于牛顿第二定律的课件
│
▼
┌─────────────────────────────────────┐
│ Agent 分析意图 │
│ - 关键词: 课件、牛顿第二定律 │
│ - 匹配 Pipeline: classroom-generator │
│ - confidence: 0.95 (直接推荐) │
└─────────────────────────────────────┘
场景 2: 需求模糊(主动推荐)
用户: 我在准备下周的物理课,讲力学部分
│
▼
┌─────────────────────────────────────┐
│ Agent 分析意图 │
│ - 关键词: 准备课、物理、力学 │
│ - 匹配 Pipeline: classroom-generator │
│ - confidence: 0.72 (主动询问) │
└─────────────────────────────────────┘
│
▼
┌─────────────────────────────────────┐
│ Agent 回复 │
│ "物理课准备啊!力学是个重要主题。 │
│ │
│ 我可以帮你:【互动课堂生成器】 │
│ • 自动生成力学课件大纲 │
│ • 创建互动幻灯片 │
│ • 生成课后测验 │
│ │
│ 要试试吗?课题就是'力学基础'?" │
└─────────────────────────────────────┘
│
▼
┌─────────────────────────────────────┐ │ Agent 回复 │ │ "我可以使用【互动课堂生成器】为你 │ │ 自动生成完整课件,包括: │ │ • 结构化大纲 │ │ • 互动幻灯片 │ │ • AI 老师讲解 │ │ • 课后测验 │ │ │ │ 需要开始生成吗?" │ └─────────────────────────────────────┘ │ ▼ (用户: 好的) │ ┌─────────────────────────────────────┐ │ Agent 调用工具 │ │ run_pipeline( │ │ id: "classroom-generator", │ │ inputs: { │ │ topic: "牛顿第二定律", │ │ difficulty: "中级" │ │ } │ │ ) │ └─────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────┐ │ Pipeline 执行 & 进度反馈 │ │ Agent: "正在生成课堂大纲..." │ │ Agent: "正在生成第 3/8 个场景..." │ │ Agent: "课堂生成完成!" │ │ [预览课堂] [下载 PPTX] │ └─────────────────────────────────────┘
### 4.3 API 设计
```typescript
// kernel-client.ts 扩展
// 列出 Pipelines
async listPipelines(options?: {
category?: string;
search?: string;
}): Promise<Pipeline[]>
// 获取 Pipeline 详情
async getPipeline(id: string): Promise<PipelineDetail>
// 运行 Pipeline
async runPipeline(request: {
pipelineId: string;
inputs: Record<string, any>;
}): Promise<{ runId: string }>
// 获取运行进度
async getPipelineProgress(runId: string): Promise<{
status: 'running' | 'completed' | 'failed';
currentStep: string;
percentage: number;
message: string;
}>
// 获取运行结果
async getPipelineResult(runId: string): Promise<{
outputs: Record<string, any>;
files: { name: string; url: string; size: number }[];
}>
// 订阅进度事件
onPipelineProgress(runId: string, callback: (progress) => void): () => void
4.4 结果预览页
- 白板/幻灯片渲染器
- Agent 授课播放器
- 测验交互组件
- 导出文件下载
4.5 路由结构
// 新增路由
/pipelines // Pipeline 列表页
/pipelines/:id // Pipeline 详情/运行页
/pipelines/run/:runId // 运行结果/进度页
/classroom/:id // 课堂预览页 (Pipeline 产出)
5. 实现步骤
Phase 1: Pipeline 基础 (1 周)
- 创建
zclaw-pipelinecrate - 实现 DSL Parser (YAML → Pipeline struct)
- 实现 ExecutionContext 状态管理
- 实现基础 Executor (顺序执行)
Phase 2: 核心动作 (1 周)
- 实现
llm.generate动作 - 实现
parallel动作 - 实现
classroom.render动作 - 实现
file.export动作
Phase 3: 前端集成 (1 周)
- Pipeline 列表 API
- Pipeline 运行 API
- 进度订阅
- 结果获取
Phase 4: 教育模板 (1 周)
- classroom-generator.yaml
- 前端预览组件
- 测试验证
6. 验证方案
6.1 单元测试
#[test]
fn test_pipeline_parse() {
let yaml = r#"
apiVersion: zclaw/v1
kind: Pipeline
...
"#;
let pipeline = PipelineParser::parse(yaml).unwrap();
assert_eq!(pipeline.metadata.name, "classroom-generator");
}
#[test]
fn test_expression_resolve() {
let ctx = ExecutionContext::new(json!({
"inputs": {"topic": "test"},
"steps": {"parse": {"output": {"items": [1, 2, 3]}}}
}));
let value = ctx.resolve("${steps.parse.output.items}").unwrap();
assert_eq!(value, json!([1, 2, 3]));
}
6.2 集成测试
# 启动 Pipeline
curl -X POST http://localhost:50051/pipelines/classroom-generator/run \
-d '{"topic": "牛顿第二定律", "difficulty": "中级"}'
# 检查进度
curl http://localhost:50051/pipelines/runs/{run_id}/progress
# 获取结果
curl http://localhost:50051/pipelines/runs/{run_id}/result
6.3 E2E 测试
- 在 UI 中选择 "互动课堂生成器"
- 输入课题,点击生成
- 观察进度条,等待完成
- 预览生成的课堂
- 下载 PPTX/HTML 文件
7. 扩展性设计
7.1 添加新 Pipeline
只需创建新的 YAML 文件:
# pipelines/marketing/campaign.yaml
apiVersion: zclaw/v1
kind: Pipeline
metadata:
name: marketing-campaign
category: marketing
spec:
steps:
- id: analyze_brand
action: llm.generate
template: skills/marketing/brand-analysis.md
# ...
7.2 添加新 Action
// 在 actions/ 目录添加新动作
pub struct CustomAction;
impl ActionExecutor for CustomAction {
async fn execute(&self, ctx: &ExecutionContext, config: &Value) -> Result<Value> {
// 实现自定义逻辑
}
}
7.3 条件分支 (未来)
steps:
- id: check_type
action: condition
branches:
- when: ${inputs.type == 'video'}
then: generate_video
- when: ${inputs.type == 'text'}
then: generate_text