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.
- checksums.yaml +4 -4
- data/.agentic.yml +2 -0
- data/.architecture/decisions/ArchitecturalFeatureBuilder.md +136 -0
- data/.architecture/decisions/ArchitectureConsiderations.md +200 -0
- data/.architecture/decisions/adr_001_observer_pattern_implementation.md +196 -0
- data/.architecture/decisions/adr_002_plan_orchestrator.md +320 -0
- data/.architecture/decisions/adr_003_plan_orchestrator_interface.md +179 -0
- data/.architecture/decisions/adrs/ADR-001-dependency-management.md +147 -0
- data/.architecture/decisions/adrs/ADR-002-system-boundaries.md +162 -0
- data/.architecture/decisions/adrs/ADR-003-content-safety.md +158 -0
- data/.architecture/decisions/adrs/ADR-004-agent-permissions.md +161 -0
- data/.architecture/decisions/adrs/ADR-005-adaptation-engine.md +127 -0
- data/.architecture/decisions/adrs/ADR-006-extension-system.md +273 -0
- data/.architecture/decisions/adrs/ADR-007-learning-system.md +156 -0
- data/.architecture/decisions/adrs/ADR-008-prompt-generation.md +325 -0
- data/.architecture/decisions/adrs/ADR-009-task-failure-handling.md +353 -0
- data/.architecture/decisions/adrs/ADR-010-task-input-handling.md +251 -0
- data/.architecture/decisions/adrs/ADR-011-task-observable-pattern.md +391 -0
- data/.architecture/decisions/adrs/ADR-012-task-output-handling.md +205 -0
- data/.architecture/decisions/adrs/ADR-013-architecture-alignment.md +211 -0
- data/.architecture/decisions/adrs/ADR-014-agent-capability-registry.md +80 -0
- data/.architecture/decisions/adrs/ADR-015-persistent-agent-store.md +100 -0
- data/.architecture/decisions/adrs/ADR-016-agent-assembly-engine.md +117 -0
- data/.architecture/decisions/adrs/ADR-017-streaming-observability.md +171 -0
- data/.architecture/decisions/capability_tools_distinction.md +150 -0
- data/.architecture/decisions/cli_command_structure.md +61 -0
- data/.architecture/implementation/agent_self_assembly_implementation.md +267 -0
- data/.architecture/implementation/agent_self_assembly_summary.md +138 -0
- data/.architecture/members.yml +187 -0
- data/.architecture/planning/self_implementation_exercise.md +295 -0
- data/.architecture/planning/session_compaction_rule.md +43 -0
- data/.architecture/planning/streaming_observability_feature.md +223 -0
- data/.architecture/principles.md +151 -0
- data/.architecture/recalibration/0-2-0.md +92 -0
- data/.architecture/recalibration/agent_self_assembly.md +238 -0
- data/.architecture/recalibration/cli_command_structure.md +91 -0
- data/.architecture/recalibration/implementation_roadmap_0-2-0.md +301 -0
- data/.architecture/recalibration/progress_tracking_0-2-0.md +114 -0
- data/.architecture/recalibration_process.md +127 -0
- data/.architecture/reviews/0-2-0.md +181 -0
- data/.architecture/reviews/cli_command_duplication.md +98 -0
- data/.architecture/templates/adr.md +105 -0
- data/.architecture/templates/implementation_roadmap.md +125 -0
- data/.architecture/templates/progress_tracking.md +89 -0
- data/.architecture/templates/recalibration_plan.md +70 -0
- data/.architecture/templates/version_comparison.md +124 -0
- data/.claude/settings.local.json +13 -0
- data/.claude-sessions/001-task-class-architecture-implementation.md +129 -0
- data/.claude-sessions/002-plan-orchestrator-interface-review.md +105 -0
- data/.claude-sessions/architecture-governance-implementation.md +37 -0
- data/.claude-sessions/architecture-review-session.md +27 -0
- data/ArchitecturalFeatureBuilder.md +136 -0
- data/ArchitectureConsiderations.md +229 -0
- data/CHANGELOG.md +57 -2
- data/CLAUDE.md +111 -0
- data/CONTRIBUTING.md +286 -0
- data/MAINTAINING.md +301 -0
- data/README.md +582 -28
- data/docs/agent_capabilities_api.md +259 -0
- data/docs/artifact_extension_points.md +757 -0
- data/docs/artifact_generation_architecture.md +323 -0
- data/docs/artifact_implementation_plan.md +596 -0
- data/docs/artifact_integration_points.md +345 -0
- data/docs/artifact_verification_strategies.md +581 -0
- data/docs/streaming_observability_architecture.md +510 -0
- data/exe/agentic +6 -1
- data/lefthook.yml +5 -0
- data/lib/agentic/adaptation_engine.rb +124 -0
- data/lib/agentic/agent.rb +181 -4
- data/lib/agentic/agent_assembly_engine.rb +442 -0
- data/lib/agentic/agent_capability_registry.rb +260 -0
- data/lib/agentic/agent_config.rb +63 -0
- data/lib/agentic/agent_specification.rb +46 -0
- data/lib/agentic/capabilities/examples.rb +530 -0
- data/lib/agentic/capabilities.rb +14 -0
- data/lib/agentic/capability_provider.rb +146 -0
- data/lib/agentic/capability_specification.rb +118 -0
- data/lib/agentic/cli/agent.rb +31 -0
- data/lib/agentic/cli/capabilities.rb +191 -0
- data/lib/agentic/cli/config.rb +134 -0
- data/lib/agentic/cli/execution_observer.rb +796 -0
- data/lib/agentic/cli.rb +1068 -0
- data/lib/agentic/default_agent_provider.rb +35 -0
- data/lib/agentic/errors/llm_error.rb +184 -0
- data/lib/agentic/execution_plan.rb +53 -0
- data/lib/agentic/execution_result.rb +91 -0
- data/lib/agentic/expected_answer_format.rb +46 -0
- data/lib/agentic/extension/domain_adapter.rb +109 -0
- data/lib/agentic/extension/plugin_manager.rb +163 -0
- data/lib/agentic/extension/protocol_handler.rb +116 -0
- data/lib/agentic/extension.rb +45 -0
- data/lib/agentic/factory_methods.rb +9 -1
- data/lib/agentic/generation_stats.rb +61 -0
- data/lib/agentic/learning/README.md +84 -0
- data/lib/agentic/learning/capability_optimizer.rb +613 -0
- data/lib/agentic/learning/execution_history_store.rb +251 -0
- data/lib/agentic/learning/pattern_recognizer.rb +500 -0
- data/lib/agentic/learning/strategy_optimizer.rb +706 -0
- data/lib/agentic/learning.rb +131 -0
- data/lib/agentic/llm_assisted_composition_strategy.rb +188 -0
- data/lib/agentic/llm_client.rb +215 -15
- data/lib/agentic/llm_config.rb +65 -1
- data/lib/agentic/llm_response.rb +163 -0
- data/lib/agentic/logger.rb +1 -1
- data/lib/agentic/observable.rb +51 -0
- data/lib/agentic/persistent_agent_store.rb +385 -0
- data/lib/agentic/plan_execution_result.rb +129 -0
- data/lib/agentic/plan_orchestrator.rb +464 -0
- data/lib/agentic/plan_orchestrator_config.rb +57 -0
- data/lib/agentic/retry_config.rb +63 -0
- data/lib/agentic/retry_handler.rb +125 -0
- data/lib/agentic/structured_outputs.rb +1 -1
- data/lib/agentic/task.rb +193 -0
- data/lib/agentic/task_definition.rb +39 -0
- data/lib/agentic/task_execution_result.rb +92 -0
- data/lib/agentic/task_failure.rb +66 -0
- data/lib/agentic/task_output_schemas.rb +112 -0
- data/lib/agentic/task_planner.rb +54 -19
- data/lib/agentic/task_result.rb +48 -0
- data/lib/agentic/ui.rb +244 -0
- data/lib/agentic/verification/critic_framework.rb +116 -0
- data/lib/agentic/verification/llm_verification_strategy.rb +60 -0
- data/lib/agentic/verification/schema_verification_strategy.rb +47 -0
- data/lib/agentic/verification/verification_hub.rb +62 -0
- data/lib/agentic/verification/verification_result.rb +50 -0
- data/lib/agentic/verification/verification_strategy.rb +26 -0
- data/lib/agentic/version.rb +1 -1
- data/lib/agentic.rb +74 -2
- data/plugins/README.md +41 -0
- metadata +245 -6
@@ -0,0 +1,613 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Agentic
|
4
|
+
module Learning
|
5
|
+
# CapabilityOptimizer improves capability implementations and composition
|
6
|
+
# based on execution history and performance metrics.
|
7
|
+
#
|
8
|
+
# @example Optimizing a capability implementation
|
9
|
+
# history_store = Agentic::Learning::ExecutionHistoryStore.new
|
10
|
+
# recognizer = Agentic::Learning::PatternRecognizer.new(history_store: history_store)
|
11
|
+
# optimizer = Agentic::Learning::CapabilityOptimizer.new(
|
12
|
+
# pattern_recognizer: recognizer,
|
13
|
+
# history_store: history_store,
|
14
|
+
# registry: Agentic.agent_capability_registry
|
15
|
+
# )
|
16
|
+
#
|
17
|
+
# # Get optimization suggestions for a capability
|
18
|
+
# suggestions = optimizer.get_optimization_suggestions("text_generation")
|
19
|
+
#
|
20
|
+
class CapabilityOptimizer
|
21
|
+
# Initialize a new CapabilityOptimizer
|
22
|
+
#
|
23
|
+
# @param options [Hash] Configuration options
|
24
|
+
# @option options [Logger] :logger Custom logger (defaults to Agentic.logger)
|
25
|
+
# @option options [PatternRecognizer] :pattern_recognizer Pattern recognizer for insights
|
26
|
+
# @option options [ExecutionHistoryStore] :history_store History store for performance data
|
27
|
+
# @option options [AgentCapabilityRegistry] :registry The capability registry
|
28
|
+
# @option options [LlmClient] :llm_client LLM client for generating optimizations (optional)
|
29
|
+
# @option options [Boolean] :auto_apply_optimizations Whether to automatically apply optimizations
|
30
|
+
def initialize(options = {})
|
31
|
+
@logger = options[:logger] || Agentic.logger
|
32
|
+
@pattern_recognizer = options[:pattern_recognizer] || raise(ArgumentError, "pattern_recognizer is required")
|
33
|
+
@history_store = options[:history_store] || raise(ArgumentError, "history_store is required")
|
34
|
+
@registry = options[:registry] || Agentic.agent_capability_registry
|
35
|
+
@llm_client = options[:llm_client]
|
36
|
+
@auto_apply_optimizations = options.fetch(:auto_apply_optimizations, false)
|
37
|
+
@optimization_cache = {}
|
38
|
+
@last_optimization = {}
|
39
|
+
end
|
40
|
+
|
41
|
+
# Get optimization suggestions for a capability
|
42
|
+
#
|
43
|
+
# @param capability_name [String] The name of the capability
|
44
|
+
# @param version [String, nil] The version of the capability (latest if nil)
|
45
|
+
# @param options [Hash] Additional options
|
46
|
+
# @option options [Boolean] :force Force new suggestions even if recently generated
|
47
|
+
# @return [Hash] Optimization suggestions
|
48
|
+
def get_optimization_suggestions(capability_name, version = nil, options = {})
|
49
|
+
# Get the capability from the registry
|
50
|
+
capability = @registry.get(capability_name, version)
|
51
|
+
return {success: false, reason: "Capability not found"} unless capability
|
52
|
+
|
53
|
+
version = capability.version
|
54
|
+
cache_key = "#{capability_name}:#{version}"
|
55
|
+
|
56
|
+
# Check cache
|
57
|
+
unless options[:force]
|
58
|
+
if @optimization_cache[cache_key] && @last_optimization[cache_key] &&
|
59
|
+
@last_optimization[cache_key] > Time.now - 24 * 60 * 60
|
60
|
+
return @optimization_cache[cache_key]
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
# Get capability execution history
|
65
|
+
history = get_capability_history(capability_name, version)
|
66
|
+
|
67
|
+
# Check if we have enough data
|
68
|
+
if history.size < 10
|
69
|
+
result = {
|
70
|
+
success: false,
|
71
|
+
reason: "Insufficient execution data",
|
72
|
+
capability: capability_name,
|
73
|
+
version: version,
|
74
|
+
suggestions: []
|
75
|
+
}
|
76
|
+
|
77
|
+
@optimization_cache[cache_key] = result
|
78
|
+
@last_optimization[cache_key] = Time.now
|
79
|
+
|
80
|
+
return result
|
81
|
+
end
|
82
|
+
|
83
|
+
# Analyze performance
|
84
|
+
performance = analyze_capability_performance(history)
|
85
|
+
|
86
|
+
# Generate suggestions
|
87
|
+
suggestions = generate_optimization_suggestions(capability, performance)
|
88
|
+
|
89
|
+
result = {
|
90
|
+
success: true,
|
91
|
+
capability: capability_name,
|
92
|
+
version: version,
|
93
|
+
performance: performance,
|
94
|
+
suggestions: suggestions
|
95
|
+
}
|
96
|
+
|
97
|
+
@optimization_cache[cache_key] = result
|
98
|
+
@last_optimization[cache_key] = Time.now
|
99
|
+
|
100
|
+
result
|
101
|
+
end
|
102
|
+
|
103
|
+
# Optimize capability composition for a task
|
104
|
+
#
|
105
|
+
# @param task [Task] The task to optimize capabilities for
|
106
|
+
# @param options [Hash] Additional options
|
107
|
+
# @return [Hash] Optimized capability composition
|
108
|
+
def optimize_capability_composition(task, options = {})
|
109
|
+
# Get task description
|
110
|
+
description = task.description
|
111
|
+
|
112
|
+
# Get currently used capabilities
|
113
|
+
if task.input && task.input[:capabilities]
|
114
|
+
current_capabilities = task.input[:capabilities]
|
115
|
+
else
|
116
|
+
# If no capabilities specified, use default inference
|
117
|
+
requirements = {}
|
118
|
+
if task.agent_spec
|
119
|
+
engine = Agentic::AgentAssemblyEngine.new(@registry)
|
120
|
+
requirements = engine.analyze_requirements(task)
|
121
|
+
end
|
122
|
+
|
123
|
+
current_capabilities = requirements.keys
|
124
|
+
end
|
125
|
+
|
126
|
+
# No need to optimize if no capabilities
|
127
|
+
if current_capabilities.empty?
|
128
|
+
return {
|
129
|
+
success: false,
|
130
|
+
reason: "No capabilities to optimize",
|
131
|
+
original_capabilities: [],
|
132
|
+
optimized_capabilities: []
|
133
|
+
}
|
134
|
+
end
|
135
|
+
|
136
|
+
# Get capability performance data
|
137
|
+
capability_performance = {}
|
138
|
+
current_capabilities.each do |capability|
|
139
|
+
suggestions = get_optimization_suggestions(capability.to_s)
|
140
|
+
capability_performance[capability.to_s] = suggestions[:performance] if suggestions[:success]
|
141
|
+
end
|
142
|
+
|
143
|
+
# Generate optimized composition
|
144
|
+
if @llm_client
|
145
|
+
generate_optimized_composition_with_llm(
|
146
|
+
description,
|
147
|
+
current_capabilities,
|
148
|
+
capability_performance,
|
149
|
+
options
|
150
|
+
)
|
151
|
+
else
|
152
|
+
generate_optimized_composition_heuristic(
|
153
|
+
description,
|
154
|
+
current_capabilities,
|
155
|
+
capability_performance,
|
156
|
+
options
|
157
|
+
)
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
# Apply optimization to a capability
|
162
|
+
#
|
163
|
+
# @param capability_name [String] The name of the capability
|
164
|
+
# @param optimization [Hash] The optimization to apply
|
165
|
+
# @param options [Hash] Additional options
|
166
|
+
# @return [Boolean] Whether the optimization was applied successfully
|
167
|
+
def apply_optimization(capability_name, optimization, options = {})
|
168
|
+
# Get the capability and provider
|
169
|
+
capability = @registry.get(capability_name)
|
170
|
+
return false unless capability
|
171
|
+
|
172
|
+
provider = @registry.get_provider(capability_name)
|
173
|
+
return false unless provider
|
174
|
+
|
175
|
+
# Check optimization type
|
176
|
+
case optimization[:type]
|
177
|
+
when :implementation
|
178
|
+
# For implementation optimizations, we would need to create a new provider
|
179
|
+
# This is a simplified example - in a real implementation, we would need to
|
180
|
+
# handle different implementation types (Proc, Class, etc.)
|
181
|
+
if provider.implementation.is_a?(Proc) && optimization[:new_implementation].is_a?(Proc)
|
182
|
+
# Create a new provider with the optimized implementation
|
183
|
+
new_provider = Agentic::CapabilityProvider.new(
|
184
|
+
capability: capability,
|
185
|
+
implementation: optimization[:new_implementation]
|
186
|
+
)
|
187
|
+
|
188
|
+
# Register the new provider
|
189
|
+
@registry.register(capability, new_provider)
|
190
|
+
|
191
|
+
return true
|
192
|
+
end
|
193
|
+
when :parameters
|
194
|
+
# For parameter optimizations, we would update the capability's parameters
|
195
|
+
# This is a simplified example - in a real implementation, we would need to
|
196
|
+
# handle different parameter types
|
197
|
+
if optimization[:parameters].is_a?(Hash)
|
198
|
+
# Update parameters (this would depend on the actual capability implementation)
|
199
|
+
# For this example, we just log that we would update the parameters
|
200
|
+
@logger.info("Would update parameters for #{capability_name}: #{optimization[:parameters]}")
|
201
|
+
|
202
|
+
return true
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
false
|
207
|
+
end
|
208
|
+
|
209
|
+
# Record capability execution metrics
|
210
|
+
#
|
211
|
+
# @param capability_name [String] The name of the capability
|
212
|
+
# @param version [String] The version of the capability
|
213
|
+
# @param metrics [Hash] The execution metrics
|
214
|
+
# @param result [Hash] The execution result
|
215
|
+
# @return [Boolean] Whether the metrics were recorded successfully
|
216
|
+
def record_execution(capability_name, version, metrics, result)
|
217
|
+
success = result && !result.key?(:error)
|
218
|
+
|
219
|
+
@history_store.record_execution(
|
220
|
+
capability_name: capability_name,
|
221
|
+
capability_version: version,
|
222
|
+
duration_ms: metrics[:duration_ms],
|
223
|
+
success: success,
|
224
|
+
metrics: metrics,
|
225
|
+
context: {
|
226
|
+
result_summary: summarize_result(result),
|
227
|
+
error: (result && result[:error]) ? result[:error] : nil
|
228
|
+
}
|
229
|
+
)
|
230
|
+
|
231
|
+
true
|
232
|
+
end
|
233
|
+
|
234
|
+
private
|
235
|
+
|
236
|
+
# Get execution history for a capability
|
237
|
+
def get_capability_history(capability_name, version)
|
238
|
+
# Get execution history for this capability
|
239
|
+
end_time = Time.now
|
240
|
+
start_time = end_time - (30 * 24 * 60 * 60) # 30 days
|
241
|
+
|
242
|
+
@history_store.get_history(
|
243
|
+
capability_name: capability_name,
|
244
|
+
capability_version: version,
|
245
|
+
start_time: start_time,
|
246
|
+
end_time: end_time
|
247
|
+
)
|
248
|
+
end
|
249
|
+
|
250
|
+
# Analyze capability performance based on history
|
251
|
+
def analyze_capability_performance(history)
|
252
|
+
# Calculate success rate
|
253
|
+
total = history.size
|
254
|
+
successful = history.count { |h| h[:success] }
|
255
|
+
success_rate = successful.to_f / total
|
256
|
+
|
257
|
+
# Calculate average duration
|
258
|
+
durations = history.map { |h| h[:duration_ms] }.compact
|
259
|
+
avg_duration = durations.empty? ? nil : durations.sum / durations.size.to_f
|
260
|
+
|
261
|
+
# Calculate trend
|
262
|
+
if history.size >= 10
|
263
|
+
recent = history.first(5)
|
264
|
+
older = history.last(5)
|
265
|
+
|
266
|
+
recent_success = recent.count { |h| h[:success] }.to_f / recent.size
|
267
|
+
older_success = older.count { |h| h[:success] }.to_f / older.size
|
268
|
+
|
269
|
+
trend = if (recent_success - older_success).abs < 0.1
|
270
|
+
:stable
|
271
|
+
elsif recent_success > older_success
|
272
|
+
:improving
|
273
|
+
else
|
274
|
+
:declining
|
275
|
+
end
|
276
|
+
else
|
277
|
+
trend = :unknown
|
278
|
+
end
|
279
|
+
|
280
|
+
# Calculate error patterns
|
281
|
+
errors = history.select { |h| !h[:success] && h[:context] && h[:context][:error] }
|
282
|
+
error_patterns = {}
|
283
|
+
|
284
|
+
errors.each do |error|
|
285
|
+
message = error[:context][:error].to_s
|
286
|
+
error_patterns[message] ||= 0
|
287
|
+
error_patterns[message] += 1
|
288
|
+
end
|
289
|
+
|
290
|
+
top_errors = error_patterns.sort_by { |_, count| -count }.first(3).map do |message, count|
|
291
|
+
{message: message, count: count}
|
292
|
+
end
|
293
|
+
|
294
|
+
{
|
295
|
+
success_rate: success_rate,
|
296
|
+
avg_duration_ms: avg_duration,
|
297
|
+
total_executions: total,
|
298
|
+
trend: trend,
|
299
|
+
top_errors: top_errors
|
300
|
+
}
|
301
|
+
end
|
302
|
+
|
303
|
+
# Generate optimization suggestions for a capability
|
304
|
+
def generate_optimization_suggestions(capability, performance)
|
305
|
+
suggestions = []
|
306
|
+
|
307
|
+
# Check success rate
|
308
|
+
if performance[:success_rate] < 0.8
|
309
|
+
suggestions << {
|
310
|
+
type: :reliability,
|
311
|
+
importance: :high,
|
312
|
+
message: "Improve reliability to address the #{performance[:success_rate] * 100}% success rate"
|
313
|
+
}
|
314
|
+
|
315
|
+
# Add specific suggestions based on error patterns
|
316
|
+
performance[:top_errors].each do |error|
|
317
|
+
suggestions << {
|
318
|
+
type: :error_handling,
|
319
|
+
importance: :high,
|
320
|
+
message: "Address common error: #{error[:message]} (#{error[:count]} occurrences)"
|
321
|
+
}
|
322
|
+
end
|
323
|
+
end
|
324
|
+
|
325
|
+
# Check performance
|
326
|
+
if performance[:avg_duration_ms] && performance[:avg_duration_ms] > 1000
|
327
|
+
suggestions << {
|
328
|
+
type: :performance,
|
329
|
+
importance: :medium,
|
330
|
+
message: "Improve performance to reduce average duration (#{performance[:avg_duration_ms].round}ms)"
|
331
|
+
}
|
332
|
+
end
|
333
|
+
|
334
|
+
# Check trend
|
335
|
+
if performance[:trend] == :declining
|
336
|
+
suggestions << {
|
337
|
+
type: :trend,
|
338
|
+
importance: :high,
|
339
|
+
message: "Address declining success rate trend"
|
340
|
+
}
|
341
|
+
end
|
342
|
+
|
343
|
+
# Add dependency suggestions
|
344
|
+
if capability.dependencies&.any?
|
345
|
+
dependency_names = capability.dependencies.map { |d| d[:name] }
|
346
|
+
|
347
|
+
dependency_names.each do |dep_name|
|
348
|
+
dep_suggestions = get_optimization_suggestions(dep_name)
|
349
|
+
|
350
|
+
if dep_suggestions[:success] && dep_suggestions[:performance][:success_rate] < 0.9
|
351
|
+
suggestions << {
|
352
|
+
type: :dependency,
|
353
|
+
importance: :medium,
|
354
|
+
message: "Improve or replace dependency: #{dep_name} (#{dep_suggestions[:performance][:success_rate] * 100}% success rate)"
|
355
|
+
}
|
356
|
+
end
|
357
|
+
end
|
358
|
+
end
|
359
|
+
|
360
|
+
# Use LLM for advanced suggestions if available
|
361
|
+
if @llm_client && performance[:total_executions] >= 20
|
362
|
+
llm_suggestions = generate_llm_suggestions(capability, performance)
|
363
|
+
suggestions.concat(llm_suggestions) if llm_suggestions.any?
|
364
|
+
end
|
365
|
+
|
366
|
+
suggestions
|
367
|
+
end
|
368
|
+
|
369
|
+
# Generate suggestions using LLM
|
370
|
+
def generate_llm_suggestions(capability, performance)
|
371
|
+
# Format the capability details
|
372
|
+
capability_details = <<~DETAILS
|
373
|
+
Capability: #{capability.name} v#{capability.version}
|
374
|
+
Description: #{capability.description}
|
375
|
+
Inputs: #{capability.inputs.map { |name, spec| "#{name} (#{spec[:type] || "any"})" }.join(", ")}
|
376
|
+
Outputs: #{capability.outputs.map { |name, spec| "#{name} (#{spec[:type] || "any"})" }.join(", ")}
|
377
|
+
Dependencies: #{capability.dependencies.map { |d| "#{d[:name]} v#{d[:version]}" }.join(", ")}
|
378
|
+
DETAILS
|
379
|
+
|
380
|
+
# Format the performance details
|
381
|
+
performance_details = <<~DETAILS
|
382
|
+
Success Rate: #{(performance[:success_rate] * 100).round(1)}%
|
383
|
+
Average Duration: #{performance[:avg_duration_ms] ? "#{performance[:avg_duration_ms].round}ms" : "Unknown"}
|
384
|
+
Total Executions: #{performance[:total_executions]}
|
385
|
+
Trend: #{performance[:trend]}
|
386
|
+
Top Errors: #{performance[:top_errors].map { |e| "#{e[:message]} (#{e[:count]} occurrences)" }.join(", ")}
|
387
|
+
DETAILS
|
388
|
+
|
389
|
+
# Create the prompt
|
390
|
+
prompt = <<~PROMPT
|
391
|
+
As an AI specialized in optimizing AI agent capabilities, please analyze this capability and its performance metrics.
|
392
|
+
|
393
|
+
Capability Details:
|
394
|
+
#{capability_details}
|
395
|
+
|
396
|
+
Performance Metrics:
|
397
|
+
#{performance_details}
|
398
|
+
|
399
|
+
Based on this information, provide 2-3 specific optimization suggestions that could improve this capability's
|
400
|
+
performance, reliability, or effectiveness. Focus on practical, implementable changes rather than general advice.
|
401
|
+
|
402
|
+
Format your response as a JSON array of suggestion objects, each with:
|
403
|
+
- type: The type of suggestion (implementation, parameters, error_handling, etc.)
|
404
|
+
- importance: The importance level (high, medium, low)
|
405
|
+
- message: A clear, specific suggestion message
|
406
|
+
- details: Optional implementation details or examples
|
407
|
+
PROMPT
|
408
|
+
|
409
|
+
# Get suggestions from the LLM
|
410
|
+
response = @llm_client.complete(
|
411
|
+
prompt: prompt,
|
412
|
+
response_format: {type: "json"}
|
413
|
+
)
|
414
|
+
|
415
|
+
# Parse the response
|
416
|
+
begin
|
417
|
+
suggestions = JSON.parse(response.to_s, symbolize_names: true)
|
418
|
+
|
419
|
+
if suggestions.is_a?(Array)
|
420
|
+
suggestions
|
421
|
+
elsif suggestions.is_a?(Hash) && suggestions[:suggestions].is_a?(Array)
|
422
|
+
suggestions[:suggestions]
|
423
|
+
else
|
424
|
+
@logger.warn("Unexpected LLM response format for capability suggestions")
|
425
|
+
[]
|
426
|
+
end
|
427
|
+
rescue => e
|
428
|
+
@logger.error("Failed to parse LLM capability suggestions: #{e.message}")
|
429
|
+
[]
|
430
|
+
end
|
431
|
+
end
|
432
|
+
|
433
|
+
# Generate optimized capability composition using LLM
|
434
|
+
def generate_optimized_composition_with_llm(description, current_capabilities, capability_performance, options)
|
435
|
+
# Format current capabilities
|
436
|
+
capabilities_text = current_capabilities.map do |capability|
|
437
|
+
perf = capability_performance[capability.to_s]
|
438
|
+
if perf
|
439
|
+
"- #{capability} (Success rate: #{(perf[:success_rate] * 100).round(1)}%, " \
|
440
|
+
"Avg duration: #{perf[:avg_duration_ms] ? "#{perf[:avg_duration_ms].round}ms" : "Unknown"})"
|
441
|
+
else
|
442
|
+
"- #{capability} (No performance data available)"
|
443
|
+
end
|
444
|
+
end.join("\n")
|
445
|
+
|
446
|
+
# Get all available capabilities
|
447
|
+
available_capabilities = @registry.list.keys - current_capabilities.map(&:to_s)
|
448
|
+
available_text = available_capabilities.map { |c| "- #{c}" }.join("\n")
|
449
|
+
|
450
|
+
# Create the prompt
|
451
|
+
prompt = <<~PROMPT
|
452
|
+
As an AI specialized in composing capabilities for AI agents, please analyze this task and the currently used capabilities.
|
453
|
+
|
454
|
+
Task Description:
|
455
|
+
#{description}
|
456
|
+
|
457
|
+
Currently Used Capabilities:
|
458
|
+
#{capabilities_text}
|
459
|
+
|
460
|
+
Other Available Capabilities:
|
461
|
+
#{available_text}
|
462
|
+
|
463
|
+
Based on this information, suggest an optimized set of capabilities for this task. You can:
|
464
|
+
1. Keep capabilities that are well-suited for the task
|
465
|
+
2. Remove capabilities that are unnecessary or underperforming
|
466
|
+
3. Add new capabilities from the available list that would enhance task performance
|
467
|
+
4. Suggest a different composition or sequence of capabilities
|
468
|
+
|
469
|
+
Format your response as a JSON object with:
|
470
|
+
{
|
471
|
+
"optimized_capabilities": ["capability1", "capability2", ...],
|
472
|
+
"additions": ["new_capability1", ...],
|
473
|
+
"removals": ["removed_capability1", ...],
|
474
|
+
"rationale": "Explanation of your changes"
|
475
|
+
}
|
476
|
+
PROMPT
|
477
|
+
|
478
|
+
# Get suggestions from the LLM
|
479
|
+
response = @llm_client.complete(
|
480
|
+
prompt: prompt,
|
481
|
+
response_format: {type: "json"}
|
482
|
+
)
|
483
|
+
|
484
|
+
# Parse the response
|
485
|
+
begin
|
486
|
+
result = JSON.parse(response.to_s, symbolize_names: true)
|
487
|
+
|
488
|
+
if result[:optimized_capabilities].is_a?(Array)
|
489
|
+
{
|
490
|
+
success: true,
|
491
|
+
original_capabilities: current_capabilities,
|
492
|
+
optimized_capabilities: result[:optimized_capabilities],
|
493
|
+
additions: result[:additions] || [],
|
494
|
+
removals: result[:removals] || [],
|
495
|
+
rationale: result[:rationale] || "No rationale provided"
|
496
|
+
}
|
497
|
+
else
|
498
|
+
@logger.warn("Unexpected LLM response format for capability composition")
|
499
|
+
{
|
500
|
+
success: false,
|
501
|
+
reason: "Failed to generate optimized composition",
|
502
|
+
original_capabilities: current_capabilities,
|
503
|
+
optimized_capabilities: current_capabilities
|
504
|
+
}
|
505
|
+
end
|
506
|
+
rescue => e
|
507
|
+
@logger.error("Failed to parse LLM capability composition: #{e.message}")
|
508
|
+
{
|
509
|
+
success: false,
|
510
|
+
reason: "Failed to parse optimization response",
|
511
|
+
original_capabilities: current_capabilities,
|
512
|
+
optimized_capabilities: current_capabilities
|
513
|
+
}
|
514
|
+
end
|
515
|
+
end
|
516
|
+
|
517
|
+
# Generate optimized capability composition using heuristics
|
518
|
+
def generate_optimized_composition_heuristic(description, current_capabilities, capability_performance, options)
|
519
|
+
optimized = current_capabilities.dup
|
520
|
+
additions = []
|
521
|
+
removals = []
|
522
|
+
changes = []
|
523
|
+
|
524
|
+
# Remove low-performing capabilities
|
525
|
+
current_capabilities.each do |capability|
|
526
|
+
capability_name = capability.to_s
|
527
|
+
perf = capability_performance[capability_name]
|
528
|
+
|
529
|
+
if perf && perf[:success_rate] < 0.5 && perf[:total_executions] > 10
|
530
|
+
# Only remove if it's not a critical capability
|
531
|
+
# This is a simplified check - in a real implementation, we would need
|
532
|
+
# to determine if a capability is critical based on the task
|
533
|
+
unless description.downcase.include?(capability_name.downcase)
|
534
|
+
optimized.delete(capability)
|
535
|
+
removals << capability_name
|
536
|
+
changes << "Removed low-performing capability: #{capability_name} (#{(perf[:success_rate] * 100).round}% success rate)"
|
537
|
+
end
|
538
|
+
end
|
539
|
+
end
|
540
|
+
|
541
|
+
# Add capabilities based on task description
|
542
|
+
available_capabilities = @registry.list.keys - current_capabilities.map(&:to_s)
|
543
|
+
|
544
|
+
available_capabilities.each do |capability|
|
545
|
+
# Check if the capability is relevant to the task
|
546
|
+
# This is a simplified check - in a real implementation, we would need
|
547
|
+
# a more sophisticated relevance determination
|
548
|
+
if description.downcase.include?(capability.downcase)
|
549
|
+
optimized << capability
|
550
|
+
additions << capability
|
551
|
+
changes << "Added potentially relevant capability: #{capability}"
|
552
|
+
end
|
553
|
+
end
|
554
|
+
|
555
|
+
# Handle common combinations
|
556
|
+
if optimized.include?("data_analysis") && !optimized.include?("text_generation")
|
557
|
+
optimized << "text_generation"
|
558
|
+
additions << "text_generation"
|
559
|
+
changes << "Added text_generation to support data_analysis"
|
560
|
+
end
|
561
|
+
|
562
|
+
if optimized.include?("web_search") && !optimized.include?("summarization")
|
563
|
+
optimized << "summarization"
|
564
|
+
additions << "summarization"
|
565
|
+
changes << "Added summarization to process web_search results"
|
566
|
+
end
|
567
|
+
|
568
|
+
if changes.empty?
|
569
|
+
{
|
570
|
+
success: false,
|
571
|
+
reason: "No composition improvements identified",
|
572
|
+
original_capabilities: current_capabilities,
|
573
|
+
optimized_capabilities: current_capabilities
|
574
|
+
}
|
575
|
+
else
|
576
|
+
{
|
577
|
+
success: true,
|
578
|
+
original_capabilities: current_capabilities,
|
579
|
+
optimized_capabilities: optimized,
|
580
|
+
additions: additions,
|
581
|
+
removals: removals,
|
582
|
+
rationale: "Applied composition optimizations: #{changes.join(", ")}"
|
583
|
+
}
|
584
|
+
end
|
585
|
+
end
|
586
|
+
|
587
|
+
# Summarize a result for storage
|
588
|
+
def summarize_result(result)
|
589
|
+
return "No result" unless result
|
590
|
+
|
591
|
+
if result[:error]
|
592
|
+
"Error: #{result[:error]}"
|
593
|
+
else
|
594
|
+
summary = []
|
595
|
+
|
596
|
+
result.each do |key, value|
|
597
|
+
summary << if value.is_a?(String)
|
598
|
+
"#{key}: #{value.length} chars"
|
599
|
+
elsif value.is_a?(Array)
|
600
|
+
"#{key}: #{value.length} items"
|
601
|
+
elsif value.is_a?(Hash)
|
602
|
+
"#{key}: #{value.keys.join(", ")}"
|
603
|
+
else
|
604
|
+
"#{key}: #{value}"
|
605
|
+
end
|
606
|
+
end
|
607
|
+
|
608
|
+
summary.join(", ")
|
609
|
+
end
|
610
|
+
end
|
611
|
+
end
|
612
|
+
end
|
613
|
+
end
|