Foundations

Prompt Chaining and Decomposition

Lesson 4 of 4 Estimated Time 45 min

Prompt Chaining and Decomposition

Some tasks are too complex for a single prompt. Your document is 50 pages long but the model’s context window is 8,000 tokens. Your output needs to be validated before being finalized. You want to analyze data, extract insights, create a summary, and generate recommendations. These scenarios call for prompt chaining: breaking your complex task into smaller steps and chaining them together.

Prompt chaining isn’t just about working around limitations—it’s often better than trying to do everything at once. Each step can be optimized independently, failures can be caught and fixed, and output quality often improves.

Breaking Complex Tasks Into Prompt Chains

The core idea is simple: Instead of asking the model to do everything in one prompt, you ask it to do one thing at a time, passing the output from one step as input to the next.

When Chaining Is Necessary

Task too complex for one prompt:

"Analyze this 50-page document, identify key themes, extract statistics,
assess credibility, summarize findings, and generate 5 recommendations."

One prompt tries to do too much. Easy for it to miss details or hallucinate.

Better as a chain:

Step 1: Summarize the document (handle large content)
Step 2: Identify key themes (synthesize the summary)
Step 3: Extract statistics (find specific data points)
Step 4: Assess credibility (evaluate sources and claims)
Step 5: Generate recommendations (based on findings)

Each step is smaller, more focused, and easier to optimize.

Common Chain Patterns

PatternWhen to Use
SequentialTask A depends on task B’s output
ParallelTasks are independent, merge results
VerificationGenerate, then check, then finalize
IterativeGenerate, evaluate, improve, repeat
HierarchicalHigh-level summary, then detailed analysis

Sequential Chaining: Output of One Becomes Input of Next

The simplest form: each step takes the previous output as input.

Pattern: Linear Chain

[Input] → [Step 1] → [Step 2] → [Step 3] → [Final Output]

Each arrow represents a prompt call.

Example: Research to Report Pipeline

import anthropic

client = anthropic.Anthropic()

# Input: A research question
research_question = "What are the latest trends in AI safety?"

# STEP 1: Generate research outline
outline_response = client.messages.create(
    model="claude-3-5-sonnet-20241022",
    max_tokens=1000,
    messages=[{
        "role": "user",
        "content": f"""Create a detailed outline for researching: {research_question}

Include major sections and key areas to cover.
Format as a numbered outline."""
    }]
)
outline = outline_response.content[0].text

# STEP 2: Write the research paper sections
research_response = client.messages.create(
    model="claude-3-5-sonnet-20241022",
    max_tokens=2000,
    messages=[{
        "role": "user",
        "content": f"""Using this outline, write a comprehensive research paper:

{outline}

Write 2-3 paragraphs for each major section."""
    }]
)
research_content = research_response.content[0].text

# STEP 3: Create an executive summary
summary_response = client.messages.create(
    model="claude-3-5-sonnet-20241022",
    max_tokens=500,
    messages=[{
        "role": "user",
        "content": f"""Write a 250-word executive summary of this research:

{research_content}

Include key findings and main takeaways."""
    }]
)
summary = summary_response.content[0].text

# STEP 4: Generate recommendations
recommendations_response = client.messages.create(
    model="claude-3-5-sonnet-20241022",
    max_tokens=800,
    messages=[{
        "role": "user",
        "content": f"""Based on this research summary, generate 5 key recommendations:

{summary}

Format as a numbered list with brief explanations."""
    }]
)
recommendations = recommendations_response.content[0].text

# Final output
print("OUTLINE:")
print(outline)
print("\n" + "="*80 + "\n")
print("RESEARCH:")
print(research_content)
print("\n" + "="*80 + "\n")
print("SUMMARY:")
print(summary)
print("\n" + "="*80 + "\n")
print("RECOMMENDATIONS:")
print(recommendations)

Key Advantage of Sequential Chains

Each step can fail and be fixed independently:

