Class: Legion::Extensions::Agentic::Executive::DualProcess::Helpers::DualProcessEngine

Inherits:
Object
  • Object
show all
Includes:
Constants
Defined in:
lib/legion/extensions/agentic/executive/dual_process/helpers/dual_process_engine.rb

Constant Summary

Constants included from Constants

Constants::COMPLEXITY_THRESHOLD, Constants::CONFIDENCE_CEILING, Constants::CONFIDENCE_FLOOR, Constants::DECAY_RATE, Constants::DECISION_OUTCOMES, Constants::DEFAULT_CONFIDENCE, Constants::EFFORT_COST, Constants::EFFORT_RECOVERY_RATE, Constants::FATIGUE_PENALTY, Constants::HEURISTIC_BOOST, Constants::MAX_DECISIONS, Constants::MAX_EFFORT_BUDGET, Constants::MAX_HEURISTICS, Constants::MAX_HISTORY, Constants::ROUTING_LABELS, Constants::SYSTEMS, Constants::SYSTEM_ONE_THRESHOLD

Instance Method Summary collapse

Constructor Details

#initializeDualProcessEngine

Returns a new instance of DualProcessEngine.



12
13
14
15
16
# File 'lib/legion/extensions/agentic/executive/dual_process/helpers/dual_process_engine.rb', line 12

def initialize
  @effort_budget = MAX_EFFORT_BUDGET
  @heuristics    = {}
  @decisions     = []
end

Instance Method Details

#best_heuristics(limit: 5) ⇒ Object



145
146
147
148
149
150
151
# File 'lib/legion/extensions/agentic/executive/dual_process/helpers/dual_process_engine.rb', line 145

def best_heuristics(limit: 5)
  @heuristics.values
             .select(&:reliable?)
             .sort_by { |h| [-h.success_rate, -h.use_count] }
             .first(limit)
             .map(&:to_h)
end

#decay_heuristicsObject



122
123
124
125
126
127
128
129
# File 'lib/legion/extensions/agentic/executive/dual_process/helpers/dual_process_engine.rb', line 122

def decay_heuristics
  @heuristics.each_value do |h|
    next unless h.use_count.positive?

    delta = -DECAY_RATE
    h.instance_variable_set(:@confidence, (h.confidence + delta).clamp(CONFIDENCE_FLOOR, CONFIDENCE_CEILING))
  end
end

#effort_levelObject



110
111
112
# File 'lib/legion/extensions/agentic/executive/dual_process/helpers/dual_process_engine.rb', line 110

def effort_level
  @effort_budget / MAX_EFFORT_BUDGET
end

#execute_system_one(query:, domain:) ⇒ Object



43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
# File 'lib/legion/extensions/agentic/executive/dual_process/helpers/dual_process_engine.rb', line 43

def execute_system_one(query:, domain:)
  matching = find_matching_heuristic(query, domain)
  start    = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC)

  if matching
    matching.use!(success: true)
    confidence = matching.confidence
    response   = matching.response
  else
    confidence = (DEFAULT_CONFIDENCE - FATIGUE_PENALTY).clamp(CONFIDENCE_FLOOR, CONFIDENCE_CEILING)
    response   = :no_heuristic
  end

  elapsed = ((::Process.clock_gettime(::Process::CLOCK_MONOTONIC) - start) * 1000).round

  decision = Decision.new(
    query:              query,
    domain:             domain,
    system_used:        :system_one,
    confidence:         confidence,
    complexity:         0.0,
    heuristic_id:       matching&.id,
    effort_cost:        0.0,
    processing_time_ms: elapsed
  )
  store_decision(decision)
  { decision_id: decision.id, system: :system_one, response: response, confidence: confidence }
end

#execute_system_two(query:, domain:, deliberation: {}) ⇒ Object



72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
# File 'lib/legion/extensions/agentic/executive/dual_process/helpers/dual_process_engine.rb', line 72

def execute_system_two(query:, domain:, deliberation: {})
  start = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
  @effort_budget = (@effort_budget - EFFORT_COST).clamp(0.0, MAX_EFFORT_BUDGET)

  confidence = deliberation.fetch(:confidence, DEFAULT_CONFIDENCE + HEURISTIC_BOOST)
                           .clamp(CONFIDENCE_FLOOR, CONFIDENCE_CEILING)
  response   = deliberation.fetch(:response, :deliberated)
  elapsed    = ((::Process.clock_gettime(::Process::CLOCK_MONOTONIC) - start) * 1000).round

  decision = Decision.new(
    query:              query,
    domain:             domain,
    system_used:        :system_two,
    confidence:         confidence,
    complexity:         deliberation.fetch(:complexity, COMPLEXITY_THRESHOLD),
    effort_cost:        EFFORT_COST,
    processing_time_ms: elapsed
  )
  store_decision(decision)
  { decision_id: decision.id, system: :system_two, response: response, confidence: confidence }
