Module: Legion::Extensions::Agentic::Learning::Curiosity::Helpers::GapDetector
- Defined in:
- lib/legion/extensions/agentic/learning/curiosity/helpers/gap_detector.rb
Overview
Analyzes tick phase results for knowledge gaps that drive curiosity.
Class Method Summary collapse
- .contradiction_gap(conflict) ⇒ Object
- .detect(prior_results) ⇒ Object
- .detect_contradiction_gaps(result) ⇒ Object
- .detect_emotional_gaps(result) ⇒ Object
- .detect_memory_gaps(result) ⇒ Object
- .detect_prediction_gaps(result) ⇒ Object
- .failed_prediction?(result) ⇒ Boolean
- .failed_prediction_gap(result) ⇒ Object
- .incomplete_knowledge_gap(traces, domain) ⇒ Object
- .low_confidence?(result) ⇒ Boolean
- .low_confidence_gap(result) ⇒ Object
- .novel_unfamiliar?(valence) ⇒ Boolean
- .novel_unfamiliar_gap(result, valence) ⇒ Object
-
.sparse_traces?(traces) ⇒ Boolean
– private helpers below –.
- .unknown_domain_gap(traces, domain) ⇒ Object
- .weak_traces?(traces) ⇒ Boolean
Class Method Details
.contradiction_gap(conflict) ⇒ Object
150 151 152 153 154 155 156 157 158 159 160 |
# File 'lib/legion/extensions/agentic/learning/curiosity/helpers/gap_detector.rb', line 150 def contradiction_gap(conflict) domain = conflict.is_a?(Hash) ? (conflict[:domain] || :general) : :general { gap_type: :contradictory, domain: domain, question: "Why do I have contradictory knowledge about #{domain}?", salience: 0.8, information_gain: 0.7, source_trace_ids: conflict.is_a?(Hash) ? Array(conflict[:trace_ids]) : [] } end |
.detect(prior_results) ⇒ Object
13 14 15 16 17 18 19 20 21 22 |
# File 'lib/legion/extensions/agentic/learning/curiosity/helpers/gap_detector.rb', line 13 def detect(prior_results) detectors = %i[memory_retrieval prediction_engine emotional_evaluation contradiction_resolution] methods = %i[detect_memory_gaps detect_prediction_gaps detect_emotional_gaps detect_contradiction_gaps] gaps = detectors.zip(methods).flat_map { |key, method| send(method, prior_results[key]) } gaps .select { |g| g[:information_gain] >= Constants::INFORMATION_GAIN_THRESHOLD } .sort_by { |g| -((g[:salience] * 0.6) + (g[:information_gain] * 0.4)) } end |
.detect_contradiction_gaps(result) ⇒ Object
54 55 56 57 58 59 60 61 |
# File 'lib/legion/extensions/agentic/learning/curiosity/helpers/gap_detector.rb', line 54 def detect_contradiction_gaps(result) return [] unless result.is_a?(Hash) conflicts = result[:active_conflicts] || result[:conflicts] return [] unless conflicts.is_a?(Array) && !conflicts.empty? conflicts.first(3).map { |c| contradiction_gap(c) } end |
.detect_emotional_gaps(result) ⇒ Object
45 46 47 48 49 50 51 52 |
# File 'lib/legion/extensions/agentic/learning/curiosity/helpers/gap_detector.rb', line 45 def detect_emotional_gaps(result) return [] unless result.is_a?(Hash) valence = result[:valence] return [] unless valence.is_a?(Hash) && novel_unfamiliar?(valence) [novel_unfamiliar_gap(result, valence)] end |
.detect_memory_gaps(result) ⇒ Object
24 25 26 27 28 29 30 31 32 33 34 |
# File 'lib/legion/extensions/agentic/learning/curiosity/helpers/gap_detector.rb', line 24 def detect_memory_gaps(result) return [] unless result.is_a?(Hash) gaps = [] traces = result[:traces] domain = result[:domain] || :general gaps << unknown_domain_gap(traces, domain) if sparse_traces?(traces) gaps << incomplete_knowledge_gap(traces, domain) if weak_traces?(traces) gaps.compact end |
.detect_prediction_gaps(result) ⇒ Object
36 37 38 39 40 41 42 43 |
# File 'lib/legion/extensions/agentic/learning/curiosity/helpers/gap_detector.rb', line 36 def detect_prediction_gaps(result) return [] unless result.is_a?(Hash) gaps = [] gaps << low_confidence_gap(result) if low_confidence?(result) gaps << failed_prediction_gap(result) if failed_prediction?(result) gaps end |
.failed_prediction?(result) ⇒ Boolean
80 81 82 |
# File 'lib/legion/extensions/agentic/learning/curiosity/helpers/gap_detector.rb', line 80 def failed_prediction?(result) result[:status] == :failed || result[:error] end |
.failed_prediction_gap(result) ⇒ Object
126 127 128 129 130 131 132 133 134 135 |
# File 'lib/legion/extensions/agentic/learning/curiosity/helpers/gap_detector.rb', line 126 def failed_prediction_gap(result) { gap_type: :uncertain, domain: result[:domain] || :general, question: 'What caused this prediction failure?', salience: 0.8, information_gain: 0.7, source_trace_ids: [] } end |
.incomplete_knowledge_gap(traces, domain) ⇒ Object
100 101 102 103 104 105 106 107 108 109 110 111 112 |
# File 'lib/legion/extensions/agentic/learning/curiosity/helpers/gap_detector.rb', line 100 def incomplete_knowledge_gap(traces, domain) weak = traces.select { |t| t.is_a?(Hash) && (t[:strength] || 1.0) < 0.3 } return nil if weak.empty? { gap_type: :incomplete, domain: domain, question: 'Why are my memories about this topic weak?', salience: 0.4, information_gain: 0.5, source_trace_ids: weak.filter_map { |t| t[:trace_id] } } end |
.low_confidence?(result) ⇒ Boolean
75 76 77 78 |
# File 'lib/legion/extensions/agentic/learning/curiosity/helpers/gap_detector.rb', line 75 def low_confidence?(result) c = result[:confidence] c.is_a?(Numeric) && c < Constants::LOW_CONFIDENCE_THRESHOLD end |
.low_confidence_gap(result) ⇒ Object
114 115 116 117 118 119 120 121 122 123 124 |
# File 'lib/legion/extensions/agentic/learning/curiosity/helpers/gap_detector.rb', line 114 def low_confidence_gap(result) confidence = result[:confidence] { gap_type: :uncertain, domain: result[:domain] || :general, question: "Why is my prediction confidence low (#{(confidence * 100).round}%)?", salience: 0.7, information_gain: 0.6, source_trace_ids: [] } end |
.novel_unfamiliar?(valence) ⇒ Boolean
84 85 86 87 |
# File 'lib/legion/extensions/agentic/learning/curiosity/helpers/gap_detector.rb', line 84 def novel_unfamiliar?(valence) (valence[:novelty] || 0.0) > Constants::DIVERSIVE_NOVELTY_THRESHOLD && (valence[:familiarity] || 1.0) < 0.3 end |
.novel_unfamiliar_gap(result, valence) ⇒ Object
137 138 139 140 141 142 143 144 145 146 147 148 |
# File 'lib/legion/extensions/agentic/learning/curiosity/helpers/gap_detector.rb', line 137 def novel_unfamiliar_gap(result, valence) novelty = valence[:novelty] || 0.0 familiarity = valence[:familiarity] || 1.0 { gap_type: :unknown, domain: result[:domain] || :general, question: 'This is novel and unfamiliar — what is it?', salience: novelty, information_gain: (1.0 - familiarity) * 0.8, source_trace_ids: [] } end |
.sparse_traces?(traces) ⇒ Boolean
– private helpers below –
65 66 67 |
# File 'lib/legion/extensions/agentic/learning/curiosity/helpers/gap_detector.rb', line 65 def sparse_traces?(traces) traces.is_a?(Array) && traces.size < Constants::EMPTY_RETRIEVAL_THRESHOLD end |
.unknown_domain_gap(traces, domain) ⇒ Object
89 90 91 92 93 94 95 96 97 98 |
# File 'lib/legion/extensions/agentic/learning/curiosity/helpers/gap_detector.rb', line 89 def unknown_domain_gap(traces, domain) { gap_type: :unknown, domain: domain, question: "What do I know about #{domain}?", salience: 0.6, information_gain: 0.7, source_trace_ids: traces&.filter_map { |t| t[:trace_id] } || [] } end |
.weak_traces?(traces) ⇒ Boolean
69 70 71 72 73 |
# File 'lib/legion/extensions/agentic/learning/curiosity/helpers/gap_detector.rb', line 69 def weak_traces?(traces) return false unless traces.is_a?(Array) traces.any? { |t| t.is_a?(Hash) && (t[:strength] || 1.0) < 0.3 } end |