Implementing Prompt Chaining

Introduction & Overview

Welcome! In our previous lesson, you learned the fundamentals of communicating with GPT-5 through the OpenAI Responses API in Ruby. You mastered initializing OpenAI::Client.new, structuring role-based messages with typed content blocks, and managing multi-turn conversations. Now, you're ready to take the next step and unlock even more powerful ways to work with GPT-5.

In this lesson, you'll discover how to design and implement multi-step workflows using GPT-5, with a special focus on a technique called prompt chaining. You'll build on everything you know about the text_message helper and input arrays to connect multiple GPT-5 calls together. By the end, you'll know how to break down sophisticated tasks into manageable, reliable steps and connect them for robust AI-powered solutions.

What is a Workflow?

A workflow is a structured sequence of steps or actions designed to accomplish a specific goal. In the context of AI systems, workflows help you organize and coordinate tasks so that each step has a clear purpose, defined inputs and outputs, and measurable success criteria. There are many types of workflows — some involve a single interaction, while others may require multiple steps, validation, or branching logic. Well-designed workflows make complex processes more predictable, easier to debug, and simpler to maintain.

Prompt Chaining and Why it Matters

Prompt chaining is one specific workflow pattern where you connect multiple separate GPT-5 calls together, with each call building upon the output of the previous one. Unlike multi-turn conversations that happen within a single session, prompt chaining involves distinct API calls that work together to solve complex problems step by step.

The power of prompt chaining lies in its reliability and modularity. Instead of asking GPT-5 to perform multiple complex tasks in a single prompt (which can lead to inconsistent results), you break the work into focused steps where you can validate and control the output at each stage. This approach makes your AI workflows more predictable and easier to debug.

Design the Workflow Before Coding

Before writing any code, it's important to break your task into clear, manageable steps. For our example, we'll build a simple three-step workflow:

  1. Generate a summary about AI in healthcare, with a strict character limit (around 300 characters).
  2. Validate that the summary meets the character requirement.
  3. Translate the validated summary into Spanish, returning only the translated text.

Each step will have its own focused prompt and clear input/output, making the workflow easy to follow and debug. This approach helps ensure each part works as expected before moving to the next.

Step 1: Generate a Constrained Summary

Let's start building our chain by creating the first step: generating a summary with specific character constraints. This step demonstrates how to structure the input array with a developer message and a user message to get predictable output from GPT-5.

require "openai"

def text_message(role, text)
  {
    role: role,
    content: [
      { type: "input_text", text: text }
    ]
  }
end

# Initialize the OpenAI client
client = OpenAI::Client.new

# Choose a model to use
model = "gpt-5"

# Step 1: Ask GPT-5 to write a summary with specific character constraints
summary_prompt = "You are a helpful assistant that writes clear, concise summaries."

summary_response = client.responses.create(
  model: model,
  input: [
    text_message("developer", summary_prompt),
    text_message("user", "Write a 300 characters summary of artificial intelligence and its current applications in healthcare.")
  ],
  reasoning: { effort: "minimal" },
  store: false
)

# Extract the summary text from GPT-5's response
summary_text = summary_response.output_text
puts "Summary:"
puts summary_text

Notice how both the role instruction and the user task are passed together inside the input array. There is no separate instructions parameter here — instead, the developer message sets GPT-5's role as a summary writer, and the user message carries the specific task and constraints. Both are built using the text_message helper, which wraps each piece of text in a typed content block (type: "input_text"). This consistent structure keeps your message-building readable and reusable across every step of your chain.

We've added the reasoning parameter with effort: "minimal" to optimize for faster response times. Since summarization is a straightforward task that doesn't require complex logical analysis, minimal reasoning effort is sufficient while keeping the workflow efficient. This balance between quality and speed is particularly important when building multi-step chains.

The user message is explicit about the character requirement. Instead of saying "write a short summary," we specify exactly "300 characters" to make the constraint testable and clear. This precision is essential in prompt chaining because the output of this step becomes the input for the next step.

When you run this code, you'll see output similar to:

Summary:
Artificial intelligence uses algorithms to analyze data, recognize patterns, and support decisions. In healthcare, AI powers medical imaging diagnosis, predictive analytics, personalized treatment, drug discovery, clinical triage, virtual assistants, workflow automation, and remote patient monitoring.

The summary_response.output_text extraction pattern provides direct access to GPT-5's generated text. This straightforward approach works well for simple text responses like this summary, making it easy to pass the output to subsequent steps in your chain.

Step 2: Validate and Guardrail the Output

