Skills-Komposition und Orchestrierung

Ein einzelner Skill löst ein einzelnes Problem, während komplexe Workflows oft erfordern, dass mehrere Skills zusammenarbeiten.

Dieser Artikel erläutert, wie Abhängigkeiten zwischen Skills entworfen werden und wie Multi-Skill-Workflows in SKILL.md orchestriert werden.


Kompositionsmuster: Zwei Ansätze

MusterBeschreibungAnwendungsfall
Sequenzielle OrchestrierungDie Ausgabe von Skill A wird als Eingabe von Skill B verwendetDatenverarbeitungs-Pipeline
Paralleler AufrufMehrere Skills gleichzeitig verwenden und die Ergebnisse zusammenführenMulti-Source-Datenaggregation

Claude kann in einer einzigen Konversation Cross-Skill-Aufrufe durchführen, kann aber jeweils nur den Inhalt eines Skills "aktiv lesen". Komplexe Multi-Skill-Workflows werden normalerweise von einem "Orchestrierungs-Skill" koordiniert.


Beispiel für sequenzielle Orchestrierung

mit"PDF hochladen → Text extrahieren → Word-Zusammenfassungsbericht generieren"Nehmen Sie dies als Beispiel: Drei Skills arbeiten nacheinander zusammen.

---
name: pdf-to-word-report
description: >
  将 PDF 文档转换为 Word 格式的摘要报告,包含文字提取、
  内容分析和格式化输出三个阶段。当用户需要从 PDF 生成报告、
  提取 PDF 内容并整理为 Word 文档时触发。
---

# PDF 转 Word 摘要报告

## 工作流程

本 Skill 协调三个处理阶段,最终生成 .docx 报告。

### 阶段一:提取 PDF 文字
使用 pdf-reading Skill(位于 /mnt/skills/public/pdf-reading/)的处理逻辑:

```bash
python /mnt/skills/public/pdf-reading/scripts/extract.py \
  --input  <PDF路径> \
  --output /home/claude/extracted_text.txt
```

完成后告知用户:已提取 {N} 页内容,共 {字数} 字。

### 阶段二:生成摘要内容
对提取的文字进行内容分析,生成:
- 文档主题(1句话)
- 关键章节摘要(每章 2-3 句)
- 重要数据与结论

将摘要内容写入 /home/claude/summary.json

### 阶段三:生成 Word 报告
使用 docx Skill 的规范,调用:

```bash
python /mnt/skills/public/docx/scripts/generate.py \
  --template /mnt/skills/public/docx/assets/report_template.docx \
  --data     /home/claude/summary.json \
  --output   /mnt/user-data/outputs/report.docx
```

完成后调用 present_files 展示下载链接。

Implementierung der Skill-Komposition über Skripte

Wenn mehrere Skills Skripte gemeinsam nutzen, kann das Hauptskript direkt die Skripte anderer Skills aufrufen, um eine Verarbeitungspipeline zu bilden.

Beispiel

# Dateipfad: scripts/pipeline_orchestrator.py
# Orchestriert die Verarbeitungspipeline mehrerer Skill-Skripte

import subprocess
import sys
import os
import json

SKILLS_BASE = "/mnt/skills/public"

def run_script(script_path: str, args: list) -> dict:
    """Führt das Skript am angegebenen Pfad aus und gibt das JSON-Ergebnis zurück"""
    cmd = [sys.executable, script_path] + args
    result = subprocess.run(cmd, capture_output=True, text=True, timeout=120)
    if result.returncode != 0:
        return {"status": "error", "message": result.stderr}
    try:
        return json.loads(result.stdout)
    except json.JSONDecodeError:
        return {"status": "success", "raw_output": result.stdout}