Step 1 produces bad outline? Regenerate step 1 with a better prompt.
Step 2 produces weak research? Don't re-run step 1; just improve step 2.

This isolation of concerns makes debugging easier.

Parallel Decomposition: Split Task, Merge Results

For independent subtasks, you can run them in parallel and merge results.

Pattern: Parallel Chain

                    ┌─→ [Step 1] ─→ Output A
[Input] ─→ Split ─┤─→ [Step 2] ─→ Output B  ─→ [Merge] ─→ Final Output
                    └─→ [Step 3] ─→ Output C

All steps run simultaneously (or in any order), then results are combined.

Example: Multi-Angle Analysis

def analyze_product_launch(product_description):
    """Analyze a product launch from multiple perspectives simultaneously"""

    client = anthropic.Anthropic()

    # Perspective 1: Market Analysis
    market_response = client.messages.create(
        model="claude-3-5-sonnet-20241022",
        max_tokens=500,
        messages=[{
            "role": "user",
            "content": f"""From a MARKET perspective, analyze this product launch:
            {product_description}

            Focus on: market size, competition, timing, customer demand"""
        }]
    )
    market_analysis = market_response.content[0].text

    # Perspective 2: Technical Feasibility
    technical_response = client.messages.create(
        model="claude-3-5-sonnet-20241022",
        max_tokens=500,
        messages=[{
            "role": "user",
            "content": f"""From a TECHNICAL perspective, analyze this product:
            {product_description}

            Focus on: feasibility, technology stack, scalability, risks"""
        }]
    )
    technical_analysis = technical_response.content[0].text

    # Perspective 3: Financial Viability
    financial_response = client.messages.create(
        model="claude-3-5-sonnet-20241022",
        max_tokens=500,
        messages=[{
            "role": "user",
            "content": f"""From a FINANCIAL perspective, analyze this product:
            {product_description}

            Focus on: costs, pricing, unit economics, profitability timeline"""
        }]
    )
    financial_analysis = financial_response.content[0].text

    # MERGE: Integrate all perspectives
    merge_response = client.messages.create(
        model="claude-3-5-sonnet-20241022",
        max_tokens=1000,
        messages=[{
            "role": "user",
            "content": f"""Synthesize these three analyses into one balanced assessment:

MARKET ANALYSIS:
{market_analysis}

TECHNICAL ANALYSIS:
{technical_analysis}

FINANCIAL ANALYSIS:
{financial_analysis}

Provide an integrated recommendation addressing all three perspectives."""
        }]
    )

    return merge_response.content[0].text

# Use it
product = "AI-powered summarization tool for legal documents"
analysis = analyze_product_launch(product)
print(analysis)

Advantages of Parallel Chains

  • Efficiency: Faster if run concurrently (APIs support concurrent calls)
  • Breadth: Multiple perspectives with less token cost than one mega-prompt
  • Robustness: If one perspective fails, others still succeed

Verification Chains: Generate Then Validate

Some tasks require validation before finalizing. A verification chain generates a candidate, checks it, and conditionally iterates.

Pattern: Generate-Validate-Finalize

[Input] → [Generate] → [Validate] → [Good?] ─Yes→ [Output]
                                       ↓ No
                                    [Improve]

                                    [Validate]

Example: Code Generation with Verification

def generate_and_verify_code(specification):
    """Generate code and verify it works"""

    client = anthropic.Anthropic()

    # STEP 1: Generate code
    code_response = client.messages.create(
        model="claude-3-5-sonnet-20241022",
        max_tokens=1000,
        messages=[{
            "role": "user",
            "content": f"""Write Python code for: {specification}

Provide only the code, no explanation."""
        }]
    )
    code = code_response.content[0].text

    # STEP 2: Verify the code
    verify_response = client.messages.create(
        model="claude-3-5-sonnet-20241022",
        max_tokens=500,
        messages=[{
            "role": "user",
            "content": f"""Review this code for issues:
{code}

Check for:
- Syntax errors
- Logic errors
- Edge cases not handled
- Missing imports

If issues found, list them. If no issues, respond: "Code looks good"."""
        }]
    )
    verification = verify_response.content[0].text

    # STEP 3: If issues found, improve
    if "Code looks good" not in verification:
        improve_response = client.messages.create(
            model="claude-3-5-sonnet-20241022",
            max_tokens=1000,
            messages=[{
                "role": "user",
                "content": f"""Fix these issues in the code:

Issues:
{verification}

Original code:
{code}

Provide corrected code only."""
            }]
        )
        code = improve_response.content[0].text

    return code, verification