end

#record_outcome(decision_id:, outcome:) ⇒ Object



94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
# File 'lib/legion/extensions/agentic/executive/dual_process/helpers/dual_process_engine.rb', line 94

def record_outcome(decision_id:, outcome:)
  return { success: false, reason: :invalid_outcome } unless Constants::DECISION_OUTCOMES.include?(outcome)

  decision = @decisions.find { |d| d.id == decision_id }
  return { success: false, reason: :not_found } unless decision

  decision.outcome = outcome

  if decision.heuristic_id
    heuristic = @heuristics[decision.heuristic_id]
    heuristic&.use!(success: outcome == :correct)
  end

  { success: true, decision_id: decision_id, outcome: outcome }
end

#recover_effortObject



118
119
120
# File 'lib/legion/extensions/agentic/executive/dual_process/helpers/dual_process_engine.rb', line 118

def recover_effort
  @effort_budget = (@effort_budget + EFFORT_RECOVERY_RATE).clamp(0.0, MAX_EFFORT_BUDGET)
end

#register_heuristic(pattern:, domain:, response:, confidence: DEFAULT_CONFIDENCE) ⇒ Object



18
19
20
21
22
23
24
25
26
# File 'lib/legion/extensions/agentic/executive/dual_process/helpers/dual_process_engine.rb', line 18

def register_heuristic(pattern:, domain:, response:, confidence: DEFAULT_CONFIDENCE)
  heuristic = Heuristic.new(pattern: pattern, domain: domain, response: response, confidence: confidence)
  if @heuristics.size >= MAX_HEURISTICS
    oldest_key = @heuristics.min_by { |_, h| h.last_used_at || h.created_at }.first
    @heuristics.delete(oldest_key)
  end
  @heuristics[heuristic.id] = heuristic
  heuristic
end

#route_decision(query:, domain:, complexity:) ⇒ Object



28
29
30
31
32
33
34
35
36
37
38
39
40
41
# File 'lib/legion/extensions/agentic/executive/dual_process/helpers/dual_process_engine.rb', line 28

def route_decision(query:, domain:, complexity:)
  matching = find_matching_heuristic(query, domain)
  use_system_one = complexity < COMPLEXITY_THRESHOLD &&
                   matching &&
                   matching.confidence >= SYSTEM_ONE_THRESHOLD

  if use_system_one
    { system: :system_one, reason: :heuristic_match, heuristic_id: matching.id }
  elsif @effort_budget >= EFFORT_COST
    { system: :system_two, reason: complexity_reason(complexity, matching) }
  else
    { system: :system_one, reason: :effort_depleted, fatigue: true }
  end
end

#routing_labelObject



114
115
116
# File 'lib/legion/extensions/agentic/executive/dual_process/helpers/dual_process_engine.rb', line 114

def routing_label
  ROUTING_LABELS.find { |range, _| range.cover?(effort_level) }&.last || :unknown
end

#system_statsObject



131
132
133
134
135
136
137
138
139
140
141
142
143
# File 'lib/legion/extensions/agentic/executive/dual_process/helpers/dual_process_engine.rb', line 131

def system_stats
  s1 = @decisions.count { |d| d.system_used == :system_one }
  s2 = @decisions.count { |d| d.system_used == :system_two }
  total = @decisions.size

  {
    system_one: s1,
    system_two: s2,
    total:      total,
    s1_ratio:   total.zero? ? 0.0 : (s1.to_f / total).round(3),
    s2_ratio:   total.zero? ? 0.0 : (s2.to_f / total).round(3)
  }
end

#to_hObject



153
154
155
156
157
158
159
160
161
162
# File 'lib/legion/extensions/agentic/executive/dual_process/helpers/dual_process_engine.rb', line 153

def to_h
  {
    effort_budget:   @effort_budget,
    effort_level:    effort_level,
    routing_label:   routing_label,
    heuristic_count: @heuristics.size,
    decision_count:  @decisions.size,
    system_stats:    system_stats
  }
end