agentic 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (130) hide show
  1. checksums.yaml +4 -4
  2. data/.agentic.yml +2 -0
  3. data/.architecture/decisions/ArchitecturalFeatureBuilder.md +136 -0
  4. data/.architecture/decisions/ArchitectureConsiderations.md +200 -0
  5. data/.architecture/decisions/adr_001_observer_pattern_implementation.md +196 -0
  6. data/.architecture/decisions/adr_002_plan_orchestrator.md +320 -0
  7. data/.architecture/decisions/adr_003_plan_orchestrator_interface.md +179 -0
  8. data/.architecture/decisions/adrs/ADR-001-dependency-management.md +147 -0
  9. data/.architecture/decisions/adrs/ADR-002-system-boundaries.md +162 -0
  10. data/.architecture/decisions/adrs/ADR-003-content-safety.md +158 -0
  11. data/.architecture/decisions/adrs/ADR-004-agent-permissions.md +161 -0
  12. data/.architecture/decisions/adrs/ADR-005-adaptation-engine.md +127 -0
  13. data/.architecture/decisions/adrs/ADR-006-extension-system.md +273 -0
  14. data/.architecture/decisions/adrs/ADR-007-learning-system.md +156 -0
  15. data/.architecture/decisions/adrs/ADR-008-prompt-generation.md +325 -0
  16. data/.architecture/decisions/adrs/ADR-009-task-failure-handling.md +353 -0
  17. data/.architecture/decisions/adrs/ADR-010-task-input-handling.md +251 -0
  18. data/.architecture/decisions/adrs/ADR-011-task-observable-pattern.md +391 -0
  19. data/.architecture/decisions/adrs/ADR-012-task-output-handling.md +205 -0
  20. data/.architecture/decisions/adrs/ADR-013-architecture-alignment.md +211 -0
  21. data/.architecture/decisions/adrs/ADR-014-agent-capability-registry.md +80 -0
  22. data/.architecture/decisions/adrs/ADR-015-persistent-agent-store.md +100 -0
  23. data/.architecture/decisions/adrs/ADR-016-agent-assembly-engine.md +117 -0
  24. data/.architecture/decisions/adrs/ADR-017-streaming-observability.md +171 -0
  25. data/.architecture/decisions/capability_tools_distinction.md +150 -0
  26. data/.architecture/decisions/cli_command_structure.md +61 -0
  27. data/.architecture/implementation/agent_self_assembly_implementation.md +267 -0
  28. data/.architecture/implementation/agent_self_assembly_summary.md +138 -0
  29. data/.architecture/members.yml +187 -0
  30. data/.architecture/planning/self_implementation_exercise.md +295 -0
  31. data/.architecture/planning/session_compaction_rule.md +43 -0
  32. data/.architecture/planning/streaming_observability_feature.md +223 -0
  33. data/.architecture/principles.md +151 -0
  34. data/.architecture/recalibration/0-2-0.md +92 -0
  35. data/.architecture/recalibration/agent_self_assembly.md +238 -0
  36. data/.architecture/recalibration/cli_command_structure.md +91 -0
  37. data/.architecture/recalibration/implementation_roadmap_0-2-0.md +301 -0
  38. data/.architecture/recalibration/progress_tracking_0-2-0.md +114 -0
  39. data/.architecture/recalibration_process.md +127 -0
  40. data/.architecture/reviews/0-2-0.md +181 -0
  41. data/.architecture/reviews/cli_command_duplication.md +98 -0
  42. data/.architecture/templates/adr.md +105 -0
  43. data/.architecture/templates/implementation_roadmap.md +125 -0
  44. data/.architecture/templates/progress_tracking.md +89 -0
  45. data/.architecture/templates/recalibration_plan.md +70 -0
  46. data/.architecture/templates/version_comparison.md +124 -0
  47. data/.claude/settings.local.json +13 -0
  48. data/.claude-sessions/001-task-class-architecture-implementation.md +129 -0
  49. data/.claude-sessions/002-plan-orchestrator-interface-review.md +105 -0
  50. data/.claude-sessions/architecture-governance-implementation.md +37 -0
  51. data/.claude-sessions/architecture-review-session.md +27 -0
  52. data/ArchitecturalFeatureBuilder.md +136 -0
  53. data/ArchitectureConsiderations.md +229 -0
  54. data/CHANGELOG.md +57 -2
  55. data/CLAUDE.md +111 -0
  56. data/CONTRIBUTING.md +286 -0
  57. data/MAINTAINING.md +301 -0
  58. data/README.md +582 -28
  59. data/docs/agent_capabilities_api.md +259 -0
  60. data/docs/artifact_extension_points.md +757 -0
  61. data/docs/artifact_generation_architecture.md +323 -0
  62. data/docs/artifact_implementation_plan.md +596 -0
  63. data/docs/artifact_integration_points.md +345 -0
  64. data/docs/artifact_verification_strategies.md +581 -0
  65. data/docs/streaming_observability_architecture.md +510 -0
  66. data/exe/agentic +6 -1
  67. data/lefthook.yml +5 -0
  68. data/lib/agentic/adaptation_engine.rb +124 -0
  69. data/lib/agentic/agent.rb +181 -4
  70. data/lib/agentic/agent_assembly_engine.rb +442 -0
  71. data/lib/agentic/agent_capability_registry.rb +260 -0
  72. data/lib/agentic/agent_config.rb +63 -0
  73. data/lib/agentic/agent_specification.rb +46 -0
  74. data/lib/agentic/capabilities/examples.rb +530 -0
  75. data/lib/agentic/capabilities.rb +14 -0
  76. data/lib/agentic/capability_provider.rb +146 -0
  77. data/lib/agentic/capability_specification.rb +118 -0
  78. data/lib/agentic/cli/agent.rb +31 -0
  79. data/lib/agentic/cli/capabilities.rb +191 -0
  80. data/lib/agentic/cli/config.rb +134 -0
  81. data/lib/agentic/cli/execution_observer.rb +796 -0
  82. data/lib/agentic/cli.rb +1068 -0
  83. data/lib/agentic/default_agent_provider.rb +35 -0
  84. data/lib/agentic/errors/llm_error.rb +184 -0
  85. data/lib/agentic/execution_plan.rb +53 -0
  86. data/lib/agentic/execution_result.rb +91 -0
  87. data/lib/agentic/expected_answer_format.rb +46 -0
  88. data/lib/agentic/extension/domain_adapter.rb +109 -0
  89. data/lib/agentic/extension/plugin_manager.rb +163 -0
  90. data/lib/agentic/extension/protocol_handler.rb +116 -0
  91. data/lib/agentic/extension.rb +45 -0
  92. data/lib/agentic/factory_methods.rb +9 -1
  93. data/lib/agentic/generation_stats.rb +61 -0
  94. data/lib/agentic/learning/README.md +84 -0
  95. data/lib/agentic/learning/capability_optimizer.rb +613 -0
  96. data/lib/agentic/learning/execution_history_store.rb +251 -0
  97. data/lib/agentic/learning/pattern_recognizer.rb +500 -0
  98. data/lib/agentic/learning/strategy_optimizer.rb +706 -0
  99. data/lib/agentic/learning.rb +131 -0
  100. data/lib/agentic/llm_assisted_composition_strategy.rb +188 -0
  101. data/lib/agentic/llm_client.rb +215 -15
  102. data/lib/agentic/llm_config.rb +65 -1
  103. data/lib/agentic/llm_response.rb +163 -0
  104. data/lib/agentic/logger.rb +1 -1
  105. data/lib/agentic/observable.rb +51 -0
  106. data/lib/agentic/persistent_agent_store.rb +385 -0
  107. data/lib/agentic/plan_execution_result.rb +129 -0
  108. data/lib/agentic/plan_orchestrator.rb +464 -0
  109. data/lib/agentic/plan_orchestrator_config.rb +57 -0
  110. data/lib/agentic/retry_config.rb +63 -0
  111. data/lib/agentic/retry_handler.rb +125 -0
  112. data/lib/agentic/structured_outputs.rb +1 -1
  113. data/lib/agentic/task.rb +193 -0
  114. data/lib/agentic/task_definition.rb +39 -0
  115. data/lib/agentic/task_execution_result.rb +92 -0
  116. data/lib/agentic/task_failure.rb +66 -0
  117. data/lib/agentic/task_output_schemas.rb +112 -0
  118. data/lib/agentic/task_planner.rb +54 -19
  119. data/lib/agentic/task_result.rb +48 -0
  120. data/lib/agentic/ui.rb +244 -0
  121. data/lib/agentic/verification/critic_framework.rb +116 -0
  122. data/lib/agentic/verification/llm_verification_strategy.rb +60 -0
  123. data/lib/agentic/verification/schema_verification_strategy.rb +47 -0
  124. data/lib/agentic/verification/verification_hub.rb +62 -0
  125. data/lib/agentic/verification/verification_result.rb +50 -0
  126. data/lib/agentic/verification/verification_strategy.rb +26 -0
  127. data/lib/agentic/version.rb +1 -1
  128. data/lib/agentic.rb +74 -2
  129. data/plugins/README.md +41 -0
  130. metadata +245 -6
