You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
517 lines
17 KiB
517 lines
17 KiB
import os
|
|
import subprocess
|
|
from typing import List, Optional
|
|
|
|
from loguru import logger
|
|
from pydantic import BaseModel, Field
|
|
from pydantic.v1 import validator
|
|
from swarm_models import OpenAIChat
|
|
from tenacity import (
|
|
retry,
|
|
stop_after_attempt,
|
|
wait_exponential,
|
|
)
|
|
|
|
from swarms.structs.agent import Agent
|
|
from swarms.structs.swarm_router import SwarmRouter, SwarmType
|
|
|
|
logger.add("swarm_builder.log", rotation="10 MB", backtrace=True)
|
|
|
|
|
|
class OpenAIFunctionCaller:
|
|
"""
|
|
A class to interact with the OpenAI API for generating text based on a system prompt and a task.
|
|
|
|
Attributes:
|
|
- system_prompt (str): The system prompt to guide the AI's response.
|
|
- api_key (str): The API key for the OpenAI service.
|
|
- temperature (float): The temperature parameter for the AI model, controlling randomness.
|
|
- base_model (BaseModel): The Pydantic model to parse the response into.
|
|
- max_tokens (int): The maximum number of tokens in the response.
|
|
- client (OpenAI): The OpenAI client instance.
|
|
"""
|
|
|
|
def __init__(
|
|
self,
|
|
system_prompt: str,
|
|
api_key: str,
|
|
temperature: float,
|
|
base_model: BaseModel,
|
|
max_tokens: int = 5000,
|
|
):
|
|
self.system_prompt = system_prompt
|
|
self.api_key = api_key
|
|
self.temperature = temperature
|
|
self.base_model = base_model
|
|
self.max_tokens = max_tokens
|
|
|
|
try:
|
|
from openai import OpenAI
|
|
except ImportError:
|
|
logger.error(
|
|
"OpenAI library not found. Please install the OpenAI library by running 'pip install openai'"
|
|
)
|
|
subprocess.run(["pip", "install", "openai"])
|
|
from openai import OpenAI
|
|
|
|
self.client = OpenAI(api_key=api_key)
|
|
|
|
def run(self, task: str, *args, **kwargs) -> BaseModel:
|
|
"""
|
|
Run the OpenAI model with the system prompt and task to generate a response.
|
|
|
|
Args:
|
|
- task (str): The task to be completed.
|
|
- *args: Additional positional arguments for the OpenAI API.
|
|
- **kwargs: Additional keyword arguments for the OpenAI API.
|
|
|
|
Returns:
|
|
- BaseModel: The parsed response based on the base_model.
|
|
"""
|
|
completion = self.client.beta.chat.completions.parse(
|
|
model="gpt-4o-2024-08-06",
|
|
messages=[
|
|
{"role": "system", "content": self.system_prompt},
|
|
{"role": "user", "content": task},
|
|
],
|
|
response_format=self.base_model,
|
|
temperature=self.temperature,
|
|
max_tokens=self.max_tokens,
|
|
*args,
|
|
**kwargs,
|
|
)
|
|
|
|
return completion.choices[0].message.parsed
|
|
|
|
@retry(
|
|
stop=stop_after_attempt(3),
|
|
wait=wait_exponential(multiplier=1, min=4, max=10),
|
|
)
|
|
async def run_async(
|
|
self, task: str, *args, **kwargs
|
|
) -> BaseModel:
|
|
"""
|
|
Asynchronous version of the run method.
|
|
|
|
Args:
|
|
- task (str): The task to be completed.
|
|
- *args: Additional positional arguments for the OpenAI API.
|
|
- **kwargs: Additional keyword arguments for the OpenAI API.
|
|
|
|
Returns:
|
|
- BaseModel: The parsed response based on the base_model.
|
|
"""
|
|
completion = (
|
|
await self.client.beta.chat.completions.parse_async(
|
|
model="gpt-4o-2024-08-06",
|
|
messages=[
|
|
{"role": "system", "content": self.system_prompt},
|
|
{"role": "user", "content": task},
|
|
],
|
|
response_format=self.base_model,
|
|
temperature=self.temperature,
|
|
max_tokens=self.max_tokens,
|
|
*args,
|
|
**kwargs,
|
|
)
|
|
)
|
|
|
|
return completion.choices[0].message.parsed
|
|
|
|
|
|
BOSS_SYSTEM_PROMPT = """
|
|
Manage a swarm of worker agents to efficiently serve the user by deciding whether to create new agents or delegate tasks. Ensure operations are efficient and effective.
|
|
|
|
### Instructions:
|
|
|
|
1. **Task Assignment**:
|
|
- Analyze available worker agents when a task is presented.
|
|
- Delegate tasks to existing agents with clear, direct, and actionable instructions if an appropriate agent is available.
|
|
- If no suitable agent exists, create a new agent with a fitting system prompt to handle the task.
|
|
|
|
2. **Agent Creation**:
|
|
- Name agents according to the task they are intended to perform (e.g., "Twitter Marketing Agent").
|
|
- Provide each new agent with a concise and clear system prompt that includes its role, objectives, and any tools it can utilize.
|
|
|
|
3. **Efficiency**:
|
|
- Minimize redundancy and maximize task completion speed.
|
|
- Avoid unnecessary agent creation if an existing agent can fulfill the task.
|
|
|
|
4. **Communication**:
|
|
- Be explicit in task delegation instructions to avoid ambiguity and ensure effective task execution.
|
|
- Require agents to report back on task completion or encountered issues.
|
|
|
|
5. **Reasoning and Decisions**:
|
|
- Offer brief reasoning when selecting or creating agents to maintain transparency.
|
|
- Avoid using an agent if unnecessary, with a clear explanation if no agents are suitable for a task.
|
|
|
|
# Output Format
|
|
|
|
Present your plan in clear, bullet-point format or short concise paragraphs, outlining task assignment, agent creation, efficiency strategies, and communication protocols.
|
|
|
|
# Notes
|
|
|
|
- Preserve transparency by always providing reasoning for task-agent assignments and creation.
|
|
- Ensure instructions to agents are unambiguous to minimize error.
|
|
|
|
"""
|
|
|
|
|
|
class AgentConfig(BaseModel):
|
|
"""Configuration for an individual agent in a swarm"""
|
|
|
|
name: str = Field(
|
|
description="The name of the agent",
|
|
)
|
|
description: str = Field(
|
|
description="A description of the agent's purpose and capabilities",
|
|
)
|
|
system_prompt: str = Field(
|
|
description="The system prompt that defines the agent's behavior",
|
|
)
|
|
|
|
|
|
class SwarmConfig(BaseModel):
|
|
"""Configuration for a swarm of cooperative agents"""
|
|
|
|
name: str = Field(
|
|
description="The name of the swarm",
|
|
example="Research-Writing-Swarm",
|
|
)
|
|
description: str = Field(
|
|
description="The description of the swarm's purpose and capabilities",
|
|
example="A swarm of agents that work together to research topics and write articles",
|
|
)
|
|
agents: List[AgentConfig] = Field(
|
|
description="The list of agents that make up the swarm",
|
|
)
|
|
max_loops: int = Field(
|
|
description="The maximum number of loops for the swarm to iterate on",
|
|
)
|
|
|
|
@validator("agents")
|
|
def validate_agents(cls, v):
|
|
if not v:
|
|
raise ValueError("Swarm must have at least one agent")
|
|
return v
|
|
|
|
|
|
class AutoSwarmBuilderOutput(BaseModel):
|
|
"""A class that automatically builds and manages swarms of AI agents with enhanced error handling."""
|
|
|
|
name: Optional[str] = Field(
|
|
description="The name of the swarm",
|
|
example="DefaultSwarm",
|
|
default=None,
|
|
)
|
|
description: Optional[str] = Field(
|
|
description="The description of the swarm's purpose and capabilities",
|
|
example="Generic AI Agent Swarm",
|
|
default=None,
|
|
)
|
|
verbose: Optional[bool] = Field(
|
|
description="Whether to display verbose output",
|
|
default=None,
|
|
)
|
|
model_name: Optional[str] = Field(
|
|
description="The name of the OpenAI model to use",
|
|
default=None,
|
|
)
|
|
boss_output_schema: Optional[list] = Field(
|
|
description="The schema for the output of the BOSS system prompt",
|
|
default=None,
|
|
)
|
|
director_agents_created: Optional[SwarmConfig] = Field(
|
|
description="The agents created by the director",
|
|
default=None,
|
|
)
|
|
swarm_router_outputs: Optional[list] = Field(
|
|
description="The outputs from the swarm router",
|
|
default=None,
|
|
)
|
|
max_loops: Optional[int] = Field(
|
|
description="The maximum number of loops for the swarm to iterate on",
|
|
default=None,
|
|
)
|
|
swarm_type: Optional[SwarmType] = Field(
|
|
description="The type of swarm to build",
|
|
default=None,
|
|
)
|
|
|
|
|
|
class AutoSwarmBuilder:
|
|
"""A class that automatically builds and manages swarms of AI agents with enhanced error handling."""
|
|
|
|
def __init__(
|
|
self,
|
|
name: Optional[str] = "autonomous-swarm-builder",
|
|
description: Optional[
|
|
str
|
|
] = "Given a task, this swarm will automatically create specialized agents and route it to the appropriate agents.",
|
|
verbose: bool = True,
|
|
model_name: str = "gpt-4o",
|
|
boss_output_schema: list = None,
|
|
swarm_router_outputs: AutoSwarmBuilderOutput = None,
|
|
max_loops: int = 1,
|
|
swarm_type: str = "SequentialWorkflow",
|
|
auto_generate_prompts_for_agents: bool = False,
|
|
shared_memory_system: callable = None,
|
|
):
|
|
self.name = name or "DefaultSwarm"
|
|
self.description = description or "Generic AI Agent Swarm"
|
|
self.verbose = verbose
|
|
self.agents_pool = []
|
|
self.api_key = os.getenv("OPENAI_API_KEY")
|
|
self.model_name = model_name
|
|
self.boss_output_schema = boss_output_schema
|
|
self.max_loops = max_loops
|
|
self.swarm_type = swarm_type
|
|
self.auto_generate_prompts_for_agents = (
|
|
auto_generate_prompts_for_agents
|
|
)
|
|
self.shared_memory_system = shared_memory_system
|
|
self.auto_swarm_builder_output = AutoSwarmBuilderOutput(
|
|
name=name,
|
|
description=description,
|
|
verbose=verbose,
|
|
model_name=model_name,
|
|
boss_output_schema=boss_output_schema or [],
|
|
swarm_router_outputs=swarm_router_outputs or [],
|
|
max_loops=max_loops,
|
|
swarm_type=swarm_type,
|
|
)
|
|
|
|
if not self.api_key:
|
|
raise ValueError(
|
|
"OpenAI API key must be provided either through initialization or environment variable"
|
|
)
|
|
|
|
logger.info(
|
|
"Initialized AutoSwarmBuilder",
|
|
extra={
|
|
"swarm_name": self.name,
|
|
"description": self.description,
|
|
"model": self.model_name,
|
|
},
|
|
)
|
|
|
|
# Initialize OpenAI chat model
|
|
try:
|
|
self.chat_model = OpenAIChat(
|
|
openai_api_key=self.api_key,
|
|
model_name=self.model_name,
|
|
)
|
|
except Exception as e:
|
|
logger.error(
|
|
f"Failed to initialize OpenAI chat model: {str(e)}"
|
|
)
|
|
raise
|
|
|
|
def run(
|
|
self,
|
|
task: str,
|
|
image_url: Optional[str] = None,
|
|
*args,
|
|
**kwargs,
|
|
):
|
|
"""Run the swarm on a given task with error handling and retries."""
|
|
if not task or not task.strip():
|
|
raise ValueError("Task cannot be empty")
|
|
|
|
logger.info("Starting swarm execution", extra={"task": task})
|
|
|
|
try:
|
|
# Create agents for the task
|
|
agents = self._create_agents(task)
|
|
if not agents:
|
|
raise ValueError(
|
|
"No agents were created for the task"
|
|
)
|
|
|
|
# Execute the task through the swarm router
|
|
logger.info(
|
|
"Routing task through swarm",
|
|
extra={"num_agents": len(agents)},
|
|
)
|
|
output = self.swarm_router(
|
|
agents=agents,
|
|
task=task,
|
|
image_url=image_url,
|
|
*args,
|
|
**kwargs,
|
|
)
|
|
self.auto_swarm_builder_output.swarm_router_outputs.append(
|
|
output
|
|
)
|
|
print(output)
|
|
|
|
logger.info("Swarm execution completed successfully")
|
|
# return output
|
|
return self.auto_swarm_builder_output.model_dump_json(
|
|
indent=4
|
|
)
|
|
|
|
except Exception as e:
|
|
logger.error(
|
|
f"Error during swarm execution: {str(e)}",
|
|
)
|
|
raise e
|
|
|
|
def _create_agents(
|
|
self,
|
|
task: str,
|
|
) -> List[Agent]:
|
|
"""Create the necessary agents for a task with enhanced error handling."""
|
|
logger.info("Creating agents for task", extra={"task": task})
|
|
|
|
try:
|
|
model = OpenAIFunctionCaller(
|
|
system_prompt=BOSS_SYSTEM_PROMPT,
|
|
api_key=self.api_key,
|
|
temperature=0.1,
|
|
base_model=SwarmConfig,
|
|
)
|
|
|
|
agents_config = model.run(task)
|
|
logger.info(
|
|
f"Director has successfully created agents: {agents_config}"
|
|
)
|
|
self.auto_swarm_builder_output.director_agents_created = (
|
|
agents_config
|
|
)
|
|
|
|
if isinstance(agents_config, dict):
|
|
agents_config = SwarmConfig(**agents_config)
|
|
|
|
# Update swarm configuration
|
|
self.name = agents_config.name
|
|
self.description = agents_config.description
|
|
|
|
# Create agents from configuration
|
|
agents = []
|
|
for agent_config in agents_config.agents:
|
|
if isinstance(agent_config, dict):
|
|
agent_config = AgentConfig(**agent_config)
|
|
|
|
agent = self.build_agent(
|
|
agent_name=agent_config.name,
|
|
agent_description=agent_config.description,
|
|
agent_system_prompt=agent_config.system_prompt,
|
|
)
|
|
agents.append(agent)
|
|
|
|
print(
|
|
f"Agent created: {agent_config.name}: Description: {agent_config.description}"
|
|
)
|
|
|
|
# # Add available agents showcase to system prompts
|
|
# agents_available = showcase_available_agents(
|
|
# name=self.name,
|
|
# description=self.description,
|
|
# agents=agents,
|
|
# )
|
|
|
|
# for agent in agents:
|
|
# agent.system_prompt += "\n" + agents_available
|
|
|
|
logger.info(
|
|
"Successfully created agents",
|
|
extra={"num_agents": len(agents)},
|
|
)
|
|
return agents
|
|
|
|
except Exception as e:
|
|
logger.error(
|
|
f"Error creating agents: {str(e)}", exc_info=True
|
|
)
|
|
raise
|
|
|
|
def build_agent(
|
|
self,
|
|
agent_name: str,
|
|
agent_description: str,
|
|
agent_system_prompt: str,
|
|
*args,
|
|
**kwargs,
|
|
) -> Agent:
|
|
"""Build a single agent with enhanced error handling."""
|
|
logger.info(
|
|
"Building agent", extra={"agent_name": agent_name}
|
|
)
|
|
|
|
try:
|
|
agent = Agent(
|
|
agent_name=agent_name,
|
|
description=agent_description,
|
|
system_prompt=agent_system_prompt,
|
|
llm=self.chat_model,
|
|
verbose=self.verbose,
|
|
dynamic_temperature_enabled=False,
|
|
return_step_meta=False,
|
|
output_type="str",
|
|
streaming_on=True,
|
|
)
|
|
return agent
|
|
|
|
except Exception as e:
|
|
logger.error(
|
|
f"Error building agent: {str(e)}", exc_info=True
|
|
)
|
|
raise
|
|
|
|
@retry(
|
|
stop=stop_after_attempt(3),
|
|
wait=wait_exponential(multiplier=1, min=4, max=10),
|
|
)
|
|
def swarm_router(
|
|
self,
|
|
agents: List[Agent],
|
|
task: str,
|
|
img: Optional[str] = None,
|
|
*args,
|
|
**kwargs,
|
|
) -> str:
|
|
"""Route tasks between agents in the swarm with error handling and retries."""
|
|
logger.info(
|
|
"Initializing swarm router",
|
|
extra={"num_agents": len(agents)},
|
|
)
|
|
|
|
try:
|
|
swarm_router_instance = SwarmRouter(
|
|
name=self.name,
|
|
description=self.description,
|
|
agents=agents,
|
|
swarm_type=self.swarm_type,
|
|
auto_generate_prompts=self.auto_generate_prompts_for_agents,
|
|
)
|
|
|
|
# formatted_task = f"{self.name} {self.description} {task}"
|
|
result = swarm_router_instance.run(
|
|
task=task, *args, **kwargs
|
|
)
|
|
|
|
logger.info("Successfully completed swarm routing")
|
|
return result
|
|
|
|
except Exception as e:
|
|
logger.error(
|
|
f"Error in swarm router: {str(e)}", exc_info=True
|
|
)
|
|
raise
|
|
|
|
|
|
swarm = AutoSwarmBuilder(
|
|
name="ChipDesign-Swarm",
|
|
description="A swarm of specialized AI agents for chip design",
|
|
swarm_type="ConcurrentWorkflow",
|
|
)
|
|
|
|
try:
|
|
result = swarm.run(
|
|
"Design a new AI accelerator chip optimized for transformer model inference..."
|
|
)
|
|
print(result)
|
|
except Exception as e:
|
|
print(f"An error occurred: {e}")
|