The second step in our chain adds a crucial validation layer that ensures our summary meets the character requirements before proceeding to translation. This validation step demonstrates how to build reliable guardrails into your prompt chains.

# Step 2: Validate that the summary meets our character requirements
unless summary_text.length.between?(250, 350)
  raise "Summary does not meet character requirement (250-350 characters). Got #{summary_text.length} characters."
end

puts "✅ SUCCESS: Summary meets character requirement: #{summary_text.length} characters"

This validation step uses a programmatic check rather than asking GPT-5 to validate its own output. We call summary_text.length to get the character count and between?(250, 350) to check the acceptable range. Using unless keeps the intent clear and readable — the body runs only when the condition is not met, which reads naturally as "unless the length is within range, raise an error."

We define a reasonable range (250–350 characters) instead of requiring exactly 300, which gives GPT-5 some flexibility while still meeting our needs. The raise call includes a descriptive message using Ruby string interpolation (#{}) with both the expected range and the actual character count, making debugging straightforward when your chain encounters problems. In production systems, you might want to implement retry logic here, perhaps asking GPT-5 to revise the summary with tighter constraints.

When the validation passes, you'll see output like:

✅ SUCCESS: Summary meets character requirement: 302 characters

Without validation, a summary that's too long or too short could cause problems in subsequent steps. By catching and handling constraint violations early, you make your entire workflow more robust.

Step 3: Feed the Output into Translation

The third step demonstrates the core concept of prompt chaining: using the output from one GPT-5 call as input to another. This step takes our validated summary and translates it into Spanish by building a new input array where the user message embeds summary_text directly.

# Step 3: Chain the summary output as input to a translation task
translation_prompt = "You are a professional translator that provides accurate Spanish translations."

translation_response = client.responses.create(
  model: model,
  input: [
    text_message("developer", translation_prompt),
    text_message("user", "Return me just the Spanish translation of the following text:\n\n#{summary_text}")
  ],
  reasoning: { effort: "minimal" },
  store: false
)

# Extract and display the final translation result
translation_text = translation_response.output_text
puts "Spanish Translation:"
puts translation_text

Just like in step one, we pass both the role instruction and the task inside the input array. The developer message focuses GPT-5 exclusively on translation, and the user message delivers the content to translate. This specialization helps GPT-5 understand its role in this step and produces more consistent results.

The key insight here is how we pass summary_text from step one into the user message for step three. Ruby string interpolation (#{summary_text}) embeds the validated summary directly into the content block passed to text_message, creating a clean handoff between the two API calls. The instruction ("Return me just the Spanish translation") and the content are kept together in a single, readable string.

Like the summary step, we use effort: "minimal" for reasoning since translation is a well-defined task that doesn't require complex problem-solving. This keeps the chain executing quickly while maintaining translation quality.

When you run this final step, you'll see output like:

Spanish Translation:
La inteligencia artificial utiliza algoritmos para analizar datos, reconocer patrones y respaldar la toma de decisiones. En el ámbito de la salud, la IA impulsa el diagnóstico por imágenes médicas, la analítica predictiva, los tratamientos personalizados, el descubrimiento de fármacos, el triaje clínico, los asistentes virtuales, la automatización de flujos de trabajo y la monitorización remota de pacientes.

Each step builds naturally on the previous one, creating a smooth workflow from English summary generation through validation to Spanish translation.

Summary & Prep for Practice

You've successfully designed and implemented a three-step prompt chain that demonstrates the core concepts of sequential AI workflows. Your chain writes a constrained summary, validates that it meets requirements, then uses that validated output as input for translation. The key patterns you've learned include decomposing complex tasks into focused steps, using validation and guardrails between steps, safely passing output from one GPT-5 call as input to the next, and optimizing each step with appropriate reasoning effort levels.

The workflow pattern you've built calls client.responses.create for each step, passing a fresh input array that contains a developer message and a user message — both constructed with the text_message helper and its typed content blocks. Each call includes reasoning configuration for optimal performance, and every result is extracted cleanly using the output_text property. This consistent, composable structure makes your chains straightforward to read, test, and maintain.

In the upcoming practice exercises, you'll implement this code yourself and extend it with additional features. The foundation you've built with prompt chaining opens up possibilities for much more sophisticated AI workflows. As you continue through this course, you'll see how these basic chaining concepts extend to tool usage, dynamic workflows, and complex agent behaviors that can handle real-world problems.

Sign up

Join the 1M+ learners on CodeSignal

Be a part of our community of 1M+ users who develop and demonstrate their skills on CodeSignal