autogen
This guide provides opinionated best practices for structuring, optimizing, and testing autogen multi-agent systems to ensure maintainability, reproducibility, and cost-efficiency.
The skill
# autogen Best Practices
Building robust `autogen` applications requires disciplined adherence to specific patterns. This guide outlines the definitive best practices for our team, focusing on modularity, performance, and testability.
## 1. Code Organization and Agent Structure
Organize your agents into dedicated modules, ensuring each agent has a single, well-defined responsibility. This mirrors traditional software architecture and enhances maintainability.
❌ BAD: Monolithic agent definitions or generic names.
```python
# agents.py
from autogen import AssistantAgent, UserProxyAgent
# Too many responsibilities, unclear role
def create_complex_agent(llm_config):
agent = AssistantAgent(
name="GenericAgent",
llm_config=llm_config,
system_message="I can do anything you ask."
)
return agent
```
✅ GOOD: Dedicated modules, clear roles, and explicit system messages.
```python
# agents/planner.py
from autogen import AssistantAgent
from autogen_ext.models.openai import OpenAIChatCompletionClient
def create_planner_agent(model_client: OpenAIChatCompletionClient) -> AssistantAgent:
"""
Creates an agent responsible for breaking down complex tasks into actionable steps.
"""
return AssistantAgent(
name="TaskPlanner",
model_client=model_client,
description="An expert in breaking down complex problems into a sequence of manageable sub-tasks.",
system_message=(
"You are a meticulous TaskPlanner. Your sole responsibility is to decompose user requests "
"into a clear, ordered list of steps. Do not execute tasks, only plan them."
)
)
# agents/coder.py
from autogen import AssistantAgent
from autogen_ext.models.openai import OpenAIChatCompletionClient
def create_coder_agent(model_client: OpenAIChatCompletionClient) -> AssistantAgent:
"""
Creates an agent capable of writing and executing Python code.
"""
return AssistantAgent(
name="PythonCoder",
model_client=model_client,
description="An expert Python programmer capable of writing, executing, and debugging code.",
system_message=(
"You are an expert Python programmer. You write clean, efficient, and well-tested code. "
"When asked to solve a problem, provide the Python code in a markdown block. "
"If execution is needed, state 'EXECUTE' after the code block."
)
)
```
## 2. LLM Caching for Reproducibility and Cost Efficiency
ALWAYS wrap your agent interactions in a caching context manager. This is non-negotiable for reproducible results and to prevent excessive API costs during development and testing.
❌ BAD: No caching, leading to variable outputs and repeated API calls.
```python
# In your main script
user_proxy.initiate_chat(assistant, message="Analyze data.") # Each run hits the LLM
```
✅ GOOD: Explicit caching with `Cache.disk()` or `Cache.redis()`.
```python
from autogen import Cache
from autogen_ext.models.openai import OpenAIChatCompletionClient
import os
# Configure your LLM client once
llm_client = OpenAIChatCompletionClient(
model="gpt-4o-mini",
api_key=os.environ.get("OPENAI_API_KEY")
)
# Create agents using the configured client
# (Assuming create_planner_agent and UserProxyAgent are defined)
planner = create_planner_agent(llm_client)
user_proxy = UserProxyAgent(
name="User",
human_input_mode="NEVER",
code_execution_config={"use_docker": False} # Or True with proper setup
)
# Use disk caching for development and testing
with Cache.disk(cache_path_root=".autogen_cache") as cache:
user_proxy.initiate_chat(
planner,
message="Develop a Python script to calculate the factorial of 5.",
cache=cache # Pass the cache object
)
# For production or shared environments, consider RedisCache
# with Cache.redis(redis_url="redis://localhost:6379/0") as cache:
# user_proxy.initiate_chat(planner, message="...", cache=cache)
```
**Note**: Set `cache_seed=None` in `llm_config` to explicitly disable caching for an agent, but this should be rare and justified.
## 3. Type Hints for Clarity and Maintainability
Utilize Python type hints extensively. This improves code readability, enables static analysis, and reduces bugs in complex multi-agent systems.
❌ BAD: Untyped function signatures.
```python
def setup_conversation(agent1, agent2, task):
# ... logic ...
pass
```
✅ GOOD: Fully typed function signatures.
```python
from autogen import Agent, GroupChatManager, UserProxyAgent
from typing import List
def setup_conversation(
agents: List[Agent],
manager: GroupChatManager,
initial_message: str
) -> None:
"""
Initiates a multi-agent conversation with a given manager and agents.
"""
user_proxy = UserProxyAgent(
name="User",
human_input_mode="NEVER",
code_execution_config={"use_docker": False}
)
user_proxy.initiate_chat(manager, message=initial_message)
```
## 4. Robust Testing Approaches
Implement a multi-layered testing strategy:
1. **Unit Tests**: For individual tool functions and agent helper methods.
2. **Integration Tests**: For multi-agent conversations, ensuring workflows complete as expected. Leverage caching to make these tests fast and deterministic.
```python
# tests/test_tools.py
import pytest
# Example tool function
def calculate_square(number: int) -> int:
return number * number
def test_calculate_square():
assert calculate_square(5) == 25
assert calculate_square(0) == 0
assert calculate_square(-2) == 4
# tests/test_workflow.py
import pytest
from autogen import Cache, GroupChatManager, UserProxyAgent
from autogen_ext.models.openai import OpenAIChatCompletionClient
import os
from agents.planner import create_planner_agent
from agents.coder import create_coder_agent
@pytest.fixture(scope="module")
def llm_client():
return OpenAIChatCompletionClient(
model="gpt-4o-mini",
api_key=os.environ.get("OPENAI_API_KEY")
)
@pytest.fixture(scope="module")
def planner_agent(llm_client):
return create_planner_agent(llm_client)
@pytest.fixture(scope="module")
def coder_agent(llm_client):
return create_coder_agent(llm_client)
def test_factorial_workflow(planner_agent, coder_agent):
user_proxy = UserProxyAgent(
name="User",
human_input_mode="NEVER",
code_execution_config={"use_docker": False}
)
manager = GroupChatManager(
agents=[user_proxy, planner_agent, coder_agent],
messages=[],
llm_config={"model": "gpt-4o-mini", "api_key": os.environ.get("OPENAI_API_KEY")}
)
with Cache.disk(cache_path_root=".autogen_test_cache") as cache:
user_proxy.initiate_chat(
manager,
message="Calculate the factorial of 5 using Python.",
cache=cache
)
# Assert that the final message contains the correct answer
final_message = manager.last_message()["content"]
assert "120" in final_message
```
## 5. Environment Management and Packaging
While not `autogen`-specific, these are crucial for any Python project.
* **Virtual Environments**: ALWAYS use `venv` or `conda` for dependency isolation.
```bash
python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
```
* **Packaging**: For reusable agents or applications, define `pyproject.toml` (Poetry/Rye) or `setup.py` for proper distribution.
```toml
# pyproject.toml (example snippet)
[project]
name = "my-autogen-app"
version = "0.1.0"
dependencies = [
"pyautogen~=0.2.8", # Pin to specific versions
"autogen-ext[openai]",
"diskcache",
"python-dotenv",
# ... other dependencies
]
```
## 6. Secure API Key Handling
NEVER hardcode API keys. Use environment variables.
❌ BAD: Hardcoded API key.
```python
llm_config = {"model": "gpt-4", "api_key": "sk-YOUR_HARDCODED_KEY"}
```
✅ GOOD: Environment variables.
```python
import os
from dotenv import load_dotenv
load_dotenv() # Load from .env file in development
llm_config = {
"model": "gpt-4",
"api_key": os.environ.get("OPENAI_API_KEY")
}
if not llm_config["api_key"]:
raise ValueError("OPENAI_API_KEY environment variable not set.")
```
```
# .env file (add to .gitignore)
OPENAI_API_KEY=sk-your-actual-openai-key-here
```
By adhering to these guidelines, our `autogen` projects will be maintainable, performant, and reliable.
How to install it
1. Open the tool and start a new conversation, project or template. 2. Paste the text below wherever the tool keeps standing instructions — usually called system prompt, instructions, or persona. 3. Save it so it applies to every run rather than a single message.