@@ -0,0 +1,500 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Agentic
4
+ module Learning
5
+ # PatternRecognizer identifies patterns and optimization opportunities from execution history.
6
+ # It analyzes historical task and plan executions to detect recurring patterns,
7
+ # success/failure correlations, and potential optimization points.
8
+ #
9
+ # @example Analyzing patterns in task executions
10
+ # history_store = Agentic::Learning::ExecutionHistoryStore.new
11
+ # recognizer = Agentic::Learning::PatternRecognizer.new(history_store: history_store)
12
+ # patterns = recognizer.analyze_agent_performance("research_agent")
13
+ #
14
+ class PatternRecognizer
15
+ # Initialize a new PatternRecognizer
16
+ #
17
+ # @param options [Hash] Configuration options
18
+ # @option options [Logger] :logger Custom logger (defaults to Agentic.logger)
19
+ # @option options [ExecutionHistoryStore] :history_store The history store to analyze
20
+ # @option options [Integer] :min_sample_size Minimum sample size for pattern detection (defaults to 10)
21
+ # @option options [Float] :significance_threshold Statistical significance threshold (defaults to 0.05)
22
+ # @option options [Integer] :time_window_days Time window in days for analysis (defaults to 30)
23
+ def initialize(options = {})
24
+ @logger = options[:logger] || Agentic.logger
25
+ @history_store = options[:history_store] || raise(ArgumentError, "history_store is required")
26
+ @min_sample_size = options[:min_sample_size] || 10
27
+ @significance_threshold = options[:significance_threshold] || 0.05
28
+ @time_window_days = options[:time_window_days] || 30
29
+ @pattern_cache = {}
30
+ @cache_expiry = {}
31
+ end
32
+
33
+ # Analyze performance patterns for a specific agent type
34
+ #
35
+ # @param agent_type [String] The agent type to analyze
36
+ # @param options [Hash] Analysis options
37
+ # @option options [Array<Symbol>] :metrics Specific metrics to analyze (defaults to all)
38
+ # @option options [Boolean] :force_refresh Force a fresh analysis even if cached (defaults to false)
39
+ # @return [Hash] Analysis results with identified patterns
40
+ def analyze_agent_performance(agent_type, options = {})
41
+ cache_key = "agent_perf:#{agent_type}:#{options[:metrics]}"
42
+
43
+ # Check cache first if not forcing refresh
44
+ if !options[:force_refresh] && @pattern_cache[cache_key] && @cache_expiry[cache_key] && @cache_expiry[cache_key] > Time.now
45
+ return @pattern_cache[cache_key]
46
+ end
47
+
48
+ # Fetch relevant history
49
+ history = fetch_agent_history(agent_type)
50
+
51
+ if history.size < @min_sample_size
52
+ @logger.info("Insufficient data to analyze patterns for #{agent_type} (#{history.size} < #{@min_sample_size})")
53
+ return {insufficient_data: true, sample_size: history.size, required_size: @min_sample_size}
54
+ end
55
+
56
+ # Perform analysis
57
+ patterns = {
58
+ success_rate: calculate_success_rate(history),
59
+ performance_trends: analyze_performance_trends(history, options[:metrics]),
60
+ failure_patterns: identify_failure_patterns(history),
61
+ optimization_opportunities: identify_optimization_opportunities(history)
62
+ }
63
+
64
+ # Cache results
65
+ @pattern_cache[cache_key] = patterns
66
+ @cache_expiry[cache_key] = Time.now + 3600 # Cache for 1 hour
67
+
68
+ patterns
69
+ end
70
+
71
+ # Identify correlation between task properties and success/performance
72
+ #
73
+ # @param task_property [Symbol] The property to analyze correlation for
74
+ # @param performance_metric [Symbol] The performance metric to correlate with
75
+ # @return [Hash] Correlation analysis results
76
+ def analyze_correlation(task_property, performance_metric)
77
+ # Fetch all history within time window
78
+ end_time = Time.now
79
+ start_time = end_time - (@time_window_days * 24 * 60 * 60)
80
+
81
+ history = @history_store.get_history(start_time: start_time, end_time: end_time)
82
+
83
+ if history.size < @min_sample_size
84
+ return {insufficient_data: true, sample_size: history.size}
85
+ end
86
+
87
+ # Extract property and metric values
88
+ data_points = history.map do |record|
89
+ property_value = extract_property_value(record, task_property)
90
+ metric_value = extract_metric_value(record, performance_metric)
91
+
92
+ {property: property_value, metric: metric_value} if property_value && metric_value
93
+ end.compact
94
+
95
+ # Calculate correlation
96
+ if data_points.size < @min_sample_size
97
+ return {insufficient_data: true, sample_size: data_points.size}
98
+ end
99
+
100
+ correlation = calculate_correlation(data_points)
101
+
102
+ {
103
+ correlation_coefficient: correlation[:coefficient],
104
+ statistical_significance: correlation[:significance],
105
+ sample_size: data_points.size,
106
+ significant: correlation[:significance] < @significance_threshold
107
+ }
108
+ end
109
+
110
+ # Recommend optimization strategies based on recognized patterns
111
+ #
112
+ # @param agent_type [String] The agent type to generate recommendations for
113
+ # @return [Array<Hash>] List of recommended optimization strategies
114
+ def recommend_optimizations(agent_type)
115
+ # Start with performance analysis
116
+ performance = analyze_agent_performance(agent_type, force_refresh: true)
117
+
118
+ if performance[:insufficient_data]
119
+ return [{type: :insufficient_data, message: "Need more execution data to make recommendations"}]
120
+ end
121
+
122
+ recommendations = []
123
+
124
+ # Check success rate
125
+ if performance[:success_rate][:overall] < 0.8
126
+ recommendations << {
127
+ type: :success_rate,
128
+ priority: :high,
129
+ message: "Improve success rate (currently #{(performance[:success_rate][:overall] * 100).round(1)}%)",
130
+ suggestions: generate_success_rate_suggestions(performance)
131
+ }
132
+ end
133
+
134
+ # Check performance trends
135
+ slow_metrics = performance[:performance_trends].select { |_, v| v[:trend] == :increasing && v[:significant] }
136
+ if slow_metrics.any?
137
+ recommendations << {
138
+ type: :performance,
139
+ priority: :medium,
140
+ message: "Performance degradation detected in #{slow_metrics.keys.join(", ")}",
141
+ suggestions: generate_performance_suggestions(slow_metrics)
142
+ }
143
+ end
144
+
145
+ # Check failure patterns
146
+ if performance[:failure_patterns]&.any?
147
+ recommendations << {
148
+ type: :failures,
149
+ priority: :high,
150
+ message: "Address common failure patterns",
151
+ patterns: performance[:failure_patterns].first(3),
152
+ suggestions: generate_failure_suggestions(performance[:failure_patterns])
153
+ }
154
+ end
155
+
156
+ # Check optimization opportunities
157
+ if performance[:optimization_opportunities]&.any?
158
+ recommendations << {
159
+ type: :optimization,
160
+ priority: :medium,
161
+ message: "Potential optimization opportunities identified",
162
+ opportunities: performance[:optimization_opportunities],
163
+ suggestions: performance[:optimization_opportunities].map { |o| o[:suggestion] }
164
+ }
165
+ end
166
+
167
+ recommendations
168
+ end
169
+
170
+ private
171
+
172
+ def fetch_agent_history(agent_type)
173
+ end_time = Time.now
174
+ start_time = end_time - (@time_window_days * 24 * 60 * 60)
175
+
176
+ @history_store.get_history(
177
+ agent_type: agent_type,
178
+ start_time: start_time,
179
+ end_time: end_time
180
+ )
181
+ end
182
+
183
+ def calculate_success_rate(history)
184
+ return {overall: 0, sample_size: 0} if history.empty?
185
+
186
+ # Overall success rate
187
+ successful = history.count { |record| record[:success] }
188
+ overall_rate = successful.to_f / history.size
189
+
190
+ # Success rate over time
191
+ time_periods = split_into_time_periods(history, 5) # Split into 5 periods
192
+ period_rates = time_periods.map do |period|
193
+ successful = period.count { |record| record[:success] }
194
+ successful.to_f / period.size
195
+ end
196
+
197
+ # Trend analysis
198
+ trend = if period_rates.size >= 3
199
+ if period_rates.last > period_rates.first
200
+ :improving
201
+ elsif period_rates.last < period_rates.first
202
+ :declining
203
+ else
204
+ :stable
205
+ end
206
+ else
207
+ :insufficient_data
208
+ end
209
+
210
+ {
211
+ overall: overall_rate,
212
+ sample_size: history.size,
213
+ period_rates: period_rates,
214
+ trend: trend
215
+ }
216
+ end
217
+
218
+ def analyze_performance_trends(history, metric_keys = nil)
219
+ return {} if history.empty?
220
+
221
+ # Extract all available metrics if none specified
222
+ metric_keys ||= history.flat_map { |r| r[:metrics]&.keys || [] }.uniq.map(&:to_sym)
223
+
224
+ # Analyze each metric
225
+ metric_keys.each_with_object({}) do |metric, results|
226
+ # Extract metric values
227
+ values = history.map { |record| record.dig(:metrics, metric.to_s) }.compact
228
+
229
+ next if values.empty?
230
+
231
+ # Split into time periods
232
+ time_periods = split_into_time_periods(history, 5)
233
+ period_values = time_periods.map do |period|
234
+ period_metrics = period.map { |record| record.dig(:metrics, metric.to_s) }.compact
235
+ period_metrics.empty? ? nil : period_metrics.sum / period_metrics.size.to_f
236
+ end.compact
237
+
238
+ # Determine trend
239
+ trend = if period_values.size >= 3
240
+ if period_values.last > period_values.first * 1.2
241
+ :increasing # 20% increase
242
+ elsif period_values.last < period_values.first * 0.8
243
+ :decreasing # 20% decrease
244
+ else
245
+ :stable
246
+ end
247
+ else
248
+ :insufficient_data
249
+ end
250
+
251
+ # Calculate statistical significance
252
+ significant = period_values.size >= 3 &&
253
+ trend != :stable &&
254
+ calculate_significance(period_values.first, period_values.last, period_values.size) < @significance_threshold
255
+
256
+ results[metric] = {
257
+ avg_value: values.sum / values.size.to_f,
258
+ min_value: values.min,
259
+ max_value: values.max,
260
+ trend: trend,
261
+ period_values: period_values,
262
+ significant: significant
263
+ }
264
+ end
265
+ end
266
+
267
+ def identify_failure_patterns(history)
268
+ failures = history.reject { |record| record[:success] }
269
+ return [] if failures.empty?
270
+
271
+ # Group failures by context patterns
272
+ failure_groups = {}
273
+
274
+ failures.each do |failure|
275
+ # Extract failure pattern from context
276
+ pattern = extract_failure_pattern(failure)
277
+
278
+ failure_groups[pattern] ||= []
279
+ failure_groups[pattern] << failure
280
+ end
281
+
282
+ # Sort by frequency and return top patterns
283
+ failure_groups
284
+ .map { |pattern, occurrences| {pattern: pattern, count: occurrences.size, examples: occurrences.first(3)} }
285
+ .sort_by { |p| -p[:count] }
286
+ .first(5)
287
+ end
288
+
289
+ def identify_optimization_opportunities(history)
290
+ opportunities = []
291
+
292
+ # Look for consistently slow tasks
293
+ duration_by_task = {}
294
+ history.each do |record|
295
+ next unless record[:task_id]
296
+ duration_by_task[record[:task_id]] ||= []
297
+ duration_by_task[record[:task_id]] << record[:duration_ms]
298
+ end
299
+
300
+ # Find tasks with high average duration
301
+ slow_tasks = duration_by_task
302
+ .select { |_, durations| durations.size >= 3 }
303
+ .map { |task_id, durations| [task_id, durations.sum / durations.size.to_f] }
304
+ .sort_by { |_, avg_duration| -avg_duration }
305
+ .first(3)
306
+
307
+ slow_tasks.each do |task_id, avg_duration|
308
+ opportunities << {
309
+ type: :slow_task,
310
+ task_id: task_id,
311
+ avg_duration_ms: avg_duration.round,
312
+ suggestion: "Optimize slow task #{task_id} (avg: #{(avg_duration / 1000).round(2)}s)"
313
+ }
314
+ end
315
+
316
+ # Look for resource-intensive tasks
317
+ if history.any? { |r| r.dig(:metrics, "tokens_used") }
318
+ tokens_by_task = {}
319
+ history.each do |record|
320
+ next unless record[:task_id] && record.dig(:metrics, "tokens_used")
321
+ tokens_by_task[record[:task_id]] ||= []
322
+ tokens_by_task[record[:task_id]] << record[:metrics]["tokens_used"]
323
+ end
324
+
325
+ token_heavy_tasks = tokens_by_task
326
+ .select { |_, tokens| tokens.size >= 3 }
327
+ .map { |task_id, tokens| [task_id, tokens.sum / tokens.size.to_f] }
328
+ .sort_by { |_, avg_tokens| -avg_tokens }
329
+ .first(3)
330
+
331
+ token_heavy_tasks.each do |task_id, avg_tokens|
332
+ opportunities << {
333
+ type: :token_heavy,
334
+ task_id: task_id,
335
+ avg_tokens: avg_tokens.round,
336
+ suggestion: "Reduce token usage in task #{task_id} (avg: #{avg_tokens.round} tokens)"
337
+ }
338
+ end
339
+ end
340
+
341
+ opportunities
342
+ end
343
+
344
+ def split_into_time_periods(history, num_periods)
345
+ return [] if history.empty?
346
+
347
+ # Sort by timestamp
348
+ sorted = history.sort_by { |r| r[:timestamp] }
349
+
350
+ # Calculate time range
351
+ start_time = Time.parse(sorted.first[:timestamp])
352
+ end_time = Time.parse(sorted.last[:timestamp])
353
+ total_duration = end_time - start_time
354
+
355
+ return [sorted] if total_duration < 60 # If less than a minute, return as single period
356
+
357
+ # Split into periods
358
+ period_duration = total_duration / num_periods
359
+
360
+ periods = []
361
+ num_periods.times do |i|
362
+ period_start = start_time + (i * period_duration)
363
+ period_end = period_start + period_duration
364
+
365
+ period_records = sorted.select do |record|
366
+ record_time = Time.parse(record[:timestamp])
367
+ record_time >= period_start && (i == num_periods - 1 || record_time < period_end)
368
+ end
369
+
370
+ periods << period_records unless period_records.empty?
371
+ end
372
+
373
+ periods
374
+ end
375
+
376
+ def extract_property_value(record, property)
377
+ case property
378
+ when :agent_type
379
+ record[:agent_type]
380
+ when :duration
381
+ record[:duration_ms]
382
+ when :task_id
383
+ record[:task_id]
384
+ when :plan_id
385
+ record[:plan_id]
386
+ else
387
+ # Try to extract from metrics or context
388
+ record.dig(:metrics, property.to_s) || record.dig(:context, property.to_s)
389
+ end
390
+ end
391
+
392
+ def extract_metric_value(record, metric)
393
+ case metric
394
+ when :success
395
+ record[:success] ? 1 : 0
396
+ when :duration
397
+ record[:duration_ms]
398
+ else
399
+ # Try to extract from metrics
400
+ record.dig(:metrics, metric.to_s)
401
+ end
402
+ end
403
+
404
+ def calculate_correlation(data_points)
405
+ # Simple correlation calculation - can be replaced with more sophisticated approach
406
+ x_values = data_points.map { |p| p[:property].to_f }
407
+ y_values = data_points.map { |p| p[:metric].to_f }
408
+
409
+ n = x_values.size
410
+ sum_x = x_values.sum
411
+ sum_y = y_values.sum
412
+ sum_xy = x_values.zip(y_values).map { |x, y| x * y }.sum
413
+ sum_x2 = x_values.map { |x| x**2 }.sum
414
+ sum_y2 = y_values.map { |y| y**2 }.sum
415
+
416
+ numerator = n * sum_xy - sum_x * sum_y
417
+ denominator = Math.sqrt((n * sum_x2 - sum_x**2) * (n * sum_y2 - sum_y**2))
418
+
419
+ coefficient = denominator.zero? ? 0 : numerator / denominator
420
+
421
+ # Calculate p-value (simplified)
422
+ t = coefficient * Math.sqrt((n - 2) / (1 - coefficient**2))
423
+ p_value = 2 * (1 - calculate_t_distribution(t.abs, n - 2))
424
+
425
+ {coefficient: coefficient, significance: p_value}
426
+ end
427
+
428
+ def calculate_significance(value1, value2, sample_size)
429
+ # Simplified significance calculation
430
+ # In a real implementation, use a proper statistical library
431
+ diff = (value2 - value1).abs
432
+ variance = (diff * 0.5)**2 # Simplified variance estimate
433
+
434
+ # t-statistic for paired samples
435
+ t = diff / Math.sqrt(variance / sample_size)
436
+
437
+ # Approximate p-value
438
+ 2 * (1 - calculate_t_distribution(t.abs, sample_size - 1))
439
+ end
440
+
441
+ def calculate_t_distribution(t, df)
442
+ # Very simplified t-distribution approximation
443
+ # In a real implementation, use a proper statistical library
444
+ if t > 10
445
+ 1.0
446
+ else
447
+ 0.5 + 0.5 * (1 - Math.exp(-0.09 * t * Math.sqrt(df)))
448
+ end
449
+ end
450
+
451
+ def extract_failure_pattern(failure)
452
+ # Extract a simplified pattern from failure context
453
+ # In a real implementation, use more sophisticated pattern recognition
454
+
455
+ if failure[:context] && failure[:context][:error_type]
456
+ "Error: #{failure[:context][:error_type]}"
457
+ elsif failure[:context] && failure[:context][:failure_reason]
458
+ "Reason: #{failure[:context][:failure_reason]}"
459
+ else
460
+ "Unknown failure"
461
+ end
462
+ end
463
+
464
+ def generate_success_rate_suggestions(performance)
465
+ suggestions = []
466
+
467
+ if performance[:failure_patterns].any?
468
+ top_failure = performance[:failure_patterns].first
469
+ suggestions << "Address most common failure pattern: #{top_failure[:pattern]}"
470
+ end
471
+
472
+ if performance[:success_rate][:trend] == :declining
473
+ suggestions << "Investigate recent changes that may have affected success rate"
474
+ end
475
+
476
+ suggestions << "Implement more robust error handling" if suggestions.empty?
477
+ suggestions
478
+ end
479
+
480
+ def generate_performance_suggestions(slow_metrics)
481
+ slow_metrics.map do |metric, data|
482
+ case metric
483
+ when :duration_ms, :duration
484
+ "Optimize execution time (#{(data[:avg_value] / 1000).round(2)}s avg)"
485
+ when :tokens_used
486
+ "Reduce token usage (#{data[:avg_value].round} avg)"
487
+ else
488
+ "Optimize #{metric} metric (#{data[:avg_value].round(2)} avg)"
489
+ end
490
+ end
491
+ end
492
+
493
+ def generate_failure_suggestions(failure_patterns)
494
+ failure_patterns.first(3).map do |pattern|
495
+ "Fix '#{pattern[:pattern]}' failure (occurs #{pattern[:count]} times)"
496
+ end
497
+ end
498
+ end
499
+ end
500
+ end