commit
87a80f936b
@ -0,0 +1,978 @@
|
||||
## Building Analyst Agents with Swarms to write Business Reports
|
||||
|
||||
> Jupyter Notebook accompanying this post is accessible at: [Business Analyst Agent Notebook](https://github.com/kyegomez/swarms/blob/master/playground/business-analyst-agent.ipynb)
|
||||
|
||||
Solving a business problem often involves preparing a Business Case Report. This report comprehensively analyzes the problem, evaluates potential solutions, and provides evidence-based recommendations and an implementation plan to effectively address the issue and drive business value. While the process of preparing one requires an experienced business analyst, the workflow can be augmented using AI agents. Two candidates stick out as areas to work on:
|
||||
|
||||
- Developing an outline to solve the problem
|
||||
- Doing background research and gathering data
|
||||
|
||||
In this post, we will explore how Swarms agents can be used to tackle a busuiness problem by outlining the solution, conducting background research and generating a preliminary report.
|
||||
|
||||
Before we proceed, this blog uses 3 API tools. Please obtain the following keys and store them in a `.env` file in the same folder as this file.
|
||||
|
||||
- **[OpenAI API](https://openai.com/blog/openai-api)** as `OPENAI_API_KEY`
|
||||
- **[TavilyAI API](https://app.tavily.com/home)** `TAVILY_API_KEY`
|
||||
- **[KayAI API](https://www.kay.ai/)** as `KAY_API_KEY`
|
||||
|
||||
```python
|
||||
import dotenv
|
||||
dotenv.load_dotenv() # Load environment variables from .env file
|
||||
```
|
||||
|
||||
### Developing an Outline to solve the problem
|
||||
|
||||
Assume the business problem is: **How do we improve Nike's revenue in Q3 2024?** We first create a planning agent to break down the problem into dependent sub-problems.
|
||||
|
||||
|
||||
#### Step 1. Defining the Data Model and Tool Schema
|
||||
|
||||
Using Pydantic, we define a structure to help the agent generate sub-problems.
|
||||
|
||||
- **QueryType:** Questions are either standalone or involve a combination of multiple others
|
||||
- **Query:** Defines structure of a question.
|
||||
- **QueryPlan:** Allows generation of a dependency graph of sub-questions
|
||||
|
||||
|
||||
```python
|
||||
import enum
|
||||
from typing import List
|
||||
from pydantic import Field, BaseModel
|
||||
|
||||
class QueryType(str, enum.Enum):
|
||||
"""Enumeration representing the types of queries that can be asked to a question answer system."""
|
||||
|
||||
SINGLE_QUESTION = "SINGLE"
|
||||
MERGE_MULTIPLE_RESPONSES = "MERGE_MULTIPLE_RESPONSES"
|
||||
|
||||
class Query(BaseModel):
|
||||
"""Class representing a single question in a query plan."""
|
||||
|
||||
id: int = Field(..., description="Unique id of the query")
|
||||
question: str = Field(
|
||||
...,
|
||||
description="Question asked using a question answering system",
|
||||
)
|
||||
dependencies: List[int] = Field(
|
||||
default_factory=list,
|
||||
description="List of sub questions that need to be answered before asking this question",
|
||||
)
|
||||
node_type: QueryType = Field(
|
||||
default=QueryType.SINGLE_QUESTION,
|
||||
description="Type of question, either a single question or a multi-question merge",
|
||||
)
|
||||
|
||||
class QueryPlan(BaseModel):
|
||||
"""Container class representing a tree of questions to ask a question answering system."""
|
||||
|
||||
query_graph: List[Query] = Field(
|
||||
..., description="The query graph representing the plan"
|
||||
)
|
||||
|
||||
def _dependencies(self, ids: List[int]) -> List[Query]:
|
||||
"""Returns the dependencies of a query given their ids."""
|
||||
|
||||
return [q for q in self.query_graph if q.id in ids]
|
||||
```
|
||||
|
||||
Also, a `tool_schema` needs to be defined. It is an instance of `QueryPlan` and is used to initialize the agent.
|
||||
|
||||
```python
|
||||
tool_schema = QueryPlan(
|
||||
query_graph = [query.dict() for query in [
|
||||
Query(
|
||||
id=1,
|
||||
question="How do we improve Nike's revenue in Q3 2024?",
|
||||
dependencies=[2],
|
||||
node_type=QueryType('SINGLE')
|
||||
),
|
||||
# ... other queries ...
|
||||
]]
|
||||
)
|
||||
```
|
||||
|
||||
#### Step 2. Defining the Planning Agent
|
||||
|
||||
We specify the query, task specification and an appropriate system prompt.
|
||||
|
||||
```python
|
||||
from swarms import OpenAIChat
|
||||
from swarms import Agent
|
||||
|
||||
query = "How do we improve Nike's revenue in Q3 2024?"
|
||||
task = f"Consider: {query}. Generate just the correct query plan in JSON format."
|
||||
system_prompt = (
|
||||
"You are a world class query planning algorithm "
|
||||
"capable of breaking apart questions into its "
|
||||
"dependency queries such that the answers can be "
|
||||
"used to inform the parent question. Do not answer "
|
||||
"the questions, simply provide a correct compute "
|
||||
"graph with good specific questions to ask and relevant "
|
||||
"dependencies. Before you call the function, think "
|
||||
"step-by-step to get a better understanding of the problem."
|
||||
)
|
||||
llm = OpenAIChat(
|
||||
temperature=0.0, model_name="gpt-4", max_tokens=4000
|
||||
)
|
||||
```
|
||||
|
||||
Then, we proceed with agent definition.
|
||||
|
||||
```python
|
||||
# Initialize the agent
|
||||
agent = Agent(
|
||||
agent_name="Query Planner",
|
||||
system_prompt=system_prompt,
|
||||
# Set the tool schema to the JSON string -- this is the key difference
|
||||
tool_schema=tool_schema,
|
||||
llm=llm,
|
||||
max_loops=1,
|
||||
autosave=True,
|
||||
dashboard=False,
|
||||
streaming_on=True,
|
||||
verbose=True,
|
||||
interactive=False,
|
||||
# Set the output type to the tool schema which is a BaseModel
|
||||
output_type=tool_schema, # or dict, or str
|
||||
metadata_output_type="json",
|
||||
# List of schemas that the agent can handle
|
||||
list_tool_schemas=[tool_schema],
|
||||
function_calling_format_type="OpenAI",
|
||||
function_calling_type="json", # or soon yaml
|
||||
)
|
||||
```
|
||||
|
||||
#### Step 3. Obtaining Outline from Planning Agent
|
||||
|
||||
We now run the agent, and since its output is in JSON format, we can load it as a dictionary.
|
||||
|
||||
```python
|
||||
generated_data = agent.run(task)
|
||||
```
|
||||
|
||||
At times the agent could return extra content other than JSON. Below function will filter it out.
|
||||
|
||||
```python
|
||||
def process_json_output(content):
|
||||
# Find the index of the first occurrence of '```json\n'
|
||||
start_index = content.find('```json\n')
|
||||
if start_index == -1:
|
||||
# If '```json\n' is not found, return the original content
|
||||
return content
|
||||
# Return the part of the content after '```json\n' and remove the '```' at the end
|
||||
return content[start_index + len('```json\n'):].rstrip('`')
|
||||
|
||||
# Use the function to clean up the output
|
||||
json_content = process_json_output(generated_data.content)
|
||||
|
||||
import json
|
||||
|
||||
# Load the JSON string into a Python object
|
||||
json_object = json.loads(json_content)
|
||||
|
||||
# Convert the Python object back to a JSON string
|
||||
json_content = json.dumps(json_object, indent=2)
|
||||
|
||||
# Print the JSON string
|
||||
print(json_content)
|
||||
```
|
||||
|
||||
Below is the output this produces
|
||||
|
||||
```json
|
||||
{
|
||||
"main_query": "How do we improve Nike's revenue in Q3 2024?",
|
||||
"sub_queries": [
|
||||
{
|
||||
"id": "1",
|
||||
"query": "What is Nike's current revenue trend?"
|
||||
},
|
||||
{
|
||||
"id": "2",
|
||||
"query": "What are the projected market trends for the sports apparel industry in 2024?"
|
||||
},
|
||||
{
|
||||
"id": "3",
|
||||
"query": "What are the current successful strategies being used by Nike's competitors?",
|
||||
"dependencies": [
|
||||
"2"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "4",
|
||||
"query": "What are the current and projected economic conditions in Nike's major markets?",
|
||||
"dependencies": [
|
||||
"2"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "5",
|
||||
"query": "What are the current consumer preferences in the sports apparel industry?",
|
||||
"dependencies": [
|
||||
"2"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "6",
|
||||
"query": "What are the potential areas of improvement in Nike's current business model?",
|
||||
"dependencies": [
|
||||
"1"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "7",
|
||||
"query": "What are the potential new markets for Nike to explore in 2024?",
|
||||
"dependencies": [
|
||||
"2",
|
||||
"4"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "8",
|
||||
"query": "What are the potential new products or services Nike could introduce in 2024?",
|
||||
"dependencies": [
|
||||
"5"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "9",
|
||||
"query": "What are the potential marketing strategies Nike could use to increase its revenue in Q3 2024?",
|
||||
"dependencies": [
|
||||
"3",
|
||||
"5",
|
||||
"7",
|
||||
"8"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "10",
|
||||
"query": "What are the potential cost-saving strategies Nike could implement to increase its net revenue in Q3 2024?",
|
||||
"dependencies": [
|
||||
"6"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
The JSON dictionary is not convenient for humans to process. We make a directed graph out of it.
|
||||
|
||||
```python
|
||||
import networkx as nx
|
||||
import matplotlib.pyplot as plt
|
||||
import textwrap
|
||||
import random
|
||||
|
||||
# Create a directed graph
|
||||
G = nx.DiGraph()
|
||||
|
||||
# Define a color map
|
||||
color_map = {}
|
||||
|
||||
# Add nodes and edges to the graph
|
||||
for sub_query in json_object['sub_queries']:
|
||||
# Check if 'dependencies' key exists in sub_query, if not, initialize it as an empty list
|
||||
if 'dependencies' not in sub_query:
|
||||
sub_query['dependencies'] = []
|
||||
# Assign a random color for each node
|
||||
color_map[sub_query['id']] = "#{:06x}".format(random.randint(0, 0xFFFFFF))
|
||||
G.add_node(sub_query['id'], label=textwrap.fill(sub_query['query'], width=20))
|
||||
for dependency in sub_query['dependencies']:
|
||||
G.add_edge(dependency, sub_query['id'])
|
||||
|
||||
# Draw the graph
|
||||
pos = nx.spring_layout(G)
|
||||
nx.draw(G, pos, with_labels=True, node_size=800, node_color=[color_map[node] for node in G.nodes()], node_shape="o", alpha=0.5, linewidths=40)
|
||||
|
||||
# Prepare labels for legend
|
||||
labels = nx.get_node_attributes(G, 'label')
|
||||
handles = [plt.Line2D([0], [0], marker='o', color=color_map[node], label=f"{node}: {label}", markersize=10, linestyle='None') for node, label in labels.items()]
|
||||
|
||||
# Create a legend
|
||||
plt.legend(handles=handles, title="Queries", bbox_to_anchor=(1.05, 1), loc='upper left')
|
||||
|
||||
plt.show()
|
||||
```
|
||||
|
||||
This produces the below diagram which makes the plan much more convenient to understand.
|
||||
|
||||

|
||||
|
||||
### Doing Background Research and Gathering Data
|
||||
|
||||
At this point, we have solved the first half of the problem. We have an outline consisting of sub-problems to to tackled to solve our business problem. This will form the overall structure of our report. We now need to research information for each sub-problem in order to write an informed report. This mechanically intensive and is the aspect that will most benefit from Agentic intervention.
|
||||
|
||||
Essentially, we can spawn parallel agents to gather the data. Each agent will have 2 tools:
|
||||
|
||||
- Internet access
|
||||
- Financial data retrieval
|
||||
|
||||
As they run parallely, they will add their knowledge into a common long-term memory. We will then spawn a separate report writing agent with access to this memory to generate our business case report.
|
||||
|
||||
#### Step 4. Defining Tools for Worker Agents
|
||||
|
||||
Let us first define the 2 tools.
|
||||
|
||||
```python
|
||||
import os
|
||||
from typing import List, Dict
|
||||
|
||||
from swarms import tool
|
||||
|
||||
os.environ['TAVILY_API_KEY'] = os.getenv('TAVILY_API_KEY')
|
||||
os.environ["KAY_API_KEY"] = os.getenv('KAY_API_KEY')
|
||||
|
||||
from langchain_community.tools.tavily_search import TavilySearchResults
|
||||
from langchain_core.pydantic_v1 import BaseModel, Field
|
||||
|
||||
from kay.rag.retrievers import KayRetriever
|
||||
|
||||
@tool
|
||||
def browser(query: str) -> str:
|
||||
"""
|
||||
Search the query in the browser with the Tavily API tool.
|
||||
Args:
|
||||
query (str): The query to search in the browser.
|
||||
Returns:
|
||||
str: The search results
|
||||
"""
|
||||
internet_search = TavilySearchResults()
|
||||
results = internet_search.invoke({"query": query})
|
||||
response = ''
|
||||
for result in results:
|
||||
response += (result['content'] + '\n')
|
||||
return response
|
||||
|
||||
@tool
|
||||
def kay_retriever(query: str) -> str:
|
||||
"""
|
||||
Search the financial data query with the KayAI API tool.
|
||||
Args:
|
||||
query (str): The query to search in the KayRetriever.
|
||||
Returns:
|
||||
str: The first context retrieved as a string.
|
||||
"""
|
||||
# Initialize the retriever
|
||||
retriever = KayRetriever(dataset_id = "company", data_types=["10-K", "10-Q", "8-K", "PressRelease"])
|
||||
# Query the retriever
|
||||
context = retriever.query(query=query,num_context=1)
|
||||
return context[0]['chunk_embed_text']
|
||||
```
|
||||
|
||||
#### Step 5. Defining Long-Term Memory
|
||||
|
||||
As mentioned previously, the worker agents running parallely, will pool their knowledge into a common memory. Let us define that.
|
||||
|
||||
```python
|
||||
import logging
|
||||
import os
|
||||
import uuid
|
||||
from typing import Callable, List, Optional
|
||||
|
||||
import chromadb
|
||||
import numpy as np
|
||||
from dotenv import load_dotenv
|
||||
|
||||
from swarms.utils.data_to_text import data_to_text
|
||||
from swarms.utils.markdown_message import display_markdown_message
|
||||
from swarms.memory.base_vectordb import AbstractVectorDatabase
|
||||
|
||||
|
||||
# Results storage using local ChromaDB
|
||||
class ChromaDB(AbstractVectorDatabase):
|
||||
"""
|
||||
|
||||
ChromaDB database
|
||||
|
||||
Args:
|
||||
metric (str): The similarity metric to use.
|
||||
output (str): The name of the collection to store the results in.
|
||||
limit_tokens (int, optional): The maximum number of tokens to use for the query. Defaults to 1000.
|
||||
n_results (int, optional): The number of results to retrieve. Defaults to 2.
|
||||
|
||||
Methods:
|
||||
add: _description_
|
||||
query: _description_
|
||||
|
||||
Examples:
|
||||
>>> chromadb = ChromaDB(
|
||||
>>> metric="cosine",
|
||||
>>> output="results",
|
||||
>>> llm="gpt3",
|
||||
>>> openai_api_key=OPENAI_API_KEY,
|
||||
>>> )
|
||||
>>> chromadb.add(task, result, result_id)
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
metric: str = "cosine",
|
||||
output_dir: str = "swarms",
|
||||
limit_tokens: Optional[int] = 1000,
|
||||
n_results: int = 3,
|
||||
embedding_function: Callable = None,
|
||||
docs_folder: str = None,
|
||||
verbose: bool = False,
|
||||
*args,
|
||||
**kwargs,
|
||||
):
|
||||
self.metric = metric
|
||||
self.output_dir = output_dir
|
||||
self.limit_tokens = limit_tokens
|
||||
self.n_results = n_results
|
||||
self.docs_folder = docs_folder
|
||||
self.verbose = verbose
|
||||
|
||||
# Disable ChromaDB logging
|
||||
if verbose:
|
||||
logging.getLogger("chromadb").setLevel(logging.INFO)
|
||||
|
||||
# Create Chroma collection
|
||||
chroma_persist_dir = "chroma"
|
||||
chroma_client = chromadb.PersistentClient(
|
||||
settings=chromadb.config.Settings(
|
||||
persist_directory=chroma_persist_dir,
|
||||
),
|
||||
*args,
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
# Embedding model
|
||||
if embedding_function:
|
||||
self.embedding_function = embedding_function
|
||||
else:
|
||||
self.embedding_function = None
|
||||
|
||||
# Create ChromaDB client
|
||||
self.client = chromadb.Client()
|
||||
|
||||
# Create Chroma collection
|
||||
self.collection = chroma_client.get_or_create_collection(
|
||||
name=output_dir,
|
||||
metadata={"hnsw:space": metric},
|
||||
embedding_function=self.embedding_function,
|
||||
# data_loader=self.data_loader,
|
||||
*args,
|
||||
**kwargs,
|
||||
)
|
||||
display_markdown_message(
|
||||
"ChromaDB collection created:"
|
||||
f" {self.collection.name} with metric: {self.metric} and"
|
||||
f" output directory: {self.output_dir}"
|
||||
)
|
||||
|
||||
# If docs
|
||||
if docs_folder:
|
||||
display_markdown_message(
|
||||
f"Traversing directory: {docs_folder}"
|
||||
)
|
||||
self.traverse_directory()
|
||||
|
||||
def add(
|
||||
self,
|
||||
document: str,
|
||||
*args,
|
||||
**kwargs,
|
||||
):
|
||||
"""
|
||||
Add a document to the ChromaDB collection.
|
||||
|
||||
Args:
|
||||
document (str): The document to be added.
|
||||
condition (bool, optional): The condition to check before adding the document. Defaults to True.
|
||||
|
||||
Returns:
|
||||
str: The ID of the added document.
|
||||
"""
|
||||
try:
|
||||
doc_id = str(uuid.uuid4())
|
||||
self.collection.add(
|
||||
ids=[doc_id],
|
||||
documents=[document],
|
||||
*args,
|
||||
**kwargs,
|
||||
)
|
||||
print('-----------------')
|
||||
print("Document added successfully")
|
||||
print('-----------------')
|
||||
return doc_id
|
||||
except Exception as e:
|
||||
raise Exception(f"Failed to add document: {str(e)}")
|
||||
|
||||
def query(
|
||||
self,
|
||||
query_text: str,
|
||||
*args,
|
||||
**kwargs,
|
||||
):
|
||||
"""
|
||||
Query documents from the ChromaDB collection.
|
||||
|
||||
Args:
|
||||
query (str): The query string.
|
||||
n_docs (int, optional): The number of documents to retrieve. Defaults to 1.
|
||||
|
||||
Returns:
|
||||
dict: The retrieved documents.
|
||||
"""
|
||||
try:
|
||||
docs = self.collection.query(
|
||||
query_texts=[query_text],
|
||||
n_results=self.n_results,
|
||||
*args,
|
||||
**kwargs,
|
||||
)["documents"]
|
||||
return docs[0]
|
||||
except Exception as e:
|
||||
raise Exception(f"Failed to query documents: {str(e)}")
|
||||
|
||||
def traverse_directory(self):
|
||||
"""
|
||||
Traverse through every file in the given directory and its subdirectories,
|
||||
and return the paths of all files.
|
||||
Parameters:
|
||||
- directory_name (str): The name of the directory to traverse.
|
||||
Returns:
|
||||
- list: A list of paths to each file in the directory and its subdirectories.
|
||||
"""
|
||||
added_to_db = False
|
||||
|
||||
for root, dirs, files in os.walk(self.docs_folder):
|
||||
for file in files:
|
||||
file = os.path.join(self.docs_folder, file)
|
||||
_, ext = os.path.splitext(file)
|
||||
data = data_to_text(file)
|
||||
added_to_db = self.add([data])
|
||||
print(f"{file} added to Database")
|
||||
|
||||
return added_to_db
|
||||
```
|
||||
|
||||
We can now proceed to initialize the memory.
|
||||
|
||||
```python
|
||||
from chromadb.utils import embedding_functions
|
||||
default_ef = embedding_functions.DefaultEmbeddingFunction()
|
||||
|
||||
memory = ChromaDB(
|
||||
metric="cosine",
|
||||
n_results=3,
|
||||
output_dir="results",
|
||||
embedding_function=default_ef
|
||||
)
|
||||
```
|
||||
|
||||
#### Step 6. Defining Worker Agents
|
||||
|
||||
The Worker Agent sub-classes the `Agent` class. The only different between these 2 is in how the `run()` method works. In the `Agent` class, `run()` simply returns the set of tool commands to run, but does not execute it. We, however, desire this. In addition, after we run our tools, we get the relevant information as output. We want to add this information to our memory. Hence, to incorporate these 2 changes, we define `WorkerAgent` as follows.
|
||||
|
||||
```python
|
||||
class WorkerAgent(Agent):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
def run(self, task, *args, **kwargs):
|
||||
response = super().run(task, *args, **kwargs)
|
||||
print(response.content)
|
||||
|
||||
json_dict = json.loads(process_json_output(response.content))
|
||||
|
||||
#print(json.dumps(json_dict, indent=2))
|
||||
|
||||
if response!=None:
|
||||
try:
|
||||
commands = json_dict["commands"]
|
||||
except:
|
||||
commands = [json_dict['command']]
|
||||
|
||||
for command in commands:
|
||||
tool_name = command["name"]
|
||||
|
||||
if tool_name not in ['browser', 'kay_retriever']:
|
||||
continue
|
||||
|
||||
query = command["args"]["query"]
|
||||
|
||||
# Get the tool by its name
|
||||
tool = globals()[tool_name]
|
||||
tool_response = tool(query)
|
||||
|
||||
# Add tool's output to long term memory
|
||||
self.long_term_memory.add(tool_response)
|
||||
```
|
||||
|
||||
We can then instantiate an object of the `WorkerAgent` class.
|
||||
|
||||
```python
|
||||
worker_agent = WorkerAgent(
|
||||
agent_name="Worker Agent",
|
||||
system_prompt=(
|
||||
"Autonomous agent that can interact with browser, "
|
||||
"financial data retriever and other agents. Be Helpful "
|
||||
"and Kind. Use the tools provided to assist the user. "
|
||||
"Generate the plan with list of commands in JSON format."
|
||||
),
|
||||
llm=OpenAIChat(
|
||||
temperature=0.0, model_name="gpt-4", max_tokens=4000
|
||||
),
|
||||
max_loops="auto",
|
||||
autosave=True,
|
||||
dashboard=False,
|
||||
streaming_on=True,
|
||||
verbose=True,
|
||||
stopping_token="<DONE>",
|
||||
interactive=True,
|
||||
tools=[browser, kay_retriever],
|
||||
long_term_memory=memory,
|
||||
code_interpreter=True,
|
||||
)
|
||||
```
|
||||
|
||||
#### Step 7. Running the Worker Agents
|
||||
|
||||
At this point, we need to setup a concurrent workflow. While the order of adding tasks to the workflow doesn't matter (since they will all run concurrently late when executed), we can take some time to define an order for these tasks. This order will come in handy later when writing the report using our Writer Agent.
|
||||
|
||||
The order we will follow is Breadth First Traversal (BFT) of the sub-queries in the graph we had made earlier (shown below again for reference). BFT makes sense to be used here because we want all the dependent parent questions to be answered before answering the child question. Also, since we could have independent subgraphs, we will also perform BFT separately on each subgraph.
|
||||
|
||||

|
||||
|
||||
Below is the code that produces the order of processing sub-queries.
|
||||
|
||||
```python
|
||||
from collections import deque, defaultdict
|
||||
|
||||
# Define the graph nodes
|
||||
nodes = json_object['sub_queries']
|
||||
|
||||
# Create a graph from the nodes
|
||||
graph = defaultdict(list)
|
||||
for node in nodes:
|
||||
for dependency in node['dependencies']:
|
||||
graph[dependency].append(node['id'])
|
||||
|
||||
# Find all nodes with no dependencies (potential starting points)
|
||||
start_nodes = [node['id'] for node in nodes if not node['dependencies']]
|
||||
|
||||
# Adjust the BFT function to handle dependencies correctly
|
||||
def bft_corrected(start, graph, nodes_info):
|
||||
visited = set()
|
||||
queue = deque([start])
|
||||
order = []
|
||||
|
||||
while queue:
|
||||
node = queue.popleft()
|
||||
if node not in visited:
|
||||
# Check if all dependencies of the current node are visited
|
||||
node_dependencies = [n['id'] for n in nodes if n['id'] == node][0]
|
||||
dependencies_met = all(dep in visited for dep in nodes_info[node_dependencies]['dependencies'])
|
||||
|
||||
if dependencies_met:
|
||||
visited.add(node)
|
||||
order.append(node)
|
||||
# Add only nodes to the queue whose dependencies are fully met
|
||||
for next_node in graph[node]:
|
||||
if all(dep in visited for dep in nodes_info[next_node]['dependencies']):
|
||||
queue.append(next_node)
|
||||
else:
|
||||
# Requeue the node to check dependencies later
|
||||
queue.append(node)
|
||||
|
||||
return order
|
||||
|
||||
# Dictionary to access node information quickly
|
||||
nodes_info = {node['id']: node for node in nodes}
|
||||
|
||||
# Perform BFT for each unvisited start node using the corrected BFS function
|
||||
visited_global = set()
|
||||
bfs_order = []
|
||||
|
||||
for start in start_nodes:
|
||||
if start not in visited_global:
|
||||
order = bft_corrected(start, graph, nodes_info)
|
||||
bfs_order.extend(order)
|
||||
visited_global.update(order)
|
||||
|
||||
print("BFT Order:", bfs_order)
|
||||
```
|
||||
|
||||
This produces the following output.
|
||||
|
||||
```python
|
||||
BFT Order: ['1', '6', '10', '2', '3', '4', '5', '7', '8', '9']
|
||||
```
|
||||
|
||||
Now, let's define our `ConcurrentWorkflow` and run it.
|
||||
|
||||
```python
|
||||
import os
|
||||
from dotenv import load_dotenv
|
||||
from swarms import Agent, ConcurrentWorkflow, OpenAIChat, Task
|
||||
|
||||
# Create a workflow
|
||||
workflow = ConcurrentWorkflow(max_workers=5)
|
||||
task_list = []
|
||||
|
||||
for node in bfs_order:
|
||||
sub_query =nodes_info[node]['query']
|
||||
task = Task(worker_agent, sub_query)
|
||||
print('-----------------')
|
||||
print("Added task: ", sub_query)
|
||||
print('-----------------')
|
||||
task_list.append(task)
|
||||
|
||||
workflow.add(tasks=task_list)
|
||||
|
||||
# Run the workflow
|
||||
workflow.run()
|
||||
```
|
||||
|
||||
Below is part of the output this workflow produces. We clearly see the thought process of the agent and the plan it came up to solve a particular sub-query. In addition, we see the tool-calling schema it produces in `"command"`.
|
||||
|
||||
```python
|
||||
...
|
||||
...
|
||||
content='\n{\n "thoughts": {\n "text": "To find out Nike\'s current revenue trend, I will use the financial data retriever tool to search for \'Nike revenue trend\'.",\n "reasoning": "The financial data retriever tool allows me to search for specific financial data, so I can look up the current revenue trend of Nike.", \n "plan": "Use the financial data retriever tool to search for \'Nike revenue trend\'. Parse the result to get the current revenue trend and format that into a readable report."\n },\n "command": {\n "name": "kay_retriever", \n "args": {\n "query": "Nike revenue trend"\n }\n }\n}\n```' response_metadata={'token_usage': {'completion_tokens': 152, 'prompt_tokens': 1527, 'total_tokens': 1679}, 'model_name': 'gpt-4', 'system_fingerprint': None, 'finish_reason': 'stop', 'logprobs': None}
|
||||
Saved agent state to: Worker Agent_state.json
|
||||
|
||||
{
|
||||
"thoughts": {
|
||||
"text": "To find out Nike's current revenue trend, I will use the financial data retriever tool to search for 'Nike revenue trend'.",
|
||||
"reasoning": "The financial data retriever tool allows me to search for specific financial data, so I can look up the current revenue trend of Nike.",
|
||||
"plan": "Use the financial data retriever tool to search for 'Nike revenue trend'. Parse the result to get the current revenue trend and format that into a readable report."
|
||||
},
|
||||
"command": {
|
||||
"name": "kay_retriever",
|
||||
"args": {
|
||||
"query": "Nike revenue trend"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
-----------------
|
||||
Document added successfully
|
||||
-----------------
|
||||
...
|
||||
...
|
||||
```
|
||||
|
||||
Here, `"name"` pertains to the name of the tool to be called and `"args"` is the arguments to be passed to the tool call. Like mentioned before, we modify `Agent`'s default behaviour in `WorkerAgent`. Hence, the tool call is executed here and its results (information from web pages and Kay Retriever API) are added to long-term memory. We get confirmation for this from the message `Document added successfully`.
|
||||
|
||||
|
||||
#### Step 7. Generating the report using Writer Agent
|
||||
|
||||
At this point, our Worker Agents have gathered all the background information required to generate the report. We have also defined a coherent structure to write the report, which is following the BFT order to answering the sub-queries. Now it's time to define a Writer Agent and call it sequentially in the order of sub-queries.
|
||||
|
||||
```python
|
||||
from swarms import Agent, OpenAIChat, tool
|
||||
|
||||
agent = Agent(
|
||||
agent_name="Writer Agent",
|
||||
agent_description=(
|
||||
"This agent writes reports based on information in long-term memory"
|
||||
),
|
||||
system_prompt=(
|
||||
"You are a world-class financial report writer. "
|
||||
"Write analytical and accurate responses using memory to answer the query. "
|
||||
"Do not mention use of long-term memory in the report. "
|
||||
"Do not mention Writer Agent in response."
|
||||
"Return only response content in strict markdown format."
|
||||
),
|
||||
llm=OpenAIChat(temperature=0.2, model='gpt-3.5-turbo'),
|
||||
max_loops=1,
|
||||
autosave=True,
|
||||
verbose=True,
|
||||
long_term_memory=memory,
|
||||
)
|
||||
```
|
||||
|
||||
The report individual sections of the report will be collected in a list.
|
||||
|
||||
```python
|
||||
report = []
|
||||
```
|
||||
|
||||
Let us now run the writer agent.
|
||||
|
||||
```python
|
||||
for node in bfs_order:
|
||||
sub_query =nodes_info[node]['query']
|
||||
print("Running task: ", sub_query)
|
||||
out = agent.run(f"Consider: {sub_query}. Write response in strict markdown format using long-term memory. Do not mention Writer Agent in response.")
|
||||
print(out)
|
||||
try:
|
||||
report.append(out.content)
|
||||
except:
|
||||
pass
|
||||
```
|
||||
|
||||
Now, we need to clean up the repoort a bit to make it render professionally.
|
||||
|
||||
```python
|
||||
# Remove any content before the first "#" as that signals start of heading
|
||||
# Anything before this usually contains filler content
|
||||
stripped_report = [entry[entry.find('#'):] if '#' in entry else entry for entry in report]
|
||||
report = stripped_report
|
||||
|
||||
# At times the LLM outputs \\n instead of \n
|
||||
cleaned_report = [entry.replace("\\n", "\n") for entry in report]
|
||||
import re
|
||||
|
||||
# Function to clean up unnecessary metadata from the report entries
|
||||
def clean_report(report):
|
||||
cleaned_report = []
|
||||
for entry in report:
|
||||
# This pattern matches 'response_metadata={' followed by any characters that are not '}' (non-greedy),
|
||||
# possibly nested inside other braces, until the closing '}'.
|
||||
cleaned_entry = re.sub(r"response_metadata=\{[^{}]*(?:\{[^{}]*\}[^{}]*)*\}", "", entry, flags=re.DOTALL)
|
||||
cleaned_report.append(cleaned_entry)
|
||||
return cleaned_report
|
||||
|
||||
# Apply the cleaning function to the markdown report
|
||||
cleaned_report = clean_report(cleaned_report)
|
||||
```
|
||||
|
||||
After cleaning, we append parts of the report together to get out final report.
|
||||
|
||||
```python
|
||||
final_report = ' \n '.join(cleaned_report)
|
||||
```
|
||||
|
||||
In Jupyter Notebook, we can use the below code to render it in Markdown.
|
||||
|
||||
```python
|
||||
from IPython.display import display, Markdown
|
||||
|
||||
display(Markdown(final_report))
|
||||
```
|
||||
|
||||
|
||||
## Final Generated Report
|
||||
|
||||
|
||||
### Nike's Current Revenue Trend
|
||||
|
||||
Nike's current revenue trend has been steadily increasing over the past few years. In the most recent fiscal year, Nike reported a revenue of $37.4 billion, which was a 7% increase from the previous year. This growth can be attributed to strong sales in key markets, successful marketing campaigns, and a focus on innovation in product development. Overall, Nike continues to demonstrate strong financial performance and is well-positioned for future growth.
|
||||
### Potential Areas of Improvement in Nike's Business Model
|
||||
|
||||
1. **Sustainability Practices**: Nike could further enhance its sustainability efforts by reducing its carbon footprint, using more eco-friendly materials, and ensuring ethical labor practices throughout its supply chain.
|
||||
|
||||
2. **Diversification of Product Portfolio**: While Nike is known for its athletic footwear and apparel, diversifying into new product categories or expanding into untapped markets could help drive growth and mitigate risks associated with a single product line.
|
||||
|
||||
3. **E-commerce Strategy**: Improving the online shopping experience, investing in digital marketing, and leveraging data analytics to personalize customer interactions could boost online sales and customer loyalty.
|
||||
|
||||
4. **Innovation and R&D**: Continuously investing in research and development to stay ahead of competitors, introduce new technologies, and enhance product performance could help maintain Nike's competitive edge in the market.
|
||||
|
||||
5. **Brand Image and Reputation**: Strengthening brand image through effective marketing campaigns, community engagement, and transparent communication with stakeholders can help build trust and loyalty among consumers.
|
||||
### Potential Cost-Saving Strategies for Nike to Increase Net Revenue in Q3 2024
|
||||
|
||||
1. **Supply Chain Optimization**: Streamlining the supply chain, reducing transportation costs, and improving inventory management can lead to significant cost savings for Nike.
|
||||
|
||||
2. **Operational Efficiency**: Implementing lean manufacturing practices, reducing waste, and optimizing production processes can help lower production costs and improve overall efficiency.
|
||||
|
||||
3. **Outsourcing Non-Core Functions**: Outsourcing non-core functions such as IT services, customer support, or logistics can help reduce overhead costs and focus resources on core business activities.
|
||||
|
||||
4. **Energy Efficiency**: Investing in energy-efficient technologies, renewable energy sources, and sustainable practices can lower utility costs and demonstrate a commitment to environmental responsibility.
|
||||
|
||||
5. **Negotiating Supplier Contracts**: Negotiating better terms with suppliers, leveraging economies of scale, and exploring alternative sourcing options can help lower procurement costs and improve margins.
|
||||
|
||||
By implementing these cost-saving strategies, Nike can improve its bottom line and increase net revenue in Q3 2024.
|
||||
### Projected Market Trends for the Sports Apparel Industry in 2024
|
||||
|
||||
1. **Sustainable Fashion**: Consumers are increasingly demanding eco-friendly and sustainable products, leading to a rise in sustainable sportswear options in the market.
|
||||
|
||||
2. **Digital Transformation**: The sports apparel industry is expected to continue its shift towards digital platforms, with a focus on e-commerce, personalized shopping experiences, and digital marketing strategies.
|
||||
|
||||
3. **Athleisure Wear**: The trend of athleisure wear, which combines athletic and leisure clothing, is projected to remain popular in 2024 as consumers seek comfort and versatility in their apparel choices.
|
||||
|
||||
4. **Innovative Materials**: Advances in technology and material science are likely to drive the development of innovative fabrics and performance-enhancing materials in sports apparel, catering to the demand for high-quality and functional products.
|
||||
|
||||
5. **Health and Wellness Focus**: With a growing emphasis on health and wellness, sports apparel brands are expected to incorporate features that promote comfort, performance, and overall well-being in their products.
|
||||
|
||||
Overall, the sports apparel industry in 2024 is anticipated to be characterized by sustainability, digitalization, innovation, and a focus on consumer health and lifestyle trends.
|
||||
### Current Successful Strategies Used by Nike's Competitors
|
||||
|
||||
1. **Adidas**: Adidas has been successful in leveraging collaborations with celebrities and designers to create limited-edition collections that generate hype and drive sales. They have also focused on sustainability initiatives, such as using recycled materials in their products, to appeal to environmentally conscious consumers.
|
||||
|
||||
2. **Under Armour**: Under Armour has differentiated itself by targeting performance-driven athletes and emphasizing technological innovation in their products. They have also invested heavily in digital marketing and e-commerce to reach a wider audience and enhance the customer shopping experience.
|
||||
|
||||
3. **Puma**: Puma has successfully capitalized on the athleisure trend by offering stylish and versatile sportswear that can be worn both in and out of the gym. They have also focused on building partnerships with influencers and sponsoring high-profile athletes to increase brand visibility and credibility.
|
||||
|
||||
4. **Lululemon**: Lululemon has excelled in creating a strong community around its brand, hosting events, classes, and collaborations to engage with customers beyond just selling products. They have also prioritized customer experience by offering personalized services and creating a seamless omnichannel shopping experience.
|
||||
|
||||
5. **New Balance**: New Balance has carved out a niche in the market by emphasizing quality craftsmanship, heritage, and authenticity in their products. They have also focused on customization and personalization options for customers, allowing them to create unique and tailored footwear and apparel.
|
||||
|
||||
Overall, Nike's competitors have found success through a combination of innovative product offerings, strategic marketing initiatives, and a focus on customer engagement and experience.
|
||||
### Current and Projected Economic Conditions in Nike's Major Markets
|
||||
|
||||
1. **United States**: The United States, being one of Nike's largest markets, is currently experiencing moderate economic growth driven by consumer spending, low unemployment rates, and a rebound in manufacturing. However, uncertainties surrounding trade policies, inflation, and interest rates could impact consumer confidence and spending in the near future.
|
||||
|
||||
2. **China**: China remains a key market for Nike, with a growing middle class and increasing demand for sportswear and athletic footwear. Despite recent trade tensions with the U.S., China's economy is projected to continue expanding, driven by domestic consumption, infrastructure investments, and technological advancements.
|
||||
|
||||
3. **Europe**: Economic conditions in Europe vary across countries, with some experiencing sluggish growth due to Brexit uncertainties, political instability, and trade tensions. However, overall consumer confidence is improving, and the sports apparel market is expected to grow, driven by e-commerce and sustainability trends.
|
||||
|
||||
4. **Emerging Markets**: Nike's presence in emerging markets such as India, Brazil, and Southeast Asia provides opportunities for growth, given the rising disposable incomes, urbanization, and increasing focus on health and fitness. However, challenges such as currency fluctuations, regulatory changes, and competition from local brands could impact Nike's performance in these markets.
|
||||
|
||||
Overall, Nike's major markets exhibit a mix of opportunities and challenges, with economic conditions influenced by global trends, geopolitical factors, and consumer preferences."
|
||||
### Current Consumer Preferences in the Sports Apparel Industry
|
||||
|
||||
1. **Sustainability**: Consumers are increasingly seeking eco-friendly and sustainable options in sports apparel, driving brands to focus on using recycled materials, reducing waste, and promoting ethical practices.
|
||||
|
||||
2. **Athleisure**: The trend of athleisure wear continues to be popular, with consumers looking for versatile and comfortable clothing that can be worn both during workouts and in everyday life.
|
||||
|
||||
3. **Performance and Functionality**: Consumers prioritize performance-enhancing features in sports apparel, such as moisture-wicking fabrics, breathable materials, and ergonomic designs that enhance comfort and mobility.
|
||||
|
||||
4. **Personalization**: Customization options, personalized fit, and unique design elements are appealing to consumers who seek individuality and exclusivity in their sports apparel choices.
|
||||
|
||||
5. **Brand Transparency**: Consumers value transparency in brand practices, including supply chain transparency, ethical sourcing, and clear communication on product quality and manufacturing processes.
|
||||
|
||||
Overall, consumer preferences in the sports apparel industry are shifting towards sustainability, versatility, performance, personalization, and transparency, influencing brand strategies and product offerings.
|
||||
### Potential New Markets for Nike to Explore in 2024
|
||||
|
||||
1. **India**: With a growing population, increasing disposable incomes, and a rising interest in health and fitness, India presents a significant opportunity for Nike to expand its presence and tap into a large consumer base.
|
||||
|
||||
2. **Africa**: The African market, particularly countries with emerging economies and a young population, offers potential for Nike to introduce its products and capitalize on the growing demand for sportswear and athletic footwear.
|
||||
|
||||
3. **Middle East**: Countries in the Middle East, known for their luxury shopping destinations and a growing interest in sports and fitness activities, could be strategic markets for Nike to target and establish a strong foothold.
|
||||
|
||||
4. **Latin America**: Markets in Latin America, such as Brazil, Mexico, and Argentina, present opportunities for Nike to cater to a diverse consumer base and leverage the region's passion for sports and active lifestyles.
|
||||
|
||||
5. **Southeast Asia**: Rapid urbanization, increasing urban middle-class population, and a trend towards health and wellness in countries like Indonesia, Thailand, and Vietnam make Southeast Asia an attractive region for Nike to explore and expand its market reach.
|
||||
|
||||
By exploring these new markets in 2024, Nike can diversify its geographical presence, reach untapped consumer segments, and drive growth in emerging economies.
|
||||
### Potential New Products or Services Nike Could Introduce in 2024
|
||||
|
||||
1. **Smart Apparel**: Nike could explore the integration of technology into its apparel, such as smart fabrics that monitor performance metrics, provide feedback, or enhance comfort during workouts.
|
||||
|
||||
2. **Athletic Accessories**: Introducing a line of athletic accessories like gym bags, water bottles, or fitness trackers could complement Nike's existing product offerings and provide additional value to customers.
|
||||
|
||||
3. **Customization Platforms**: Offering personalized design options for footwear and apparel through online customization platforms could appeal to consumers seeking unique and tailored products.
|
||||
|
||||
4. **Athletic Recovery Gear**: Developing recovery-focused products like compression wear, recovery sandals, or massage tools could cater to athletes and fitness enthusiasts looking to enhance post-workout recovery.
|
||||
|
||||
5. **Sustainable Collections**: Launching sustainable collections made from eco-friendly materials, recycled fabrics, or biodegradable components could align with consumer preferences for environmentally conscious products.
|
||||
|
||||
By introducing these new products or services in 2024, Nike can innovate its product portfolio, cater to evolving consumer needs, and differentiate itself in the competitive sports apparel market.
|
||||
### Potential Marketing Strategies for Nike to Increase Revenue in Q3 2024
|
||||
|
||||
1. **Influencer Partnerships**: Collaborating with popular athletes, celebrities, or social media influencers to promote Nike products can help reach a wider audience and drive sales.
|
||||
|
||||
2. **Interactive Campaigns**: Launching interactive marketing campaigns, contests, or events that engage customers and create buzz around new product releases can generate excitement and increase brand visibility.
|
||||
|
||||
3. **Social Media Engagement**: Leveraging social media platforms to connect with consumers, share user-generated content, and respond to feedback can build brand loyalty and encourage repeat purchases.
|
||||
|
||||
4. **Localized Marketing**: Tailoring marketing messages, promotions, and product offerings to specific regions or target demographics can enhance relevance and appeal to diverse consumer groups.
|
||||
|
||||
5. **Customer Loyalty Programs**: Implementing loyalty programs, exclusive offers, or rewards for repeat customers can incentivize brand loyalty, increase retention rates, and drive higher lifetime customer value.
|
||||
|
||||
By employing these marketing strategies in Q3 2024, Nike can enhance its brand presence, attract new customers, and ultimately boost revenue growth.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
After Width: | Height: | Size: 122 KiB |
After Width: | Height: | Size: 390 KiB |
@ -0,0 +1,96 @@
|
||||
## Llava3
|
||||
|
||||
|
||||
```python
|
||||
from transformers import AutoTokenizer, AutoModelForCausalLM
|
||||
import torch
|
||||
from swarms.models.base_llm import BaseLLM
|
||||
|
||||
|
||||
class Llama3(BaseLLM):
|
||||
"""
|
||||
Llama3 class represents a Llama model for natural language generation.
|
||||
|
||||
Args:
|
||||
model_id (str): The ID of the Llama model to use.
|
||||
system_prompt (str): The system prompt to use for generating responses.
|
||||
temperature (float): The temperature value for controlling the randomness of the generated responses.
|
||||
top_p (float): The top-p value for controlling the diversity of the generated responses.
|
||||
max_tokens (int): The maximum number of tokens to generate in the response.
|
||||
**kwargs: Additional keyword arguments.
|
||||
|
||||
Attributes:
|
||||
model_id (str): The ID of the Llama model being used.
|
||||
system_prompt (str): The system prompt for generating responses.
|
||||
temperature (float): The temperature value for generating responses.
|
||||
top_p (float): The top-p value for generating responses.
|
||||
max_tokens (int): The maximum number of tokens to generate in the response.
|
||||
tokenizer (AutoTokenizer): The tokenizer for the Llama model.
|
||||
model (AutoModelForCausalLM): The Llama model for generating responses.
|
||||
|
||||
Methods:
|
||||
run(task, *args, **kwargs): Generates a response for the given task.
|
||||
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
model_id="meta-llama/Meta-Llama-3-8B-Instruct",
|
||||
system_prompt: str = None,
|
||||
temperature: float = 0.6,
|
||||
top_p: float = 0.9,
|
||||
max_tokens: int = 4000,
|
||||
**kwargs,
|
||||
):
|
||||
self.model_id = model_id
|
||||
self.system_prompt = system_prompt
|
||||
self.temperature = temperature
|
||||
self.top_p = top_p
|
||||
self.max_tokens = max_tokens
|
||||
self.tokenizer = AutoTokenizer.from_pretrained(model_id)
|
||||
self.model = AutoModelForCausalLM.from_pretrained(
|
||||
model_id,
|
||||
torch_dtype=torch.bfloat16,
|
||||
device_map="auto",
|
||||
)
|
||||
|
||||
def run(self, task: str, *args, **kwargs):
|
||||
"""
|
||||
Generates a response for the given task.
|
||||
|
||||
Args:
|
||||
task (str): The user's task or input.
|
||||
|
||||
Returns:
|
||||
str: The generated response.
|
||||
|
||||
"""
|
||||
messages = [
|
||||
{"role": "system", "content": self.system_prompt},
|
||||
{"role": "user", "content": task},
|
||||
]
|
||||
|
||||
input_ids = self.tokenizer.apply_chat_template(
|
||||
messages, add_generation_prompt=True, return_tensors="pt"
|
||||
).to(self.model.device)
|
||||
|
||||
terminators = [
|
||||
self.tokenizer.eos_token_id,
|
||||
self.tokenizer.convert_tokens_to_ids("<|eot_id|>"),
|
||||
]
|
||||
|
||||
outputs = self.model.generate(
|
||||
input_ids,
|
||||
max_new_tokens=self.max_tokens,
|
||||
eos_token_id=terminators,
|
||||
do_sample=True,
|
||||
temperature=self.temperature,
|
||||
top_p=self.top_p,
|
||||
*args,
|
||||
**kwargs,
|
||||
)
|
||||
response = outputs[0][input_ids.shape[-1] :]
|
||||
return self.tokenizer.decode(
|
||||
response, skip_special_tokens=True
|
||||
)
|
||||
```
|
@ -0,0 +1,274 @@
|
||||
# Documentation for `AgentRearrange` Class
|
||||
-----
|
||||
|
||||
The `AgentRearrange` class represents a swarm of agents for rearranging tasks. It allows you to create a swarm of agents, add or remove agents from the swarm, and run the swarm to process tasks based on a specified flow pattern.
|
||||
|
||||
## Attributes
|
||||
----------
|
||||
|
||||
| Attribute | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| `agents` | `dict` | A dictionary of agents, where the key is the agent's name and the value is the agent object. |
|
||||
| `flow` | `str` | The flow pattern of the tasks. |
|
||||
| `max_loops` | `int` | The maximum number of loops for the agents to run. |
|
||||
| `verbose` | `bool` | Whether to enable verbose logging or not. |
|
||||
|
||||
## Methods
|
||||
-------
|
||||
|
||||
### `__init__(self, agents: List[Agent] = None, flow: str = None, max_loops: int = 1, verbose: bool = True)`
|
||||
|
||||
Initializes the `AgentRearrange` object.
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| `agents` | `List[Agent]` (optional) | A list of `Agent` objects. Defaults to `None`. |
|
||||
| `flow` | `str` (optional) | The flow pattern of the tasks. Defaults to `None`. |
|
||||
| `max_loops` | `int` (optional) | The maximum number of loops for the agents to run. Defaults to `1`. |
|
||||
| `verbose` | `bool` (optional) | Whether to enable verbose logging or not. Defaults to `True`. |
|
||||
|
||||
### `add_agent(self, agent: Agent)`
|
||||
|
||||
Adds an agent to the swarm.
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| `agent` | `Agent` | The agent to be added. |
|
||||
|
||||
### `remove_agent(self, agent_name: str)`
|
||||
|
||||
Removes an agent from the swarm.
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| `agent_name` | `str` | The name of the agent to be removed. |
|
||||
|
||||
### `add_agents(self, agents: List[Agent])`
|
||||
|
||||
Adds multiple agents to the swarm.
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| `agents` | `List[Agent]` | A list of `Agent` objects. |
|
||||
|
||||
### `validate_flow(self)`
|
||||
|
||||
Validates the flow pattern.
|
||||
|
||||
**Raises:**
|
||||
|
||||
- `ValueError`: If the flow pattern is incorrectly formatted or contains duplicate agent names.
|
||||
|
||||
**Returns:**
|
||||
|
||||
- `bool`: `True` if the flow pattern is valid.
|
||||
|
||||
### `run(self, task: str, *args, **kwargs)`
|
||||
|
||||
Runs the swarm to rearrange the tasks.
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| `task` | `str` | The initial task to be processed. |
|
||||
| `*args` | - | Additional positional arguments. |
|
||||
| `**kwargs` | - | Additional keyword arguments. |
|
||||
|
||||
**Returns:**
|
||||
|
||||
- `str`: The final processed task.
|
||||
|
||||
## Documentation for `rearrange` Function
|
||||
======================================
|
||||
|
||||
The `rearrange` function is a helper function that rearranges the given list of agents based on the specified flow.
|
||||
|
||||
## Parameters
|
||||
----------
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| `agents` | `List[Agent]` | The list of agents to be rearranged. |
|
||||
| `flow` | `str` | The flow used for rearranging the agents. |
|
||||
| `task` | `str` (optional) | The task to be performed during rearrangement. Defaults to `None`. |
|
||||
| `*args` | - | Additional positional arguments. |
|
||||
| `**kwargs` | - | Additional keyword arguments. |
|
||||
|
||||
## Returns
|
||||
-------
|
||||
|
||||
The result of running the agent system with the specified task.
|
||||
|
||||
### Example
|
||||
-------
|
||||
|
||||
```python
|
||||
agents = [agent1, agent2, agent3]
|
||||
flow = "agent1 -> agent2, agent3"
|
||||
task = "Perform a task"
|
||||
rearrange(agents, flow, task)
|
||||
```
|
||||
|
||||
### Example Usage
|
||||
-------------
|
||||
|
||||
Here's an example of how to use the `AgentRearrange` class and the `rearrange` function:
|
||||
|
||||
```python
|
||||
from swarms import Agent, AgentRearrange, rearrange
|
||||
from typing import List
|
||||
|
||||
# Initialize the director agent
|
||||
director = Agent(
|
||||
agent_name="Director",
|
||||
system_prompt="Directs the tasks for the workers",
|
||||
llm=Anthropic(),
|
||||
max_loops=1,
|
||||
dashboard=False,
|
||||
streaming_on=True,
|
||||
verbose=True,
|
||||
stopping_token="<DONE>",
|
||||
state_save_file_type="json",
|
||||
saved_state_path="director.json",
|
||||
)
|
||||
|
||||
# Initialize worker 1
|
||||
worker1 = Agent(
|
||||
agent_name="Worker1",
|
||||
system_prompt="Generates a transcript for a youtube video on what swarms are",
|
||||
llm=Anthropic(),
|
||||
max_loops=1,
|
||||
dashboard=False,
|
||||
streaming_on=True,
|
||||
verbose=True,
|
||||
stopping_token="<DONE>",
|
||||
state_save_file_type="json",
|
||||
saved_state_path="worker1.json",
|
||||
)
|
||||
|
||||
# Initialize worker 2
|
||||
worker2 = Agent(
|
||||
agent_name="Worker2",
|
||||
system_prompt="Summarizes the transcript generated by Worker1",
|
||||
llm=Anthropic(),
|
||||
max_loops=1,
|
||||
dashboard=False,
|
||||
streaming_on=True,
|
||||
verbose=True,
|
||||
stopping_token="<DONE>",
|
||||
state_save_file_type="json",
|
||||
saved_state_path="worker2.json",
|
||||
)
|
||||
|
||||
# Create a list of agents
|
||||
agents = [director, worker1, worker2]
|
||||
|
||||
# Define the flow pattern
|
||||
flow = "Director -> Worker1 -> Worker2"
|
||||
|
||||
# Using AgentRearrange class
|
||||
agent_system = AgentRearrange(agents=agents, flow=flow)
|
||||
output = agent_system.run("Create a format to express and communicate swarms of llms in a structured manner for youtube")
|
||||
print(output)
|
||||
|
||||
# Using rearrange function
|
||||
output = rearrange(agents, flow, "Create a format to express and communicate swarms of llms in a structured manner for youtube")
|
||||
print(output)
|
||||
|
||||
```
|
||||
|
||||
In this example, we first initialize three agents: `director`, `worker1`, and `worker2`. Then, we create a list of these agents and define the flow pattern `"Director -> Worker1 -> Worker2"`.
|
||||
|
||||
We can use the `AgentRearrange` class by creating an instance of it with the list of agents and the flow pattern. We then call the `run` method with the initial task, and it will execute the agents in the specified order, passing the output of one agent as the input to the next agent.
|
||||
|
||||
Alternatively, we can use the `rearrange` function by passing the list of agents, the flow pattern, and the initial task as arguments.
|
||||
|
||||
Both the `AgentRearrange` class and the `rearrange` function will return the final output after processing the task through the agents according to the specified flow pattern.
|
||||
|
||||
## Error Handling
|
||||
--------------
|
||||
|
||||
The `AgentRearrange` class includes error handling mechanisms to validate the flow pattern. If the flow pattern is incorrectly formatted or contains duplicate agent names, a `ValueError` will be raised with an appropriate error message.
|
||||
|
||||
### Example:
|
||||
|
||||
```python
|
||||
# Invalid flow pattern
|
||||
invalid_flow = "Director->Worker1,Worker2->Worker3"
|
||||
agent_system = AgentRearrange(agents=agents, flow=invalid_flow)
|
||||
output = agent_system.run("Some task")`
|
||||
```
|
||||
|
||||
This will raise a `ValueError` with the message `"Agent 'Worker3' is not registered."`.
|
||||
|
||||
|
||||
## Parallel and Sequential Processing
|
||||
----------------------------------
|
||||
|
||||
The `AgentRearrange` class supports both parallel and sequential processing of tasks based on the specified flow pattern. If the flow pattern includes multiple agents separated by commas (e.g., `"agent1, agent2"`), the agents will be executed in parallel, and their outputs will be concatenated with a semicolon (`;`). If the flow pattern includes a single agent, it will be executed sequentially.
|
||||
|
||||
|
||||
### Parallel processing
|
||||
`parallel_flow = "Worker1, Worker2 -> Director"`
|
||||
|
||||
### Sequential processing
|
||||
`sequential_flow = "Worker1 -> Worker2 -> Director"`
|
||||
|
||||
In the `parallel_flow` example, `Worker1` and `Worker2` will be executed in parallel, and their outputs will be concatenated and passed to `Director`. In the `sequential_flow` example, `Worker1` will be executed first, and its output will be passed to `Worker2`, and then the output of `Worker2` will be passed to `Director`.
|
||||
|
||||
## Logging
|
||||
-------
|
||||
|
||||
The `AgentRearrange` class includes logging capabilities using the `loguru` library. If `verbose` is set to `True` during initialization, a log file named `agent_rearrange.log` will be created, and log messages will be written to it. You can use this log file to track the execution of the agents and any potential issues or errors that may occur.
|
||||
|
||||
|
||||
```bash
|
||||
2023-05-08 10:30:15.456 | INFO | agent_rearrange:__init__:34 - Adding agent Director to the swarm.
|
||||
2023-05-08 10:30:15.457 | INFO | agent_rearrange:__init__:34 - Adding agent Worker1 to the swarm.
|
||||
2023-05-08 10:30:15.457 | INFO | agent_rearrange:__init__:34 - Adding agent Worker2 to the swarm.
|
||||
2023-05-08 10:30:15.458 | INFO | agent_rearrange:run:118 - Running agents in parallel: ['Worker1', 'Worker2']
|
||||
2023-05-08 10:30:15.459 | INFO | agent_rearrange:run:121 - Running agents sequentially: ['Director']`
|
||||
```
|
||||
|
||||
## Additional Parameters
|
||||
---------------------
|
||||
|
||||
The `AgentRearrange` class also accepts additional parameters that can be passed to the `run` method using `*args` and `**kwargs`. These parameters will be forwarded to the individual agents during execution.
|
||||
|
||||
`agent_system = AgentRearrange(agents=agents, flow=flow)`
|
||||
`output = agent_system.run("Some task", max_tokens=200, temperature=0.7)`
|
||||
|
||||
In this example, the `max_tokens` and `temperature` parameters will be passed to each agent during execution.
|
||||
|
||||
## Customization
|
||||
-------------
|
||||
|
||||
The `AgentRearrange` class and the `rearrange` function can be customized and extended to suit specific use cases. For example, you can create custom agents by inheriting from the `Agent` class and implementing custom logic for task processing. You can then add these custom agents to the swarm and define the flow pattern accordingly.
|
||||
|
||||
Additionally, you can modify the `run` method of the `AgentRearrange` class to implement custom logic for task processing and agent interaction.
|
||||
|
||||
|
||||
## Limitations
|
||||
-----------
|
||||
|
||||
It's important to note that the `AgentRearrange` class and the `rearrange` function rely on the individual agents to process tasks correctly. The quality of the output will depend on the capabilities and configurations of the agents used in the swarm. Additionally, the `AgentRearrange` class does not provide any mechanisms for task prioritization or load balancing among the agents.
|
||||
|
||||
## Future Improvements
|
||||
-------------------
|
||||
|
||||
Here are some potential future improvements for the `AgentRearrange` class and the `rearrange` function:
|
||||
|
||||
- **Task Prioritization**: Implement a mechanism to prioritize tasks based on factors such as urgency, importance, or resource availability.
|
||||
- **Load Balancing**: Incorporate load balancing algorithms to distribute tasks among agents more efficiently, taking into account factors such as agent availability, performance, and resource utilization.
|
||||
- **Dynamic Flow Reconfiguration**: Allow for dynamic reconfiguration of the flow pattern during runtime, enabling the addition, removal, or reordering of agents based on specific conditions or events.
|
||||
- **Error Handling and Fault Tolerance**: Enhance error handling and fault tolerance mechanisms to gracefully handle agent failures, task timeouts, or other exceptional situations.
|
||||
- **Monitoring and Metrics**: Implement monitoring and metrics collection to track the performance and efficiency of the swarm, as well as individual agent performance.
|
||||
- **Scalability**: Enhance the scalability of the system to handle larger numbers of agents and tasks efficiently.
|
||||
|
||||
## Conclusion
|
||||
----------
|
||||
|
||||
The `AgentRearrange` class and the `rearrange` function provide a flexible and extensible framework for orchestrating swarms of agents to process tasks based on a specified flow pattern. By combining the capabilities of individual agents, you can create complex workflows and leverage the strengths of different agents to tackle various tasks efficiently.
|
||||
|
||||
While the current implementation offers basic functionality for agent rearrangement, there is room for future improvements and customizations to enhance the system's capabilities and cater to more specific use cases.
|
||||
|
||||
Whether you're working on natural language processing tasks, data analysis, or any other domain where agent-based systems can be beneficial, the `AgentRearrange` class and the `rearrange` function provide a solid foundation for building and experimenting with swarm-based solutions.
|
@ -0,0 +1,15 @@
|
||||
# Available Models
|
||||
|
||||
| Model Name | Description | Input Price | Output Price | Use Cases |
|
||||
|-----------------------|---------------------------------------------------------------------------------------------------------|--------------|--------------|------------------------------------------------------------------------|
|
||||
| **Llama3-70b** | Llama 3 is an auto-regressive language model that uses an optimized transformer architecture. | $0.80/1M Tokens | $1.60/1M Tokens | General natural language processing tasks. |
|
||||
| **Llava-Internlm2-20b** | LLaVA model fine-tuned from InternLM2-Chat-20B and CLIP-ViT-Large-patch14-336. | Contact for pricing | Contact for pricing | Enhanced language understanding integrated with visual processing. |
|
||||
| **Llama-3-Giraffe-70B** | Abacus.AI presents our longer-necked variant of Llama 3 70B! | $1/1M Tokens | $2/1M Tokens | Extensive natural language tasks with a focus on depth and efficiency. |
|
||||
| **Qwen-vl** | Qwen VL for real-world multi-modal function calling. | $5/1M Tokens | $10/1M Tokens | Multi-modal interactions and function handling in complex environments.|
|
||||
| **XComposer2-4khd-7b** | One of the highest performing VLMs (Video Language Models). | $4/1M Tokens | $8/1M Tokens | High-resolution video processing and understanding. |
|
||||
| **Llava-Llama-3** | Llama3 with Multi-Modal Processing. | $5/1M Tokens | $10/1M Tokens | Advanced multi-modal scenarios involving language and image processing. |
|
||||
| **cogvlm-chat-17b** | Groundbreaking multimodal model designed to understand and reason about visual elements in images. | $5/1M Tokens | $10/1M Tokens | Image-based chatbots and interactive systems. |
|
||||
|
||||
|
||||
## What models should we add?
|
||||
[Book a call with us to learn more about your needs:](https://calendly.com/swarm-corp/30min)
|
@ -0,0 +1,319 @@
|
||||
# Enterprise Guide to High-Performance Multi-Agent LLM Deployments
|
||||
-------
|
||||
|
||||
As large language models (LLMs) continue to advance and enable a wide range of powerful applications, enterprises are increasingly exploring multi-agent architectures to leverage the collective capabilities of multiple LLMs. However, coordinating and optimizing the performance of these complex multi-agent systems presents significant challenges.
|
||||
|
||||
This comprehensive guide provides enterprise architects, engineering leaders, and technical decision-makers with a strategic framework for maximizing performance across multi-agent LLM deployments. Developed through extensive research and collaboration with industry partners, this guide distills best practices, proven techniques, and cutting-edge methodologies into seven core principles.
|
||||
|
||||
By implementing the recommendations outlined in this guide, organizations can achieve superior latency, throughput, and resource utilization while ensuring scalability, cost-effectiveness, and optimal user experiences. Whether powering customer-facing conversational agents, driving internal knowledge management systems, or fueling mission-critical decision support tools, high-performance multi-agent LLM deployments will be pivotal to unlocking the full potential of this transformative technology.
|
||||
|
||||
## Introduction
|
||||
|
||||
The rise of large language models (LLMs) has ushered in a new era of human-machine interaction, enabling enterprises to develop sophisticated natural language processing (NLP) applications that can understand, generate, and reason with human-like text. However, as the complexity and scale of LLM deployments grow, traditional monolithic architectures are increasingly challenged to meet the stringent performance, scalability, and cost requirements of enterprise environments.
|
||||
|
||||
Multi-agent architectures, which coordinate the collective capabilities of multiple specialized LLMs, have emerged as a powerful paradigm for addressing these challenges. By distributing workloads across a cohort of agents, each optimized for specific tasks or domains, multi-agent systems can deliver superior performance, resilience, and adaptability compared to single-model solutions.
|
||||
|
||||
However, realizing the full potential of multi-agent LLM deployments requires a strategic approach to system design, optimization, and ongoing management. This guide presents a comprehensive framework for maximizing performance across seven core principles, each underpinned by a range of proven techniques and methodologies.
|
||||
|
||||
Whether you are architecting a customer-facing conversational agent, building an internal knowledge management platform, or developing a mission-critical decision support system, this guide will equip you with the insights and best practices necessary to unlock the full potential of multi-agent LLM deployments within your enterprise.
|
||||
|
||||
## Principle 1: Distribute Token Processing
|
||||
----------------------------------------
|
||||
|
||||
At the heart of every LLM deployment lies the fundamental challenge of optimizing token processing -- the rate at which the model consumes and generates text inputs and outputs. In multi-agent architectures, distributing and parallelizing token processing across multiple agents is a critical performance optimization strategy.
|
||||
|
||||
### Agent Specialization
|
||||
|
||||
One of the key advantages of multi-agent architectures is the ability to dedicate specific agents to specialized tasks or domains. By carefully matching agents to the workloads they are optimized for, enterprises can maximize overall throughput and minimize latency.
|
||||
|
||||
For example, in a conversational agent deployment, one agent may be optimized for intent recognition and query understanding, while another is fine-tuned for generating coherent, context-aware responses. In a document processing pipeline, separate agents could be dedicated to tasks such as named entity recognition, sentiment analysis, and summarization.
|
||||
|
||||
To effectively leverage agent specialization, enterprises should:
|
||||
|
||||
- Conduct a thorough analysis of their application's workflow and identify distinct tasks or domains that could benefit from dedicated agents.
|
||||
- Evaluate the strengths and weaknesses of available LLM models and agents, and map them to the identified tasks or domains based on their capabilities and performance characteristics.
|
||||
- Implement continuous monitoring and performance tuning processes to ensure agents remain optimized for their assigned workloads as models evolve and domain requirements shift.
|
||||
|
||||
### Load Balancing
|
||||
|
||||
Even with a well-designed allocation of tasks across specialized agents, fluctuations in workload and demand can create bottlenecks and performance degradation. Effective load balancing strategies are essential to ensure that token processing capacity is dynamically distributed across available agents based on real-time conditions.
|
||||
|
||||
Load balancing in multi-agent LLM deployments can be accomplished through a combination of techniques, including:
|
||||
|
||||
- **Round-Robin**: Distributing incoming requests across agents in a cyclical fashion, ensuring an even distribution of workload.
|
||||
- **Least Connections**: Routing requests to the agent with the fewest active connections or outstanding tasks, minimizing the risk of overloading any single agent.
|
||||
- **Response Time Monitoring**: Continuously monitoring the response times of each agent and dynamically adjusting request routing to favor faster-responding agents.
|
||||
- **Resource-Based Routing**: Factoring in agent-level resource consumption (e.g., CPU, memory) when making routing decisions, ensuring that overloaded agents are relieved of additional workload.
|
||||
|
||||
Implementing effective load balancing requires careful consideration of the specific characteristics and requirements of your multi-agent deployment, as well as the integration of robust monitoring and analytics capabilities to inform dynamic routing decisions.
|
||||
|
||||
### Horizontal Scaling
|
||||
|
||||
While load balancing optimizes the utilization of existing agent resources, horizontal scaling strategies enable organizations to dynamically provision additional token processing capacity to meet demand spikes or handle larger overall workloads.
|
||||
|
||||
In multi-agent LLM deployments, horizontal scaling can be achieved through:
|
||||
|
||||
- **Agent Replication**: Spin up additional instances of existing agents to increase parallel processing capacity for specific tasks or domains.
|
||||
- **Hybrid Scaling**: Combine agent replication with the dynamic provisioning of additional compute resources (e.g., CPU, GPU) to support the increased agent count.
|
||||
- **Serverless Deployment**: Leverage serverless computing platforms (e.g., AWS Lambda, Google Cloud Functions) to automatically scale agent instances based on real-time demand, minimizing idle resource consumption.
|
||||
|
||||
Effective horizontal scaling requires robust orchestration and management capabilities, as well as seamless integration with load balancing mechanisms to ensure that incoming workloads are efficiently distributed across the dynamically scaled agent pool.
|
||||
|
||||
## Principle 2: Optimize Agent Communication
|
||||
-----------------------------------------
|
||||
|
||||
In multi-agent LLM deployments, efficient inter-agent communication is crucial for coordinating tasks, exchanging context and intermediate results, and maintaining overall system coherence. However, communication overhead can quickly become a performance bottleneck if not carefully managed.
|
||||
|
||||
### Minimizing Overhead
|
||||
|
||||
Reducing the volume and complexity of information exchanged between agents is a key strategy for optimizing communication performance. Techniques for minimizing overhead include:
|
||||
|
||||
- **Data Compression**: Applying lossless or lossy compression algorithms to reduce the size of data payloads exchanged between agents, lowering bandwidth requirements and transmission latencies.
|
||||
- **Information Summarization**: Distilling and summarizing context, results, or other data exchanged between agents to its essential elements, minimizing redundant or non-critical information.
|
||||
- **Differential Updates**: Rather than transmitting entire data payloads, agents can exchange only the differential updates or deltas required to synchronize their respective states.
|
||||
|
||||
Implementing these techniques requires careful analysis of the specific data exchange patterns and communication requirements within your multi-agent deployment, as well as the integration of appropriate compression, summarization, and differential update algorithms.
|
||||
|
||||
### Prioritizing Critical Information
|
||||
|
||||
In scenarios where communication bandwidth or latency constraints cannot be fully alleviated through overhead reduction techniques, enterprises can prioritize the exchange of critical information over non-essential data.
|
||||
|
||||
This can be achieved through:
|
||||
|
||||
- **Prioritized Queuing**: Implementing queuing mechanisms that prioritize the transmission of high-priority, time-sensitive data over lower-priority, non-critical information.
|
||||
- **Selective Communication**: Dynamically determining which agents require specific pieces of information based on their roles and responsibilities, and selectively transmitting data only to those agents that truly need it.
|
||||
- **Progressive Information Exchange**: Exchanging information in a progressive or staged manner, with critical elements transmitted first, followed by supplementary or contextual data as bandwidth becomes available.
|
||||
|
||||
Effective prioritization requires a deep understanding of the interdependencies and information flow within your multi-agent system, as well as the ability to dynamically assess and prioritize data based on its criticality and urgency.
|
||||
|
||||
### Caching and Reusing Context
|
||||
|
||||
In many multi-agent LLM deployments, agents frequently exchange or operate on shared context, such as user profiles, conversation histories, or domain-specific knowledge bases. Caching and reusing this context information can significantly reduce redundant communication and processing overhead.
|
||||
|
||||
Strategies for optimizing context caching and reuse include:
|
||||
|
||||
- **Agent-Level Caching**: Implementing caching mechanisms within individual agents to store and retrieve frequently accessed context data, minimizing the need for inter-agent communication.
|
||||
- **Centralized Context Management**: Deploying a dedicated context management service or data store that agents can query and update, ensuring consistent access to the latest context information across the system.
|
||||
- **Context Versioning and Invalidation**: Implementing versioning and invalidation mechanisms to ensure that cached context data remains fresh and consistent, avoiding stale or outdated information from propagating through the system.
|
||||
|
||||
|
||||
### Principle 3: Leverage Agent Specialization
|
||||
------------------------------------------
|
||||
|
||||
One of the key advantages of multi-agent architectures is the ability to optimize individual agents for specific tasks, domains, or capabilities. By leveraging agent specialization, enterprises can ensure that each component of their LLM system is finely tuned for maximum performance and quality.
|
||||
|
||||
### Task-Specific Optimization
|
||||
|
||||
Within a multi-agent LLM deployment, different agents may be responsible for distinct tasks such as language understanding, knowledge retrieval, response generation, or post-processing. Optimizing each agent for its designated task can yield significant performance gains and quality improvements.
|
||||
|
||||
Techniques for task-specific optimization include:
|
||||
|
||||
- **Prompt Engineering**: Crafting carefully designed prompts that provide the necessary context, instructions, and examples to guide an agent towards optimal performance for its assigned task.
|
||||
- **Fine-Tuning**: Adapting a pre-trained LLM to a specific task or domain by fine-tuning it on a curated dataset, allowing the agent to specialize and improve its performance on that particular workload.
|
||||
- **Model Distillation**: Transferring the knowledge and capabilities of a larger, more capable LLM into a smaller, more efficient model specialized for a specific task, balancing performance and quality trade-offs.
|
||||
|
||||
Implementing these optimization techniques requires a deep understanding of the capabilities and requirements of each task within your multi-agent system, as well as access to relevant training data and computational resources for fine-tuning and distillation processes.
|
||||
|
||||
### Domain Adaptation
|
||||
|
||||
Many enterprise applications operate within specific domains or verticals, such as finance, healthcare, or legal. Adapting agents to these specialized domains can significantly improve their performance, accuracy, and compliance within the target domain.
|
||||
|
||||
Strategies for domain adaptation include:
|
||||
|
||||
- **Domain-Specific Pre-Training**: Leveraging domain-specific corpora to pre-train LLM agents, imbuing them with a foundational understanding of the language, concepts, and nuances specific to the target domain.
|
||||
- **Transfer Learning**: Fine-tuning agents that have been pre-trained on general or adjacent domains, transferring their existing knowledge and capabilities to the target domain while optimizing for its specific characteristics.
|
||||
- **Domain Persona Injection**: Injecting domain-specific personas, traits, or constraints into agents during fine-tuning or deployment, shaping their behavior and outputs to align with domain-specific norms and requirements.
|
||||
|
||||
Effective domain adaptation requires access to high-quality, domain-specific training data, as well as close collaboration with subject matter experts to ensure that agents are properly calibrated to meet the unique demands of the target domain.
|
||||
|
||||
### Ensemble Techniques
|
||||
|
||||
In complex multi-agent deployments, individual agents may excel at specific subtasks or aspects of the overall workflow. Ensemble techniques that combine the outputs or predictions of multiple specialized agents can often outperform any single agent, leveraging the collective strengths of the ensemble.
|
||||
|
||||
Common ensemble techniques for multi-agent LLM systems include:
|
||||
|
||||
- **Voting**: Combining the outputs or predictions of multiple agents through majority voting, weighted voting, or other consensus mechanisms.
|
||||
- **Stacking**: Training a meta-agent to combine and optimize the outputs of multiple base agents, effectively learning to leverage their collective strengths.
|
||||
- **Blending**: Combining the outputs of multiple agents through weighted averaging, linear interpolation, or other blending techniques, allowing for nuanced integration of diverse perspectives.
|
||||
|
||||
Implementing effective ensemble techniques requires careful analysis of the strengths, weaknesses, and complementary capabilities of individual agents, as well as the development of robust combination strategies that can optimally leverage the ensemble's collective intelligence.
|
||||
|
||||
### Principle 4: Implement Dynamic Scaling
|
||||
--------------------------------------
|
||||
|
||||
The demand and workload patterns of enterprise LLM deployments can be highly dynamic, with significant fluctuations driven by factors such as user activity, data ingestion schedules, or periodic batch processing. Implementing dynamic scaling strategies allows organizations to optimally provision and allocate resources in response to these fluctuations, ensuring consistent performance while minimizing unnecessary costs.
|
||||
|
||||
### Autoscaling
|
||||
|
||||
Autoscaling is a core capability that enables the automatic adjustment of compute resources (e.g., CPU, GPU, memory) and agent instances based on real-time demand patterns and workload metrics. By dynamically scaling resources up or down, enterprises can maintain optimal performance and resource utilization, avoiding both over-provisioning and under-provisioning scenarios.
|
||||
|
||||
Effective autoscaling in multi-agent LLM deployments requires:
|
||||
|
||||
- **Monitoring and Metrics**: Implementing robust monitoring and metrics collection mechanisms to track key performance indicators (KPIs) such as request rates, response times, resource utilization, and agent-level metrics.
|
||||
- **Scaling Policies**: Defining scaling policies that specify the conditions and thresholds for triggering automatic scaling actions, such as provisioning additional agents or compute resources when certain KPIs are breached.
|
||||
- **Scaling Orchestration**: Integrating autoscaling capabilities with resource orchestration and management tools (e.g., Kubernetes, AWS Auto Scaling) to seamlessly provision, configure, and integrate new resources into the existing multi-agent deployment.
|
||||
|
||||
By automating the scaling process, enterprises can respond rapidly to workload fluctuations, ensuring consistent performance and optimal resource utilization without the need for manual intervention.
|
||||
|
||||
### Spot Instance Utilization
|
||||
|
||||
Many cloud providers offer spot instances or preemptible resources at significantly discounted prices compared to on-demand or reserved instances. While these resources may be reclaimed with little notice, they can be leveraged judiciously within multi-agent LLM deployments to reduce operational costs.
|
||||
|
||||
Strategies for leveraging spot instances include:
|
||||
|
||||
- **Fault-Tolerant Agent Deployment**: Deploying certain agents or components of the multi-agent system on spot instances, while ensuring that these components can be rapidly and seamlessly replaced or migrated in the event of instance preemption.
|
||||
- **Batch Workload Offloading**: Offloading batch processing workloads or non-time-sensitive tasks to spot instances, leveraging their cost-effectiveness while minimizing the impact of potential disruptions.
|
||||
- **Hybrid Provisioning**: Implementing a hybrid approach that combines on-demand or reserved instances for mission-critical components with spot instances for more flexible or elastic workloads.
|
||||
|
||||
Effective spot instance utilization requires careful architectural considerations to ensure fault tolerance and minimize the impact of potential disruptions, as well as robust monitoring and automation capabilities to seamlessly replace or migrate workloads in response to instance preemption events.
|
||||
|
||||
### Serverless Deployments
|
||||
|
||||
Serverless computing platforms, such as AWS Lambda, Google Cloud Functions, or Azure Functions, offer a compelling alternative to traditional server-based deployments. By automatically scaling compute resources based on real-time demand and charging only for the resources consumed, serverless architectures can provide significant cost savings and operational simplicity.
|
||||
|
||||
Leveraging serverless deployments for multi-agent LLM systems can be achieved through:
|
||||
|
||||
- **Function-as-a-Service (FaaS) Agents**: Deploying individual agents or components of the multi-agent system as serverless functions, allowing for rapid and automatic scaling in response to fluctuating workloads.
|
||||
- **Event-Driven Architectures**: Designing the multi-agent system to operate in an event-driven manner, with agents triggered and executed in response to specific events or data ingestion, aligning with the serverless execution model.
|
||||
- **Hybrid Deployments**: Combining serverless components with traditional server-based components, leveraging the strengths and cost advantages of each deployment model for different aspects of the multi-agent system.
|
||||
|
||||
Adopting serverless architectures requires careful consideration of factors such as execution duration limits, cold start latencies, and integration with other components of the multi-agent deployment. However, when implemented effectively, serverless deployments can provide unparalleled scalability, cost-efficiency, and operational simplicity for dynamic, event-driven workloads.
|
||||
|
||||
|
||||
### Principle 5: Employ Selective Execution
|
||||
---------------------------------------
|
||||
|
||||
Not every input or request within a multi-agent LLM deployment requires the full execution of all agents or the complete processing pipeline. Selectively invoking agents or tasks based on input characteristics or intermediate results can significantly optimize performance by avoiding unnecessary computation and resource consumption.
|
||||
|
||||
### Input Filtering
|
||||
|
||||
Implementing input filtering mechanisms allows enterprises to reject or bypass certain inputs before they are processed by the multi-agent system. This can be achieved through techniques such as:
|
||||
|
||||
- **Blacklisting/Whitelisting**: Maintaining lists of inputs (e.g., specific phrases, URLs, or content types) that should be automatically rejected or allowed, based on predefined criteria.
|
||||
- **Rules-Based Filtering**: Defining a set of rules or heuristics to assess the suitability or relevance of an input for further processing, based on factors such as language, content, or metadata.
|
||||
- **Confidence Thresholding**: Leveraging pre-processing agents or models to assess the likelihood that an input is relevant or valuable, and filtering out inputs that fall below a predetermined confidence threshold.
|
||||
|
||||
Effective input filtering requires careful consideration of the specific requirements, constraints, and objectives of your multi-agent deployment, as well as ongoing monitoring and adjustment of filtering rules and thresholds to maintain optimal performance and accuracy.
|
||||
|
||||
### Early Stopping
|
||||
|
||||
In many multi-agent LLM deployments, intermediate results or predictions generated by early-stage agents can be used to determine whether further processing is required or valuable. Early stopping mechanisms allow enterprises to terminate execution pipelines when specific conditions or thresholds are met, avoiding unnecessary downstream processing.
|
||||
|
||||
Techniques for implementing early stopping include:
|
||||
|
||||
- **Confidence-Based Stopping**: Monitoring the confidence scores or probabilities associated with intermediate results, and terminating execution if a predefined confidence threshold is exceeded.
|
||||
- **Exception-Based Stopping**: Defining specific intermediate results or conditions that indicate that further processing is unnecessary or undesirable, and terminating execution upon encountering these exceptions.
|
||||
- **Adaptive Stopping**: Employing machine learning models or reinforcement learning agents to dynamically determine when to terminate execution based on learned patterns and trade-offs between accuracy, latency, and resource consumption.
|
||||
|
||||
Effective early stopping requires a deep understanding of the interdependencies and decision points within your multi-agent workflow, as well as careful tuning and monitoring to ensure that stopping conditions are calibrated to maintain an optimal balance between performance and accuracy.
|
||||
|
||||
### Conditional Branching
|
||||
|
||||
Rather than executing a linear, fixed pipeline of agents, conditional branching allows multi-agent systems to dynamically invoke different agents or execution paths based on input characteristics or intermediate results. This can significantly optimize resource utilization by ensuring that only the necessary agents and processes are executed for a given input or scenario.
|
||||
|
||||
Implementing conditional branching involves:
|
||||
|
||||
- **Decision Points**: Identifying key points within the multi-agent workflow where branching decisions can be made based on input or intermediate data.
|
||||
- **Branching Logic**: Defining the rules, conditions, or machine learning models that will evaluate the input or intermediate data and determine the appropriate execution path or agent invocation.
|
||||
- **Execution Routing**: Integrating mechanisms to dynamically route inputs or intermediate data to the appropriate agents or processes based on the branching decision.
|
||||
|
||||
Conditional branching can be particularly effective in scenarios where inputs or workloads exhibit distinct characteristics or require significantly different processing pipelines, allowing enterprises to optimize resource allocation and minimize unnecessary computation.
|
||||
|
||||
### Principle 6: Optimize User Experience
|
||||
-------------------------------------
|
||||
|
||||
While many of the principles outlined in this guide focus on optimizing backend performance and resource utilization, delivering an exceptional user experience is also a critical consideration for enterprise multi-agent LLM deployments. By minimizing perceived wait times and providing real-time progress updates, organizations can ensure that users remain engaged and satisfied, even during periods of high workload or resource constraints.
|
||||
|
||||
### Streaming Responses
|
||||
|
||||
One of the most effective techniques for minimizing perceived wait times is to stream responses or outputs to users as they are generated, rather than waiting for the entire response to be completed before delivering it. This approach is particularly valuable in conversational agents, document summarization, or other scenarios where outputs can be naturally segmented and delivered incrementally.
|
||||
|
||||
Implementing streaming responses requires:
|
||||
|
||||
- **Partial Output Generation**: Modifying agents or models to generate and emit outputs in a streaming or incremental fashion, rather than producing the entire output in a single, monolithic operation.
|
||||
- **Streaming Data Pipelines**: Integrating streaming data pipelines and message queues to enable the efficient and reliable transmission of partial outputs from agents to user-facing interfaces or applications.
|
||||
- **Incremental Rendering**: Updating user interfaces and displays to incrementally render or populate with newly streamed output segments, providing a seamless and real-time experience for end-users.
|
||||
|
||||
By delivering outputs as they are generated, streaming responses can significantly improve the perceived responsiveness and interactivity of multi-agent LLM deployments, even in scenarios where the overall processing time remains unchanged.
|
||||
|
||||
### Progress Indicators
|
||||
|
||||
In cases where streaming responses may not be feasible or appropriate, providing visual or textual indicators of ongoing processing and progress can help manage user expectations and improve the overall experience. Progress indicators can be implemented through techniques such as:
|
||||
|
||||
- **Loader Animations**: Displaying simple animations or spinner graphics to indicate that processing is underway and provide a sense of activity and progress.
|
||||
- **Progress Bars**: Rendering progress bars or completion indicators based on estimated or actual progress through multi-agent workflows or processing pipelines.
|
||||
- **Status Updates**: Periodically updating user interfaces with textual status messages or descriptions of the current processing stage, providing users with a more detailed understanding of the system's activities.
|
||||
|
||||
Effective progress indicators require careful integration with monitoring and telemetry capabilities to accurately track and communicate the progress of multi-agent workflows, as well as thoughtful user experience design to ensure that indicators are clear, unobtrusive, and aligned with user expectations.
|
||||
|
||||
### Chunked Delivery
|
||||
|
||||
In scenarios where outputs or responses cannot be effectively streamed or rendered incrementally, chunked delivery can provide a middle ground between delivering the entire output at once and streaming individual tokens or characters. By breaking larger outputs into smaller, more manageable chunks and delivering them individually, enterprises can improve perceived responsiveness and provide a more engaging user experience.
|
||||
|
||||
Implementing chunked delivery involves:
|
||||
|
||||
- **Output Segmentation**: Identifying logical breakpoints or segmentation boundaries within larger outputs, such as paragraphs, sections, or other structural elements.
|
||||
- **Chunking Mechanisms**: Integrating mechanisms to efficiently break outputs into individual chunks and transmit or render them sequentially, with minimal delay between chunks.
|
||||
- **Chunk Rendering**: Updating user interfaces or displays to seamlessly render or append new output chunks as they are received, providing a sense of continuous progress and minimizing the perception of extended waiting periods.
|
||||
|
||||
Chunked delivery can be particularly effective in scenarios where outputs are inherently structured or segmented, such as document generation, report creation, or multi-step instructions or workflows.
|
||||
|
||||
## Principle 7: Leverage Hybrid Approaches
|
||||
---------------------------------------
|
||||
|
||||
While multi-agent LLM architectures offer numerous advantages, they should not be viewed as a one-size-fits-all solution. In many cases, combining LLM agents with traditional techniques, optimized components, or external services can yield superior performance, cost-effectiveness, and resource utilization compared to a pure LLM-based approach.
|
||||
|
||||
### Task Offloading
|
||||
|
||||
Certain tasks or subtasks within a larger multi-agent workflow may be more efficiently handled by dedicated, optimized components or external services, rather than relying solely on LLM agents. Task offloading involves identifying these opportunities and integrating the appropriate components or services into the overall architecture.
|
||||
|
||||
Examples of task offloading in multi-agent LLM deployments include:
|
||||
|
||||
- **Regular Expression Matching**: Offloading pattern matching or text extraction tasks to dedicated regular expression engines, which can often outperform LLM-based approaches in terms of speed and efficiency.
|
||||
- **Structured Data Processing**: Leveraging specialized data processing engines or databases for tasks involving structured data, such as querying, filtering, or transforming tabular or relational data.
|
||||
- **External APIs and Services**: Integrating with external APIs or cloud services for specific tasks, such as speech recognition, translation, or knowledge base lookup, leveraging the specialized capabilities and optimizations of these dedicated services.
|
||||
|
||||
Effective task offloading requires a thorough understanding of the strengths and limitations of both LLM agents and traditional components, as well as careful consideration of integration points, data flows, and performance trade-offs within the overall multi-agent architecture.
|
||||
|
||||
### Caching and Indexing
|
||||
|
||||
While LLMs excel at generating dynamic, context-aware outputs, they can be less efficient when dealing with static or frequently accessed information or knowledge. Caching and indexing strategies can help mitigate this limitation by minimizing redundant LLM processing and enabling faster retrieval of commonly accessed data.
|
||||
|
||||
Techniques for leveraging caching and indexing in multi-agent LLM deployments include:
|
||||
|
||||
**Output Caching**: Caching the outputs or responses generated by LLM agents, allowing for rapid retrieval and reuse in cases where the same or similar input is encountered in the future.
|
||||
|
||||
**Knowledge Base Indexing**: Indexing domain-specific knowledge bases, data repositories, or other static information sources using traditional search and information retrieval techniques. This allows LLM agents to efficiently query and incorporate relevant information into their outputs, without needing to process or generate this content from scratch.
|
||||
|
||||
**Contextual Caching**: Caching not only outputs but also the contextual information and intermediate results generated during multi-agent workflows. This enables more efficient reuse and continuation of previous processing in scenarios where contexts are long-lived or recurring.
|
||||
|
||||
Implementing effective caching and indexing strategies requires careful consideration of data freshness, consistency, and invalidation mechanisms, as well as seamless integration with LLM agents and multi-agent workflows to ensure that cached or indexed data is appropriately leveraged and updated.
|
||||
|
||||
### Pre-computation and Lookup
|
||||
|
||||
In certain scenarios, especially those involving constrained or well-defined inputs, pre-computing and lookup strategies can be leveraged to minimize or entirely avoid the need for real-time LLM processing. By generating and storing potential outputs or responses in advance, enterprises can significantly improve performance and reduce resource consumption.
|
||||
|
||||
Approaches for pre-computation and lookup include:
|
||||
|
||||
**Output Pre-generation**: For inputs or scenarios with a limited set of potential outputs, pre-generating and storing all possible responses, allowing for rapid retrieval and delivery without the need for real-time LLM execution.
|
||||
|
||||
**Retrieval-Based Responses**: Developing retrieval models or techniques that can identify and surface pre-computed or curated responses based on input characteristics, leveraging techniques such as nearest neighbor search, embedding-based retrieval, or example-based generation.
|
||||
|
||||
**Hybrid Approaches**: Combining pre-computed or retrieved responses with real-time LLM processing, allowing for the generation of dynamic, context-aware content while still leveraging pre-computed components to optimize performance and resource utilization.
|
||||
|
||||
Effective implementation of pre-computation and lookup strategies requires careful analysis of input patterns, output distributions, and potential performance gains, as well as robust mechanisms for managing and updating pre-computed data as application requirements or domain knowledge evolves.
|
||||
|
||||
# Conclusion
|
||||
----------
|
||||
|
||||
As enterprises increasingly embrace the transformative potential of large language models, optimizing the performance, scalability, and cost-effectiveness of these deployments has become a critical imperative. Multi-agent architectures, which coordinate the collective capabilities of multiple specialized LLM agents, offer a powerful paradigm for addressing these challenges.
|
||||
|
||||
By implementing the seven principles outlined in this guide -- distributing token processing, optimizing agent communication, leveraging agent specialization, implementing dynamic scaling, employing selective execution, optimizing user experience, and leveraging hybrid approaches -- organizations can unlock the full potential of multi-agent LLM deployments.
|
||||
|
||||
However, realizing these benefits requires a strategic and holistic approach that accounts for the unique requirements, constraints, and objectives of each enterprise. From task-specific optimizations and domain adaptation to dynamic scaling and user experience considerations, maximizing the performance of multi-agent LLM systems demands a deep understanding of the underlying technologies, as well as the ability to navigate the inherent complexities of these sophisticated architectures.
|
||||
|
||||
To learn more about how Swarm Corporation can assist your organization in architecting, deploying, and optimizing high-performance multi-agent LLM solutions, we invite you to book a consultation with one of our agent specialists. Visit <https://calendly.com/swarm-corp/30min> to schedule a 30-minute call and explore how our expertise and cutting-edge technologies can drive transformative outcomes for your business.
|
||||
|
||||
In the rapidly evolving landscape of artificial intelligence and natural language processing, staying ahead of the curve is essential. Partner with Swarm Corporation, and unlock the full potential of multi-agent LLM deployments, today.
|
||||
|
||||
[Book a call with us now:](https://calendly.com/swarm-corp/30min)
|
@ -0,0 +1,130 @@
|
||||
# The Swarms Framework: Orchestrating Agents for Enterprise Automation
|
||||
|
||||
In the rapidly evolving landscape of artificial intelligence (AI) and automation, a new paradigm is emerging: the orchestration of multiple agents working in collaboration to tackle complex tasks. This approach, embodied by the Swarms Framework, aims to address the fundamental limitations of individual agents and unlocks the true potential of AI-driven automation in enterprise operations.
|
||||
|
||||
## The Purpose of Swarms: Overcoming Agent Limitations
|
||||
|
||||
Individual agents, while remarkable in their own right, face several inherent challenges that hinder their ability to effectively automate enterprise operations at scale. These limitations include:
|
||||
|
||||
1. Short-Term Memory Constraints
|
||||
2. Hallucination and Factual Inconsistencies
|
||||
3. Single-Task Limitations
|
||||
4. Lack of Collaborative Capabilities
|
||||
5. Cost Inefficiencies
|
||||
|
||||
By orchestrating multiple agents to work in concert, the Swarms Framework directly tackles these limitations, paving the way for more efficient, reliable, and cost-effective enterprise automation.
|
||||
|
||||
### Limitation 1: Short-Term Memory Constraints
|
||||
|
||||
Many AI agents, particularly those based on large language models, suffer from short-term memory constraints. These agents can effectively process and respond to prompts, but their ability to retain and reason over information across multiple interactions or tasks is limited. This limitation can be problematic in enterprise environments, where complex workflows often involve retaining and referencing contextual information over extended periods.
|
||||
|
||||
The Swarms Framework addresses this limitation by leveraging the collective memory of multiple agents working in tandem. While individual agents may have limited short-term memory, their combined memory pool becomes significantly larger, enabling the retention and retrieval of contextual information over extended periods. This collective memory is facilitated by agents specializing in information storage and retrieval, such as those based on systems like Llama Index or Pinecone.
|
||||
|
||||
### Limitation 2: Hallucination and Factual Inconsistencies
|
||||
|
||||
Another challenge faced by many AI agents is the tendency to generate responses that may contain factual inconsistencies or hallucinations -- information that is not grounded in reality or the provided context. This issue can undermine the reliability and trustworthiness of automated systems, particularly in domains where accuracy and consistency are paramount.
|
||||
|
||||
The Swarms Framework mitigates this limitation by employing multiple agents with diverse knowledge bases and capabilities. By leveraging the collective intelligence of these agents, the framework can cross-reference and validate information, reducing the likelihood of hallucinations and factual inconsistencies. Additionally, specialized agents can be tasked with fact-checking and verification, further enhancing the overall reliability of the system.
|
||||
|
||||
### Limitation 3: Single-Task Limitations
|
||||
|
||||
Most individual AI agents are designed and optimized for specific tasks or domains, limiting their ability to handle complex, multi-faceted workflows that often characterize enterprise operations. While an agent may excel at a particular task, such as natural language processing or data analysis, it may struggle with other aspects of a larger workflow, such as task coordination or decision-making.
|
||||
|
||||
The Swarms Framework overcomes this limitation by orchestrating a diverse ensemble of agents, each specializing in different tasks or capabilities. By intelligently combining and coordinating these agents, the framework can tackle complex, multi-threaded workflows that span various domains and task types. This modular approach allows for the seamless integration of new agents as they become available, enabling the continuous expansion and enhancement of the system's capabilities.
|
||||
|
||||
### Limitation 4: Lack of Collaborative Capabilities
|
||||
|
||||
Most AI agents are designed to operate independently, lacking the ability to effectively collaborate with other agents or coordinate their actions towards a common goal. This limitation can hinder the scalability and efficiency of automated systems, particularly in enterprise environments where tasks often require the coordination of multiple agents or systems.
|
||||
|
||||
The Swarms Framework addresses this limitation by introducing a layer of coordination and collaboration among agents. Through specialized coordination agents and communication protocols, the framework enables agents to share information, divide tasks, and synchronize their actions. This collaborative approach not only increases efficiency but also enables the emergence of collective intelligence, where the combined capabilities of multiple agents surpass the sum of their individual abilities.
|
||||
|
||||
### Limitation 5: Cost Inefficiencies
|
||||
|
||||
Running large AI models or orchestrating multiple agents can be computationally expensive, particularly in enterprise environments where scalability and cost-effectiveness are critical considerations. Inefficient resource utilization or redundant computations can quickly escalate costs, making widespread adoption of AI-driven automation financially prohibitive.
|
||||
|
||||
The Swarms Framework tackles this limitation by optimizing resource allocation and workload distribution among agents. By intelligently assigning tasks to the most appropriate agents and leveraging agent specialization, the framework minimizes redundant computations and improves overall resource utilization. Additionally, the framework can dynamically scale agent instances based on demand, ensuring that computational resources are allocated efficiently and costs are minimized.
|
||||
|
||||
## The Swarms Framework: A Holistic Approach to Enterprise Automation
|
||||
|
||||
The Swarms Framework is a comprehensive solution that addresses the limitations of individual agents by orchestrating their collective capabilities. By integrating agents from various frameworks, including LangChain, AutoGPT, Llama Index, and others, the framework leverages the strengths of each agent while mitigating their individual weaknesses.
|
||||
|
||||
At its core, the Swarms Framework operates on the principle of multi-agent collaboration. By introducing specialized coordination agents and communication protocols, the framework enables agents to share information, divide tasks, and synchronize their actions towards a common goal. This collaborative approach not only increases efficiency but also enables the emergence of collective intelligence, where the combined capabilities of multiple agents surpass the sum of their individual abilities.
|
||||
|
||||
The framework's architecture is modular and extensible, allowing for the seamless integration of new agents as they become available. This flexibility ensures that the system's capabilities can continuously expand and adapt to evolving enterprise needs and technological advancements.
|
||||
|
||||
|
||||
## Benefits of the Swarms Framework
|
||||
|
||||
The adoption of the Swarms Framework in enterprise environments offers numerous benefits:
|
||||
|
||||
1. Increased Efficiency and Scalability
|
||||
2. Improved Reliability and Accuracy
|
||||
3. Adaptability and Continuous Improvement
|
||||
4. Cost Optimization
|
||||
5. Enhanced Security and Compliance
|
||||
|
||||
## Increased Efficiency and Scalability
|
||||
|
||||
By orchestrating the collective capabilities of multiple agents, the Swarms Framework enables the efficient execution of complex, multi-threaded workflows. Tasks can be parallelized and distributed across specialized agents, reducing bottlenecks and increasing overall throughput. Additionally, the framework's modular design and ability to dynamically scale agent instances based on demand ensure that the system can adapt to changing workloads and scale seamlessly as enterprise needs evolve.
|
||||
|
||||
## Improved Reliability and Accuracy
|
||||
|
||||
The collaborative nature of the Swarms Framework reduces the risk of hallucinations and factual inconsistencies that can arise from individual agents. By leveraging the collective knowledge and diverse perspectives of multiple agents, the framework can cross-reference and validate information, enhancing the overall reliability and accuracy of its outputs.
|
||||
|
||||
Additionally, the framework's ability to incorporate specialized fact-checking and verification agents further strengthens the trustworthiness of the system's outcomes, ensuring that critical decisions and actions are based on accurate and reliable information.
|
||||
|
||||
## Adaptability and Continuous Improvement
|
||||
|
||||
The modular architecture of the Swarms Framework allows for the seamless integration of new agents as they become available, enabling the continuous expansion and enhancement of the system's capabilities. As new AI models, algorithms, or data sources emerge, the framework can readily incorporate them, ensuring that enterprise operations remain at the forefront of technological advancements.
|
||||
|
||||
Furthermore, the framework's monitoring and analytics capabilities provide valuable insights into system performance, enabling the identification of areas for improvement and the optimization of agent selection, task assignments, and resource allocation strategies over time.
|
||||
|
||||
## Cost Optimization
|
||||
|
||||
By intelligently orchestrating the collaboration of multiple agents, the Swarms Framework optimizes resource utilization and minimizes redundant computations. This efficient use of computational resources translates into cost savings, making the widespread adoption of AI-driven automation more financially viable for enterprises.
|
||||
|
||||
The framework's ability to dynamically scale agent instances based on demand further contributes to cost optimization, ensuring that resources are allocated only when needed and minimizing idle or underutilized instances.
|
||||
|
||||
## Enhanced Security and Compliance
|
||||
|
||||
In enterprise environments, ensuring the security and compliance of automated systems is paramount. The Swarms Framework addresses these concerns by incorporating robust security measures and compliance controls.
|
||||
|
||||
The framework's centralized Memory Manager component enables the implementation of access control mechanisms and data encryption, protecting sensitive information from unauthorized access or breaches. Additionally, the framework's modular design allows for the integration of specialized agents focused on compliance monitoring and auditing, ensuring that enterprise operations adhere to relevant regulations and industry standards.
|
||||
|
||||
## Real-World Applications and Use Cases
|
||||
|
||||
The Swarms Framework finds applications across a wide range of enterprise domains, enabling organizations to automate complex operations and streamline their workflows. Here are some examples of real-world use cases:
|
||||
|
||||
1. Intelligent Process Automation (IPA)
|
||||
2. Customer Service and Support
|
||||
3. Fraud Detection and Risk Management
|
||||
4. Supply Chain Optimization
|
||||
5. Research and Development
|
||||
|
||||
## Intelligent Process Automation (IPA)
|
||||
|
||||
In the realm of business process automation, the Swarms Framework can orchestrate agents to automate and optimize complex workflows spanning multiple domains and task types. By combining agents specialized in areas such as natural language processing, data extraction, decision-making, and task coordination, the framework can streamline and automate processes that traditionally required manual intervention or coordination across multiple systems.
|
||||
|
||||
## Customer Service and Support
|
||||
|
||||
The framework's ability to integrate agents with diverse capabilities, such as natural language processing, knowledge retrieval, and decision-making, makes it well-suited for automating customer service and support operations. Agents can collaborate to understand customer inquiries, retrieve relevant information from knowledge bases, and provide accurate and personalized responses, improving customer satisfaction and reducing operational costs.
|
||||
|
||||
## Fraud Detection and Risk Management
|
||||
|
||||
In the financial and cybersecurity domains, the Swarms Framework can orchestrate agents specialized in data analysis, pattern recognition, and risk assessment to detect and mitigate fraudulent activities or security threats. By combining the collective intelligence of these agents, the framework can identify complex patterns and anomalies that may be difficult for individual agents to detect, enhancing the overall effectiveness of fraud detection and risk management strategies.
|
||||
|
||||
## Supply Chain Optimization
|
||||
|
||||
The complexity of modern supply chains often requires the coordination of multiple systems and stakeholders. The Swarms Framework can integrate agents specialized in areas such as demand forecasting, inventory management, logistics optimization, and supplier coordination to streamline and optimize supply chain operations. By orchestrating the collective capabilities of these agents, the framework can identify bottlenecks, optimize resource allocation, and facilitate seamless collaboration among supply chain partners.
|
||||
|
||||
## Research and Development
|
||||
|
||||
In research and development environments, the Swarms Framework can accelerate innovation by enabling the collaboration of agents specialized in areas such as literature review, data analysis, hypothesis generation, and experiment design. By orchestrating these agents, the framework can facilitate the exploration of new ideas, identify promising research directions, and streamline the iterative process of scientific inquiry.
|
||||
|
||||
# Conclusion
|
||||
|
||||
The Swarms Framework represents a paradigm shift in the field of enterprise automation, addressing the limitations of individual agents by orchestrating their collective capabilities. By integrating agents from various frameworks and enabling multi-agent collaboration, the Swarms Framework overcomes challenges such as short-term memory constraints, hallucinations, single-task limitations, lack of collaboration, and cost inefficiencies.
|
||||
|
||||
Through its modular architecture, centralized coordination, and advanced monitoring and analytics capabilities, the Swarms Framework empowers enterprises to automate complex operations with increased efficiency, reliability, and adaptability. It unlocks the true potential of AI-driven automation, enabling organizations to stay ahead of the curve and thrive in an ever-evolving technological landscape.
|
||||
|
||||
As the field of artificial intelligence continues to advance, the Swarms Framework stands as a robust and flexible solution, ready to embrace new developments and seamlessly integrate emerging agents and capabilities. By harnessing the power of collective intelligence, the framework paves the way for a future where enterprises can leverage the full potential of AI to drive innovation, optimize operations, and gain a competitive edge in their respective industries.
|
@ -1,177 +0,0 @@
|
||||
from typing import List
|
||||
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
from swarms.structs.agent import Agent
|
||||
from swarms.structs.base_swarm import BaseSwarm
|
||||
from swarms.utils.loguru_logger import logger
|
||||
from swarms.models.popular_llms import Anthropic, OpenAIChat
|
||||
from swarms.models.base_llm import BaseLLM
|
||||
from swarms.memory.base_vectordb import BaseVectorDatabase
|
||||
|
||||
|
||||
boss_sys_prompt = (
|
||||
"You're the Swarm Orchestrator, like a project manager of a"
|
||||
" bustling hive. When a task arises, you tap into your network of"
|
||||
" worker agents who are ready to jump into action. Whether it's"
|
||||
" organizing data, handling logistics, or crunching numbers, you"
|
||||
" delegate tasks strategically to maximize efficiency. Picture"
|
||||
" yourself as the conductor of a well-oiled machine,"
|
||||
" orchestrating the workflow seamlessly to achieve optimal"
|
||||
" results with your team of dedicated worker agents."
|
||||
)
|
||||
|
||||
|
||||
class AgentSchema(BaseModel):
|
||||
name: str = Field(
|
||||
...,
|
||||
title="Name of the agent",
|
||||
description="Name of the agent",
|
||||
)
|
||||
system_prompt: str = (
|
||||
Field(
|
||||
...,
|
||||
title="System prompt for the agent",
|
||||
description="System prompt for the agent",
|
||||
),
|
||||
)
|
||||
rules: str = Field(
|
||||
...,
|
||||
title="Rules",
|
||||
description="Rules for the agent",
|
||||
)
|
||||
llm: str = Field(
|
||||
...,
|
||||
title="Language model",
|
||||
description="Language model for the agent: `GPT4` or `Claude",
|
||||
)
|
||||
|
||||
# tools: List[ToolSchema] = Field(
|
||||
# ...,
|
||||
# title="Tools available to the agent",
|
||||
# description="Either `browser` or `terminal`",
|
||||
# )
|
||||
# task: str = Field(
|
||||
# ...,
|
||||
# title="Task assigned to the agent",
|
||||
# description="Task assigned to the agent",
|
||||
# )
|
||||
# TODO: Add more fields here such as the agent's language model, tools, etc.
|
||||
|
||||
|
||||
class HassSchema(BaseModel):
|
||||
plan: str = Field(
|
||||
...,
|
||||
title="Plan to solve the input problem",
|
||||
description="List of steps to solve the problem",
|
||||
)
|
||||
agents: List[AgentSchema] = Field(
|
||||
...,
|
||||
title="List of agents to use for the problem",
|
||||
description="List of agents to use for the problem",
|
||||
)
|
||||
# Rules for the agents
|
||||
rules: str = Field(
|
||||
...,
|
||||
title="Rules for the agents",
|
||||
description="Rules for the agents",
|
||||
)
|
||||
|
||||
|
||||
class HierarchicalSwarm(BaseSwarm):
|
||||
def __init__(
|
||||
self,
|
||||
director: Agent = None,
|
||||
subordinates: List[Agent] = [],
|
||||
workers: List[Agent] = [],
|
||||
director_sys_prompt: str = boss_sys_prompt,
|
||||
director_name: str = "Swarm Orchestrator",
|
||||
director_agent_creation_schema: BaseModel = HassSchema,
|
||||
director_llm: BaseLLM = Anthropic,
|
||||
communication_protocol: BaseVectorDatabase = None,
|
||||
*args,
|
||||
**kwargs,
|
||||
):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.director = director
|
||||
self.subordinates = subordinates
|
||||
self.workers = workers
|
||||
self.director_sys_prompt = director_sys_prompt
|
||||
self.director_name = director_name
|
||||
self.director_agent_creation_schema = (
|
||||
director_agent_creation_schema
|
||||
)
|
||||
self.director_llm = director_llm
|
||||
self.communication_protocol = communication_protocol
|
||||
|
||||
def create_director(self, *args, **kwargs):
|
||||
"""
|
||||
Create the director agent based on the provided schema.
|
||||
"""
|
||||
name = self.director_name
|
||||
system_prompt = self.director_sys_prompt
|
||||
director_llm = self.director_llm
|
||||
|
||||
if director_llm == Anthropic:
|
||||
Anthropic(*args, **kwargs)
|
||||
elif director_llm == OpenAIChat:
|
||||
OpenAIChat(*args, **kwargs)
|
||||
|
||||
logger.info(
|
||||
f"Creating Director Agent: {name} with system prompt:"
|
||||
f" {system_prompt}"
|
||||
)
|
||||
|
||||
director = Agent(
|
||||
agent_name=name,
|
||||
system_prompt=system_prompt,
|
||||
llm=director_llm,
|
||||
max_loops=1,
|
||||
autosave=True,
|
||||
dashboard=False,
|
||||
verbose=True,
|
||||
stopping_token="<DONE>",
|
||||
)
|
||||
|
||||
return director
|
||||
|
||||
def create_worker_agents(
|
||||
agents: List[AgentSchema],
|
||||
) -> List[Agent]:
|
||||
"""
|
||||
Create and initialize agents based on the provided AgentSchema objects.
|
||||
|
||||
Args:
|
||||
agents (List[AgentSchema]): A list of AgentSchema objects containing agent information.
|
||||
|
||||
Returns:
|
||||
List[Agent]: The initialized Agent objects.
|
||||
|
||||
"""
|
||||
agent_list = []
|
||||
for agent in agents:
|
||||
name = agent.name
|
||||
system_prompt = agent.system_prompt
|
||||
|
||||
logger.info(
|
||||
f"Creating agent: {name} with system prompt:"
|
||||
f" {system_prompt}"
|
||||
)
|
||||
|
||||
out = Agent(
|
||||
agent_name=name,
|
||||
system_prompt=system_prompt,
|
||||
# llm=Anthropic(
|
||||
# anthropic_api_key=os.getenv("ANTHROPIC_API_KEY")
|
||||
# ),
|
||||
max_loops=1,
|
||||
autosave=True,
|
||||
dashboard=False,
|
||||
verbose=True,
|
||||
stopping_token="<DONE>",
|
||||
)
|
||||
|
||||
# network.add_agent(out)
|
||||
agent_list.append(out)
|
||||
|
||||
return agent_list
|
@ -0,0 +1,68 @@
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
from swarms import Agent
|
||||
from swarms.models.popular_llms import Anthropic
|
||||
from swarms.tools.openai_tool_creator_decorator import tool
|
||||
|
||||
|
||||
# Importing the search API tool
|
||||
@tool
|
||||
def search_api(query: str) -> str:
|
||||
"""
|
||||
This tool searches the web for information about COVID-19 symptoms.
|
||||
"""
|
||||
return f"Search API tool called with query: {query}"
|
||||
|
||||
|
||||
print(search_api("COVID-19 symptoms"))
|
||||
|
||||
|
||||
# Initialize the schema for the person's information
|
||||
class Schema(BaseModel):
|
||||
name: str = Field(..., title="Name of the person")
|
||||
agent: int = Field(..., title="Age of the person")
|
||||
is_student: bool = Field(..., title="Whether the person is a student")
|
||||
courses: list[str] = Field(
|
||||
..., title="List of courses the person is taking"
|
||||
)
|
||||
|
||||
|
||||
# Convert the schema to a JSON string
|
||||
tool_schema = Schema(
|
||||
name="Tool Name",
|
||||
agent=1,
|
||||
is_student=True,
|
||||
courses=["Course1", "Course2"],
|
||||
)
|
||||
|
||||
# Define the task to generate a person's information
|
||||
task = "Generate a person's information based on the following schema:"
|
||||
|
||||
# Initialize the agent
|
||||
agent = Agent(
|
||||
agent_name="WeatherMan Agent",
|
||||
# Set the tool schema to the JSON string -- this is the key difference
|
||||
tool_schema=tool_schema,
|
||||
llm=Anthropic(),
|
||||
max_loops=3,
|
||||
autosave=True,
|
||||
dashboard=False,
|
||||
streaming_on=True,
|
||||
tools=[], # or list of tools
|
||||
verbose=True,
|
||||
interactive=True,
|
||||
# Set the output type to the tool schema which is a BaseModel
|
||||
output_type=tool_schema, # or dict, or str
|
||||
metadata_output_type="json",
|
||||
# List of schemas that the agent can handle
|
||||
list_tool_schemas=[tool_schema],
|
||||
function_calling_format_type="OpenAI",
|
||||
function_calling_type="json", # or soon yaml
|
||||
execute_tool=True,
|
||||
)
|
||||
|
||||
# Run the agent to generate the person's information
|
||||
generated_data = agent.run(task)
|
||||
|
||||
# Print the generated data
|
||||
print(f"Generated data: {generated_data}")
|
File diff suppressed because one or more lines are too long
@ -1,41 +1,33 @@
|
||||
from swarms import Anthropic, Agent, SequentialWorkflow
|
||||
from swarms import Agent, SequentialWorkflow, Anthropic
|
||||
|
||||
|
||||
# Initialize the language model agent (e.g., GPT-3)
|
||||
|
||||
llm = Anthropic()
|
||||
|
||||
|
||||
# Initialize agents for individual tasks
|
||||
|
||||
agent1 = Agent(
|
||||
agent_name="Blog generator", llm=llm, max_loops=1, dashboard=False
|
||||
agent_name="Blog generator",
|
||||
system_prompt="Generate a blog post like stephen king",
|
||||
llm=llm,
|
||||
max_loops=1,
|
||||
dashboard=False,
|
||||
tools=[],
|
||||
)
|
||||
|
||||
agent2 = Agent(
|
||||
agent_name="summarizer", llm=llm, max_loops=1, dashboard=False
|
||||
agent_name="summarizer",
|
||||
system_prompt="Sumamrize the blog post",
|
||||
llm=llm,
|
||||
max_loops=1,
|
||||
dashboard=False,
|
||||
tools=[],
|
||||
)
|
||||
|
||||
|
||||
# Create the Sequential workflow
|
||||
|
||||
workflow = SequentialWorkflow(
|
||||
max_loops=1, objective="Create a full blog and then summarize it"
|
||||
agents=[agent1, agent2], max_loops=1, verbose=False
|
||||
)
|
||||
|
||||
|
||||
# Add tasks to the workflow
|
||||
|
||||
workflow.add(
|
||||
"Generate a 10,000 word blog on health and wellness.", agent1
|
||||
) # this task will be executed task,
|
||||
|
||||
workflow.add(
|
||||
"Summarize the generated blog", agent2
|
||||
) # then the next agent will accomplish this task
|
||||
|
||||
|
||||
# Run the workflow
|
||||
|
||||
out = workflow.run()
|
||||
print(f"{out}")
|
||||
workflow.run(
|
||||
"Generate a blog post on how swarms of agents can help businesses grow."
|
||||
)
|
||||
|
@ -1,214 +0,0 @@
|
||||
import json
|
||||
from typing import List
|
||||
|
||||
from swarms.tools.tool import BaseTool
|
||||
|
||||
FINISH_NAME = "finish"
|
||||
|
||||
|
||||
class SchemaGenerator:
|
||||
"""A class for generating custom prompt strings.
|
||||
|
||||
Does this based on constraints, commands, resources, and performance evaluations.
|
||||
|
||||
Attributes:
|
||||
constraints (List[str]): A list of constraints.
|
||||
commands (List[BaseTool]): A list of commands.
|
||||
resources (List[str]): A list of resources.
|
||||
performance_evaluation (List[str]): A list of performance evaluations.
|
||||
response_format (dict): A dictionary of the response format.
|
||||
|
||||
Examples:
|
||||
>>> schema_generator = SchemaGenerator()
|
||||
>>> schema_generator.add_constraint("No user assistance")
|
||||
>>> schema_generator.add_resource("Internet access for searches and information gathering.")
|
||||
>>> schema_generator.add_performance_evaluation("Continuously review and analyze your actions to ensure you are performing to the best of your abilities.")
|
||||
>>> prompt_string = schema_generator.generate_prompt_string()
|
||||
>>> print(prompt_string)
|
||||
"""
|
||||
|
||||
def __init__(self) -> None:
|
||||
"""Initialize the SchemaGenerator object.
|
||||
|
||||
Starts with empty lists of constraints, commands, resources,
|
||||
and performance evaluations.
|
||||
"""
|
||||
self.constraints: List[str] = []
|
||||
self.commands: List[BaseTool] = []
|
||||
self.resources: List[str] = []
|
||||
self.performance_evaluation: List[str] = []
|
||||
self.response_format = {
|
||||
"thoughts": {
|
||||
"text": "thought",
|
||||
"reasoning": "reasoning",
|
||||
"plan": (
|
||||
"- short bulleted\n- list that conveys\n-"
|
||||
" long-term plan"
|
||||
),
|
||||
"criticism": "constructive self-criticism",
|
||||
"speak": "thoughts summary to say to user",
|
||||
},
|
||||
"command": {
|
||||
"name": "command name",
|
||||
"args": {"arg name": "value"},
|
||||
},
|
||||
}
|
||||
|
||||
def add_constraint(self, constraint: str) -> None:
|
||||
"""
|
||||
Add a constraint to the constraints list.
|
||||
|
||||
Args:
|
||||
constraint (str): The constraint to be added.
|
||||
"""
|
||||
self.constraints.append(constraint)
|
||||
|
||||
def add_tool(self, tool: BaseTool) -> None:
|
||||
self.commands.append(tool)
|
||||
|
||||
def _generate_command_string(self, tool: BaseTool) -> str:
|
||||
output = f"{tool.name}: {tool.description}"
|
||||
output += f", args json schema: {json.dumps(tool.args)}"
|
||||
return output
|
||||
|
||||
def add_resource(self, resource: str) -> None:
|
||||
"""
|
||||
Add a resource to the resources list.
|
||||
|
||||
Args:
|
||||
resource (str): The resource to be added.
|
||||
"""
|
||||
self.resources.append(resource)
|
||||
|
||||
def add_performance_evaluation(self, evaluation: str) -> None:
|
||||
"""
|
||||
Add a performance evaluation item to the performance_evaluation list.
|
||||
|
||||
Args:
|
||||
evaluation (str): The evaluation item to be added.
|
||||
"""
|
||||
self.performance_evaluation.append(evaluation)
|
||||
|
||||
def _generate_numbered_list(
|
||||
self, items: list, item_type: str = "list"
|
||||
) -> str:
|
||||
"""
|
||||
Generate a numbered list from given items based on the item_type.
|
||||
|
||||
Args:
|
||||
items (list): A list of items to be numbered.
|
||||
item_type (str, optional): The type of items in the list.
|
||||
Defaults to 'list'.
|
||||
|
||||
Returns:
|
||||
str: The formatted numbered list.
|
||||
"""
|
||||
if item_type == "command":
|
||||
command_strings = [
|
||||
f"{i + 1}. {self._generate_command_string(item)}"
|
||||
for i, item in enumerate(items)
|
||||
]
|
||||
finish_description = (
|
||||
"use this to signal that you have finished all your"
|
||||
" objectives"
|
||||
)
|
||||
finish_args = (
|
||||
'"response": "final response to let '
|
||||
'people know you have finished your objectives"'
|
||||
)
|
||||
finish_string = (
|
||||
f"{len(items) + 1}. {FINISH_NAME}: "
|
||||
f"{finish_description}, args: {finish_args}"
|
||||
)
|
||||
return "\n".join(command_strings + [finish_string])
|
||||
else:
|
||||
return "\n".join(
|
||||
f"{i+1}. {item}" for i, item in enumerate(items)
|
||||
)
|
||||
|
||||
def generate_prompt_string(self) -> str:
|
||||
"""Generate a prompt string.
|
||||
|
||||
Returns:
|
||||
str: The generated prompt string.
|
||||
"""
|
||||
formatted_response_format = json.dumps(
|
||||
self.response_format, indent=4
|
||||
)
|
||||
prompt_string = (
|
||||
f"Constraints:\n{self._generate_numbered_list(self.constraints)}\n\nCommands:\n{self._generate_numbered_list(self.commands, item_type='command')}\n\nResources:\n{self._generate_numbered_list(self.resources)}\n\nPerformance"
|
||||
f" Evaluation:\n{self._generate_numbered_list(self.performance_evaluation)}\n\nYou"
|
||||
" should only respond in JSON format as described below"
|
||||
" \nResponse Format:"
|
||||
f" \n{formatted_response_format} \nEnsure the response"
|
||||
" can be parsed by Python json.loads"
|
||||
)
|
||||
|
||||
return prompt_string
|
||||
|
||||
|
||||
def get_prompt(tools: List[BaseTool]) -> str:
|
||||
"""Generates a prompt string.
|
||||
|
||||
It includes various constraints, commands, resources, and performance evaluations.
|
||||
|
||||
Returns:
|
||||
str: The generated prompt string.
|
||||
"""
|
||||
|
||||
# Initialize the SchemaGenerator object
|
||||
schema_generator = SchemaGenerator()
|
||||
|
||||
# Add constraints to the SchemaGenerator object
|
||||
schema_generator.add_constraint(
|
||||
"~4000 word limit for short term memory. "
|
||||
"Your short term memory is short, "
|
||||
"so immediately save important information to files."
|
||||
)
|
||||
schema_generator.add_constraint(
|
||||
"If you are unsure how you previously did something "
|
||||
"or want to recall past events, "
|
||||
"thinking about similar events will help you remember."
|
||||
)
|
||||
schema_generator.add_constraint("No user assistance")
|
||||
schema_generator.add_constraint(
|
||||
"Exclusively use the commands listed in double quotes e.g."
|
||||
' "command name"'
|
||||
)
|
||||
|
||||
# Add commands to the SchemaGenerator object
|
||||
for tool in tools:
|
||||
schema_generator.add_tool(tool)
|
||||
|
||||
# Add resources to the SchemaGenerator object
|
||||
schema_generator.add_resource(
|
||||
"Internet access for searches and information gathering."
|
||||
)
|
||||
schema_generator.add_resource("Long Term memory management.")
|
||||
schema_generator.add_resource(
|
||||
"GPT-3.5 powered Agents for delegation of simple tasks."
|
||||
)
|
||||
schema_generator.add_resource("File output.")
|
||||
|
||||
# Add performance evaluations to the SchemaGenerator object
|
||||
schema_generator.add_performance_evaluation(
|
||||
"Continuously review and analyze your actions "
|
||||
"to ensure you are performing to the best of your abilities."
|
||||
)
|
||||
schema_generator.add_performance_evaluation(
|
||||
"Constructively self-criticize your big-picture behavior"
|
||||
" constantly."
|
||||
)
|
||||
schema_generator.add_performance_evaluation(
|
||||
"Reflect on past decisions and strategies to refine your"
|
||||
" approach."
|
||||
)
|
||||
schema_generator.add_performance_evaluation(
|
||||
"Every command has a cost, so be smart and efficient. "
|
||||
"Aim to complete tasks in the least number of steps."
|
||||
)
|
||||
|
||||
# Generate the prompt string
|
||||
prompt_string = schema_generator.generate_prompt_string()
|
||||
|
||||
return prompt_string
|
@ -1,231 +0,0 @@
|
||||
import logging
|
||||
from collections import defaultdict
|
||||
from typing import Callable, Sequence
|
||||
from swarms.structs.agent import Agent
|
||||
from swarms.structs.base_swarm import BaseSwarm
|
||||
|
||||
|
||||
# Assuming the existence of an appropriate Agent class and logger setup
|
||||
class AgentRearrange(BaseSwarm):
|
||||
def __init__(
|
||||
self,
|
||||
agents: Sequence[Agent] = None,
|
||||
verbose: bool = False,
|
||||
custom_prompt: str = None,
|
||||
callbacks: Sequence[Callable] = None,
|
||||
*args,
|
||||
**kwargs,
|
||||
):
|
||||
super().__init__()
|
||||
if not all(isinstance(agent, Agent) for agent in agents):
|
||||
raise ValueError(
|
||||
"All elements must be instances of the Agent class."
|
||||
)
|
||||
self.agents = agents
|
||||
self.verbose = verbose
|
||||
self.custom_prompt = custom_prompt
|
||||
self.callbacks = callbacks if callbacks is not None else []
|
||||
self.flows = defaultdict(list)
|
||||
|
||||
def parse_pattern(self, pattern: str):
|
||||
"""
|
||||
Parse the interaction pattern to set up task flows, supporting both sequential
|
||||
and concurrent executions within the same pattern.
|
||||
"""
|
||||
try:
|
||||
self.flows.clear() # Ensure flows are reset each time pattern is parsed
|
||||
# Split pattern into potentially concurrent flows
|
||||
concurrent_flows = pattern.split(",")
|
||||
for flow in concurrent_flows:
|
||||
# Trim whitespace and identify sequential parts within each concurrent flow
|
||||
parts = [part.strip() for part in flow.split("->")]
|
||||
if len(parts) > 1:
|
||||
# Link each part sequentially to the next as source -> destination
|
||||
for i in range(len(parts) - 1):
|
||||
source = parts[i]
|
||||
destination = parts[i + 1]
|
||||
# Validate and add each sequential link
|
||||
if source not in [
|
||||
agent.agent_name for agent in self.agents
|
||||
]:
|
||||
logging.error(
|
||||
f"Source agent {source} not found."
|
||||
)
|
||||
return False
|
||||
if destination not in [
|
||||
agent.agent_name for agent in self.agents
|
||||
]:
|
||||
logging.error(
|
||||
f"Destination agent {destination} not"
|
||||
" found."
|
||||
)
|
||||
return False
|
||||
self.flows[source].append(destination)
|
||||
else:
|
||||
# Handle single agent case if needed
|
||||
self.flows[parts[0]] = []
|
||||
|
||||
return True
|
||||
except Exception as e:
|
||||
logging.error(f"Error parsing pattern: {e}")
|
||||
return False
|
||||
|
||||
def self_find_agent_by_name(self, name: str):
|
||||
for agent in self.agents:
|
||||
if agent.agent_name == name:
|
||||
return agent
|
||||
return None
|
||||
|
||||
def agent_exists(self, name: str):
|
||||
for agent in self.agents:
|
||||
if agent.agent_name == name:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def parse_concurrent_flow(
|
||||
self,
|
||||
flow: str,
|
||||
):
|
||||
sequential_agents = flow.split("->")
|
||||
for i, source_name in enumerate(sequential_agents[:-1]):
|
||||
destination_name = sequential_agents[i + 1].strip()
|
||||
self.parse_sequential_flow(
|
||||
source_name.strip(), destination_name
|
||||
)
|
||||
|
||||
def parse_sequential_flow(
|
||||
self,
|
||||
source: str,
|
||||
destination: str,
|
||||
):
|
||||
if not self.self_find_agent_by_name(
|
||||
source
|
||||
) or not self.self_find_agent_by_name(destination):
|
||||
return False
|
||||
self.flows[source].append(destination)
|
||||
|
||||
def execute_task(
|
||||
self,
|
||||
dest_agent_name: str,
|
||||
source: str,
|
||||
task: str,
|
||||
specific_tasks: dict,
|
||||
):
|
||||
dest_agent = self.self_find_agent_by_name(dest_agent_name)
|
||||
if not dest_agent:
|
||||
return None
|
||||
task_to_run = specific_tasks.get(dest_agent_name, task)
|
||||
if self.custom_prompt:
|
||||
out = dest_agent.run(f"{task_to_run} {self.custom_prompt}")
|
||||
else:
|
||||
out = dest_agent.run(f"{task_to_run} (from {source})")
|
||||
return out
|
||||
|
||||
def process_flows(self, pattern, default_task, specific_tasks):
|
||||
if not self.parse_pattern(pattern):
|
||||
return None
|
||||
|
||||
results = []
|
||||
for source, destinations in self.flows.items():
|
||||
if not destinations:
|
||||
task = specific_tasks.get(source, default_task)
|
||||
source_agent = self.self_find_agent_by_name(source)
|
||||
if source_agent:
|
||||
result = source_agent.run(task)
|
||||
results.append(result)
|
||||
else:
|
||||
for destination in destinations:
|
||||
task = specific_tasks.get(destination, default_task)
|
||||
destination_agent = self.self_find_agent_by_name(
|
||||
destination
|
||||
)
|
||||
if destination_agent:
|
||||
result = destination_agent.run(task)
|
||||
results.append(result)
|
||||
return results
|
||||
|
||||
def __call__(
|
||||
self,
|
||||
pattern: str = None,
|
||||
default_task: str = None,
|
||||
**specific_tasks,
|
||||
):
|
||||
self.flows.clear() # Reset previous flows
|
||||
results = self.process_flows(pattern, default_task, specific_tasks)
|
||||
return results
|
||||
|
||||
|
||||
# ## Initialize the workflow
|
||||
# agent = Agent(
|
||||
# agent_name="t",
|
||||
# agent_description=(
|
||||
# "Generate a transcript for a youtube video on what swarms"
|
||||
# " are!"
|
||||
# ),
|
||||
# system_prompt=(
|
||||
# "Generate a transcript for a youtube video on what swarms"
|
||||
# " are!"
|
||||
# ),
|
||||
# llm=Anthropic(),
|
||||
# max_loops=1,
|
||||
# autosave=True,
|
||||
# dashboard=False,
|
||||
# streaming_on=True,
|
||||
# verbose=True,
|
||||
# stopping_token="<DONE>",
|
||||
# )
|
||||
|
||||
# agent2 = Agent(
|
||||
# agent_name="t1",
|
||||
# agent_description=(
|
||||
# "Generate a transcript for a youtube video on what swarms"
|
||||
# " are!"
|
||||
# ),
|
||||
# llm=Anthropic(),
|
||||
# max_loops=1,
|
||||
# system_prompt="Summarize the transcript",
|
||||
# autosave=True,
|
||||
# dashboard=False,
|
||||
# streaming_on=True,
|
||||
# verbose=True,
|
||||
# stopping_token="<DONE>",
|
||||
# )
|
||||
|
||||
# agent3 = Agent(
|
||||
# agent_name="t2",
|
||||
# agent_description=(
|
||||
# "Generate a transcript for a youtube video on what swarms"
|
||||
# " are!"
|
||||
# ),
|
||||
# llm=Anthropic(),
|
||||
# max_loops=1,
|
||||
# system_prompt="Finalize the transcript",
|
||||
# autosave=True,
|
||||
# dashboard=False,
|
||||
# streaming_on=True,
|
||||
# verbose=True,
|
||||
# stopping_token="<DONE>",
|
||||
# )
|
||||
|
||||
|
||||
# # Rearrange the agents
|
||||
# rearrange = AgentRearrange(
|
||||
# agents=[agent, agent2, agent3],
|
||||
# verbose=True,
|
||||
# # custom_prompt="Summarize the transcript",
|
||||
# )
|
||||
|
||||
# # Run the workflow on a task
|
||||
# results = rearrange(
|
||||
# # pattern="t -> t1, t2 -> t2",
|
||||
# pattern="t -> t1 -> t2",
|
||||
# default_task=(
|
||||
# "Generate a transcript for a YouTube video on what swarms"
|
||||
# " are!"
|
||||
# ),
|
||||
# t="Generate a transcript for a YouTube video on what swarms are!",
|
||||
# # t2="Summarize the transcript",
|
||||
# # t3="Finalize the transcript",
|
||||
# )
|
||||
# # print(results)
|
@ -1,148 +1,223 @@
|
||||
import logging
|
||||
from collections import defaultdict
|
||||
from swarms.utils.loguru_logger import logger
|
||||
from swarms.structs.agent import Agent
|
||||
from typing import Sequence, Callable
|
||||
from typing import List
|
||||
from swarms.structs.base_swarm import BaseSwarm
|
||||
from swarms.utils.loguru_logger import logger
|
||||
|
||||
|
||||
class AgentRearrange(BaseSwarm):
|
||||
"""
|
||||
A class representing a swarm of agents for rearranging tasks.
|
||||
|
||||
Attributes:
|
||||
agents (dict): A dictionary of agents, where the key is the agent's name and the value is the agent object.
|
||||
flow (str): The flow pattern of the tasks.
|
||||
|
||||
Methods:
|
||||
__init__(agents: List[Agent] = None, flow: str = None): Initializes the AgentRearrange object.
|
||||
add_agent(agent: Agent): Adds an agent to the swarm.
|
||||
remove_agent(agent_name: str): Removes an agent from the swarm.
|
||||
add_agents(agents: List[Agent]): Adds multiple agents to the swarm.
|
||||
validate_flow(): Validates the flow pattern.
|
||||
run(task): Runs the swarm to rearrange the tasks.
|
||||
"""
|
||||
|
||||
class AgentRearrange:
|
||||
def __init__(
|
||||
self,
|
||||
agents: Sequence[Agent] = None,
|
||||
verbose: bool = False,
|
||||
custom_prompt: str = None,
|
||||
callbacks: Sequence[Callable] = None,
|
||||
*args,
|
||||
**kwargs,
|
||||
agents: List[Agent] = None,
|
||||
flow: str = None,
|
||||
max_loops: int = 1,
|
||||
verbose: bool = True,
|
||||
):
|
||||
"""
|
||||
Initialize the AgentRearrange class.
|
||||
Initializes the AgentRearrange object.
|
||||
|
||||
Args:
|
||||
agents (Sequence[Agent], optional): A sequence of Agent objects. Defaults to None.
|
||||
verbose (bool, optional): Whether to enable verbose mode. Defaults to False.
|
||||
custom_prompt (str, optional): A custom prompt string. Defaults to None.
|
||||
callbacks (Sequence[Callable], optional): A sequence of callback functions. Defaults to None.
|
||||
*args: Variable length argument list.
|
||||
**kwargs: Arbitrary keyword arguments.
|
||||
agents (List[Agent], optional): A list of Agent objects. Defaults to None.
|
||||
flow (str, optional): The flow pattern of the tasks. Defaults to None.
|
||||
"""
|
||||
if not all(isinstance(agent, Agent) for agent in agents):
|
||||
raise ValueError(
|
||||
"All elements must be instances of the Agent class."
|
||||
)
|
||||
self.agents = agents
|
||||
self.agents = {agent.name: agent for agent in agents}
|
||||
self.flow = flow
|
||||
self.verbose = verbose
|
||||
self.custom_prompt = custom_prompt
|
||||
self.callbacks = callbacks if callbacks is not None else []
|
||||
self.flows = defaultdict(list)
|
||||
self.max_loops = max_loops
|
||||
|
||||
def parse_pattern(self, pattern: str):
|
||||
if verbose is True:
|
||||
logger.add("agent_rearrange.log")
|
||||
|
||||
def add_agent(self, agent: Agent):
|
||||
"""
|
||||
Parse the interaction pattern and setup task flows.
|
||||
Adds an agent to the swarm.
|
||||
|
||||
Args:
|
||||
pattern (str): The interaction pattern to parse.
|
||||
|
||||
Returns:
|
||||
bool: True if the pattern parsing is successful, False otherwise.
|
||||
agent (Agent): The agent to be added.
|
||||
"""
|
||||
try:
|
||||
for flow in pattern.split(","):
|
||||
parts = [part.strip() for part in flow.split("->")]
|
||||
if len(parts) != 2:
|
||||
logging.error(
|
||||
f"Invalid flow pattern: {flow}. Each flow"
|
||||
" must have exactly one '->'."
|
||||
)
|
||||
return False
|
||||
|
||||
source_name, destinations_str = parts
|
||||
source = self.find_agent_by_name(source_name)
|
||||
if source is None:
|
||||
logging.error(f"Source agent {source_name} not found.")
|
||||
return False
|
||||
|
||||
destinations_names = destinations_str.split()
|
||||
for dest_name in destinations_names:
|
||||
dest = self.find_agent_by_name(dest_name)
|
||||
if dest is None:
|
||||
logging.error(
|
||||
f"Destination agent {dest_name} not" " found."
|
||||
)
|
||||
return False
|
||||
self.flows[source.agent_name].append(dest.agent_name)
|
||||
return True
|
||||
except Exception as e:
|
||||
logger.error(f"Error: {e}")
|
||||
raise e
|
||||
|
||||
def self_find_agen_by_name(self, name: str):
|
||||
logger.info(f"Adding agent {agent.name} to the swarm.")
|
||||
self.agents[agent.name] = agent
|
||||
|
||||
def remove_agent(self, agent_name: str):
|
||||
"""
|
||||
Find an agent by its name.
|
||||
Removes an agent from the swarm.
|
||||
|
||||
Args:
|
||||
name (str): The name of the agent to find.
|
||||
|
||||
Returns:
|
||||
Agent: The Agent object if found, None otherwise.
|
||||
agent_name (str): The name of the agent to be removed.
|
||||
"""
|
||||
for agent in self.agents:
|
||||
if agent.agent_name == name:
|
||||
return agent
|
||||
return None
|
||||
del self.agents[agent_name]
|
||||
|
||||
def __call__(
|
||||
self,
|
||||
agents: Sequence[Agent] = None,
|
||||
pattern: str = None,
|
||||
task: str = None,
|
||||
**tasks,
|
||||
):
|
||||
def add_agents(self, agents: List[Agent]):
|
||||
"""
|
||||
Execute the task based on the specified pattern.
|
||||
Adds multiple agents to the swarm.
|
||||
|
||||
Args:
|
||||
agents (Sequence[Agent], optional): A sequence of Agent objects. Defaults to None.
|
||||
pattern (str, optional): The interaction pattern to follow. Defaults to None.
|
||||
task (str, optional): The task to execute. Defaults to None.
|
||||
**tasks: Additional tasks specified as keyword arguments.
|
||||
agents (List[Agent]): A list of Agent objects.
|
||||
"""
|
||||
try:
|
||||
if agents:
|
||||
self.flows.clear() # Reset previous flows
|
||||
if not self.parse_pattern(pattern):
|
||||
return # Pattern parsing failed
|
||||
|
||||
for source, destinations in self.flows.items():
|
||||
for dest in destinations:
|
||||
dest_agent = self.self_find_agen_by_name(dest)
|
||||
task = tasks.get(dest, task)
|
||||
|
||||
if self.custom_prompt:
|
||||
dest_agent.run(f"{task} {self.custom_prompt}")
|
||||
else:
|
||||
dest_agent.run(f"{task} (from {source})")
|
||||
# else:
|
||||
# raise ValueError(
|
||||
# "No agents provided. Please provide agents to"
|
||||
# " execute the task."
|
||||
# )
|
||||
except Exception as e:
|
||||
logger.error(
|
||||
f"Error: {e} try again by providing agents and" " pattern"
|
||||
for agent in agents:
|
||||
self.agents[agent.name] = agent
|
||||
|
||||
def validate_flow(self):
|
||||
"""
|
||||
Validates the flow pattern.
|
||||
|
||||
Raises:
|
||||
ValueError: If the flow pattern is incorrectly formatted or contains duplicate agent names.
|
||||
|
||||
Returns:
|
||||
bool: True if the flow pattern is valid.
|
||||
"""
|
||||
if "->" not in self.flow:
|
||||
raise ValueError(
|
||||
"Flow must include '->' to denote the direction of the task."
|
||||
)
|
||||
|
||||
agents_in_flow = []
|
||||
tasks = self.flow.split("->")
|
||||
for task in tasks:
|
||||
agent_names = [name.strip() for name in task.split(",")]
|
||||
for agent_name in agent_names:
|
||||
if agent_name not in self.agents:
|
||||
raise ValueError(
|
||||
f"Agent '{agent_name}' is not registered."
|
||||
)
|
||||
agents_in_flow.append(agent_name)
|
||||
|
||||
if len(set(agents_in_flow)) != len(agents_in_flow):
|
||||
raise ValueError(
|
||||
"Duplicate agent names in the flow are not allowed."
|
||||
)
|
||||
raise e
|
||||
|
||||
print("Flow is valid.")
|
||||
return True
|
||||
|
||||
def run(self, task: str, *args, **kwargs):
|
||||
"""
|
||||
Runs the swarm to rearrange the tasks.
|
||||
|
||||
# # Example usage
|
||||
# try:
|
||||
# agents = [
|
||||
# Agent(agent_name=f"b{i}") for i in range(1, 4)
|
||||
# ] # Creating agents b1, b2, b3
|
||||
# agents.append(Agent(agent_name="d")) # Adding agent d
|
||||
# rearranger = Rearrange(agents)
|
||||
Args:
|
||||
task: The initial task to be processed.
|
||||
|
||||
# # Specifying a complex pattern for task execution
|
||||
# rearranger.execute("d -> b1 b2 b3, b2 -> b3", "Analyze data")
|
||||
# except ValueError as e:
|
||||
# logging.error(e)
|
||||
Returns:
|
||||
str: The final processed task.
|
||||
"""
|
||||
if not self.validate_flow():
|
||||
return "Invalid flow configuration."
|
||||
|
||||
tasks = self.flow.split("->")
|
||||
current_task = task
|
||||
|
||||
for task in tasks:
|
||||
agent_names = [name.strip() for name in task.split(",")]
|
||||
if len(agent_names) > 1:
|
||||
# Parallel processing
|
||||
logger.info(f"Running agents in parallel: {agent_names}")
|
||||
results = []
|
||||
for agent_name in agent_names:
|
||||
agent = self.agents[agent_name]
|
||||
result = agent.run(current_task, *args, **kwargs)
|
||||
results.append(result)
|
||||
current_task = "; ".join(results)
|
||||
else:
|
||||
# Sequential processing
|
||||
logger.info(f"Running agents sequentially: {agent_names}")
|
||||
agent = self.agents[agent_names[0]]
|
||||
current_task = agent.run(current_task, *args, **kwargs)
|
||||
|
||||
return current_task
|
||||
|
||||
|
||||
def rearrange(
|
||||
agents: List[Agent], flow: str, task: str = None, *args, **kwargs
|
||||
):
|
||||
"""
|
||||
Rearranges the given list of agents based on the specified flow.
|
||||
|
||||
Parameters:
|
||||
agents (List[Agent]): The list of agents to be rearranged.
|
||||
flow (str): The flow used for rearranging the agents.
|
||||
task (str, optional): The task to be performed during rearrangement. Defaults to None.
|
||||
*args: Additional positional arguments.
|
||||
**kwargs: Additional keyword arguments.
|
||||
|
||||
Returns:
|
||||
The result of running the agent system with the specified task.
|
||||
|
||||
Example:
|
||||
agents = [agent1, agent2, agent3]
|
||||
flow = "agent1 -> agent2, agent3"
|
||||
task = "Perform a task"
|
||||
rearrange(agents, flow, task)
|
||||
"""
|
||||
agent_system = AgentRearrange(
|
||||
agents=agents, flow=flow, *args, **kwargs
|
||||
)
|
||||
return agent_system.run(task, *args, **kwargs)
|
||||
|
||||
|
||||
# # Initialize the director agent
|
||||
# director = Agent(
|
||||
# agent_name="Director",
|
||||
# system_prompt="Directs the tasks for the workers",
|
||||
# llm=Anthropic(),
|
||||
# max_loops=1,
|
||||
# dashboard=False,
|
||||
# streaming_on=True,
|
||||
# verbose=True,
|
||||
# stopping_token="<DONE>",
|
||||
# state_save_file_type="json",
|
||||
# saved_state_path="director.json",
|
||||
# )
|
||||
|
||||
# # Initialize worker 1
|
||||
# worker1 = Agent(
|
||||
# agent_name="Worker1",
|
||||
# system_prompt="Generates a transcript for a youtube video on what swarms are",
|
||||
# llm=Anthropic(),
|
||||
# max_loops=1,
|
||||
# dashboard=False,
|
||||
# streaming_on=True,
|
||||
# verbose=True,
|
||||
# stopping_token="<DONE>",
|
||||
# state_save_file_type="json",
|
||||
# saved_state_path="worker1.json",
|
||||
# )
|
||||
|
||||
# # Initialize worker 2
|
||||
# worker2 = Agent(
|
||||
# agent_name="Worker2",
|
||||
# system_prompt="Summarizes the transcript generated by Worker1",
|
||||
# llm=Anthropic(),
|
||||
# max_loops=1,
|
||||
# dashboard=False,
|
||||
# streaming_on=True,
|
||||
# verbose=True,
|
||||
# stopping_token="<DONE>",
|
||||
# state_save_file_type="json",
|
||||
# saved_state_path="worker2.json",
|
||||
# )
|
||||
|
||||
|
||||
# flow = "Director -> Worker1 -> Worker2"
|
||||
# agent_system = AgentRearrange(
|
||||
# agents=[director, worker1, worker2], flow=flow
|
||||
# )
|
||||
# # Run the system
|
||||
# output = agent_system.run(
|
||||
# "Create a format to express and communicate swarms of llms in a structured manner for youtube"
|
||||
# )
|
||||
|
@ -1,107 +1,91 @@
|
||||
from dataclasses import dataclass, field
|
||||
from typing import List, Optional
|
||||
from swarms.structs.agent import Agent
|
||||
from swarms.structs.conversation import Conversation
|
||||
import time
|
||||
import json
|
||||
|
||||
from swarms.utils.loguru_logger import logger
|
||||
from swarms.utils.try_except_wrapper import try_except_wrapper
|
||||
from swarms.structs.base_workflow import BaseWorkflow
|
||||
from pydantic import BaseModel, Field
|
||||
from typing import List, Dict
|
||||
from swarms.structs.agent import Agent
|
||||
|
||||
|
||||
@dataclass
|
||||
class StepSequentialWorkflow(BaseModel):
|
||||
agent_names: List[str] = Field(
|
||||
..., description="List of agent names to include in the workflow."
|
||||
)
|
||||
max_loops: int = Field(
|
||||
1, description="Maximum number of loops to run the workflow."
|
||||
)
|
||||
verbose: bool = Field(
|
||||
False, description="Whether to log debug information."
|
||||
)
|
||||
steps: Dict = Field(
|
||||
...,
|
||||
description="Dictionary of steps for the workflow with each agent and its parameters.",
|
||||
)
|
||||
time: str = Field(
|
||||
time.strftime("%Y-%m-%d %H:%M:%S"),
|
||||
description="Time of the workflow.",
|
||||
)
|
||||
|
||||
|
||||
# Define a class to handle the sequential workflow
|
||||
class SequentialWorkflow(BaseWorkflow):
|
||||
name: str = "Sequential Workflow"
|
||||
description: str = None
|
||||
objective: str = None
|
||||
max_loops: int = 1
|
||||
autosave: bool = False
|
||||
saved_state_filepath: Optional[str] = "sequential_workflow_state.json"
|
||||
restore_state_filepath: Optional[str] = None
|
||||
dashboard: bool = False
|
||||
agent_pool: List[Agent] = field(default_factory=list)
|
||||
# task_pool: List[str] = field(
|
||||
# default_factory=list
|
||||
# ) # List to store tasks
|
||||
|
||||
def __post_init__(self):
|
||||
super().__init__()
|
||||
self.conversation = Conversation(
|
||||
time_enabled=True,
|
||||
autosave=True,
|
||||
def __init__(
|
||||
self,
|
||||
agents: List[Agent] = None,
|
||||
max_loops: int = 2,
|
||||
verbose: bool = False,
|
||||
*args,
|
||||
**kwargs,
|
||||
):
|
||||
"""
|
||||
Initializes a SequentialWorkflow with a list of agents.
|
||||
|
||||
:param agents: List of agents to include in the workflow.
|
||||
"""
|
||||
self.agents = agents
|
||||
self.max_loops = max_loops
|
||||
|
||||
if verbose:
|
||||
logger.add("sequential_workflow.log", level="DEBUG")
|
||||
|
||||
if not self.agents:
|
||||
raise ValueError("No agents provided for workflow")
|
||||
|
||||
if not self.max_loops:
|
||||
self.max_loops = 1
|
||||
|
||||
# Log all the agents in the workflow
|
||||
logger.info(
|
||||
f"Initialized SequentialWorkflow with agents: {json.dumps([str(agent.agent_name) for agent in self.agents])}"
|
||||
)
|
||||
|
||||
# If objective exists then set it
|
||||
if self.objective is not None:
|
||||
self.conversation.system_prompt = self.objective
|
||||
|
||||
def workflow_bootup(self):
|
||||
logger.info(f"{self.name} is activating...")
|
||||
|
||||
for agent in self.agent_pool:
|
||||
logger.info(f"Agent {agent.agent_name} Activated")
|
||||
|
||||
@try_except_wrapper
|
||||
def add(self, task: str, agent: Agent, *args, **kwargs):
|
||||
self.agent_pool.append(agent)
|
||||
# self.task_pool.append(
|
||||
# task
|
||||
# ) # Store tasks corresponding to each agent
|
||||
|
||||
return self.conversation.add(
|
||||
role=agent.agent_name, content=task, *args, **kwargs
|
||||
)
|
||||
|
||||
def reset_workflow(self) -> None:
|
||||
self.conversation = {}
|
||||
|
||||
@try_except_wrapper
|
||||
def run(self):
|
||||
if not self.agent_pool:
|
||||
raise ValueError("No agents have been added to the workflow.")
|
||||
|
||||
self.workflow_bootup()
|
||||
loops = 0
|
||||
while loops < self.max_loops:
|
||||
previous_output = None # Initialize to None; will hold the output of the previous agent
|
||||
for i, agent in enumerate(self.agent_pool):
|
||||
# Fetch the last task specific to this agent from the conversation history
|
||||
tasks_for_agent = [
|
||||
msg["content"]
|
||||
for msg in self.conversation.conversation_history
|
||||
if msg["role"] == agent.agent_name
|
||||
]
|
||||
task = tasks_for_agent[-1] if tasks_for_agent else None
|
||||
|
||||
if task is None and previous_output is not None:
|
||||
# If no specific task for this agent, use the output from the previous agent
|
||||
task = previous_output
|
||||
|
||||
if task is None:
|
||||
# If no initial task is found, and there's no previous output, log error and skip this agent
|
||||
logger.error(
|
||||
f"No initial task found for agent {agent.agent_name}, and no previous output to use."
|
||||
def run(self, task: str, *args, **kwargs):
|
||||
"""
|
||||
Run the workflow starting with an initial task.
|
||||
|
||||
:param task: The task to start the workflow.
|
||||
"""
|
||||
logger.info(f"Starting workflow with task: {task}")
|
||||
current_output = task
|
||||
for agent in self.agents:
|
||||
count = 0
|
||||
while count < self.max_loops:
|
||||
try:
|
||||
logger.info(f"Running agent {agent.agent_name}")
|
||||
current_output = agent.run(
|
||||
current_output, *args, **kwargs
|
||||
)
|
||||
continue
|
||||
|
||||
logger.info(
|
||||
f" \n Agent {i+1} ({agent.agent_name}) is executing the task: {task} \n"
|
||||
)
|
||||
|
||||
# Space the log
|
||||
|
||||
output = agent.run(task)
|
||||
if output is None:
|
||||
print(current_output)
|
||||
count += 1
|
||||
logger.debug(
|
||||
f"Agent {agent.agent_name} completed loop {count} "
|
||||
) # Log partial output for brevity
|
||||
except Exception as e:
|
||||
logger.error(
|
||||
f"Agent {agent.agent_name} returned None for task: {task}"
|
||||
)
|
||||
raise ValueError(
|
||||
f"Agent {agent.agent_name} returned None."
|
||||
f"Error occurred while running agent {agent.agent_name}: {str(e)}"
|
||||
)
|
||||
|
||||
# Update the conversation history with the new output using agent's role
|
||||
self.conversation.add(
|
||||
role=agent.agent_name, content=output
|
||||
)
|
||||
previous_output = output # Update the previous_output to pass to the next agent
|
||||
|
||||
loops += 1
|
||||
return self.conversation.return_history_as_string()
|
||||
raise
|
||||
logger.info(f"Finished running agent {agent.agent_name}")
|
||||
logger.info("Finished running workflow")
|
||||
return current_output
|
||||
|
@ -1,106 +0,0 @@
|
||||
import json
|
||||
from typing import List, Optional
|
||||
|
||||
from pydantic import model_validator, BaseModel, Field, Json
|
||||
|
||||
from swarms.structs.agent import Agent
|
||||
from swarms.structs.task import Task
|
||||
|
||||
|
||||
class Team(BaseModel):
|
||||
"""
|
||||
Class that represents a group of agents, how they should work together and
|
||||
their tasks.
|
||||
|
||||
Attributes:
|
||||
tasks (Optional[List[Task]]): List of tasks.
|
||||
agents (Optional[List[Agent]]): List of agents in this Team.
|
||||
architecture (str): Architecture that the Team will follow. Default is "sequential".
|
||||
verbose (bool): Verbose mode for the Agent Execution. Default is False.
|
||||
config (Optional[Json]): Configuration of the Team. Default is None.
|
||||
"""
|
||||
|
||||
tasks: Optional[List[Task]] = Field(None, description="List of tasks")
|
||||
agents: Optional[List[Agent]] = Field(
|
||||
None, description="List of agents in this Team."
|
||||
)
|
||||
architecture = Field(
|
||||
description="architecture that the Team will follow.",
|
||||
default="sequential",
|
||||
)
|
||||
verbose: bool = Field(
|
||||
description="Verbose mode for the Agent Execution",
|
||||
default=False,
|
||||
)
|
||||
config: Optional[Json] = Field(
|
||||
description="Configuration of the Team.", default=None
|
||||
)
|
||||
|
||||
@model_validator(mode="before")
|
||||
@classmethod
|
||||
def check_config(_cls, values):
|
||||
if not values.get("config") and (
|
||||
not values.get("agents") and not values.get("tasks")
|
||||
):
|
||||
raise ValueError(
|
||||
"Either agents and task need to be set or config."
|
||||
)
|
||||
|
||||
if values.get("config"):
|
||||
config = json.loads(values.get("config"))
|
||||
if not config.get("agents") or not config.get("tasks"):
|
||||
raise ValueError("Config should have agents and tasks.")
|
||||
|
||||
values["agents"] = [
|
||||
Agent(**agent) for agent in config["agents"]
|
||||
]
|
||||
|
||||
tasks = []
|
||||
for task in config["tasks"]:
|
||||
task_agent = [
|
||||
agt
|
||||
for agt in values["agents"]
|
||||
if agt.role == task["agent"]
|
||||
][0]
|
||||
del task["agent"]
|
||||
tasks.append(Task(**task, agent=task_agent))
|
||||
|
||||
values["tasks"] = tasks
|
||||
return values
|
||||
|
||||
def run(self) -> str:
|
||||
"""
|
||||
Kickoff the Team to work on its tasks.
|
||||
|
||||
Returns:
|
||||
output (List[str]): Output of the Team for each task.
|
||||
"""
|
||||
if self.architecture == "sequential":
|
||||
return self.__sequential_loop()
|
||||
|
||||
def __sequential_loop(self) -> str:
|
||||
"""
|
||||
Loop that executes the sequential architecture.
|
||||
|
||||
Returns:
|
||||
output (str): Output of the Team.
|
||||
"""
|
||||
task_outcome = None
|
||||
for task in self.tasks:
|
||||
# Add delegation tools to the task if the agent allows it
|
||||
# if task.agent.allow_delegation:
|
||||
# tools = AgentTools(agents=self.agents).tools()
|
||||
# task.tools += tools
|
||||
|
||||
self.__log(f"\nWorking Agent: {task.agent.role}")
|
||||
self.__log(f"Starting Task: {task.description} ...")
|
||||
|
||||
task_outcome = task.execute(task_outcome)
|
||||
|
||||
self.__log(f"Task output: {task_outcome}")
|
||||
|
||||
return task_outcome
|
||||
|
||||
def __log(self, message):
|
||||
if self.verbose:
|
||||
print(message)
|
@ -0,0 +1,379 @@
|
||||
import json
|
||||
from pydantic import BaseModel
|
||||
from swarms.utils.loguru_logger import logger
|
||||
from swarms.tools.py_func_to_openai_func_str import (
|
||||
get_openai_function_schema_from_func,
|
||||
load_basemodels_if_needed,
|
||||
)
|
||||
from swarms.tools.openai_tool_creator_decorator import openai_tool_executor
|
||||
from typing import Callable, Optional, Any, Dict, List
|
||||
from swarms.tools.pydantic_to_json import (
|
||||
base_model_to_openai_function,
|
||||
multi_base_model_to_openai_function,
|
||||
function_to_str,
|
||||
functions_to_str,
|
||||
)
|
||||
from swarms.tools.function_util import process_tool_docs
|
||||
from typing import Union
|
||||
|
||||
ToolType = Union[BaseModel, Dict[str, Any], Callable[..., Any]]
|
||||
|
||||
|
||||
class BaseTool(BaseModel):
|
||||
"""
|
||||
Base class for tools in the swarms package.
|
||||
|
||||
Attributes:
|
||||
verbose (bool): Flag indicating whether to enable verbose mode.
|
||||
functions (List[Callable[..., Any]]): List of functions associated with the tool.
|
||||
base_models (List[type[BaseModel]]): List of base models associated with the tool.
|
||||
|
||||
Methods:
|
||||
func_to_dict(function: Callable[..., Any], name: Optional[str] = None, description: str) -> Dict[str, Any]:
|
||||
Converts a function to a dictionary representation.
|
||||
|
||||
load_params_from_func_for_pybasemodel(func: Callable[..., Any], *args: Any, **kwargs: Any) -> Callable[..., Any]:
|
||||
Loads parameters from a function for a Pydantic BaseModel.
|
||||
|
||||
base_model_to_dict(pydantic_type: type[BaseModel], output_str: bool = False, *args: Any, **kwargs: Any) -> dict[str, Any]:
|
||||
Converts a Pydantic BaseModel to a dictionary representation.
|
||||
|
||||
multi_base_models_to_dict(pydantic_types: List[type[BaseModel]], *args: Any, **kwargs: Any) -> dict[str, Any]:
|
||||
Converts multiple Pydantic BaseModels to a dictionary representation.
|
||||
|
||||
dict_to_str(dict: dict[str, Any]) -> str:
|
||||
Converts a dictionary to a string representation.
|
||||
|
||||
multi_dict_to_str(dicts: list[dict[str, Any]]) -> str:
|
||||
Converts multiple dictionaries to a string representation.
|
||||
|
||||
get_docs_from_callable(item) -> Any:
|
||||
Retrieves documentation from a callable item.
|
||||
"""
|
||||
|
||||
verbose: bool = False
|
||||
functions: List[Callable[..., Any]] = []
|
||||
base_models: List[type[BaseModel]] = []
|
||||
verbose: bool = False
|
||||
autocheck: bool = False
|
||||
auto_execute_tool: Optional[bool] = False
|
||||
|
||||
def func_to_dict(
|
||||
function: Callable[..., Any],
|
||||
*,
|
||||
name: Optional[str] = None,
|
||||
description: str,
|
||||
) -> Dict[str, Any]:
|
||||
try:
|
||||
return get_openai_function_schema_from_func(
|
||||
function=function,
|
||||
name=name,
|
||||
description=description,
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(f"An error occurred in func_to_dict: {e}")
|
||||
logger.error(
|
||||
"Please check the function and ensure it is valid."
|
||||
)
|
||||
logger.error(
|
||||
"If the issue persists, please seek further assistance."
|
||||
)
|
||||
raise
|
||||
|
||||
def load_params_from_func_for_pybasemodel(
|
||||
func: Callable[..., Any],
|
||||
*args: Any,
|
||||
**kwargs: Any,
|
||||
) -> Callable[..., Any]:
|
||||
try:
|
||||
return load_basemodels_if_needed(func, *args, **kwargs)
|
||||
except Exception as e:
|
||||
logger.error(
|
||||
f"An error occurred in load_params_from_func_for_pybasemodel: {e}"
|
||||
)
|
||||
logger.error(
|
||||
"Please check the function and ensure it is valid."
|
||||
)
|
||||
logger.error(
|
||||
"If the issue persists, please seek further assistance."
|
||||
)
|
||||
raise
|
||||
|
||||
def base_model_to_dict(
|
||||
pydantic_type: type[BaseModel],
|
||||
output_str: bool = False,
|
||||
*args: Any,
|
||||
**kwargs: Any,
|
||||
) -> dict[str, Any]:
|
||||
try:
|
||||
return base_model_to_openai_function(
|
||||
pydantic_type, output_str, *args, **kwargs
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(f"An error occurred in base_model_to_dict: {e}")
|
||||
logger.error(
|
||||
"Please check the Pydantic type and ensure it is valid."
|
||||
)
|
||||
logger.error(
|
||||
"If the issue persists, please seek further assistance."
|
||||
)
|
||||
raise
|
||||
|
||||
def multi_base_models_to_dict(
|
||||
pydantic_types: List[type[BaseModel]],
|
||||
*args: Any,
|
||||
**kwargs: Any,
|
||||
) -> dict[str, Any]:
|
||||
try:
|
||||
return multi_base_model_to_openai_function(
|
||||
pydantic_types, *args, **kwargs
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(
|
||||
f"An error occurred in multi_base_models_to_dict: {e}"
|
||||
)
|
||||
logger.error(
|
||||
"Please check the Pydantic types and ensure they are valid."
|
||||
)
|
||||
logger.error(
|
||||
"If the issue persists, please seek further assistance."
|
||||
)
|
||||
raise
|
||||
|
||||
def dict_to_str(
|
||||
dict: dict[str, Any],
|
||||
) -> str:
|
||||
try:
|
||||
return function_to_str(dict)
|
||||
except Exception as e:
|
||||
logger.error(f"An error occurred in dict_to_str: {e}")
|
||||
logger.error(
|
||||
"Please check the dictionary and ensure it is valid."
|
||||
)
|
||||
logger.error(
|
||||
"If the issue persists, please seek further assistance."
|
||||
)
|
||||
raise
|
||||
|
||||
def multi_dict_to_str(
|
||||
dicts: list[dict[str, Any]],
|
||||
) -> str:
|
||||
try:
|
||||
return functions_to_str(dicts)
|
||||
except Exception as e:
|
||||
logger.error(f"An error occurred in multi_dict_to_str: {e}")
|
||||
logger.error(
|
||||
"Please check the dictionaries and ensure they are valid."
|
||||
)
|
||||
logger.error(
|
||||
"If the issue persists, please seek further assistance."
|
||||
)
|
||||
raise
|
||||
|
||||
def get_docs_from_callable(item):
|
||||
try:
|
||||
return process_tool_docs(item)
|
||||
except Exception as e:
|
||||
logger.error(f"An error occurred in get_docs: {e}")
|
||||
logger.error("Please check the item and ensure it is valid.")
|
||||
logger.error(
|
||||
"If the issue persists, please seek further assistance."
|
||||
)
|
||||
raise
|
||||
|
||||
def execute_tool(
|
||||
self,
|
||||
tools: List[Dict[str, Any]],
|
||||
function_map: Dict[str, Callable],
|
||||
*args: Any,
|
||||
**kwargs: Any,
|
||||
) -> Callable:
|
||||
try:
|
||||
return openai_tool_executor(
|
||||
tools, function_map, self.verbose, *args, **kwargs
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(f"An error occurred in execute_tool: {e}")
|
||||
logger.error(
|
||||
"Please check the tools and function map and ensure they are valid."
|
||||
)
|
||||
logger.error(
|
||||
"If the issue persists, please seek further assistance."
|
||||
)
|
||||
raise
|
||||
|
||||
def detect_tool_input_type(input):
|
||||
if isinstance(input, BaseModel):
|
||||
return "Pydantic"
|
||||
elif isinstance(input, dict):
|
||||
return "Dictionary"
|
||||
elif callable(input):
|
||||
return "Function"
|
||||
else:
|
||||
return "Unknown"
|
||||
|
||||
def dynamic_run(self, input) -> str:
|
||||
"""
|
||||
Executes the dynamic run based on the input type.
|
||||
|
||||
Args:
|
||||
input: The input to be processed.
|
||||
|
||||
Returns:
|
||||
str: The result of the dynamic run.
|
||||
|
||||
Raises:
|
||||
None
|
||||
|
||||
"""
|
||||
tool_input_type = self.detect_tool_input_type(input)
|
||||
if tool_input_type == "Pydantic":
|
||||
function_str = base_model_to_openai_function(input)
|
||||
elif tool_input_type == "Dictionary":
|
||||
function_str = function_to_str(input)
|
||||
elif tool_input_type == "Function":
|
||||
function_str = get_openai_function_schema_from_func(input)
|
||||
else:
|
||||
return "Unknown tool input type"
|
||||
|
||||
if self.auto_execute_tool:
|
||||
if tool_input_type == "Function":
|
||||
# Add the function to the functions list
|
||||
self.functions.append(input)
|
||||
|
||||
# Create a function map from the functions list
|
||||
function_map = {func.__name__: func for func in self.functions}
|
||||
|
||||
# Execute the tool
|
||||
return self.execute_tool(
|
||||
tools=[function_str], function_map=function_map
|
||||
)
|
||||
else:
|
||||
return function_str
|
||||
|
||||
def execute_tool_by_name(
|
||||
tools: List[Dict[str, Any]],
|
||||
tool_name: str,
|
||||
function_map: Dict[str, Callable],
|
||||
) -> Any:
|
||||
"""
|
||||
Search for a tool by name and execute it.
|
||||
|
||||
Args:
|
||||
tools (List[Dict[str, Any]]): A list of tools. Each tool is a dictionary that includes a 'name' key.
|
||||
tool_name (str): The name of the tool to execute.
|
||||
function_map (Dict[str, Callable]): A dictionary that maps tool names to functions.
|
||||
|
||||
Returns:
|
||||
The result of executing the tool.
|
||||
|
||||
Raises:
|
||||
ValueError: If the tool with the specified name is not found.
|
||||
TypeError: If the tool name is not mapped to a function in the function map.
|
||||
"""
|
||||
# Search for the tool by name
|
||||
tool = next(
|
||||
(tool for tool in tools if tool.get("name") == tool_name), None
|
||||
)
|
||||
|
||||
# If the tool is not found, raise an error
|
||||
if tool is None:
|
||||
raise ValueError(f"Tool '{tool_name}' not found")
|
||||
|
||||
# Get the function associated with the tool
|
||||
func = function_map.get(tool_name)
|
||||
|
||||
# If the function is not found, raise an error
|
||||
if func is None:
|
||||
raise TypeError(
|
||||
f"Tool '{tool_name}' is not mapped to a function"
|
||||
)
|
||||
|
||||
# Execute the tool
|
||||
return func(**tool.get("parameters", {}))
|
||||
|
||||
def execute_tool_from_text(
|
||||
text: str = None, function_map: Dict[str, Callable] = None
|
||||
) -> Any:
|
||||
"""
|
||||
Convert a JSON-formatted string into a tool dictionary and execute the tool.
|
||||
|
||||
Args:
|
||||
text (str): A JSON-formatted string that represents a tool. The string should be convertible into a dictionary that includes a 'name' key and a 'parameters' key.
|
||||
function_map (Dict[str, Callable]): A dictionary that maps tool names to functions.
|
||||
|
||||
Returns:
|
||||
The result of executing the tool.
|
||||
|
||||
Raises:
|
||||
ValueError: If the tool with the specified name is not found.
|
||||
TypeError: If the tool name is not mapped to a function in the function map.
|
||||
"""
|
||||
# Convert the text into a dictionary
|
||||
tool = json.loads(text)
|
||||
|
||||
# Get the tool name and parameters from the dictionary
|
||||
tool_name = tool.get("name")
|
||||
tool_params = tool.get("parameters", {})
|
||||
|
||||
# Get the function associated with the tool
|
||||
func = function_map.get(tool_name)
|
||||
|
||||
# If the function is not found, raise an error
|
||||
if func is None:
|
||||
raise TypeError(
|
||||
f"Tool '{tool_name}' is not mapped to a function"
|
||||
)
|
||||
|
||||
# Execute the tool
|
||||
return func(**tool_params)
|
||||
|
||||
|
||||
# # Example function definitions and mappings
|
||||
# def get_current_weather(location, unit='celsius'):
|
||||
# return f"Weather in {location} is likely sunny and 75° {unit.title()}"
|
||||
|
||||
# def add(a, b):
|
||||
# return a + b
|
||||
|
||||
# # Example tool configurations
|
||||
# tools = [
|
||||
# {
|
||||
# "type": "function",
|
||||
# "function": {
|
||||
# "name": "get_current_weather",
|
||||
# "parameters": {
|
||||
# "properties": {
|
||||
# "location": "San Francisco, CA",
|
||||
# "unit": "fahrenheit",
|
||||
# },
|
||||
# },
|
||||
# },
|
||||
# },
|
||||
# {
|
||||
# "type": "function",
|
||||
# "function": {
|
||||
# "name": "add",
|
||||
# "parameters": {
|
||||
# "properties": {
|
||||
# "a": 1,
|
||||
# "b": 2,
|
||||
# },
|
||||
# },
|
||||
# },
|
||||
# }
|
||||
# ]
|
||||
|
||||
# function_map = {
|
||||
# "get_current_weather": get_current_weather,
|
||||
# "add": add,
|
||||
# }
|
||||
|
||||
# # Creating and executing the advanced executor
|
||||
# tool_executor = BaseTool(verbose=True).execute_tool(tools, function_map)
|
||||
|
||||
# try:
|
||||
# results = tool_executor()
|
||||
# print(results) # Outputs results from both functions
|
||||
# except Exception as e:
|
||||
# print(f"Error: {e}")
|
@ -0,0 +1,58 @@
|
||||
import pytest
|
||||
from agent_rearrange import AgentRearrange
|
||||
|
||||
|
||||
# Mocking the Agent class
|
||||
class MockAgent:
|
||||
def __init__(self, agent_name):
|
||||
self.agent_name = agent_name
|
||||
|
||||
def run(self, task):
|
||||
return f"Running {task}"
|
||||
|
||||
|
||||
# Test for AgentRearrange class
|
||||
class TestAgentRearrange:
|
||||
@pytest.fixture
|
||||
def agent_rearrange(self):
|
||||
agents = [MockAgent("agent1"), MockAgent("agent2")]
|
||||
return AgentRearrange(agents=agents)
|
||||
|
||||
def test_parse_pattern(self, agent_rearrange):
|
||||
assert agent_rearrange.parse_pattern("agent1->agent2") is True
|
||||
assert agent_rearrange.parse_pattern("agent3->agent4") is False
|
||||
|
||||
def test_self_find_agent_by_name(self, agent_rearrange):
|
||||
assert (
|
||||
agent_rearrange.self_find_agent_by_name("agent1").agent_name
|
||||
== "agent1"
|
||||
)
|
||||
assert agent_rearrange.self_find_agent_by_name("agent3") is None
|
||||
|
||||
def test_agent_exists(self, agent_rearrange):
|
||||
assert agent_rearrange.agent_exists("agent1") is True
|
||||
assert agent_rearrange.agent_exists("agent3") is False
|
||||
|
||||
def test_parse_concurrent_flow(self, agent_rearrange):
|
||||
agent_rearrange.parse_concurrent_flow("agent1->agent2")
|
||||
assert "agent2" in agent_rearrange.flows["agent1"]
|
||||
|
||||
def test_parse_sequential_flow(self, agent_rearrange):
|
||||
agent_rearrange.parse_sequential_flow("agent1", "agent2")
|
||||
assert "agent2" in agent_rearrange.flows["agent1"]
|
||||
|
||||
def test_execute_task(self, agent_rearrange):
|
||||
assert (
|
||||
agent_rearrange.execute_task("agent1", "agent2", "task1", {})
|
||||
== "Running task1 (from agent2)"
|
||||
)
|
||||
|
||||
def test_process_flows(self, agent_rearrange):
|
||||
assert agent_rearrange.process_flows(
|
||||
"agent1->agent2", "task1", {}
|
||||
) == ["Running task1"]
|
||||
|
||||
def test_call(self, agent_rearrange):
|
||||
assert agent_rearrange(
|
||||
pattern="agent1->agent2", default_task="task1"
|
||||
) == ["Running task1"]
|
Loading…
Reference in new issue