← Back to Thinking

03 - Agent Decision Trees: How Contextual Intelligence Actually Thinks

Vince Mease• Technical Deep Dive• 9/6/2025
project:routekit-shellblogtechnicalagentsdecision-treescontextual-intelligence

Part 2: Breaking down the decision logic that transforms retrieved context into intelligent actions

Agent Decision Trees: How Contextual Intelligence Actually Thinks

Part 3 of the Technical Deep Dive Series

In Part 2: Anatomy of a Retrieval, we saw how the Guardrailed Retriever Stack finds relevant context. But retrieval is only half the story. The real magic happens in what agents do with that context.

This is the decision engine that transforms generic AI into contextual intelligence.

The Core Problem: Context Without Intelligence is Just Data

Here's what happens when you give raw context to a generic AI:

// Generic AI approach
const context = await retrieve("How should I implement user auth?");
const response = await ai.complete({
  prompt: `Context: ${context}\n\nQuestion: How should I implement user auth?`,
  model: "gpt-4"
});
// Result: Generic OAuth2 tutorial, ignores your actual architecture

Here's what happens with contextual intelligence:

// Contextual intelligence approach
const context = await retrieveWithRouting("How should I implement user auth?", config);
const decision = await agentDecisionTree.process(context, query, projectState);
// Result: Specific implementation that matches your patterns, dependencies, and constraints

The Agent Decision Framework

Every enhanced agent in RouteKit Shell follows this decision tree:

Rendering diagram...

Step 1: Context Gathering - The Intelligence Foundation

Every agent starts with systematic context gathering. Here's the actual implementation:

// .claude/agents/engineering/routekit-shell-frontend-developer.md
export class ContextualFrontendAgent {
  async gatherContext(query: string): Promise<ContextBundle> {
    // Step 1a: Search for existing solutions
    const existingSolutions = await ragQuery({
      query: `${query} implementation examples`,
      k: 3
    });
    
    // Step 1b: Find architectural patterns
    const patterns = await ragQuery({
      query: `${extractDomain(query)} patterns architecture`,
      k: 3
    });
    
    // Step 1c: Identify constraints
    const constraints = await ragQuery({
      query: `technical constraints ${extractDomain(query)}`,
      k: 2
    });
    
    return {
      existingSolutions,
      patterns,
      constraints,
      confidence: calculateContextConfidence([existingSolutions, patterns, constraints])
    };
  }
}

Step 2: Pattern Analysis - Finding What Already Works

The agent analyzes retrieved context to identify reusable patterns:

async analyzePatterns(context: ContextBundle): Promise<PatternAnalysis> {
  // Extract pattern signatures
  const signatures = context.existingSolutions.map(solution => ({
    dependencies: extractDependencies(solution.text),
    architecture: identifyArchitecture(solution.text),
    complexity: assessComplexity(solution.text)
  }));
  
  // Find consensus patterns
  const consensus = findConsensus(signatures);
  
  // Identify deviations and their reasons
  const deviations = signatures.filter(sig => 
    !matchesPattern(sig, consensus)
  ).map(sig => ({
    pattern: sig,
    reason: identifyDeviationReason(sig, context.constraints)
  }));
  
  return {
    primaryPattern: consensus,
    alternativePatterns: deviations,
    confidence: consensus.support / signatures.length
  };
}

Step 3: Constraint Evaluation - Respecting Project Reality

Generic AI ignores constraints. Contextual intelligence respects them:

async evaluateConstraints(
  patterns: PatternAnalysis, 
  context: ContextBundle
): Promise<ConstraintEvaluation> {
  
  const constraints = {
    // Technical constraints
    dependencies: extractAllowedDependencies(context),
    architecture: extractArchitecturalConstraints(context),
    performance: extractPerformanceConstraints(context),
    
    // Business constraints  
    timeline: extractTimelineConstraints(context),
    team: extractTeamConstraints(context),
    maintenance: extractMaintenanceConstraints(context)
  };
  
  // Filter patterns by constraints
  const viablePatterns = patterns.filter(pattern => 
    satisfiesConstraints(pattern, constraints)
  );
  
  // Rank by constraint satisfaction
  const rankedPatterns = viablePatterns.sort((a, b) => 
    constraintScore(b, constraints) - constraintScore(a, constraints)
  );
  
  return {
    constraints,
    viablePatterns: rankedPatterns,
    rejectedPatterns: patterns.filter(p => !viablePatterns.includes(p))
  };
}

