Dynamic Chain Construction
Static chains follow predetermined paths. Dynamic chains construct themselves based on input, intermediate results, or external conditions. This flexibility enables more powerful and efficient workflows.
Why Dynamic Chains?
Consider these scenarios:
- Variable input complexity: Simple requests need fewer steps than complex ones
- Discovered requirements: Initial analysis reveals what processing is needed
- Resource optimization: Skip unnecessary steps based on content
- Adaptive workflows: Adjust processing based on intermediate results
Dynamic Chain Patterns
Pattern 1: Analyze-Then-Build
First analyze the input, then construct an appropriate chain:
async function analyzeAndBuild(input) {
// Step 1: Analyze what's needed
const analysis = await analyzeInput(input);
// Step 2: Build chain based on analysis
const chainSteps = [];
if (analysis.needsTranslation) {
chainSteps.push(translateStep);
}
if (analysis.containsCode) {
chainSteps.push(codeAnalysisStep);
}
if (analysis.length > 5000) {
chainSteps.push(summarizationStep);
}
chainSteps.push(finalProcessingStep);
// Step 3: Execute the constructed chain
return executeChain(chainSteps, input);
}
Pattern 2: Progressive Expansion
Start simple, add steps as needed:
Loading Prompt Playground...
Pattern 3: Iterative Refinement Loop
Repeat steps until quality threshold is met:
async function iterativeRefinement(input, qualityThreshold = 0.8) {
let result = await generateInitial(input);
let quality = await evaluateQuality(result);
let iterations = 0;
const maxIterations = 5;
while (quality.score < qualityThreshold && iterations < maxIterations) {
const feedback = quality.suggestions;
result = await refineWithFeedback(result, feedback);
quality = await evaluateQuality(result);
iterations++;
}
return {
result,
finalQuality: quality.score,
iterations
};
}
Building Dynamic Chain Logic
Step Templates
Define reusable step templates that can be composed:
const stepTemplates = {
translate: {
id: 'translate',
dependencies: [],
prompt: (input, config) => `Translate to ${config.targetLanguage}: ${input}`,
validation: output => output.length > 0
},
summarize: {
id: 'summarize',
dependencies: [],
prompt: (input, config) => `Summarize in ${config.maxWords} words: ${input}`,
validation: output => output.split(' ').length <= config.maxWords * 1.1
},
extractEntities: {
id: 'extractEntities',
dependencies: [],
prompt: input => `Extract named entities: ${input}`,
validation: output => Array.isArray(JSON.parse(output))
}
};
Chain Builder
Loading Prompt Playground...
Runtime Chain Modification
Modify the chain during execution:
async function adaptiveChain(input, initialSteps) {
let steps = [...initialSteps];
let currentIndex = 0;
const results = [];
while (currentIndex < steps.length) {
const step = steps[currentIndex];
const previousResult = results[results.length - 1]?.output || input;
const result = await executeStep(step, previousResult);
results.push({ step: step.id, output: result });
// Check if we need to modify the chain
const modification = await evaluateAndModify(result, steps.slice(currentIndex + 1));
if (modification.insertSteps) {
// Insert new steps after current position
steps.splice(currentIndex + 1, 0, ...modification.insertSteps);
}
if (modification.skipSteps) {
// Mark steps to skip
modification.skipSteps.forEach(stepId => {
const idx = steps.findIndex(s => s.id === stepId);
if (idx > currentIndex) steps[idx].skip = true;
});
}
// Move to next non-skipped step
do {
currentIndex++;
} while (currentIndex < steps.length && steps[currentIndex].skip);
}
return results;
}
Decision Points
Complexity Assessment
Loading Prompt Playground...
Early Termination
Stop the chain early when appropriate:
async function chainWithEarlyTermination(input, steps) {
for (const step of steps) {
const result = await executeStep(step, input);
// Check termination conditions
if (result.complete && !step.required) {
return { result, terminatedEarly: true, reason: 'objective_achieved' };
}
if (result.blocked && !result.canContinue) {
return { result, terminatedEarly: true, reason: 'blocked' };
}
input = result.output;
}
return { result: input, terminatedEarly: false };
}
Exercise: Design a Dynamic Chain
Create a dynamic chain system for document processing:
Loading Prompt Playground...
Safeguards for Dynamic Chains
Maximum Steps Limit
const MAX_CHAIN_LENGTH = 20;
function validateChain(steps) {
if (steps.length > MAX_CHAIN_LENGTH) {
throw new Error(`Chain too long: ${steps.length} > ${MAX_CHAIN_LENGTH}`);
}
}
Loop Detection
function detectLoops(executionHistory) {
const stepCounts = {};
for (const step of executionHistory) {
stepCounts[step.id] = (stepCounts[step.id] || 0) + 1;
if (stepCounts[step.id] > 3) {
throw new Error(`Potential loop detected: ${step.id} executed ${stepCounts[step.id]} times`);
}
}
}
Resource Budgets
async function chainWithBudget(input, steps, budget) {
let tokensUsed = 0;
let timeSpent = 0;
for (const step of steps) {
if (tokensUsed >= budget.maxTokens) {
return { result: input, reason: 'token_budget_exhausted' };
}
const startTime = Date.now();
const result = await executeStep(step, input);
tokensUsed += result.tokensUsed;
timeSpent += Date.now() - startTime;
if (timeSpent >= budget.maxTime) {
return { result, reason: 'time_budget_exhausted' };
}
input = result.output;
}
return { result: input, tokensUsed, timeSpent };
}
Key Takeaways
- Dynamic chains adapt their structure based on input and results
- Use analyze-then-build to determine needed steps upfront
- Progressive expansion adds steps as requirements are discovered
- Iterative refinement loops until quality thresholds are met
- Define step templates for flexible composition
- Include safeguards: max length, loop detection, resource budgets
- Early termination saves resources when objectives are achieved
In the next module, we'll explore parallel and sequential execution patterns.

