Prompt Chains
The PromptChain class connects multiple prompt steps into a pipeline where each step's output feeds into the next as a template variable. This enables multi-step reasoning patterns like summarize→translate, extract→analyze, or generate→review→refine.
Basic Chain
using Prompt;
var chain = new PromptChain()
.AddStep("summarize",
new PromptTemplate("Summarize this text in 2 sentences: {{text}}"),
"summary")
.AddStep("translate",
new PromptTemplate("Translate to French: {{summary}}"),
"french")
.AddStep("keywords",
new PromptTemplate("Extract 5 keywords from: {{summary}}"),
"keywords");
var result = await chain.RunAsync(new Dictionary<string, string>
{
["text"] = "Your long article text here..."
});
Console.WriteLine(result.FinalResponse); // keywords output
Console.WriteLine(result.GetOutput("summary")); // the summary
Console.WriteLine(result.GetOutput("french")); // French translation
How It Works
- Each step renders its template using all accumulated variables
- The rendered prompt is sent to Azure OpenAI
- The response is stored under the step's
outputVariablename - Subsequent steps can reference it as
{{outputVariable}}
Configuration
Set a system prompt, retry policy, and model options for the entire chain:
var chain = new PromptChain()
.WithSystemPrompt("You are a senior technical writer.")
.WithMaxRetries(5)
.WithOptions(new PromptOptions
{
Temperature = 0.3f,
MaxTokens = 2000
})
.AddStep("outline",
new PromptTemplate("Create an outline for a guide about {{topic}}."),
"outline")
.AddStep("draft",
new PromptTemplate("Write the full guide based on this outline:\n\n{{outline}}"),
"draft");
Working with Results
The ChainResult provides detailed information about every step:
ChainResult result = await chain.RunAsync(initialVars);
// Final output
string? finalAnswer = result.FinalResponse;
// Access any step's output by variable name
string? summary = result.GetOutput("summary");
// Total execution time
Console.WriteLine($"Total time: {result.TotalElapsed}");
// Iterate over individual steps
foreach (StepResult step in result.Steps)
{
Console.WriteLine($"Step: {step.StepName}");
Console.WriteLine($" Prompt: {step.RenderedPrompt}");
Console.WriteLine($" Response: {step.Response}");
Console.WriteLine($" Time: {step.Elapsed}");
}
// All accumulated variables (inputs + outputs)
foreach (var (key, value) in result.Variables)
{
Console.WriteLine($"{key} = {value}");
}
Export Results as JSON
string json = result.ToJson();
// Includes step details, timing, all variables — useful for logging and debugging
Validation
Check that all template variables can be satisfied before running:
var errors = chain.Validate(new Dictionary<string, string>
{
["text"] = "sample input"
});
if (errors.Count > 0)
{
foreach (var error in errors)
Console.WriteLine($"⚠ {error}");
}
else
{
Console.WriteLine("Chain is valid.");
}
Validation traces variable flow through the chain — it checks that each step's required variables are either in the initial input or produced by a prior step.
Common Patterns
Summarize → Translate
var chain = new PromptChain()
.AddStep("summarize",
new PromptTemplate("Summarize in 3 sentences:\n\n{{text}}"),
"summary")
.AddStep("translate",
new PromptTemplate("Translate to {{language}}:\n\n{{summary}}"),
"translation");
var result = await chain.RunAsync(new Dictionary<string, string>
{
["text"] = longArticle,
["language"] = "Spanish"
});
Generate → Review → Refine
var chain = new PromptChain()
.WithOptions(PromptOptions.ForCodeGeneration())
.AddStep("generate",
new PromptTemplate("Write a {{language}} function that {{task}}."),
"code")
.AddStep("review",
new PromptTemplate("Review this code for bugs and edge cases:\n\n{{code}}"),
"review")
.AddStep("refine",
new PromptTemplate("Fix the issues found in the review and return the improved code.\n\nCode:\n{{code}}\n\nReview:\n{{review}}"),
"final_code");
Extract → Analyze
var chain = new PromptChain()
.WithOptions(PromptOptions.ForDataExtraction())
.AddStep("extract",
new PromptTemplate("Extract all dates, names, and monetary amounts from:\n\n{{document}}"),
"entities")
.AddStep("analyze",
new PromptTemplate("Analyze these extracted entities and identify patterns or anomalies:\n\n{{entities}}"),
"analysis");
Serialization
Save and load chain definitions as JSON for reuse:
// Save definition
string json = chain.ToJson();
await chain.SaveToFileAsync("chains/summarize-translate.json");
// Load definition
var loaded = PromptChain.FromJson(json);
var fromFile = await PromptChain.LoadFromFileAsync("chains/summarize-translate.json");
// Run the loaded chain
var result = await loaded.RunAsync(initialVars);
Cancellation
Cancel long-running chains:
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(60));
try
{
var result = await chain.RunAsync(initialVars, cts.Token);
}
catch (OperationCanceledException)
{
Console.WriteLine("Chain cancelled — partial results may be available.");
}
Cancellation is checked between steps and passed through to each Azure OpenAI call, so a cancel request will stop the chain as soon as the current step completes.