Multi-Skill Collaboration Workflow
This project integrates the first three hands-on projects into a complete "data report generation" workflow.
Goal: Master multi-Skill orchestration and how to design an orchestration Skill that coordinates the overall workflow.
Workflow Goal
The user uploads a business data file, and the system automatically completes the following end-to-end tasks, ultimately delivering a complete business report.
Design Principles for Orchestration Skill
The orchestration Skill does not perform specific business processing; it is only responsible for scheduling the outputs of other Skills, passing intermediate results, and reporting progress to the user at key points.
| What the orchestration Skill does | What the orchestration Skill does not do |
|---|---|
| Call sub-Skill scripts in order | Re-implement functionality already existing in sub-Skills |
| Pass file paths between steps | Directly manipulate data content |
| Report overall progress to the user | Focus on internal details of a single step |
| Handle remediation logic after step failures | Catch exceptions inside sub-Skills |
Orchestration Script
Example
# File path: scripts/orchestrator.py
# Orchestration entry script for multi-Skill collaboration
import subprocess
import sys
import json
import os
import shutil
SKILLS_BASE = "/mnt/skills/public"
WORK_DIR = "/home/claude/report_work"
OUTPUT_DIR = "/mnt/user-data/outputs"
def run(script_path: str, *args) -> dict:
"""Run the specified script and return the JSON result"""
cmd = [sys.executable, script_path] + list(args)
result = subprocess.run(cmd, capture_output=True, text=True, timeout=120)
try:
return json.loads(result.stdout)
except json.JSONDecodeError:
return {
"status": "error",
"message": result.stderr or result.stdout or "Script has no output"
}
def orchestrate(input_file: str) -> dict:
"""Execute the complete report generation workflow"""
os.makedirs(WORK_DIR, exist_ok=True)
# ── Phase 1: Data cleaning ─────────────────────────────────
print("【1/3】Cleaning data...", flush=True)
clean_result = run(
f"{SKILLS_BASE}/data-cleaner/scripts/clean_data.py",
input_file,
os.path.join(WORK_DIR, "cleaned.csv")
)
if clean_result.get("status") != "success":
return {"status": "error", "stage": "Data cleaning",
"message": clean_result.get("message")}
print(f" Cleaned: removed {clean_result.get('dup_removed', 0)} duplicate rows,"
f"filled {clean_result.get('nulls_filled', 0)} missing values", flush=True)
# ── Phase 2: Statistical analysis ─────────────────────────────────
print("【2/3】Generating statistical report...", flush=True)
stats_result = run(
f"{SKILLS_BASE}/data-cleaner/scripts/calc_stats.py",
clean_result["output"],
os.path.join(WORK_DIR, "stats.json")
)
if stats_result.get("status") != "success":
return {"status": "error", "stage": "Statistical analysis",
"message": stats_result.get("message")}
# ── Phase 3: Generate Excel report ──────────────────────────
print("【3/3】Generating Excel report...", flush=True)
output_name = os.path.splitext(os.path.basename(input_file))[0] + "_report.xlsx"
output_path = os.path.join(OUTPUT_DIR, output_name)
report_result = run(
f"{SKILLS_BASE}/data-cleaner/scripts/gen_report.py",
clean_result["output"],
stats_result.get("output", ""),
output_path
)
if report_result.get("status") != "success":
return {"status": "error", "stage": "Report generation",
"message": report_result.get("message")}
# Clean up working directory
shutil.rmtree(WORK_DIR, ignore_errors=True)
return {
"status": "success",
"output": output_path,
"clean_stats": {
"dup_removed": clean_result.get("dup_removed", 0),
"nulls_filled": clean_result.get("nulls_filled", 0)
}
}
if __name__ == "__main__":
if len(sys.argv) < 2:
print(json.dumps({"status": "error", "message": "Usage: python orchestrator.py <file path>"}))
sys.exit(1)
result = orchestrate(sys.argv[1])
print(json.dumps(result, ensure_ascii=False, indent=2))
# Orchestration entry script for multi-Skill collaboration
import subprocess
import sys
import json
import os
import shutil
SKILLS_BASE = "/mnt/skills/public"
WORK_DIR = "/home/claude/report_work"
OUTPUT_DIR = "/mnt/user-data/outputs"
def run(script_path: str, *args) -> dict:
"""Run the specified script and return the JSON result"""
cmd = [sys.executable, script_path] + list(args)
result = subprocess.run(cmd, capture_output=True, text=True, timeout=120)
try:
return json.loads(result.stdout)
except json.JSONDecodeError:
return {
"status": "error",
"message": result.stderr or result.stdout or "Script has no output"
}
def orchestrate(input_file: str) -> dict:
"""Execute the complete report generation workflow"""
os.makedirs(WORK_DIR, exist_ok=True)
# ── Phase 1: Data cleaning ─────────────────────────────────
print("【1/3】Cleaning data...", flush=True)
clean_result = run(
f"{SKILLS_BASE}/data-cleaner/scripts/clean_data.py",
input_file,
os.path.join(WORK_DIR, "cleaned.csv")
)
if clean_result.get("status") != "success":
return {"status": "error", "stage": "Data cleaning",
"message": clean_result.get("message")}
print(f" Cleaned: removed {clean_result.get('dup_removed', 0)} duplicate rows,"
f"filled {clean_result.get('nulls_filled', 0)} missing values", flush=True)
# ── Phase 2: Statistical analysis ─────────────────────────────────
print("【2/3】Generating statistical report...", flush=True)
stats_result = run(
f"{SKILLS_BASE}/data-cleaner/scripts/calc_stats.py",
clean_result["output"],
os.path.join(WORK_DIR, "stats.json")
)
if stats_result.get("status") != "success":
return {"status": "error", "stage": "Statistical analysis",
"message": stats_result.get("message")}
# ── Phase 3: Generate Excel report ──────────────────────────
print("【3/3】Generating Excel report...", flush=True)
output_name = os.path.splitext(os.path.basename(input_file))[0] + "_report.xlsx"
output_path = os.path.join(OUTPUT_DIR, output_name)
report_result = run(
f"{SKILLS_BASE}/data-cleaner/scripts/gen_report.py",
clean_result["output"],
stats_result.get("output", ""),
output_path
)
if report_result.get("status") != "success":
return {"status": "error", "stage": "Report generation",
"message": report_result.get("message")}
# Clean up working directory
shutil.rmtree(WORK_DIR, ignore_errors=True)
return {
"status": "success",
"output": output_path,
"clean_stats": {
"dup_removed": clean_result.get("dup_removed", 0),
"nulls_filled": clean_result.get("nulls_filled", 0)
}
}
if __name__ == "__main__":
if len(sys.argv) < 2:
print(json.dumps({"status": "error", "message": "Usage: python orchestrator.py <file path>"}))
sys.exit(1)
result = orchestrate(sys.argv[1])
print(json.dumps(result, ensure_ascii=False, indent=2))
【1/3】正在清洗数据...
已清洗:删除重复行 5 条,填充空值 18 处
【2/3】正在生成统计报告...
【3/3】正在生成 Excel 报告...
{
"status": "success",
"output": "/mnt/user-data/outputs/example_sales_report.xlsx",
"clean_stats": {"dup_removed": 5, "nulls_filled": 18}
}
SKILL.md for the Orchestration Skill
--- name: report-pipeline version: 1.0.0 description: > 完整的数据报告生成流水线:自动对上传的 CSV/Excel 文件执行数据清洗、 统计分析并生成 Excel 报告。当用户需要一键生成数据报告、 从原始数据直接输出分析报告时触发。 --- # 数据报告流水线 ## 前置条件 需要以下 Skills 已安装: - data-cleaner v1.0+ - 本 Skill 的脚本位于 /mnt/skills/public/report-pipeline/ ## 执行 获取用户上传的文件路径后,直接运行编排脚本: ```bash python scripts/orchestrator.py <文件路径> ``` 脚本会实时输出每阶段的进度,完成后输出最终文件路径。 解析 JSON 输出后调用 present_files 展示报告文件。 ## 失败处理 若任意阶段失败,输出包含 stage 和 message 的错误信息, 告知用户是哪个阶段失败以及可能的原因,不要继续执行后续阶段。Other extensions