Real Decision Tree Walkthrough: Frontend Component Architecture

Let's trace a real query through the decision tree:

Query: "Create a user profile component with avatar upload"

Step 1: Context Gathering

// RAG queries executed in parallel
const context = await Promise.all([
  ragQuery("user profile component implementation", 3),
  ragQuery("avatar upload patterns React", 3), 
  ragQuery("file upload constraints security", 2)
]);

// Results found:
// - 3 existing profile components in different projects
// - 2 avatar upload implementations  
// - 1 security constraint document about file uploads

Step 2: Pattern Analysis

// Pattern extraction
const patterns = analyzePatterns(context);
/*
Primary Pattern Found:
- React functional component with hooks
- Separate upload service for file handling
- Optimistic UI updates with fallback
- Integration with existing design system

Confidence: 0.85 (85% of examples follow this pattern)
*/

Step 3: Constraint Evaluation

// Constraint checking
const constraints = evaluateConstraints(patterns, context);
/*
Active Constraints:
- Must use existing RouteKit design system components
- File upload limited to 2MB (security constraint)
- No new dependencies without approval
- Must support mobile responsive design

Viable Patterns: 1 (primary pattern satisfies all constraints)
*/

Step 4: Solution Architecture

// Architecture decision
const architecture = designSolution(patterns[0], constraints);
/*
Recommended Architecture:
Component: ProfileComponent.tsx
Service: AvatarUploadService.ts
Design: Use existing Card, Button, Avatar from design system
Validation: Client-side size check + server-side validation
State: React hooks for upload state management
*/

Step 5: Implementation Planning

// Implementation breakdown
const plan = createImplementationPlan(architecture, context);
/*
Implementation Plan:
1. Create ProfileComponent using existing Card pattern
2. Implement AvatarUploadService following existing file service pattern
3. Add upload validation using existing validation utils
4. Integrate with existing error handling system
5. Add loading states using existing spinner component
6. Write tests following existing component test patterns

Files to create: 3
Files to modify: 1 (add service to index)
Estimated complexity: Medium
*/

Step 6: Validation & Safety

// Final validation
const validation = validateSolution(plan, context, constraints);
/*
Validation Results:
✅ Follows existing architectural patterns
✅ Uses approved dependencies only  
✅ Meets security constraints
✅ Integrates with existing design system
✅ Has test coverage plan
✅ Handles error cases per existing patterns

Safety Score: 0.95
Ready for implementation: true
*/

The Decision Tree Outputs: Contextual vs Generic

Generic AI Output:

Create a React component with useState for the profile data and use 
a file input for avatar upload. You'll need to handle the file upload 
to your backend and update the UI accordingly.

Contextual Intelligence Output:

// Based on your existing Card and Avatar patterns, here's the recommended approach:

// ProfileComponent.tsx - follows your established component patterns
import { Card, Avatar, Button } from '@/components/design-system';
import { useAvatarUpload } from '@/hooks/useAvatarUpload'; // matches existing hook patterns

export const ProfileComponent = () => {
  const { uploadAvatar, isUploading, error } = useAvatarUpload({
    maxSize: 2 * 1024 * 1024, // respects your 2MB security constraint
    onSuccess: (url) => updateUserProfile({ avatarUrl: url })
  });

  // Implementation follows your existing error handling patterns
  // Uses your established loading state conventions
  // Integrates with your existing validation system
};

// This approach:
// ✅ Uses your existing design system components
// ✅ Follows your established hook patterns  
// ✅ Respects your file size security constraints
// ✅ Integrates with your existing error handling
// ✅ Matches your team's component structure conventions

Advanced Decision Patterns

