Prompt Chaining

Split a Big Task into Steps

Prompt chaining breaks a task into several smaller calls, where the output of one step is the input to the next. Each step has a simple, focused prompt that is easy to test. You can also check the output between steps with code.

customer email ──► [1 extract: order id, issue] ──► [2 classify: refund / replace / info]
                                                              │
                         code checks the order in the database ◄┘
                                                              │
                                        [3 draft reply using facts] ──► agent reviews

A Chain with Stand-in Steps

Example

Each function below stands in for one model call with its own prompt. Code between the steps validates and adds real data:

import json, re

ORDERS = {"SE-20931": {"item": "Prestige pressure cooker", "status": "delivered", "days_since": 2}}

def step_extract(email):            # stand-in for model call 1
    oid = re.search(r"SE-\d+", email)
    return {"order_id": oid.group(0) if oid else None, "issue": "damaged" if "dent" in email else "other"}

def step_classify(info, order):     # stand-in for model call 2
    if info["issue"] == "damaged" and order["days_since"] <= 7:
        return "replace"
    return "info"

def step_draft(action, order):      # stand-in for model call 3
    if action == "replace":
        return f"Sorry your {order['item']} arrived damaged. We have booked a free replacement."
    return "Thanks for writing. An agent will contact you soon."

email = "Hi, order SE-20931 arrived today and the lid has a big dent. Please help."
info = step_extract(email);              print("1 extract :", json.dumps(info))
order = ORDERS.get(info["order_id"])
if order is None:
    raise SystemExit("unknown order - ask the customer")
action = step_classify(info, order);     print("2 classify:", action)
print("3 draft   :", step_draft(action, order))

Output

1 extract : {"order_id": "SE-20931", "issue": "damaged"}
2 classify: replace
3 draft   : Sorry your Prestige pressure cooker arrived damaged. We have booked a free replacement.

The Same Chain with Real Calls

def ask(system: str, user: str, model="claude-haiku-5-5", max_tokens=300) -> str:
    r = client.messages.create(model=model, max_tokens=max_tokens, system=system,
                               messages=[{"role": "user", "content": user}])
    return next(b.text for b in r.content if b.type == "text")

info_json = ask("Extract order_id and issue as JSON with keys order_id, issue. JSON only.", email)
info = json.loads(info_json)                      # validate! (see Structured Output)
order = db.get_order(info["order_id"])
action = ask("Answer with one word: replace, refund or info.",
             f"<issue>{info['issue']}</issue><order>{json.dumps(order)}</order>")
reply = ask("You are ShopEase support. Write a short, warm reply.",
            f"<action>{action}</action><order>{json.dumps(order)}</order>", model="claude-sonnet-5-5")

Tip: Use a small cheap model for simple steps (extract, classify) and a stronger one only for the step that needs it (writing the reply).