Recursive Decision Points
Create multi-level quality control workflows by nesting decision points within branches. This enables iterative refinement, progressive quality checks, and complex routing logic.
Overview
Recursive decision points allow you to:
- Iteratively refine content until quality thresholds are met
- Chain multiple quality checks with fallback handling
- Create progressive improvement workflows with multiple attempts
- Handle edge cases with default branches
- Build sophisticated routing logic with nested conditions
Multi-Level Content Refinement Example
This real-world example demonstrates recursive decision points for content quality control with multiple refinement stages:
- cURL
- Python
- JavaScript/Node.js
curl -X POST http://localhost:2008/objectGen \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_KEY" \
-d '{
"prompt": "Generate a technical blog post about microservices architecture and best practices",
"definition": {
"type": "object",
"properties": {
"content": {
"type": "string",
"instruction": "Generate a technical blog post about microservices",
"scoringCriteria": {
"dimensions": {
"technical_accuracy": {
"description": "Rate the technical accuracy from 0-100",
"type": "numeric"
},
"readability": {
"description": "Rate the readability from 0-100",
"type": "numeric"
},
"completeness": {
"description": "Rate the completeness of the content from 0-100",
"type": "numeric"
}
}
},
"decisionPoint": {
"name": "ContentQualityRouter",
"branches": [
{
"name": "HighQuality",
"conditions": [
{
"field": "technical_accuracy",
"operator": "gte",
"number_value": 90
},
{
"field": "readability",
"operator": "gte",
"number_value": 90
},
{
"field": "completeness",
"operator": "gte",
"number_value": 90
}
],
"then": {
"type": "object",
"instruction": "Content meets quality standards, finalize it",
"selectFields": ["content"],
"properties": {
"final_content": {
"type": "string",
"instruction": "Return the final polished content"
},
"quality_summary": {
"type": "string",
"instruction": "Provide a brief summary of the quality metrics"
}
}
}
},
{
"name": "LowReadability",
"conditions": [
{
"field": "readability",
"operator": "lt",
"number_value": 90
},
{
"field": "technical_accuracy",
"operator": "gte",
"number_value": 80
}
],
"then": {
"type": "object",
"instruction": "Improve readability while maintaining technical accuracy",
"selectFields": ["content"],
"properties": {
"improved_content": {
"type": "string",
"instruction": "Rewrite the content with simpler language",
"scoringCriteria": {
"dimensions": {
"technical_accuracy": {
"description": "Rate the technical accuracy from 0-100",
"type": "numeric"
},
"readability": {
"description": "Rate the readability from 0-100",
"type": "numeric"
},
"completeness": {
"description": "Rate the completeness from 0-100",
"type": "numeric"
}
}
},
"decisionPoint": {
"name": "RecursiveQualityCheck",
"branches": [
{
"name": "MeetsStandards",
"conditions": [
{
"field": "readability",
"operator": "gte",
"number_value": 90
}
],
"then": {
"type": "object",
"instruction": "Finalize the improved content",
"selectFields": ["improved_content"],
"properties": {
"final_content": {
"type": "string",
"instruction": "Return the final content"
}
}
}
}
],
"default": {
"type": "object",
"instruction": "Accept current version",
"selectFields": ["improved_content"],
"properties": {
"fallback_content": {
"type": "string",
"instruction": "Return the content as-is"
}
}
}
}
}
}
}
}
],
"default": {
"type": "object",
"instruction": "Handle edge cases",
"selectFields": ["content"],
"properties": {
"fallback_content": {
"type": "string",
"instruction": "Return the original content"
}
}
}
}
}
}
}
}'
import requests
url = "http://localhost:2008/objectGen"
headers = {
"Content-Type": "application/json",
"Authorization": "Bearer YOUR_API_KEY"
}
data = {
"prompt": "Generate a technical blog post about microservices architecture and best practices",
"definition": {
"type": "object",
"properties": {
"content": {
"type": "string",
"instruction": "Generate a technical blog post about microservices",
"scoringCriteria": {
"dimensions": {
"technical_accuracy": {
"description": "Rate the technical accuracy from 0-100",
"type": "numeric"
},
"readability": {
"description": "Rate the readability from 0-100",
"type": "numeric"
},
"completeness": {
"description": "Rate the completeness of the content from 0-100",
"type": "numeric"
}
}
},
"decisionPoint": {
"name": "ContentQualityRouter",
"branches": [
{
"name": "HighQuality",
"conditions": [
{"field": "technical_accuracy", "operator": "gte", "number_value": 90},
{"field": "readability", "operator": "gte", "number_value": 90},
{"field": "completeness", "operator": "gte", "number_value": 90}
],
"then": {
"type": "object",
"instruction": "Content meets quality standards, finalize it",
"selectFields": ["content"],
"properties": {
"final_content": {
"type": "string",
"instruction": "Return the final polished content"
},
"quality_summary": {
"type": "string",
"instruction": "Provide a brief summary of the quality metrics"
}
}
}
},
{
"name": "LowReadability",
"conditions": [
{"field": "readability", "operator": "lt", "number_value": 90},
{"field": "technical_accuracy", "operator": "gte", "number_value": 80}
],
"then": {
"type": "object",
"instruction": "Improve readability while maintaining technical accuracy",
"selectFields": ["content"],
"properties": {
"improved_content": {
"type": "string",
"instruction": "Rewrite the content with simpler language",
"scoringCriteria": {
"dimensions": {
"technical_accuracy": {"description": "Rate accuracy 0-100", "type": "numeric"},
"readability": {"description": "Rate readability 0-100", "type": "numeric"},
"completeness": {"description": "Rate completeness 0-100", "type": "numeric"}
}
},
"decisionPoint": {
"name": "RecursiveQualityCheck",
"branches": [
{
"name": "MeetsStandards",
"conditions": [{"field": "readability", "operator": "gte", "number_value": 90}],
"then": {
"type": "object",
"instruction": "Finalize the improved content",
"selectFields": ["improved_content"],
"properties": {
"final_content": {"type": "string", "instruction": "Return the final content"}
}
}
}
],
"default": {
"type": "object",
"instruction": "Accept current version",
"selectFields": ["improved_content"],
"properties": {
"fallback_content": {"type": "string", "instruction": "Return the content as-is"}
}
}
}
}
}
}
}
],
"default": {
"type": "object",
"instruction": "Handle edge cases",
"selectFields": ["content"],
"properties": {
"fallback_content": {"type": "string", "instruction": "Return the original content"}
}
}
}
}
}
}
}
response = requests.post(url, headers=headers, json=data)
result = response.json()
# Access the generated fields
fields = result.get("data", {}).get("fields", {})
content = fields.get("content", {}).get("string_value", "")
final_content = fields.get("final_content", {}).get("string_value", "")
quality_summary = fields.get("quality_summary", {}).get("string_value", "")
print(f"Original Content Length: {len(content)} chars")
print(f"\nFinal Content Length: {len(final_content)} chars")
print(f"\nQuality Summary:\n{quality_summary}")
print(f"\nFirst 300 chars of final content:\n{final_content[:300]}...")
const fetch = require('node-fetch');
const url = 'http://localhost:2008/objectGen';
const headers = {
'Content-Type': 'application/json',
'Authorization': 'Bearer YOUR_API_KEY'
};
const data = {
prompt: 'Generate a technical blog post about microservices architecture and best practices',
definition: {
type: 'object',
properties: {
content: {
type: 'string',
instruction: 'Generate a technical blog post about microservices',
scoringCriteria: {
dimensions: {
technical_accuracy: { description: 'Rate the technical accuracy from 0-100', type: 'numeric' },
readability: { description: 'Rate the readability from 0-100', type: 'numeric' },
completeness: { description: 'Rate the completeness of the content from 0-100', type: 'numeric' }
}
},
decisionPoint: {
name: 'ContentQualityRouter',
branches: [
{
name: 'HighQuality',
conditions: [
{ field: 'technical_accuracy', operator: 'gte', number_value: 90 },
{ field: 'readability', operator: 'gte', number_value: 90 },
{ field: 'completeness', operator: 'gte', number_value: 90 }
],
then: {
type: 'object',
instruction: 'Content meets quality standards, finalize it',
selectFields: ['content'],
properties: {
final_content: { type: 'string', instruction: 'Return the final polished content' },
quality_summary: { type: 'string', instruction: 'Provide a brief summary of the quality metrics' }
}
}
},
{
name: 'LowReadability',
conditions: [
{ field: 'readability', operator: 'lt', number_value: 90 },
{ field: 'technical_accuracy', operator: 'gte', number_value: 80 }
],
then: {
type: 'object',
instruction: 'Improve readability while maintaining technical accuracy',
selectFields: ['content'],
properties: {
improved_content: {
type: 'string',
instruction: 'Rewrite the content with simpler language',
scoringCriteria: {
dimensions: {
technical_accuracy: { description: 'Rate accuracy 0-100', type: 'numeric' },
readability: { description: 'Rate readability 0-100', type: 'numeric' },
completeness: { description: 'Rate completeness 0-100', type: 'numeric' }
}
},
decisionPoint: {
name: 'RecursiveQualityCheck',
branches: [
{
name: 'MeetsStandards',
conditions: [{ field: 'readability', operator: 'gte', number_value: 90 }],
then: {
type: 'object',
instruction: 'Finalize the improved content',
selectFields: ['improved_content'],
properties: {
final_content: { type: 'string', instruction: 'Return the final content' }
}
}
}
],
default: {
type: 'object',
instruction: 'Accept current version',
selectFields: ['improved_content'],
properties: {
fallback_content: { type: 'string', instruction: 'Return the content as-is' }
}
}
}
}
}
}
}
],
default: {
type: 'object',
instruction: 'Handle edge cases',
selectFields: ['content'],
properties: {
fallback_content: { type: 'string', instruction: 'Return the original content' }
}
}
}
}
}
}
};
fetch(url, {
method: 'POST',
headers: headers,
body: JSON.stringify(data)
})
.then(response => response.json())
.then(result => {
const fields = result.data?.fields || {};
const content = fields.content?.string_value || '';
const finalContent = fields.final_content?.string_value || '';
const qualitySummary = fields.quality_summary?.string_value || '';
console.log(`Original Content Length: ${content.length} chars`);
console.log(`\nFinal Content Length: ${finalContent.length} chars`);
console.log(`\nQuality Summary:\n${qualitySummary}`);
console.log(`\nFirst 300 chars of final content:\n${finalContent.substring(0, 300)}...`);
})
.catch(error => console.error('Error:', error));
Expected Response
The response shows the result after the content went through the recursive quality checks:
{
"data": {
"fields": {
"content": {
"string_value": "Microservices: A Practical Guide to Building Scalable and Resilient Applications\n\nMicroservices architecture has emerged as a popular approach for building complex and scalable applications..."
},
"final_content": {
"string_value": "Microservices: A Practical Guide to Building Scalable and Resilient Applications\n\nMicroservices architecture has emerged as a popular approach for building complex and scalable applications..."
},
"quality_summary": {
"string_value": "The content is well-written, comprehensive, and provides a balanced view of microservices, covering both benefits and challenges. The best practices section is particularly valuable."
}
}
}
}
Response Flow
- Initial Generation: Creates
contentfield - First Decision Point: Scores the content (technical_accuracy, readability, completeness)
- Branch Execution: If
HighQualityconditions met → generatesfinal_contentandquality_summary - Recursive Path: If quality is insufficient → generates
improved_contentwith its own decision point - Nested Decision: The
improved_contentis scored again and routed throughRecursiveQualityCheck - Final Output: Returns the best version based on quality thresholds
Recursive Decision Point Pattern
The key to recursive decision points is nesting decisionPoint within branch then properties:
{
"decisionPoint": {
"name": "FirstLevel",
"branches": [
{
"name": "NeedsWork",
"conditions": [...],
"then": {
"type": "object",
"properties": {
"improved_version": {
"type": "string",
"scoringCriteria": { ... },
"decisionPoint": {
"name": "SecondLevel",
"branches": [
{
"name": "Acceptable",
"conditions": [...],
"then": {
// Final output
}
}
],
"default": {
// Fallback handling
}
}
}
}
}
}
]
}
}
Key Features
1. Default Branches
Handle edge cases and unexpected scenarios:
"default": {
"type": "object",
"instruction": "Handle cases that don't match any branch",
"selectFields": ["content"],
"properties": {
"fallback_content": {
"type": "string",
"instruction": "Return the content with minimal modifications"
}
}
}
2. Multiple Recursion Levels
Your example demonstrates up to 3 levels of nested decision points:
- Level 1: ContentQualityRouter (checks initial quality)
- Level 2: RecursiveQualityCheck (re-checks improved content)
- Level 3: FinalQualityCheck (last attempt with lower thresholds)
3. Progressive Threshold Relaxation
Notice how thresholds can be adjusted at each level:
- First check: Requires ≥90 for all metrics
- Second check: Requires ≥90 readability (after improvement)
- Final check: Accepts ≥85 (more lenient)
Use Cases
- Content Quality Control: Iteratively refine until quality standards are met
- Code Review Workflows: Multiple review stages with different criteria
- Data Validation: Progressive validation with fallback handling
- Translation Quality: Refine translations through multiple passes
- Compliance Checking: Multi-stage approval workflows
- A/B Testing: Generate variants and select best performer
Best Practices
- Limit Recursion Depth: Keep to 2-3 levels maximum to avoid excessive token usage
- Always Include Default: Handle edge cases gracefully
- Use selectFields: Pass relevant context to nested branches
- Progressive Thresholds: Relax criteria at deeper levels
- Clear Naming: Use descriptive names for decision points and branches
- Score Consistently: Use same dimensions across recursion levels
- Prevent Infinite Loops: Ensure decreasing thresholds or iteration limits
Use selectFields to pass only the relevant content to nested branches. This reduces token usage and keeps the context focused.
Deep recursion (3+ levels) with multiple branches can significantly increase token usage. Monitor costs and set reasonable recursion limits.
Advanced: Multi-Path Recursion
Your full example shows multiple recursive paths:
ContentQualityRouter
├── HighQuality → final_content
├── LowReadability → improved_content
│ └── RecursiveQualityCheck
│ ├── MeetsStandards → final_content
│ └── NeedsMoreWork → refined_content
│ └── FinalQualityCheck
│ └── Acceptable → final_content
├── LowAccuracy → corrected_content
│ └── AccuracyRecheck
│ └── ImprovedAccuracy → final_content
└── LowCompleteness → expanded_content
└── CompletenessRecheck
└── NowComplete → final_content
Each path handles a specific quality issue and recursively refines until acceptable.
Next Steps
- See Decision Trees for basic decision point patterns
- Learn about Array Generation for list-based structures
- Explore Epistemic Uncertainty for confidence modeling