Pattern 1: Cross-Domain Intelligence

When a query spans multiple domains, agents collaborate:

// Frontend agent recognizes backend implications
if (requiresBackendChanges(solution)) {
  const backendContext = await ragQuery(
    `backend API patterns ${extractBackendRequirements(solution)}`, 
    3
  );
  
  solution.backendRecommendations = await consultBackendAgent(
    backendContext, 
    solution
  );
}

Pattern 2: Progressive Complexity

Agents start simple and add complexity only when needed:

const complexityLevels = [
  'minimal_viable_implementation',
  'production_ready_implementation', 
  'enterprise_scale_implementation'
];

let selectedComplexity = complexityLevels[0];

// Upgrade complexity based on constraints
if (constraints.userLoad > 10000) selectedComplexity = complexityLevels[1];
if (constraints.compliance.required) selectedComplexity = complexityLevels[2];

Pattern 3: Failure Recovery

When primary patterns fail constraints, agents have fallback strategies:

// Primary pattern fails constraints
if (!satisfiesConstraints(primaryPattern, constraints)) {
  // Try alternative patterns
  for (const altPattern of alternativePatterns) {
    if (satisfiesConstraints(altPattern, constraints)) {
      return adaptPattern(altPattern, constraints);
    }
  }
  
  // No existing pattern works - synthesize new approach
  return synthesizeNewPattern(constraints, context);
}

The Self-Improving Loop

Here's the remarkable part: agents learn from their decisions.

// After implementation
async recordDecisionOutcome(
  decision: AgentDecision,
  outcome: ImplementationOutcome
) {
  // Store successful patterns for future use
  if (outcome.success) {
    await storePattern({
      query: decision.originalQuery,
      solution: decision.recommendedSolution,
      context: decision.context,
      satisfaction: outcome.userSatisfaction
    });
  }
  
  // Learn from failures
  if (!outcome.success) {
    await recordAntiPattern({
      pattern: decision.recommendedSolution,
      failureReason: outcome.failureReason,
      context: decision.context
    });
  }
}

This creates a feedback loop where successful decisions become preferred patterns for future queries.

Performance Characteristics

The decision tree is surprisingly fast:

Decision Tree Phase Time Range Process
Context Gathering 200-500ms 3-5 parallel RAG queries
Pattern Analysis 50-100ms JavaScript pattern matching
Constraint Evaluation 20-50ms Rule engine evaluation
Solution Architecture 30-80ms Template generation
Implementation Planning 40-100ms Complexity analysis
Validation & Safety 10-30ms Constraint checking
TOTAL DECISION TIME 350-860ms Complete contextual decision

Performance Comparison:

Approach Time Required Context Quality
Human architect research 2-4 hours High (with experience)
Generic AI with manual context 15-30 minutes back-and-forth Low (context switching)
Contextual intelligence Under 1 second High (maintained context)

The Meta-Decision: When to Decide vs When to Ask

Agents make meta-decisions about their own decision-making:

const decisionConfidence = calculateConfidence(
  contextQuality,
  patternMatch,
  constraintCertainty
);

if (decisionConfidence > 0.8) {
  // High confidence - proceed with recommendation
  return makeDecision(context, patterns, constraints);
} else if (decisionConfidence > 0.5) {
  // Medium confidence - present options
  return presentAlternatives(viablePatterns, reasoning);
} else {
  // Low confidence - ask clarifying questions
  return requestClarification(missingContext, uncertainConstraints);
}

What Makes This Different from Generic AI

Generic AI: "Here are some options for user authentication"

Contextual Intelligence: "Based on your existing JWT middleware, MongoDB user schema, and the security constraints documented in Decision 003, I recommend extending your existing auth service with OAuth2 support using the same error handling patterns you've established. This integrates with your existing user management flow and respects your no-new-dependencies constraint."

The difference: Context-aware reasoning that respects established patterns and constraints.

Continue the Series

Next: 04 - From Architecture to Product Intelligence - How contextual AI transforms product development


*This is Part 3 of our Technical Deep Dive series exploring AI-first development frameworks. Next: