ExamplescriptintermediateRunnableguided-flow
Prompt Chaining
Runnable example (intermediate) for script using openai, pydantic.
Key Facts
- Level
- intermediate
- Runtime
- Python • OpenAI API
- Pattern
- Inspectable flow with visible system boundaries
- Interaction
- Live sandbox • Script
- Updated
- 14 March 2026
Navigate this example
Library
Browse examplesReopen the wider library to compare adjacent patterns and linked learning paths.Interaction
Run sandbox nowTry the interaction directly in this example’s guided sandbox surface.Source
Open full sourceRead the real implementation, highlighted checkpoints, and runtime requirements.MCP
Call via MCPUse the same resource inside agents, deterministic exports, and MCP setup flows.
Linked principles
1-prompt-chaining.py
python
from typing import Optional
from datetime import datetime
from pydantic import BaseModel, Field
from openai import OpenAI
import os
import logging
# Set up logging configuration
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s - %(levelname)s - %(message)s",
datefmt="%Y-%m-%d %H:%M:%S",
)
logger = logging.getLogger(__name__)
client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
model = "gpt-4o"
# --------------------------------------------------------------
# Step 1: Define the data models for each stage
# --------------------------------------------------------------
class EventExtraction(BaseModel):
"""First LLM call: Extract basic event information"""
description: str = Field(description="Raw description of the event")
is_calendar_event: bool = Field(
description="Whether this text describes a calendar event"
)
confidence_score: float = Field(description="Confidence score between 0 and 1")
class EventDetails(BaseModel):
"""Second LLM call: Parse specific event details"""
name: str = Field(description="Name of the event")
date: str = Field(
description="Date and time of the event. Use ISO 8601 to format this value."
)
duration_minutes: int = Field(description="Expected duration in minutes")
participants: list[str] = Field(description="List of participants")
class EventConfirmation(BaseModel):
"""Third LLM call: Generate confirmation message"""
confirmation_message: str = Field(
description="Natural language confirmation message"
)
calendar_link: Optional[str] = Field(
description="Generated calendar link if applicable"
)
# --------------------------------------------------------------
# Step 2: Define the functions
# --------------------------------------------------------------
def extract_event_info(user_input: str) -> EventExtraction:
"""First LLM call to determine if input is a calendar event"""
logger.info("Starting event extraction analysis")
logger.debug(f"Input text: {user_input}")
today = datetime.now()
date_context = f"Today is {today.strftime('%A, %B %d, %Y')}."
completion = client.beta.chat.completions.parse(
model=model,
messages=[
{
"role": "system",
"content": f"{date_context} Analyze if the text describes a calendar event.",
},
{"role": "user", "content": user_input},
],
response_format=EventExtraction,
)
result = completion.choices[0].message.parsed
logger.info(
f"Extraction complete - Is calendar event: {result.is_calendar_event}, Confidence: {result.confidence_score:.2f}"
)
return result
def parse_event_details(description: str) -> EventDetails:
"""Second LLM call to extract specific event details"""
logger.info("Starting event details parsing")
today = datetime.now()
date_context = f"Today is {today.strftime('%A, %B %d, %Y')}."
completion = client.beta.chat.completions.parse(
model=model,
messages=[
{
"role": "system",
"content": f"{date_context} Extract detailed event information. When dates reference 'next Tuesday' or similar relative dates, use this current date as reference.",
},
{"role": "user", "content": description},
],
response_format=EventDetails,
)
result = completion.choices[0].message.parsed
logger.info(
f"Parsed event details - Name: {result.name}, Date: {result.date}, Duration: {result.duration_minutes}min"
)
logger.debug(f"Participants: {', '.join(result.participants)}")
return result
def generate_confirmation(event_details: EventDetails) -> EventConfirmation:
"""Third LLM call to generate a confirmation message"""
logger.info("Generating confirmation message")
completion = client.beta.chat.completions.parse(
model=model,
messages=[
{
"role": "system",
"content": "Generate a natural confirmation message for the event. Sign of with your name; Susie",
},
{"role": "user", "content": str(event_details.model_dump())},
],
response_format=EventConfirmation,
)
result = completion.choices[0].message.parsed
logger.info("Confirmation message generated successfully")
return result
# --------------------------------------------------------------
# Step 3: Chain the functions together
# --------------------------------------------------------------
def process_calendar_request(user_input: str) -> Optional[EventConfirmation]:
"""Main function implementing the prompt chain with gate check"""
logger.info("Processing calendar request")
logger.debug(f"Raw input: {user_input}")
# First LLM call: Extract basic info
initial_extraction = extract_event_info(user_input)
# Gate check: Verify if it's a calendar event with sufficient confidence
if (
not initial_extraction.is_calendar_event
or initial_extraction.confidence_score < 0.7
):
logger.warning(
f"Gate check failed - is_calendar_event: {initial_extraction.is_calendar_event}, confidence: {initial_extraction.confidence_score:.2f}"
)
return None
logger.info("Gate check passed, proceeding with event processing")
# Second LLM call: Get detailed event information
event_details = parse_event_details(initial_extraction.description)
# Third LLM call: Generate confirmation
confirmation = generate_confirmation(event_details)
logger.info("Calendar request processing completed successfully")
return confirmation
# --------------------------------------------------------------
# Step 4: Test the chain with a valid input
# --------------------------------------------------------------
user_input = "Let's schedule a 1h team meeting next Tuesday at 2pm with Alice and Bob to discuss the project roadmap."
result = process_calendar_request(user_input)
if result:
print(f"Confirmation: {result.confirmation_message}")
if result.calendar_link:
print(f"Calendar Link: {result.calendar_link}")
else:
print("This doesn't appear to be a calendar event request.")
# --------------------------------------------------------------
# Step 5: Test the chain with an invalid input
# --------------------------------------------------------------
user_input = "Can you send an email to Alice and Bob to discuss the project roadmap?"
result = process_calendar_request(user_input)
if result:
print(f"Confirmation: {result.confirmation_message}")
if result.calendar_link:
print(f"Calendar Link: {result.calendar_link}")
else:
print("This doesn't appear to be a calendar event request.")
Related principles
- P1delegationDesign for delegation rather than direct manipulationDesign experiences around the assignment of work, the expression of intent, the setting of constraints, and the review of results, rather than requiring users to execute each step manually.Open principle →
- P8trustMake hand-offs, approvals, and blockers explicitWhen the system cannot proceed, the reason should be immediately visible, along with any action required from the user or another dependency.Open principle →
- P9orchestrationRepresent delegated work as a system, not merely as a conversationWhere work involves multiple steps, agents, dependencies, or concurrent activities, it should be represented as a structured system rather than solely as a message stream.Open principle →
- P10delegationOptimise for steering, not only initiatingThe system should support users not only in starting tasks, but also in guiding, refining, reprioritising, and correcting work while it is underway.Open principle →