Skills Composition and Orchestration

A single Skill solves a single problem, while complex workflows often require multiple Skills to work together.

This article introduces how to design dependencies between Skills and how to orchestrate multi-Skill workflows in SKILL.md.


Composition Patterns: Two Approaches

PatternDescriptionApplicable Scenario
Sequential OrchestrationThe output of Skill A is used as the input of Skill BData processing pipeline
Parallel InvocationUse multiple Skills simultaneously and merge the resultsMulti-source data aggregation

Claude can make cross-Skill calls in a single conversation, but can only "actively read" the body of one Skill at a time. Complex multi-Skill workflows are usually coordinated by an "orchestration Skill."


Sequential Orchestration Example

with"Upload PDF → Extract text → Generate Word summary report"Take this as an example: three Skills collaborate in sequence.

---
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 展示下载链接。

Implementing Skill Composition via Scripts

When multiple Skills share scripts, the main script can directly call the scripts of other Skills to form a processing pipeline.

Example

# File path: scripts/pipeline_orchestrator.py
# Orchestrates the processing pipeline of multiple Skill scripts

import subprocess
import sys
import os
import json

SKILLS_BASE = "/mnt/skills/public"

def run_script(script_path: str, args: list) -> dict:
    """Runs the script at the specified path and returns the JSON result"""
    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:
    """
Execute the complete PDF → Word report generation pipeline

Stage 1: PDF text extraction
Stage 2: Content summary generation (handled by Claude, skipped here)
Stage 3: Word document generation
    """

    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")

    # Stage 1: Extract PDF text
    print("Stage 1: Extracting PDF text...")
    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" Extracted {step1.get('pages', '?')} pages")

    # Stage 3: Generate Word report (the summary is generated and passed in by Claude before this script is called)
    summary_json = os.path.join(output_dir, "summary.json")
    if not os.path.exists(summary_json):
        return {"status": "error", "stage": 3,
                "message": "Missing summary.json, please complete Stage 2 first"}

    print("Stage 3: Generating Word report...")
    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))

Avoiding Circular Dependencies

When Skill A depends on Skill B and Skill B depends on Skill A, a circular dependency is formed, making execution impossible.

When designing Skill composition, you should maintain one-way dependency relationships.

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

Shared Utility Functions

Common functions needed by multiple Skills can be extracted into a shared module to avoid duplicate implementation.

Example

# File path: /home/claude/shared/file_utils.py
# File utility functions shared by multiple Skills

import os

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

def resolve_upload(filename: str) -> str:
    """Parses a filename into the full upload path"""
    if os.path.isabs(filename):
        return filename  # Already an absolute path
    return os.path.join(UPLOAD_DIR, filename)

def resolve_output(filename: str) -> str:
    """Parses a filename into the full output path and ensures the directory exists"""
    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

# Used in each Skill script:
# import sys; sys.path.insert(0, "/home/claude/shared")
# from file_utils import resolve_upload, resolve_output
Other Extensions