Class: Agentic::AgentAssemblyEngine

Inherits:
Object
  • Object
show all
Defined in:
lib/agentic/agent_assembly_engine.rb

Overview

Engine for assembling agents based on task requirements

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(registry = AgentCapabilityRegistry.instance, agent_store = nil) ⇒ AgentAssemblyEngine

Initialize a new agent assembly engine

Parameters:



13
14
15
16
# File 'lib/agentic/agent_assembly_engine.rb', line 13

def initialize(registry = AgentCapabilityRegistry.instance, agent_store = nil)
  @registry = registry
  @agent_store = agent_store
end

Instance Attribute Details

#agent_storePersistentAgentStore? (readonly)

The agent store for persistence

Returns:



7
8
9
# File 'lib/agentic/agent_assembly_engine.rb', line 7

def agent_store
  @agent_store
end

#registryAgentCapabilityRegistry (readonly)

The capability registry

Returns:



7
8
9
# File 'lib/agentic/agent_assembly_engine.rb', line 7

def registry
  @registry
end

Instance Method Details

#analyze_requirements(task) ⇒ Hash

Analyze task requirements to determine needed capabilities

Parameters:

  • task (Task)

    The task to analyze

Returns:

  • (Hash)

    The capability requirements



57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
# File 'lib/agentic/agent_assembly_engine.rb', line 57

def analyze_requirements(task)
  # Extract requirements from the task description and agent specification
  requirements = {}

  # Use the task description to infer capabilities
  infer_capabilities_from_description(task.description, requirements)

  # Use the agent specification to infer capabilities
  if task.agent_spec
    infer_capabilities_from_agent_spec(task.agent_spec, requirements)
  end

  # Use the task input to infer capabilities
  if task.input && !task.input.empty?
    infer_capabilities_from_input(task.input, requirements)
  end

  requirements
end

#assemble_agent(task, strategy: nil, store: true) ⇒ Agent

Assemble an agent for a task

Parameters:

  • task (Task)

    The task to assemble an agent for

  • strategy (AgentCompositionStrategy, nil) (defaults to: nil)

    The strategy to use for assembly

  • store (Boolean) (defaults to: true)

    Whether to use the agent store for finding and storing agents

Returns:

  • (Agent)

    The assembled agent



23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
# File 'lib/agentic/agent_assembly_engine.rb', line 23

def assemble_agent(task, strategy: nil, store: true)
  # Check if we should try to find an existing agent in the store
  if store && @agent_store
    existing_agent = find_suitable_agent(task)
    if existing_agent
      Agentic.logger.info("Using existing agent from store for task: #{task.id}")
      return existing_agent
    end
  end

  # Use the default strategy if none provided
  strategy ||= DefaultCompositionStrategy.new

  # Analyze task requirements
  requirements = analyze_requirements(task)

  # Select capabilities based on requirements
  capabilities = select_capabilities(requirements, strategy)

  # Create a new agent with the selected capabilities
  agent = build_agent(task, capabilities)

  # Store the assembled agent if requested
  if store && @agent_store
    store_agent(agent, task, requirements)
  end

  # Return the assembled agent
  agent
end

#build_agent(task, capabilities) ⇒ Agent

Build an agent with the selected capabilities

Parameters:

  • task (Task)

    The task the agent will perform

  • capabilities (Array<Hash>)

    The selected capabilities

Returns:

  • (Agent)

    The assembled agent



90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
# File 'lib/agentic/agent_assembly_engine.rb', line 90

def build_agent(task, capabilities)
  # Create a new agent
  agent = Agent.build do |a|
    # Set basic agent properties from the task agent spec
    if task.agent_spec
      a.role = task.agent_spec.name
      a.purpose = task.agent_spec.description
      a.backstory = task.agent_spec.instructions
    end
  end

  # Add capabilities to the agent
  capabilities.each do |capability_info|
    agent.add_capability(capability_info[:name], capability_info[:version])
  rescue => e
    Agentic.logger.warn("Failed to add capability: #{capability_info[:name]} v#{capability_info[:version]} - #{e.message}")
  end

  agent
end

#find_suitable_agent(task) ⇒ Agent?

Find a suitable agent in the store for a task

Parameters:

  • task (Task)

    The task to find an agent for

Returns:

  • (Agent, nil)

    A suitable agent or nil if none found



114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
# File 'lib/agentic/agent_assembly_engine.rb', line 114

def find_suitable_agent(task)
  return nil unless @agent_store

  # Analyze task requirements
  requirements = analyze_requirements(task)

  # Get required capabilities
  required_capabilities = requirements.keys
  return nil if required_capabilities.empty?

  # Find agents with matching capabilities
  matching_agents = []

  # Start with the most important capabilities
  primary_capabilities = requirements.select { |_, info| info[:importance] >= 0.8 }.keys
  return nil if primary_capabilities.empty?

  # Find agents with the primary capabilities
  primary_capabilities.each do |capability|
    # Find agents with this capability
    agents = @agent_store.all(capability: capability)

    # Add to matching agents
    matching_agents.concat(agents)
  end

  # Return nil if no matching agents found
  return nil if matching_agents.empty?

  # Score each agent based on how well it matches the requirements
  scored_agents = matching_agents.map do |agent_config|
    score = calculate_agent_match_score(agent_config, requirements)
    {config: agent_config, score: score}
  end

  # Sort by score (highest first)
  scored_agents.sort_by! { |a| -a[:score] }

  # Get the best match
  best_match = scored_agents.first

  # If the best match has a score below threshold, don't use it
  return nil if best_match[:score] < 0.5

  # Build the agent from the stored configuration
  @agent_store.build_agent(best_match[:config][:id])
end

#select_capabilities(requirements, strategy) ⇒ Array<Hash>

Select capabilities based on requirements

Parameters:

  • requirements (Hash)

    The capability requirements

  • strategy (AgentCompositionStrategy)

    The strategy to use for selection

Returns:

  • (Array<Hash>)

    The selected capabilities



81
82
83
84
# File 'lib/agentic/agent_assembly_engine.rb', line 81

def select_capabilities(requirements, strategy)
  # Use the strategy to select capabilities
  strategy.select_capabilities(requirements, @registry)
end

#store_agent(agent, task, requirements) ⇒ String?

Store an assembled agent in the persistent store

Parameters:

  • agent (Agent)

    The agent to store

  • task (Task)

    The task the agent was assembled for

  • requirements (Hash)

    The capability requirements

Returns:

  • (String, nil)

    The ID of the stored agent or nil if not stored



167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
# File 'lib/agentic/agent_assembly_engine.rb', line 167

def store_agent(agent, task, requirements)
  return nil unless @agent_store

  # Generate a name for the agent based on the task
  name = generate_agent_name(task)

  # Generate metadata for the agent
   = {
    task_id: task.id,
    task_description: task.description,
    assembled_at: Time.now.iso8601,
    requirements: requirements,
    assembly_engine_version: "1.0.0"  # Add version tracking
  }

  # Store the agent
  @agent_store.store(agent, name: name, metadata: )
end