def run_pipeline(pdf_path: str, output_dir: str) -> dict:
    """
Führe die vollständige Pipeline zur Erstellung von PDF → Word-Berichten aus

Stufe 1: PDF-Textextraktion
Stufe 2: Generierung der Inhaltszusammenfassung (von Claude übernommen, hier übersprungen)
Stufe 3: Word-Dokumenterstellung
    """

    os.makedirs(output_dir, exist_ok=True)
    extracted_txt = os.path.join(output_dir, "extracted.txt")
    final_docx    = os.path.join(output_dir, "report.docx")

    # Stufe 1: PDF-Text extrahieren
    print("Stufe 1: PDF-Text wird extrahiert...")
    step1 = run_script(
        f"{SKILLS_BASE}/pdf-reading/scripts/extract.py",
        ["--input", pdf_path, "--output", extracted_txt]
    )
    if step1.get("status") == "error":
        return {"status": "error", "stage": 1, "message": step1["message"]}
    print(f" Extrahierte {step1.get('pages', '?')} Seiten")

    # Phase 3: Word-Bericht generieren (die Zusammenfassung wird von Claude generiert und übergeben, bevor dieses Skript aufgerufen wird)
    summary_json = os.path.join(output_dir, "summary.json")
    if not os.path.exists(summary_json):
        return {"status": "error", "stage": 3,
                "message": "summary.json fehlt, bitte schließen Sie zuerst Phase 2 ab"}

    print("Phase 3: Word-Bericht wird generiert...")
    step3 = run_script(
        f"{SKILLS_BASE}/docx/scripts/generate.py",
        ["--data", summary_json, "--output", final_docx]
    )
    if step3.get("status") == "error":
        return {"status": "error", "stage": 3, "message": step3["message"]}

    return {"status": "success", "output": final_docx}

if __name__ == "__main__":
    pdf_path   = sys.argv[1] if len(sys.argv) > 1 else ""
    output_dir = sys.argv[2] if len(sys.argv) > 2 else "/home/claude/pipeline_output"
    result = run_pipeline(pdf_path, output_dir)
    print(json.dumps(result, ensure_ascii=False, indent=2))

Zirkuläre Abhängigkeiten vermeiden

Wenn Skill A von Skill B abhängt und Skill B von Skill A abhängt, entsteht eine zirkuläre Abhängigkeit, wodurch die Ausführung unmöglich wird.

Beim Entwerfen der Skill-Komposition sollten Sie unidirektionale Abhängigkeitsbeziehungen beibehalten.

Correct: One-way dependency Correct: One-way dependency pdf-reading summarizer docx-writer Incorrect: Circular dependency Incorrect: Circular dependency Skill A Skill B ← Mutually dependent, cannot start

Gemeinsam genutzte Hilfsfunktionen

Gemeinsame Funktionen, die von mehreren Skills benötigt werden, können in ein gemeinsames Modul ausgelagert werden, um doppelte Implementierungen zu vermeiden.

Beispiel

# Dateipfad: /home/claude/shared/file_utils.py
# Datei-Hilfsfunktionen, die von mehreren Skills gemeinsam genutzt werden

import os

UPLOAD_DIR = "/mnt/user-data/uploads"
OUTPUT_DIR = "/mnt/user-data/outputs"

def resolve_upload(filename: str) -> str:
    """Parst einen Dateinamen in den vollständigen Upload-Pfad"""
    if os.path.isabs(filename):
        return filename  # Bereits ein absoluter Pfad
    return os.path.join(UPLOAD_DIR, filename)

def resolve_output(filename: str) -> str:
    """Parst einen Dateinamen in den vollständigen Ausgabepfad und stellt sicher, dass das Verzeichnis vorhanden ist"""
    if os.path.isabs(filename):
        path = filename
    else:
        path = os.path.join(OUTPUT_DIR, filename)
    os.makedirs(os.path.dirname(path), exist_ok=True)
    return path

# In jedem Skill-Skript verwendet:
# import sys; sys.path.insert(0, "/home/claude/shared")
# from file_utils import resolve_upload, resolve_output
Andere Erweiterungen