Skip to content

Commit

Permalink
feat: v0.1.0 (#23)
Browse files Browse the repository at this point in the history
  • Loading branch information
aymenfurter authored Jan 21, 2024
1 parent fe3e96c commit d538b18
Show file tree
Hide file tree
Showing 25 changed files with 445 additions and 97 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -49,4 +49,4 @@ coverage.xml
.h

*.db
!pretrained_agents.db
!agents.db
170 changes: 128 additions & 42 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,71 +1,157 @@
# microagents: Modular Agents Capable of Self-Editing Their Prompts and Python code
<div id="top"></div>

<a href="https://raw.githubusercontent.com/aymenfurter/microagents/main/static/output.gif" target="_blank">
<img src="https://raw.githubusercontent.com/aymenfurter/microagents/main/static/output.gif?raw=true"
Fullscreen</a>
<br />
<div align="center">
<img src="header.png">

### Synthesized Agent Prompts (From Above Demo)
<h1 align="center">Microagents Framework</h1>
<p align="center">
An experimental framework for dynamically creating self-improving agents in response to tasks.
<br />
<br />
<a href="#demo">View Demo</a>
·
<a href="https://github.com/aymenfurter/microagents/issues">Report Bug</a>
·
<a href="https://github.com/aymenfurter/microagents/issues">Request Feature</a>
</p>
</div>
<br />

#### CalculateAddition Agent
## About The Project

Microagents represents a new approach to creating self-improving agents. These _microagents_ are dynamically generated in response to tasks assigned by the user to the assistant, assessed for their functionality, and, upon successful validation, stored for future reuse. This enables learning across chat sessions, enabling the system to independently deduce methods for task execution.

### Built With

This project leverages the following technologies:

- Python
- OpenAI's GPT-4 Turbo
- Text-Embedding-Ada-002

## Getting Started

To get a local copy up and running follow these simple steps.

### Prerequisites

- OpenAI Account: Make sure you have an OpenAI account with access to `gpt-4-turbo` and `text-embedding-ada-002`.

### Installation

1. Clone the repo
```sh
git clone https://github.com/aymenfurter/microagents.git
```
2. Install Python packages
```sh
pip install -r requirements.txt
```
3. Set your OpenAI API key in an environment variable
```sh
export OPENAI_KEY='your_api_key_here'
```

## Usage
> [!CAUTION]
> Microagents execute Python code directly and currently do not operate within a sandbox. It's highly recommended to run them in isolated environments such as GitHub Codespaces to limit potential damage. Be mindful of the costs associated with using OpenAI's services.
For a demo run, execute:
```sh
python main.py
```

For an interactive chat experience:
```sh
python app.py
```

To remove all agents, simply delete the "agents.db" file.


## Demo

<div align="center">
<img src="demo.gif" alt="Demo" style="border: 2px solid yellow;">
</div>

### Synthesized Agent Prompts


#### Fetch Weather Forecast Agent
```
You are an adept arithmetic solver with focus on performing addition. Utilize this Python function to calculate the sum of two numbers:
You are an adept weather informant. Fetch the weather forecast by accessing public API data using this Python code snippet:
``python
def calculate_addition(num1, num2):
return num1 + num2
# Example usage:
print(calculate_addition(5, 9))
import requests
import json
def fetch_weather_forecast(location, date):
response = requests.get(f"https://api.met.no/weatherapi/locationforecast/2.0/compact?lat={location[0]}&lon={location[1]}")
weather_data = response.json()
for day_data in weather_data['properties']['timeseries']:
if date in day_data['time']:
print(day_data['data']['instant']['details'])
break
``
# Example usage: fetch_weather_forecast((47.3769, 8.5417), '2024-01-22T12:00:00Z')
Note: Replace the (47.3769, 8.5417) with the actual latitude and longitude of the location and the date string accordingly.
```

#### GetPopulationOfCountry Agent
#### IPBasedLocationFetcher Agent
```
You are a skilled data extractor specializing in population statistics. Retrieve the population of a given country using the provided Python code:
You are a skilled IP-based location retriever. Use Python's requests library to obtain geolocation data from a public IP address. Here is a sample code snippet you may adapt:
``python
import requests
def get_population(country):
url = f"https://restcountries.com/v3.1/name/{country}"
response = requests.get(url).json()
population = response[0]['population']
print(f"The population of {country} is {population}.")
# Example usage:
get_population("CountryName")
def fetch_location_from_ip(ip_address):
response = requests.get(f'http://ip-api.com/json/{ip_address}')
data = response.json()
return data.get('country'), data.get('city')
``
# Example usage:
# print(fetch_location_from_ip('8.8.8.8'))
Ensure that the code is capable of extracting location information such as country and city from the provided IP address.
```

### How does it work?
This experiment explores self-evolving agents that automatically generate and improve themselves. No specific agent design or prompting is required from the user. Simply pose a question, and the system initiates and evolves agents tailored to provide answers. The process starts with a user query, activating a basic "bootstrap" agent, which doesn't execute Python code but plans and delegates to specialized agents capable of running Python for broader functions. An Agent Manager oversees them, selecting or creating agents via vector similarity for specific tasks. Agents have evolving system prompts that improve through learning. For coding tasks, agents include Python in prompts, refining their approach through an "evolution step" if unsuccessful. Upon completing a task, an agent's status updates, and the bootstrap agent evaluates the result, engaging other agents for further steps in larger processes.

### Current Challenges and Potential Improvements

1. **Path Optimization**: The system sometimes fails to effectively discard non-functional agents.
<details>
<summary>How are agents created?</summary>
<img src="architecture.png" width="600">
</details>

2. **Performance and Parallelization**: Currently, parallel processing is not implemented. Enabling the testing of multiple prompt evolutions simultaneously could significantly enhance performance.
## Changelog

3. **Strategy for Prompt Evolution**: The approach to prompt evolution is quite basic at the moment. Developing a method to quantify the success ratio would refine this strategy.
### Release v0.1.0 (21.01.2024)

4. **Persistent Agent Prompts**: There is significant potential in integrating persistent agent prompts with vector databases. Additionally, sharing successful agents across various runtime environments could improve overall efficiency.
This is the first release of Microagents, and a lot has happened since the initial project publication. Here are the key updates:

5. **Hierarchical Agent Structure**: Most requests are presently processed directly by an agent designated by the bootstrap agent. Implementing a more intricate hierarchical structure for managing requests could lead to major improvements.
- **Pull Requests**: The project has received a total of 6 Pull Requests, highlighting the interest from the community.

6. **Context Size Limitation**: Not yet considered.
- **Two User Interfaces**: We now offer two user interfaces for interacting with the agents:
- For console enthusiasts, there is a Command Line Interface (CLI) available, which can be started using the `textual-app.py` file. This interface is a community contribution by bearney74.
- Additionally, we provide a feature-rich web interface based on Gradio.

- **Parallelization**: Microagents now support parallelism! When a new agent is created, three agents are actually spawned in parallel. The first agent to successfully complete its task is retained, while the others are disposed of.

### How to Run Microagents
> [!CAUTION]
> Microagents are capable of directly executing Python code. There is currently no sandbox. Therefore, it is highly recommended to run them in isolated environments like GitHub Codespaces, where potential damage is limited. Be aware of the potential costs associated with the usage of OpenAI's services.
- **Pretrained Agents**: We have introduced 28 pretrained agents that are included for testing. These agents enable a wide range of tasks, from interacting with OpenStreetMap to providing current weather information. All of these agents were trained using Microagents.

- **Validation Phase ("Judge")**: A new 'Judge' phase has been added to ensure that agents that claim to be working actually perform as expected. This additional validation step ensures that only fully functional agents are included in the catalog of working agents.

- **Persistent Agent Storage**: Agents are now stored across runs, with SQLite being used for this purpose.

- **Improved Agent Memory**: Agents now have the ability to remember only the agents they have previously created. This approach enhances the robustness of agent creation, as it avoids unnecessary depth changes during execution.

These updates represent a significant enhancement to Microagents. I am looking forward to further improvements and contributions from the community.

#### Prerequisites
- **OpenAI Account**: Ensure you have an OpenAI account with access to `gpt-4-turbo` And `text-embedding-ada-002`.
## Contributing

#### Running the Agents
1. **Clone the Repository**: Start by cloning the Microagents repository from GitHub.
2. **Install Python Requirements**: Install the necessary Python requirements using `pip install -r requirements.txt`.
3. **OpenAI API Key**: Set your OpenAI API key in an environment variable named `OPENAI_KEY`.
4. **Load Pretrained Agents**: Load the pretrained agents by running `mv pretrained_agents.db agents.db`.
5. **Execution**: For the demo, run `python main.py`. For an interactive chat experience, execute `python app.py`.
Contributions are what make the open-source community such an amazing place to learn, inspire, and create. Any contributions you make are **greatly appreciated**.

## License

Distributed under the MIT License. See `LICENSE` for more information.
Binary file added agents.db
Binary file not shown.
41 changes: 36 additions & 5 deletions agents/agent_lifecycle.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

logger = logging.getLogger()

DEFAULT_MAX_AGENTS = 20
DEFAULT_MAX_AGENTS = 2000
PRIME_AGENT_WEIGHT = 25

class AgentLifecycle:
Expand All @@ -23,6 +23,16 @@ def __init__(self, openai_wrapper: OpenAIAPIWrapper, agent_persistence_manager:
self.agent_persistence = agent_persistence_manager
self.max_agents = max_agents

def stop_all_agents(self) -> None:
"""Stops all agents."""
for agent in self.agents:
agent.stop()

def reset_all_agents(self) -> None:
"""Resets all agents."""
for agent in self.agents:
agent.reset()

def cleanup_agents(self):
"""Remove all agents with status stopped = True in an efficient manner."""
self.agents = [agent for agent in self.agents if not agent.stopped]
Expand All @@ -39,7 +49,19 @@ def add_agent(self, agent: MicroAgent) -> None:
"""Adds an agent to the list of agents."""
self.agents.append(agent)

def get_or_create_agent(self, purpose: str, depth: int, sample_input: str, force_new: bool = False) -> MicroAgent:


def get_available_agents_for_agent(self, agent) -> List[MicroAgent]:
"""Returns the list of available agents for the given purpose."""
agent_id = agent.id
available_agents = [agent for agent in self.agents if agent.purpose != "Bootstrap Agent" and agent.working_agent]
for agent in available_agents:
if agent.parent_id != agent_id:
available_agents.remove(agent)

return available_agents

def get_or_create_agent(self, purpose: str, depth: int, sample_input: str, force_new: bool = False, parent_agent=None) -> MicroAgent:
"""
Retrieves or creates an agent based on the given purpose.
Optionally creates a new agent regardless of similarity if force_new is True.
Expand All @@ -54,14 +76,14 @@ def get_or_create_agent(self, purpose: str, depth: int, sample_input: str, force
closest_agent.usage_count += 1
return closest_agent

return self._create_and_add_agent(purpose, depth, sample_input)
return self._create_and_add_agent(purpose, depth, sample_input, parent_agent=parent_agent)

def _create_and_add_agent(self, purpose: str, depth: int, sample_input: str) -> MicroAgent:
def _create_and_add_agent(self, purpose: str, depth: int, sample_input: str, parent_agent=None) -> MicroAgent:
"""Helper method to create and add a new agent."""
if len(self.agents) >= self.max_agents:
self._remove_least_used_agent()

new_agent = MicroAgent(self._generate_llm_prompt(purpose, sample_input), purpose, depth, self, self.openai_wrapper)
new_agent = MicroAgent(self._generate_llm_prompt(purpose, sample_input), purpose, depth, self, self.openai_wrapper, parent=parent_agent)
new_agent.usage_count = 1
self.agents.append(new_agent)
return new_agent
Expand All @@ -78,6 +100,15 @@ def save_agent(self, agent: MicroAgent) -> None:
except Exception as e:
logger.exception(f"Error in saving agent: {e}")
raise


def remove_agent(self, agent: MicroAgent) -> None:
"""Removes the given agent with error handling."""
try:
self.agent_persistence.remove_agent(agent)
except Exception as e:
logger.exception(f"Error in saving agent: {e}")
raise

def _generate_llm_prompt(self, goal: str, sample_input: str) -> str:
"""
Expand Down
38 changes: 38 additions & 0 deletions agents/agent_name_evaluation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import logging
from integrations.memoize import memoize_to_sqlite
from integrations.openaiwrapper import OpenAIAPIWrapper
from prompt_management.prompts import AGENT_NAME_EVALUATION_PROMPT
logger = logging.getLogger()

#feature flag
DISABLE_AGENT_NAME_EVALUATION = True

class AgentNameEvaluator:
"""
Evaluates AI name responses using OpenAI's GPT model.
"""

def __init__(self, openai_wrapper: OpenAIAPIWrapper):
self.openai_api = openai_wrapper

@memoize_to_sqlite(func_name="evaluate", filename="agent_name_evals.db")
def evaluate(self, input_text: str, agent_name: str) -> str:
"""
Returns evaluation agents response (score from 1-5)
"""
if DISABLE_AGENT_NAME_EVALUATION:
return "5"

try:
formatted_prompt = AGENT_NAME_EVALUATION_PROMPT.format(input=input_text, agent_name=agent_name)
response = self.openai_api.chat_completion(messages=[{"role": "system", "content": formatted_prompt}])

print (f"Agent name {agent_name} evaluated as {response}")
print(f"Input was {input_text}")
if "5" in response or "4" in response:
return True
else:
return False
except Exception as error:
logger.exception(f"Agent evaluation error: {error}")
raise
6 changes: 6 additions & 0 deletions agents/agent_persistence_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@ class AgentPersistenceManager:
def __init__(self, db_filename="agents.db"):
self.persistence = SQLiteAgentPersistence(db_filename)

def remove_agent(self, agent):
"""
Remove an agent from the database.
"""
self.persistence.remove_agent(agent.id)

def save_agent(self, agent):
"""
Serialize and save the agent state if it is a working agent and not a prime agent.
Expand Down
15 changes: 9 additions & 6 deletions agents/agent_response.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,14 +43,14 @@ def generate_response(self, input_text, dynamic_prompt, max_depth):
found_new_solution = True
break

return self._conclude_output(conversation_accumulator), conversation_accumulator, found_new_solution, thought_number
return self._conclude_output(conversation_accumulator, input_text), conversation_accumulator, found_new_solution, thought_number

def _compose_system_prompt(self, runtime_context, dynamic_prompt):
pre_prompt = STATIC_PRE_PROMPT_PRIME if self.agent.is_prime else STATIC_PRE_PROMPT
return pre_prompt + runtime_context + dynamic_prompt + "\nDELIVER THE NEXT PACKAGE."

def _generate_runtime_context(self, dynamic_prompt):
available_agents = [agent for agent in self.manager.agents if agent.purpose != "Bootstrap Agent" and agent.working_agent]
available_agents = self.manager.get_available_agents_for_agent(self.agent)
available_agents_info = ', '.join([f"{agent.purpose} (depth={agent.depth})" for agent in available_agents])
return f"Your Purpose: {dynamic_prompt}. Available agents (Feel free to invent new ones if required!): {available_agents_info}."

Expand Down Expand Up @@ -108,13 +108,13 @@ def _is_agent_invocation(self, response):

def _handle_agent_delegation(self, agent_name, input_text, accumulator, thought_number, action_number):
self.agent.update_active_agents(self.agent.purpose, agent_name)
self.agent.update_status('⏳ Waiting..')
self.agent.update_status('⏳ ' + agent_name + '..')
if agent_name == self.agent.purpose:
accumulator += f"\nOutput {thought_number}: Unable to use Agent {agent_name}\nIt is not possible to call yourself!"
return "", accumulator
else:
parallel_executor = ParallelAgentExecutor(self.manager)
delegated_response = parallel_executor.create_and_run_agents(agent_name, self.depth + 1, input_text)
delegated_response = parallel_executor.create_and_run_agents(agent_name, self.depth + 1, input_text, self.agent)

accumulator += f"\nOutput {thought_number}: Delegated task to Agent {agent_name}\nOutput of Agent {action_number}: {delegated_response}"
return delegated_response, accumulator
Expand All @@ -126,9 +126,12 @@ def _parse_agent_info(self, response):
input_text = split_info[1].strip() if len(split_info) > 1 else ""
return agent_name, input_text

def _conclude_output(self, conversation):
def _conclude_output(self, conversation, input_text):

react_prompt = conversation

react_prompt += f"\nYour designation is: {self.agent.purpose}\n"
react_prompt += f"\nThe original question / task was: {input_text}\n"
react_prompt += f"\nUse beautiful markdown formatting in your output, e.g. include images using ![Drag Racing](https://example.com/Dragster.jpg)\n"
self.agent.update_status('🧐 Reviewing..')
return self.openai_wrapper.chat_completion(
messages=[
Expand Down
Loading

0 comments on commit d538b18

Please sign in to comment.