When to Use Verification Chains

  • Code generation (verify syntax, logic)
  • Financial calculations (verify math)
  • Factual claims (verify against sources)
  • Security-sensitive tasks (verify no bad practices)

Building Multi-Step Workflows: Real-World Example

Let’s build a complete, realistic workflow: document analysis pipeline.

Scenario: Analyze Customer Feedback

A company has collected 100 customer reviews. They want:

  1. Categorize themes
  2. Extract actionable insights
  3. Prioritize by impact
  4. Generate action plan

Complete Workflow Implementation

class CustomerFeedbackAnalyzer:
    def __init__(self):
        self.client = anthropic.Anthropic()

    def step_1_categorize_themes(self, reviews):
        """Extract themes from customer reviews"""
        reviews_text = "\n".join([f"- {r}" for r in reviews])

        response = self.client.messages.create(
            model="claude-3-5-sonnet-20241022",
            max_tokens=1000,
            messages=[{
                "role": "user",
                "content": f"""Analyze these customer reviews and identify
the 5 most common themes/problems:

{reviews_text}

Format: Numbered list with theme name and frequency."""
            }]
        )
        return response.content[0].text

    def step_2_extract_insights(self, themes):
        """Generate actionable insights from themes"""
        response = self.client.messages.create(
            model="claude-3-5-sonnet-20241022",
            max_tokens=800,
            messages=[{
                "role": "user",
                "content": f"""Given these customer feedback themes:

{themes}

Extract actionable insights: What are customers really asking for?
What problems are most pressing?

Format: Numbered list of insights with rationale."""
            }]
        )
        return response.content[0].text

    def step_3_prioritize(self, insights):
        """Prioritize insights by business impact"""
        response = self.client.messages.create(
            model="claude-3-5-sonnet-20241022",
            max_tokens=600,
            messages=[{
                "role": "user",
                "content": f"""Prioritize these insights by impact:
- How many customers affected?
- Revenue impact?
- Customer satisfaction impact?

Insights:
{insights}

Provide prioritized list with impact scoring."""
            }]
        )
        return response.content[0].text

    def step_4_action_plan(self, prioritized_insights):
        """Generate action plan"""
        response = self.client.messages.create(
            model="claude-3-5-sonnet-20241022",
            max_tokens=1000,
            messages=[{
                "role": "user",
                "content": f"""Create an action plan addressing these
prioritized insights:

{prioritized_insights}

For each insight, provide:
- Specific action
- Owner (engineering, product, support, marketing)
- Timeline (quick win, medium term, long term)
- Expected impact

Format as table or numbered list."""
            }]
        )
        return response.content[0].text

    def run_full_analysis(self, reviews):
        """Run the complete pipeline"""
        print("Step 1: Categorizing themes...")
        themes = self.step_1_categorize_themes(reviews)
        print(themes)
        print("\n" + "="*80 + "\n")

        print("Step 2: Extracting insights...")
        insights = self.step_2_extract_insights(themes)
        print(insights)
        print("\n" + "="*80 + "\n")

        print("Step 3: Prioritizing insights...")
        prioritized = self.step_3_prioritize(insights)
        print(prioritized)
        print("\n" + "="*80 + "\n")

        print("Step 4: Creating action plan...")
        action_plan = self.step_4_action_plan(prioritized)
        print(action_plan)

        return {
            "themes": themes,
            "insights": insights,
            "prioritized": prioritized,
            "action_plan": action_plan
        }

# Usage
analyzer = CustomerFeedbackAnalyzer()
sample_reviews = [
    "App crashes on startup",
    "Would love dark mode",
    "App crashes on startup",
    "Great app, fast and reliable",
    "Would love dark mode",
    "Customer support never responds",
    "App crashes on startup",
    "Pricing is too high",
    # ... more reviews
]

result = analyzer.run_full_analysis(sample_reviews)

Error Handling and Retry Logic in Chains

Real chains need robustness. What happens if a step fails?

Retry Pattern

def call_with_retry(prompt, max_retries=3):
    """Call API with exponential backoff retry"""
    import time

    for attempt in range(max_retries):
        try:
            response = client.messages.create(
                model="claude-3-5-sonnet-20241022",
                max_tokens=1000,
                messages=[{"role": "user", "content": prompt}]
            )
            return response.content[0].text
        except Exception as e:
            if attempt < max_retries - 1:
                wait_time = 2 ** attempt  # Exponential backoff
                time.sleep(wait_time)
            else:
                raise

Fallback Pattern

def get_analysis_with_fallback(text):
    """Try detailed analysis, fall back to simpler version if it fails"""

    # Try detailed analysis
    try:
        response = client.messages.create(
            model="claude-3-5-sonnet-20241022",
            max_tokens=2000,
            messages=[{
                "role": "user",
                "content": f"Provide deep analysis of: {text}"
            }]
        )
        return response.content[0].text
    except:
        # Fall back to simpler analysis
        response = client.messages.create(
            model="claude-3-5-sonnet-20241022",
            max_tokens=500,
            messages=[{
                "role": "user",
                "content": f"Summarize: {text}"
            }]
        )
        return response.content[0].text

Key Takeaway

Prompt chaining breaks complex tasks into sequential steps, with each step’s output feeding into the next. Sequential chains work for dependent tasks; parallel chains for independent analyses. Verification chains ensure quality before finalizing. Most real applications use some combination of these patterns. Proper error handling and retry logic make chains robust in production.

Exercise: Design a 4-Step Prompt Chain for Document Analysis

Your task is to design (not necessarily run) a complete prompt chain for analyzing a technical document.

The Scenario

You have a 40-page technical whitepaper on quantum computing. You need to:

  1. Make it digestible for non-technical stakeholders
  2. Extract key research findings
  3. Identify competitive implications
  4. Generate strategic recommendations

Your Task

Design a 4-step chain:

For each step, provide:

  1. Step name and purpose (what is this step’s job?)
  2. Input (what data does it receive?)
  3. Prompt (the actual prompt you’d send to the model)
  4. Output (what format does output take?)
  5. Why this ordering? (why does this step come here, not elsewhere?)

Template to Follow

## Step 1: [Name]

**Purpose:** [What does this step accomplish?]

**Input:** [What comes into this step?]

**Prompt:**
[Write the actual prompt you'd use]

**Output Format:**
[How is the output structured?]

**Rationale:**
[Why is this step first?]

---

## Step 2: [Name]
[Repeat template]

---

[Continue for steps 3 and 4]

Example to Get You Started

## Step 1: Simplify Technical Content

Purpose: Create a non-technical summary that executives can understand
Input: Full 40-page whitepaper
Prompt: "Summarize this technical whitepaper in language suitable for
non-technical executives. Avoid jargon; if necessary, explain it..."

Output Format: 2-3 page summary with key concepts
Rationale: We start with simplification because it prepares the ground
for more detailed analysis. Stakeholders need to understand the basics
before we can discuss implications.

Bonus Challenge

Consider:

  • What could go wrong in each step?
  • How would you handle failures? (Add a verification step? Fallback?)
  • Could any steps run in parallel? (Which ones are independent?)
  • How would you loop or iterate if early steps aren’t sufficient?

This exercise shows you how to think about complex tasks as choreographed sequences rather than monolithic prompts.