Module: Legion::Extensions::Agentic::Executive::GoalManagement::Helpers::Decomposer
- Extended by:
- JSON::Helper
- Defined in:
- lib/legion/extensions/agentic/executive/goal_management/helpers/decomposer.rb
Constant Summary collapse
- DOMAIN_TEMPLATES =
{ safety: ->(c) { ["diagnose: #{c}", "implement fix for: #{c}", "verify health after: #{c}"] }, cognition: ->(c) { ["analyze gaps in: #{c}", "design approach for: #{c}", "validate: #{c}"] }, perception: ->(c) { ["observe current state of: #{c}", "identify patterns in: #{c}", "calibrate: #{c}"] } }.freeze
Class Method Summary collapse
- .build_decomposition_prompt(goal) ⇒ Object
- .decompose(goal:, strategy: :heuristic) ⇒ Object
- .decompose_by_domain(content, domain) ⇒ Object
- .decompose_heuristic(goal) ⇒ Object
- .decompose_with_llm(goal) ⇒ Object
- .default_steps(content) ⇒ Object
- .extract_response_content(response, prompt) ⇒ Object
- .log ⇒ Object
- .parse_sub_goals(content, domain) ⇒ Object
Class Method Details
.build_decomposition_prompt(goal) ⇒ Object
76 77 78 79 80 81 82 83 84 |
# File 'lib/legion/extensions/agentic/executive/goal_management/helpers/decomposer.rb', line 76 def build_decomposition_prompt(goal) <<~PROMPT Decompose this goal into 2-5 concrete sub-goals. Return JSON array. Goal: #{goal[:content]} Domain: #{goal[:domain]} Each sub-goal: {"content": "action description", "domain": "#{goal[:domain]}", "priority": 0.0-1.0} Return ONLY the JSON array, no other text. PROMPT end |
.decompose(goal:, strategy: :heuristic) ⇒ Object
24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
# File 'lib/legion/extensions/agentic/executive/goal_management/helpers/decomposer.rb', line 24 def decompose(goal:, strategy: :heuristic) sub_goals, strategy_used = case strategy when :llm result = decompose_with_llm(goal) result ? [result, :llm] : [decompose_heuristic(goal), :heuristic] else [decompose_heuristic(goal), :heuristic] end log.info "[decomposer] decomposed goal=#{goal[:id]} strategy=#{strategy_used}" { success: true, sub_goals: sub_goals, strategy_used: strategy_used } rescue StandardError => e log.error "Decomposer: #{e.}" { success: false, error: e. } end |
.decompose_by_domain(content, domain) ⇒ Object
60 61 62 63 64 65 |
# File 'lib/legion/extensions/agentic/executive/goal_management/helpers/decomposer.rb', line 60 def decompose_by_domain(content, domain) template = DOMAIN_TEMPLATES[domain] return nil unless template template.call(content) end |
.decompose_heuristic(goal) ⇒ Object
39 40 41 42 43 44 |
# File 'lib/legion/extensions/agentic/executive/goal_management/helpers/decomposer.rb', line 39 def decompose_heuristic(goal) content = goal[:content].to_s.downcase domain = (goal[:domain] || :general).to_sym steps = decompose_by_domain(content, domain) || default_steps(content) steps.map { |step| { content: step, domain: domain, priority: goal[:priority] || 0.5 } } end |
.decompose_with_llm(goal) ⇒ Object
46 47 48 49 50 51 52 53 54 55 56 57 58 |
# File 'lib/legion/extensions/agentic/executive/goal_management/helpers/decomposer.rb', line 46 def decompose_with_llm(goal) return nil unless defined?(Legion::LLM) && Legion::LLM.respond_to?(:chat) prompt = build_decomposition_prompt(goal) response = Legion::LLM.chat( message: prompt, caller: { extension: 'lex-agentic-executive', operation: 'decompose' } ) parse_sub_goals(extract_response_content(response, prompt), goal[:domain]) rescue StandardError => e log.error "Decomposer#decompose_with_llm: #{e.}" nil end |
.default_steps(content) ⇒ Object
67 68 69 70 71 72 73 74 |
# File 'lib/legion/extensions/agentic/executive/goal_management/helpers/decomposer.rb', line 67 def default_steps(content) [ "analyze current state of: #{content}", "plan approach for: #{content}", "execute: #{content}", "verify result of: #{content}" ] end |
.extract_response_content(response, prompt) ⇒ Object
105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 |
# File 'lib/legion/extensions/agentic/executive/goal_management/helpers/decomposer.rb', line 105 def extract_response_content(response, prompt) return response.strip if response.is_a?(String) return response.content if response.respond_to?(:content) if response.respond_to?(:ask) asked = response.ask(prompt) return extract_response_content(asked, prompt) end return nil unless response.is_a?(Hash) response[:content] || response['content'] || response.dig(:message, :content) || response.dig('message', 'content') || response[:response] || response['response'] end |
.log ⇒ Object
14 15 16 |
# File 'lib/legion/extensions/agentic/executive/goal_management/helpers/decomposer.rb', line 14 def log Legion::Logging end |
.parse_sub_goals(content, domain) ⇒ Object
86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 |
# File 'lib/legion/extensions/agentic/executive/goal_management/helpers/decomposer.rb', line 86 def parse_sub_goals(content, domain) return nil unless content cleaned = content.gsub(/```(?:json)?\s*\n?/, '').strip data = json_load(cleaned) return nil unless data.is_a?(Array) && !data.empty? data.map do |sg| { content: sg[:content].to_s, domain: (sg[:domain] || domain).to_sym, priority: (sg[:priority] || 0.5).to_f.clamp(0.0, 1.0) } end rescue StandardError => e log.error "Decomposer#parse_sub_goals: #{e.}" nil end |