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,706 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Agentic
4
+ module Learning
5
+ # StrategyOptimizer improves execution strategies based on historical performance data.
6
+ # It uses insights from the PatternRecognizer to automatically generate optimized
7
+ # strategies for tasks, agents, and plans.
8
+ #
9
+ # @example Optimizing a prompt template
10
+ # history_store = Agentic::Learning::ExecutionHistoryStore.new
11
+ # recognizer = Agentic::Learning::PatternRecognizer.new(history_store: history_store)
12
+ # optimizer = Agentic::Learning::StrategyOptimizer.new(
13
+ # pattern_recognizer: recognizer,
14
+ # history_store: history_store
15
+ # )
16
+ #
17
+ # improved_prompt = optimizer.optimize_prompt_template(
18
+ # original_template: "Please research the following topic: {topic}",
19
+ # agent_type: "research_agent"
20
+ # )
21
+ #
22
+ class StrategyOptimizer
23
+ # Initialize a new StrategyOptimizer
24
+ #
25
+ # @param options [Hash] Configuration options
26
+ # @option options [Logger] :logger Custom logger (defaults to Agentic.logger)
27
+ # @option options [PatternRecognizer] :pattern_recognizer Pattern recognizer for insights
28
+ # @option options [ExecutionHistoryStore] :history_store History store for performance data
29
+ # @option options [LlmClient] :llm_client LLM client for generating optimizations (optional)
30
+ # @option options [Integer] :optimization_interval_hours Hours between optimization attempts (defaults to 24)
31
+ # @option options [Boolean] :auto_apply_optimizations Whether to automatically apply optimizations (defaults to false)
32
+ def initialize(options = {})
33
+ @logger = options[:logger] || Agentic.logger
34
+ @pattern_recognizer = options[:pattern_recognizer] || raise(ArgumentError, "pattern_recognizer is required")
35
+ @history_store = options[:history_store] || raise(ArgumentError, "history_store is required")
36
+ @llm_client = options[:llm_client]
37
+ @optimization_interval_hours = options[:optimization_interval_hours] || 24
38
+ @auto_apply_optimizations = options.fetch(:auto_apply_optimizations, false)
39
+ @optimization_cache = {}
40
+ @last_optimization = {}
41
+ end
42
+
43
+ # Optimize a prompt template based on historical performance
44
+ #
45
+ # @param original_template [String] The original prompt template
46
+ # @param agent_type [String] The agent type using this prompt
47
+ # @param options [Hash] Optimization options
48
+ # @option options [Boolean] :force Force optimization even if recently optimized
49
+ # @option options [Symbol] :optimization_strategy Strategy to use (:conservative, :balanced, :aggressive)
50
+ # @option options [Hash] :context Additional context for optimization
51
+ # @return [Hash] Optimization result with improved template and explanation
52
+ def optimize_prompt_template(original_template, agent_type, options = {})
53
+ cache_key = "prompt:#{agent_type}:#{Digest::MD5.hexdigest(original_template)}"
54
+
55
+ # Check cache and optimization interval
56
+ unless options[:force]
57
+ if @optimization_cache[cache_key] &&
58
+ @last_optimization[cache_key] &&
59
+ @last_optimization[cache_key] > Time.now - (@optimization_interval_hours * 3600)
60
+ return @optimization_cache[cache_key]
61
+ end
62
+ end
63
+
64
+ # Get performance data
65
+ performance = @pattern_recognizer.analyze_agent_performance(agent_type)
66
+
67
+ if performance[:insufficient_data]
68
+ @logger.info("Insufficient data to optimize prompt for #{agent_type}")
69
+ return {
70
+ optimized: false,
71
+ reason: "Insufficient performance data",
72
+ original_template: original_template,
73
+ improved_template: original_template
74
+ }
75
+ end
76
+
77
+ # Generate optimization
78
+ optimization = if @llm_client
79
+ generate_optimized_prompt_with_llm(original_template, agent_type, performance, options)
80
+ else
81
+ generate_optimized_prompt_heuristic(original_template, agent_type, performance, options)
82
+ end
83
+
84
+ # Cache result
85
+ @optimization_cache[cache_key] = optimization
86
+ @last_optimization[cache_key] = Time.now
87
+
88
+ optimization
89
+ end
90
+
91
+ # Optimize LLM parameters based on historical performance
92
+ #
93
+ # @param original_params [Hash] The original LLM parameters
94
+ # @param agent_type [String] The agent type using these parameters
95
+ # @param options [Hash] Optimization options
96
+ # @option options [Boolean] :force Force optimization even if recently optimized
97
+ # @option options [Symbol] :optimization_strategy Strategy to use (:conservative, :balanced, :aggressive)
98
+ # @return [Hash] Optimization result with improved parameters and explanation
99
+ def optimize_llm_parameters(original_params, agent_type, options = {})
100
+ cache_key = "params:#{agent_type}:#{Digest::MD5.hexdigest(original_params.to_s)}"
101
+
102
+ # Check cache and optimization interval
103
+ unless options[:force]
104
+ if @optimization_cache[cache_key] &&
105
+ @last_optimization[cache_key] &&
106
+ @last_optimization[cache_key] > Time.now - (@optimization_interval_hours * 3600)
107
+ return @optimization_cache[cache_key]
108
+ end
109
+ end
110
+
111
+ # Get performance data
112
+ performance = @pattern_recognizer.analyze_agent_performance(agent_type)
113
+
114
+ if performance[:insufficient_data]
115
+ @logger.info("Insufficient data to optimize LLM parameters for #{agent_type}")
116
+ return {
117
+ optimized: false,
118
+ reason: "Insufficient performance data",
119
+ original_params: original_params,
120
+ improved_params: original_params.dup
121
+ }
122
+ end
123
+
124
+ # Generate optimization
125
+ optimization = generate_optimized_parameters(original_params, agent_type, performance, options)
126
+
127
+ # Cache result
128
+ @optimization_cache[cache_key] = optimization
129
+ @last_optimization[cache_key] = Time.now
130
+
131
+ optimization
132
+ end
133
+
134
+ # Optimize task sequence based on historical performance
135
+ #
136
+ # @param original_sequence [Array<Hash>] Original task sequence
137
+ # @param plan_type [String] The type of plan
138
+ # @param options [Hash] Optimization options
139
+ # @option options [Boolean] :force Force optimization even if recently optimized
140
+ # @return [Hash] Optimization result with improved sequence and explanation
141
+ def optimize_task_sequence(original_sequence, plan_type, options = {})
142
+ cache_key = "sequence:#{plan_type}:#{Digest::MD5.hexdigest(original_sequence.to_s)}"
143
+
144
+ # Check cache and optimization interval
145
+ unless options[:force]
146
+ if @optimization_cache[cache_key] &&
147
+ @last_optimization[cache_key] &&
148
+ @last_optimization[cache_key] > Time.now - (@optimization_interval_hours * 3600)
149
+ return @optimization_cache[cache_key]
150
+ end
151
+ end
152
+
153
+ # Get historical plan executions
154
+ end_time = Time.now
155
+ start_time = end_time - (30 * 24 * 60 * 60) # 30 days
156
+
157
+ plan_history = @history_store.get_history(
158
+ plan_id: plan_type,
159
+ start_time: start_time,
160
+ end_time: end_time
161
+ )
162
+
163
+ if plan_history.size < 5
164
+ @logger.info("Insufficient data to optimize task sequence for #{plan_type}")
165
+ return {
166
+ optimized: false,
167
+ reason: "Insufficient plan execution data",
168
+ original_sequence: original_sequence,
169
+ improved_sequence: original_sequence.dup
170
+ }
171
+ end
172
+
173
+ # Generate optimization
174
+ optimization = generate_optimized_sequence(original_sequence, plan_history, options)
175
+
176
+ # Cache result
177
+ @optimization_cache[cache_key] = optimization
178
+ @last_optimization[cache_key] = Time.now
179
+
180
+ optimization
181
+ end
182
+
183
+ # Apply learned optimizations to existing configurations
184
+ #
185
+ # @param target [Symbol] Type of target to optimize (:prompts, :parameters, :sequences)
186
+ # @param registry [Hash] Registry of current configurations
187
+ # @return [Hash] Results of optimization applications
188
+ def apply_optimizations(target, registry)
189
+ results = {}
190
+
191
+ case target
192
+ when :prompts
193
+ registry.each do |key, template|
194
+ agent_type = extract_agent_type_from_key(key)
195
+ next unless agent_type
196
+
197
+ result = optimize_prompt_template(template, agent_type)
198
+ results[key] = result
199
+
200
+ if result[:optimized] && @auto_apply_optimizations
201
+ # Logic to apply optimization to registry would go here
202
+ @logger.info("Auto-applied optimized prompt for #{key}")
203
+ end
204
+ end
205
+
206
+ when :parameters
207
+ registry.each do |key, params|
208
+ agent_type = extract_agent_type_from_key(key)
209
+ next unless agent_type
210
+
211
+ result = optimize_llm_parameters(params, agent_type)
212
+ results[key] = result
213
+
214
+ if result[:optimized] && @auto_apply_optimizations
215
+ # Logic to apply optimization to registry would go here
216
+ @logger.info("Auto-applied optimized parameters for #{key}")
217
+ end
218
+ end
219
+
220
+ when :sequences
221
+ registry.each do |key, sequence|
222
+ plan_type = key.to_s
223
+
224
+ result = optimize_task_sequence(sequence, plan_type)
225
+ results[key] = result
226
+
227
+ if result[:optimized] && @auto_apply_optimizations
228
+ # Logic to apply optimization to registry would go here
229
+ @logger.info("Auto-applied optimized sequence for #{key}")
230
+ end
231
+ end
232
+ end
233
+
234
+ results
235
+ end
236
+
237
+ # Generate a performance report for a specific agent type
238
+ #
239
+ # @param agent_type [String] The agent type to report on
240
+ # @return [Hash] Performance report with metrics and optimization suggestions
241
+ def generate_performance_report(agent_type)
242
+ performance = @pattern_recognizer.analyze_agent_performance(agent_type)
243
+
244
+ if performance[:insufficient_data]
245
+ return {
246
+ agent_type: agent_type,
247
+ status: :insufficient_data,
248
+ message: "Not enough execution data to generate a meaningful report"
249
+ }
250
+ end
251
+
252
+ # Get recommendations
253
+ recommendations = @pattern_recognizer.recommend_optimizations(agent_type)
254
+
255
+ {
256
+ agent_type: agent_type,
257
+ status: :complete,
258
+ timestamp: Time.now.iso8601,
259
+ metrics: {
260
+ success_rate: performance[:success_rate][:overall],
261
+ trend: performance[:success_rate][:trend],
262
+ sample_size: performance[:success_rate][:sample_size]
263
+ },
264
+ performance_trends: performance[:performance_trends],
265
+ failure_patterns: performance[:failure_patterns],
266
+ recommendations: recommendations
267
+ }
268
+ end
269
+
270
+ private
271
+
272
+ def generate_optimized_prompt_with_llm(original_template, agent_type, performance, options)
273
+ return {optimized: false, reason: "LLM client not available"} unless @llm_client
274
+
275
+ # Get failure patterns and recommendations
276
+ recommendations = @pattern_recognizer.recommend_optimizations(agent_type)
277
+
278
+ # Prepare context for the LLM
279
+ context = {
280
+ original_template: original_template,
281
+ agent_type: agent_type,
282
+ success_rate: performance[:success_rate][:overall],
283
+ trend: performance[:success_rate][:trend],
284
+ failure_patterns: performance[:failure_patterns].first(3),
285
+ recommendations: recommendations.first(3)
286
+ }
287
+
288
+ # Generate optimization prompt
289
+ optimization_prompt = <<~PROMPT
290
+ I need to optimize this prompt template for a #{agent_type}:
291
+
292
+ """
293
+ #{original_template}
294
+ """
295
+
296
+ Performance data:
297
+ - Success rate: #{(context[:success_rate] * 100).round(1)}%
298
+ - Trend: #{context[:trend]}
299
+
300
+ Common failure patterns:
301
+ #{context[:failure_patterns].map { |p| "- #{p[:pattern]} (#{p[:count]} occurrences)" }.join("\n")}
302
+
303
+ Recommendations:
304
+ #{recommendations.map { |r| "- #{r[:message]}" }.join("\n")}
305
+
306
+ Please provide an improved version of this prompt template that addresses these issues.
307
+ Format your response as:
308
+
309
+ IMPROVED_TEMPLATE:
310
+ [Your improved template here]
311
+
312
+ EXPLANATION:
313
+ [Explanation of changes and how they address the issues]
314
+ PROMPT
315
+
316
+ # Generate optimization with LLM
317
+ response = @llm_client.complete(optimization_prompt)
318
+
319
+ # Parse response
320
+ improved_template = extract_improved_template(response)
321
+ explanation = extract_explanation(response)
322
+
323
+ if improved_template && improved_template != original_template
324
+ {
325
+ optimized: true,
326
+ original_template: original_template,
327
+ improved_template: improved_template,
328
+ explanation: explanation,
329
+ confidence: calculate_confidence(performance, improved_template, original_template)
330
+ }
331
+ else
332
+ {
333
+ optimized: false,
334
+ reason: "LLM optimization did not produce a different template",
335
+ original_template: original_template,
336
+ improved_template: original_template
337
+ }
338
+ end
339
+ end
340
+
341
+ def generate_optimized_prompt_heuristic(original_template, agent_type, performance, options)
342
+ # Simple heuristic-based optimization without LLM
343
+ improved_template = original_template.dup
344
+ changes = []
345
+
346
+ # Check for common failure patterns and apply heuristic improvements
347
+ Array(performance[:failure_patterns]).each do |pattern|
348
+ case pattern[:pattern]
349
+ when /timeout/i, /too slow/i
350
+ if !improved_template.include?("time limit") && !improved_template.include?("time constraint")
351
+ improved_template = improved_template.sub(/\A/, "You must complete this task efficiently within the time limit. ")
352
+ changes << "Added time constraint reminder"
353
+ end
354
+
355
+ when /unclear instructions/i, /ambiguous/i
356
+ if !improved_template.include?("clear and specific")
357
+ improved_template = improved_template.sub(/\A/, "Be clear and specific in your response. ")
358
+ changes << "Added clarity instruction"
359
+ end
360
+
361
+ when /missing information/i, /incomplete/i
362
+ if !improved_template.include?("comprehensive") && !improved_template.include?("complete")
363
+ improved_template = improved_template.sub(/\A/, "Provide a comprehensive and complete response. ")
364
+ changes << "Added comprehensiveness instruction"
365
+ end
366
+ end
367
+ end
368
+
369
+ # Check success rate trend
370
+ if performance[:success_rate][:trend] == :declining
371
+ if !improved_template.include?("step by step")
372
+ improved_template = improved_template.sub(/\A/, "Follow these instructions step by step. ")
373
+ changes << "Added step-by-step instruction due to declining success rate"
374
+ end
375
+ end
376
+
377
+ if changes.empty?
378
+ {
379
+ optimized: false,
380
+ reason: "No heuristic improvements identified",
381
+ original_template: original_template,
382
+ improved_template: original_template
383
+ }
384
+ else
385
+ {
386
+ optimized: true,
387
+ original_template: original_template,
388
+ improved_template: improved_template,
389
+ explanation: "Applied heuristic improvements: #{changes.join(", ")}",
390
+ confidence: 0.7 # Heuristic confidence is fixed
391
+ }
392
+ end
393
+ end
394
+
395
+ def generate_optimized_parameters(original_params, agent_type, performance, options)
396
+ # Copy original parameters
397
+ improved_params = original_params.dup
398
+ changes = []
399
+ strategy = options[:optimization_strategy] || :balanced
400
+
401
+ # Adjust parameters based on performance data
402
+ if performance[:performance_trends]
403
+ # Check for token usage trends
404
+ if performance[:performance_trends][:tokens_used] &&
405
+ performance[:performance_trends][:tokens_used][:trend] == :increasing &&
406
+ performance[:performance_trends][:tokens_used][:significant]
407
+
408
+ # Reduce max tokens if usage is increasing
409
+ if improved_params[:max_tokens] && improved_params[:max_tokens] > 100
410
+ case strategy
411
+ when :conservative
412
+ new_max = improved_params[:max_tokens] * 0.95 # Reduce by 5%
413
+ when :balanced
414
+ new_max = improved_params[:max_tokens] * 0.9 # Reduce by 10%
415
+ when :aggressive
416
+ new_max = improved_params[:max_tokens] * 0.8 # Reduce by 20%
417
+ end
418
+
419
+ improved_params[:max_tokens] = new_max.to_i
420
+ changes << "Reduced max_tokens from #{original_params[:max_tokens]} to #{improved_params[:max_tokens]}"
421
+ end
422
+ end
423
+ end
424
+
425
+ # Check success rate and adjust temperature
426
+ if performance[:success_rate][:overall] < 0.8 && improved_params[:temperature]
427
+ # Lower temperature for more predictable results if success rate is low
428
+ case strategy
429
+ when :conservative
430
+ if improved_params[:temperature] > 0.1
431
+ improved_params[:temperature] = [improved_params[:temperature] - 0.1, 0.1].max
432
+ changes << "Reduced temperature from #{original_params[:temperature]} to #{improved_params[:temperature]}"
433
+ end
434
+ when :balanced
435
+ if improved_params[:temperature] > 0.1
436
+ improved_params[:temperature] = [improved_params[:temperature] - 0.2, 0.1].max
437
+ changes << "Reduced temperature from #{original_params[:temperature]} to #{improved_params[:temperature]}"
438
+ end
439
+ when :aggressive
440
+ if improved_params[:temperature] > 0.05
441
+ improved_params[:temperature] = [improved_params[:temperature] - 0.3, 0.05].max
442
+ changes << "Reduced temperature from #{original_params[:temperature]} to #{improved_params[:temperature]}"
443
+ end
444
+ end
445
+ elsif performance[:success_rate][:overall] > 0.95 &&
446
+ performance[:success_rate][:trend] == :stable &&
447
+ improved_params[:temperature] &&
448
+ improved_params[:temperature] < 0.9
449
+ # Increase temperature slightly for more creative results if success rate is high and stable
450
+ case strategy
451
+ when :conservative
452
+ # No change for conservative strategy with high success
453
+ when :balanced
454
+ improved_params[:temperature] = [improved_params[:temperature] + 0.1, 0.9].min
455
+ changes << "Increased temperature from #{original_params[:temperature]} to #{improved_params[:temperature]}"
456
+ when :aggressive
457
+ improved_params[:temperature] = [improved_params[:temperature] + 0.2, 0.9].min
458
+ changes << "Increased temperature from #{original_params[:temperature]} to #{improved_params[:temperature]}"
459
+ end
460
+ end
461
+
462
+ if changes.empty?
463
+ {
464
+ optimized: false,
465
+ reason: "No parameter improvements identified",
466
+ original_params: original_params,
467
+ improved_params: original_params.dup
468
+ }
469
+ else
470
+ {
471
+ optimized: true,
472
+ original_params: original_params,
473
+ improved_params: improved_params,
474
+ explanation: "Applied parameter optimizations: #{changes.join(", ")}",
475
+ confidence: calculate_parameter_confidence(performance, original_params, improved_params)
476
+ }
477
+ end
478
+ end
479
+
480
+ def generate_optimized_sequence(original_sequence, plan_history, options)
481
+ # Copy original sequence
482
+ improved_sequence = original_sequence.map(&:dup)
483
+ changes = []
484
+
485
+ # Find slow tasks and potential parallelization opportunities
486
+ task_durations = {}
487
+ dependencies = {}
488
+
489
+ # Extract task durations and dependencies from history
490
+ plan_history.each do |execution|
491
+ next unless execution[:context] && execution[:context][:task_durations]
492
+
493
+ execution[:context][:task_durations].each do |task_id, duration|
494
+ task_durations[task_id] ||= []
495
+ task_durations[task_id] << duration
496
+ end
497
+
498
+ Array(execution[:context][:task_dependencies]).each do |task_id, deps|
499
+ dependencies[task_id] = deps
500
+ end
501
+ end
502
+
503
+ # Calculate average durations
504
+ avg_durations = {}
505
+ task_durations.each do |task_id, durations|
506
+ avg_durations[task_id] = durations.sum / durations.size.to_f
507
+ end
508
+
509
+ # Find slow tasks
510
+ slow_tasks = avg_durations.sort_by { |_, duration| -duration }.first(3).map(&:first)
511
+
512
+ # Look for parallelization opportunities
513
+ if dependencies.any?
514
+ # Identify tasks that have no dependencies between them
515
+ independent_tasks = []
516
+
517
+ original_sequence.each_with_index do |task, i|
518
+ next_task = original_sequence[i + 1]
519
+ next unless next_task
520
+
521
+ task_id = task[:id]
522
+ next_id = next_task[:id]
523
+
524
+ # Check if these tasks are independent
525
+ if (!dependencies[task_id] || !dependencies[task_id].include?(next_id)) &&
526
+ (!dependencies[next_id] || !dependencies[next_id].include?(task_id))
527
+
528
+ independent_tasks << [task_id, next_id]
529
+ end
530
+ end
531
+
532
+ # Apply parallelization
533
+ if independent_tasks.any?
534
+ # This is a simplified implementation - real parallelization would require
535
+ # more sophisticated task scheduling logic
536
+ pair = independent_tasks.first
537
+
538
+ # Mark tasks for parallel execution
539
+ improved_sequence.each do |task|
540
+ if pair.include?(task[:id])
541
+ task[:parallel] = true
542
+ task[:parallel_group] = "group_#{pair.join("_")}"
543
+ end
544
+ end
545
+
546
+ changes << "Marked tasks #{pair.join(" and ")} for parallel execution"
547
+ end
548
+ end
549
+
550
+ # Optimize slow tasks
551
+ slow_tasks.each do |task_id|
552
+ task_idx = improved_sequence.find_index { |t| t[:id] == task_id }
553
+ next unless task_idx
554
+
555
+ task = improved_sequence[task_idx]
556
+
557
+ # Add optimization hint
558
+ task[:optimization_hint] = "This task is slower than average (#{(avg_durations[task_id] / 1000).round(2)}s)"
559
+
560
+ # Suggest potential optimizations
561
+ if avg_durations[task_id] > 10000 # Very slow task (>10s)
562
+ task[:suggested_optimization] = "Consider breaking down into smaller sub-tasks"
563
+ changes << "Added breakdown suggestion for slow task #{task_id}"
564
+ elsif task[:description] && task[:description].length > 200
565
+ task[:suggested_optimization] = "Consider simplifying task description"
566
+ changes << "Added simplification suggestion for task #{task_id}"
567
+ else
568
+ task[:suggested_optimization] = "Review for optimization opportunities"
569
+ changes << "Added general optimization suggestion for task #{task_id}"
570
+ end
571
+ end
572
+
573
+ if changes.empty?
574
+ {
575
+ optimized: false,
576
+ reason: "No sequence improvements identified",
577
+ original_sequence: original_sequence,
578
+ improved_sequence: original_sequence.map(&:dup)
579
+ }
580
+ else
581
+ {
582
+ optimized: true,
583
+ original_sequence: original_sequence,
584
+ improved_sequence: improved_sequence,
585
+ explanation: "Applied sequence optimizations: #{changes.join(", ")}",
586
+ confidence: 0.8
587
+ }
588
+ end
589
+ end
590
+
591
+ def extract_improved_template(response)
592
+ # Extract the improved template from LLM response
593
+ match = response.match(/IMPROVED_TEMPLATE:\s*(.+?)(?:EXPLANATION:|$)/mi)
594
+ match ? match[1].strip : nil
595
+ end
596
+
597
+ def extract_explanation(response)
598
+ # Extract the explanation from LLM response
599
+ match = response.match(/EXPLANATION:\s*(.+?)$/mi)
600
+ match ? match[1].strip : "No explanation provided"
601
+ end
602
+
603
+ def calculate_confidence(performance, improved_template, original_template)
604
+ # Calculate confidence based on performance data and degree of change
605
+
606
+ # Base confidence on performance data quality
607
+ base_confidence = if performance[:success_rate][:sample_size] > 100
608
+ 0.9
609
+ elsif performance[:success_rate][:sample_size] > 50
610
+ 0.8
611
+ elsif performance[:success_rate][:sample_size] > 20
612
+ 0.7
613
+ else
614
+ 0.6
615
+ end
616
+
617
+ # Adjust based on degree of change
618
+ change_ratio = calculate_change_ratio(improved_template, original_template)
619
+
620
+ if change_ratio > 0.5
621
+ # Large changes reduce confidence
622
+ base_confidence *= 0.8
623
+ elsif change_ratio < 0.1
624
+ # Very small changes increase confidence
625
+ base_confidence *= 1.1
626
+ end
627
+
628
+ # Cap at 0.95
629
+ [base_confidence, 0.95].min
630
+ end
631
+
632
+ def calculate_change_ratio(improved, original)
633
+ return 0.0 if improved == original
634
+
635
+ # Calculate Levenshtein distance as a ratio of the original length
636
+ distance = levenshtein_distance(improved, original)
637
+ distance.to_f / original.length
638
+ end
639
+
640
+ def levenshtein_distance(str1, str2)
641
+ # Simple Levenshtein distance implementation
642
+ m = str1.length
643
+ n = str2.length
644
+
645
+ # Initialize the matrix
646
+ matrix = Array.new(m + 1) { Array.new(n + 1, 0) }
647
+
648
+ # Fill the first row and column
649
+ (0..m).each { |i| matrix[i][0] = i }
650
+ (0..n).each { |j| matrix[0][j] = j }
651
+
652
+ # Fill the rest of the matrix
653
+ (1..m).each do |i|
654
+ (1..n).each do |j|
655
+ cost = (str1[i - 1] == str2[j - 1]) ? 0 : 1
656
+ matrix[i][j] = [
657
+ matrix[i - 1][j] + 1,
658
+ matrix[i][j - 1] + 1,
659
+ matrix[i - 1][j - 1] + cost
660
+ ].min
661
+ end
662
+ end
663
+
664
+ matrix[m][n]
665
+ end
666
+
667
+ def calculate_parameter_confidence(performance, original_params, improved_params)
668
+ # Calculate confidence for parameter optimizations
669
+
670
+ # Base confidence on performance data quality
671
+ base_confidence = if performance[:success_rate][:sample_size] > 100
672
+ 0.85
673
+ elsif performance[:success_rate][:sample_size] > 50
674
+ 0.75
675
+ elsif performance[:success_rate][:sample_size] > 20
676
+ 0.65
677
+ else
678
+ 0.5
679
+ end
680
+
681
+ # Count changed parameters
682
+ changes = 0
683
+ original_params.each do |key, value|
684
+ changes += 1 if improved_params[key] && improved_params[key] != value
685
+ end
686
+
687
+ # Adjust confidence based on number of changes
688
+ if changes > 2
689
+ # More changes reduce confidence
690
+ base_confidence *= 0.9
691
+ elsif changes == 1
692
+ # Single change increases confidence
693
+ base_confidence *= 1.05
694
+ end
695
+
696
+ # Cap at 0.9
697
+ [base_confidence, 0.9].min
698
+ end
699
+
700
+ def extract_agent_type_from_key(key)
701
+ # Extract agent type from registry key
702
+ key.to_s.split(".").first
703
+ end
704
+ end
705
+ end
706
+ end