| name | autogen |
| description | [Applies to: **/*.py] This guide provides opinionated best practices for structuring, optimizing, and testing autogen multi-agent systems to ensure maintainability, reproducibility, and cost-efficiency. |
| source | cursor_mdc |
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.
from autogen import AssistantAgent, UserProxyAgent
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.
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."
)
)
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.
user_proxy.initiate_chat(assistant, message="Analyze data.")
✅ GOOD: Explicit caching with Cache.disk() or Cache.redis().
from autogen import Cache
from autogen_ext.models.openai import OpenAIChatCompletionClient
import os
llm_client = OpenAIChatCompletionClient(
model="gpt-4o-mini",
api_key=os.environ.get("OPENAI_API_KEY")
)
planner = create_planner_agent(llm_client)
user_proxy = UserProxyAgent(
name="User",
human_input_mode="NEVER",
code_execution_config={"use_docker": False}
)
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
)
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.
def setup_conversation(agent1, agent2, task):
pass
✅ GOOD: Fully typed function signatures.
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:
- Unit Tests: For individual tool functions and agent helper methods.
- Integration Tests: For multi-agent conversations, ensuring workflows complete as expected. Leverage caching to make these tests fast and deterministic.
import pytest
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
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
)
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.
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.
[project]
name = "my-autogen-app"
version = "0.1.0"
dependencies = [
"pyautogen~=0.2.8",
"autogen-ext[openai]",
"diskcache",
"python-dotenv",
]
6. Secure API Key Handling
NEVER hardcode API keys. Use environment variables.
❌ BAD: Hardcoded API key.
llm_config = {"model": "gpt-4", "api_key": "sk-YOUR_HARDCODED_KEY"}
✅ GOOD: Environment variables.
import os
from dotenv import load_dotenv
load_dotenv()
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.