Introduction to AutoGen with Local LLMs
AutoGen is Microsoft's open-source framework for building conversational multi-agent systems powered by large language models. It enables developers to create complex agent workflows where multiple AI agents collaborate, debate, and execute tasks autonomously. While AutoGen works seamlessly with cloud-based APIs like OpenAI and Azure OpenAI, running it with local LLMs opens up a new world of possibilities — complete data privacy, zero token costs, offline capability, and full control over model selection and configuration.
This tutorial walks you through setting up AutoGen with two leading local LLM serving solutions: Ollama (optimized for ease of use and desktop-class inference) and vLLM (designed for high-throughput production serving). By the end, you'll have a fully functional multi-agent system running entirely on your own hardware.
Why Run AutoGen with Local LLMs?
There are several compelling reasons to pair AutoGen with locally hosted models:
- Data Privacy: Sensitive data never leaves your infrastructure — critical for healthcare, legal, and enterprise use cases
- Cost Efficiency: Eliminate per-token API charges, especially beneficial for long-running agent conversations with extensive back-and-forth
- Offline Operation: Build agents that work in air-gapped environments, on edge devices, or during network outages
- Full Model Control: Experiment with different model sizes, quantizations, and custom fine-tuned variants without vendor lock-in
- Low Latency: Avoid network round-trips to cloud providers when running on local GPUs
- Reproducibility: Pin exact model versions for consistent behavior across development and testing
Prerequisites
Before diving in, ensure you have the following:
- Python 3.10+ installed on your system
- pip package manager up to date
- At least 16GB RAM for running 7B-parameter models comfortably (32GB+ recommended)
- A GPU with 8GB+ VRAM if you want GPU-accelerated inference (both Ollama and vLLM support CPU-only mode, but performance will be slower)
- Basic familiarity with Python and virtual environments
Create a dedicated project directory and virtual environment to keep dependencies isolated:
mkdir autogen-local-llms
cd autogen-local-llms
python -m venv venv
source venv/bin/activate # On Windows: venv\Scripts\activate
Setting Up AutoGen with Ollama
Ollama provides a frictionless way to download, manage, and serve LLMs locally with an OpenAI-compatible API. It's ideal for development workstations and single-user scenarios.
Step 1: Install and Launch Ollama
Install Ollama using the official script or package manager for your OS. On Linux/macOS:
curl -fsSL https://ollama.com/install.sh | sh
On Windows, download the installer from ollama.com. Once installed, start the Ollama server (it typically runs as a background service, but you can also launch it manually):
ollama serve
The server listens on http://localhost:11434 by default. Verify it's running by visiting http://localhost:11434 in your browser — you should see "Ollama is running".
Step 2: Pull a Model
Download a model that suits your hardware and task requirements. For a good balance of capability and resource usage, Meta's Llama 3.1 8B or Mistral 7B are excellent choices:
ollama pull llama3.1:8b
# Or for a smaller footprint:
ollama pull mistral:7b
Verify the model is available:
ollama list
You can test the model directly from the command line:
ollama run llama3.1:8b "Explain the concept of multi-agent AI systems in one paragraph."
Step 3: Install AutoGen with Ollama Support
Install the core AutoGen library along with the Ollama-specific extensions:
pip install pyautogen
pip install litellm # Provides Ollama integration layer
Note that litellm acts as a translation layer, mapping various LLM providers (including Ollama's API) into a unified format that AutoGen understands.
Step 4: Configure AutoGen to Use Ollama
AutoGen uses a config_list dictionary to specify which LLM endpoints to use. For Ollama, you configure it as an OpenAI-compatible endpoint pointing to the local server. Here's a complete working example of a two-agent conversation:
import autogen
# Configure the LLM to point to your local Ollama server
config_list = [
{
"model": "llama3.1:8b",
"api_key": "ollama", # Ollama doesn't require a real key, but a placeholder is needed
"base_url": "http://localhost:11434/v1",
"api_type": "openai", # Ollama's API is OpenAI-compatible
}
]
# Create the assistant agent (the LLM-powered agent)
assistant = autogen.AssistantAgent(
name="assistant",
llm_config={
"config_list": config_list,
"temperature": 0.7,
"max_tokens": 1024,
"timeout": 120,
},
system_message="You are a helpful AI assistant. Provide clear, concise responses.",
)
# Create the user proxy agent (acts on behalf of the human user)
user_proxy = autogen.UserProxyAgent(
name="user_proxy",
human_input_mode="NEVER", # Fully automated mode
max_consecutive_auto_reply=5,
code_execution_config={
"work_dir": "coding_workdir",
"use_docker": False, # Set to True if you have Docker installed
},
)
# Initiate the conversation
user_proxy.initiate_chat(
assistant,
message="Write a Python function that calculates the Fibonacci sequence up to n terms using recursion, and include a test case for n=10.",
)
print("\nConversation complete! Check 'coding_workdir' for generated files.")
This script creates two agents: an AssistantAgent backed by your local Llama 3.1 model via Ollama, and a UserProxyAgent that can execute code. The conversation runs fully automated — the assistant writes code, and the user proxy can execute it and report results back.
Step 5: Advanced Ollama Configuration
For more granular control, you can specify additional parameters that Ollama supports. Here's an enhanced configuration example:
# Enhanced configuration with Ollama-specific options
config_list = [
{
"model": "llama3.1:8b",
"api_key": "ollama",
"base_url": "http://localhost:11434/v1",
"api_type": "openai",
# Additional parameters passed through litellm
"api_version": None, # Not used by Ollama but required by litellm schema
"num_retries": 3, # Auto-retry on failures
"request_timeout": 300, # Longer timeout for complex generations
}
]
# You can also specify model-specific generation parameters
llm_config = {
"config_list": config_list,
"temperature": 0.3, # Lower temperature for more deterministic coding tasks
"max_tokens": 2048,
"top_p": 0.9,
"frequency_penalty": 0.1,
"presence_penalty": 0.1,
"timeout": 180,
"seed": 42, # For reproducible outputs
}
assistant = autogen.AssistantAgent(
name="precise_assistant",
llm_config=llm_config,
system_message="You are an expert Python developer. Write clean, well-documented code with proper error handling.",
)
Setting Up AutoGen with vLLM
vLLM is a high-performance LLM serving engine designed for production workloads. It features PagedAttention for efficient memory management, continuous batching, and supports tensor parallelism across multiple GPUs. Use vLLM when you need maximum throughput, want to serve multiple concurrent users, or are deploying in a server environment.
Step 1: Install vLLM
vLLM requires a GPU with CUDA support. Install it via pip:
pip install vllm
For systems with multiple GPUs or specific CUDA versions, consult the vLLM documentation. You can verify the installation:
python -c "import vllm; print(vllm.__version__)"
Step 2: Download a Model for vLLM
vLLM works with models from Hugging Face. Popular choices include:
meta-llama/Llama-3.1-8B-Instruct— requires Hugging Face login and license acceptancemistralai/Mistral-7B-Instruct-v0.3— openly availableQwen/Qwen2.5-7B-Instruct— strong open-weight model
For models requiring authentication, log in to Hugging Face CLI:
pip install huggingface_hub
huggingface-cli login
# Paste your Hugging Face token when prompted
Step 3: Launch the vLLM Server
Start vLLM with an OpenAI-compatible API endpoint. Here's the command to serve Mistral 7B on a single GPU:
python -m vllm.entrypoints.openai.api_server \
--model mistralai/Mistral-7B-Instruct-v0.3 \
--host 0.0.0.0 \
--port 8000 \
--max-model-len 4096 \
--gpu-memory-utilization 0.9 \
--dtype auto
Key parameters explained:
--model: Hugging Face model ID or local path--host 0.0.0.0: Listen on all network interfaces (use127.0.0.1for local-only access)--port 8000: Default port for the API server--max-model-len 4096: Maximum context length (adjust based on model and VRAM)--gpu-memory-utilization 0.9: Fraction of GPU memory to use for model weights (leaves headroom for KV cache)--dtype auto: Automatically select the optimal data type (fp16 for compatible GPUs)
For multi-GPU setups, add tensor parallelism:
# Serve on 2 GPUs with tensor parallelism
python -m vllm.entrypoints.openai.api_server \
--model mistralai/Mistral-7B-Instruct-v0.3 \
--tensor-parallel-size 2 \
--host 0.0.0.0 \
--port 8000
The server will take 30-60 seconds to load the model (depending on size and disk speed). Once ready, you'll see output indicating the API is available at http://localhost:8000.
Step 4: Verify the vLLM Server
Test the endpoint with a simple curl request:
curl http://localhost:8000/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "mistralai/Mistral-7B-Instruct-v0.3",
"messages": [{"role": "user", "content": "Hello! What is 2+2?"}],
"max_tokens": 50
}'
You should receive a JSON response with the model's answer.
Step 5: Configure AutoGen for vLLM
The configuration is nearly identical to Ollama since both expose OpenAI-compatible APIs. Here's a complete multi-agent setup using vLLM:
import autogen
# vLLM server configuration
config_list = [
{
"model": "mistralai/Mistral-7B-Instruct-v0.3",
"api_key": "EMPTY", # vLLM doesn't require authentication by default
"base_url": "http://localhost:8000/v1",
"api_type": "openai",
}
]
# Create a specialized coding assistant
coding_assistant = autogen.AssistantAgent(
name="coder",
llm_config={
"config_list": config_list,
"temperature": 0.2,
"max_tokens": 2048,
"timeout": 300,
"seed": 123,
},
system_message="""You are an expert software engineer.
When asked to write code:
1. First explain your approach briefly
2. Write clean, well-commented code
3. Include error handling
4. Add a usage example or test case""",
)
# Create a reviewer agent that critiques the code
reviewer = autogen.AssistantAgent(
name="reviewer",
llm_config={
"config_list": config_list,
"temperature": 0.5,
"max_tokens": 1024,
"timeout": 300,
},
system_message="""You are a code reviewer. After the coder produces code:
1. Check for correctness and edge cases
2. Suggest improvements for readability and performance
3. Verify the code follows best practices
Be constructive and specific in your feedback.""",
)
# User proxy to manage the workflow
user_proxy = autogen.UserProxyAgent(
name="user_proxy",
human_input_mode="NEVER",
max_consecutive_auto_reply=10,
code_execution_config={
"work_dir": "vllm_workspace",
"use_docker": False,
},
)
# Create a group chat with both agents
groupchat = autogen.GroupChat(
agents=[user_proxy, coding_assistant, reviewer],
messages=[],
max_round=8,
speaker_selection_method="auto",
)
manager = autogen.GroupChatManager(
name="manager",
groupchat=groupchat,
llm_config={
"config_list": config_list,
"temperature": 0.1,
"max_tokens": 512,
"timeout": 120,
},
)
# Launch the collaborative session
user_proxy.initiate_chat(
manager,
message="""I need a Python implementation of a task scheduler that:
1. Accepts tasks with priorities and deadlines
2. Uses a priority queue
3. Supports adding, removing, and listing tasks
4. Includes thread-safe operations
Please have the coder implement it and the reviewer provide feedback.""",
)
print("\nCollaborative session complete!")
This example demonstrates a group chat pattern where a coding agent and a reviewer agent collaborate under the management of a group chat coordinator. The manager automatically selects which agent should speak next based on conversation context, creating a natural collaborative workflow entirely powered by your local vLLM instance.
Handling Common Issues
Ollama Troubleshooting
- Connection refused: Ensure
ollama serveis running. Check withcurl http://localhost:11434 - Slow responses on CPU: Ollama defaults to GPU if available. Set
OLLAMA_NUM_THREADS=8environment variable for better CPU performance - Model not found: Verify the model name matches exactly with
ollama list. Model names are case-sensitive - Timeout errors in AutoGen: Increase the
timeoutparameter inllm_config(try 300 or 600 seconds for larger models)
vLLM Troubleshooting
- CUDA out of memory: Reduce
--max-model-lenor lower--gpu-memory-utilizationto 0.8. Consider using a smaller model or quantization - Model loading fails: Check Hugging Face authentication. Some models require accepting license terms on the Hugging Face website
- Slow first response: vLLM performs upfront optimization on first request. Subsequent requests will be significantly faster
- Port already in use: Change
--portto an available port and update thebase_urlin AutoGen accordingly
Best Practices for Local LLM + AutoGen Deployments
1. Match Model Capability to Agent Role
Not all agents need the most powerful model. Consider using different models for different agents in the same workflow:
# Use a larger model for complex reasoning tasks
planner_config = {
"config_list": [{
"model": "llama3.1:70b", # Powerful model for planning
"base_url": "http://localhost:11434/v1",
"api_key": "ollama",
"api_type": "openai",
}],
"temperature": 0.7,
}
# Use a smaller, faster model for straightforward execution
executor_config = {
"config_list": [{
"model": "llama3.1:8b", # Smaller model for code execution
"base_url": "http://localhost:11434/v1",
"api_key": "ollama",
"api_type": "openai",
}],
"temperature": 0.1,
}
planner = autogen.AssistantAgent(name="planner", llm_config=planner_config)
executor = autogen.AssistantAgent(name="executor", llm_config=executor_config)
2. Implement Robust Error Handling
Local LLMs can occasionally produce malformed outputs or timeout. Wrap your AutoGen runs in try-except blocks:
import time
import sys
def run_agent_workflow_with_retry(max_retries=3):
for attempt in range(max_retries):
try:
user_proxy.initiate_chat(
assistant,
message="Your task description here",
)
return True
except Exception as e:
print(f"Attempt {attempt + 1} failed: {e}")
if attempt < max_retries - 1:
wait_time = 2 ** attempt * 10 # Exponential backoff
print(f"Retrying in {wait_time} seconds...")
time.sleep(wait_time)
else:
print("All retry attempts exhausted.")
raise
return False
run_agent_workflow_with_retry()
3. Optimize for Your Hardware
Configure generation parameters based on your available resources:
- Limited VRAM (8GB): Use quantized models (Q4_K_M or Q5_K_M quants in Ollama), set
max_tokensto 1024 or lower, and limit conversation rounds - Ample VRAM (24GB+): You can run larger models like 70B parameter variants, increase
max_tokensto 4096, and support longer multi-turn conversations - CPU-only mode: Expect 5-10x slower inference. Use smaller models (3B-7B parameters), increase timeouts significantly (600+ seconds), and limit parallel agent conversations
4. Leverage Streaming for Better UX
AutoGen supports streaming responses. Enable it to see agent outputs in real-time rather than waiting for the full response:
llm_config = {
"config_list": config_list,
"temperature": 0.7,
"max_tokens": 2048,
"stream": True, # Enable streaming
"timeout": 300,
}
assistant = autogen.AssistantAgent(
name="streaming_assistant",
llm_config=llm_config,
)
5. Cache Model Responses During Development
When iterating on agent logic, cache LLM responses to avoid redundant inference calls:
# Enable caching in the config
llm_config = {
"config_list": config_list,
"temperature": 0,
"max_tokens": 1024,
"use_cache": True, # Cache identical requests
}
# You can also specify a custom cache seed for reproducibility
assistant = autogen.AssistantAgent(
name="cached_assistant",
llm_config=llm_config,
)
# Clear cache between major changes
assistant.client.cache = None # Reset the cache
6. Monitor Resource Usage
Keep an eye on GPU memory and CPU utilization when running extended agent sessions. For vLLM, use the built-in metrics endpoint:
# Enable metrics in vLLM
python -m vllm.entrypoints.openai.api_server \
--model mistralai/Mistral-7B-Instruct-v0.3 \
--host 0.0.0.0 \
--port 8000 \
--enable-metrics
# Query metrics (in a separate terminal)
curl http://localhost:8000/metrics
For Ollama, monitor with system tools like nvidia-smi or htop.
7. Use Structured Outputs When Possible
Guide local models to produce well-formatted responses by crafting detailed system messages:
system_message = """You are a data extraction agent.
Always respond in valid JSON format with the following structure:
{
"summary": "Brief summary of findings",
"details": ["key point 1", "key point 2"],
"confidence": 0.0 to 1.0
}
Do not include any text outside the JSON block.
Wrap the JSON in json code fences."""
extractor = autogen.AssistantAgent(
name="extractor",
llm_config=llm_config,
system_message=system_message,
)
Performance Comparison: Ollama vs vLLM
Here's a practical comparison to help you choose the right backend:
- Ollama excels at ease of setup, model management (simple pull/run commands), and desktop use. It automatically handles model downloading, quantization selection, and GPU detection. Ideal for individual developers and prototyping.
- vLLM delivers significantly higher throughput for concurrent requests, supports advanced features like prefix caching and speculative decoding, and scales across multiple GPUs. Best for production deployments, serving multiple AutoGen sessions simultaneously, or when latency is critical.
For most AutoGen development workflows, start with Ollama for rapid iteration, then move to vLLM if you need production-grade performance or are running multiple concurrent agent sessions.
Complete End-to-End Example: Code Review Pipeline
Let's tie everything together with a complete, production-ready example that works with either Ollama or vLLM. This pipeline implements an automated code generation, review, and improvement cycle:
import autogen
import os
import json
from datetime import datetime
# ============================================
# CONFIGURATION - Switch between Ollama and vLLM
# ============================================
USE_OLLAMA = True # Set to False for vLLM
if USE_OLLAMA:
# Ollama configuration
BASE_URL = "http://localhost:11434/v1"
CODING_MODEL = "llama3.1:8b"
REVIEW_MODEL = "llama3.1:8b"
API_KEY = "ollama"
else:
# vLLM configuration
BASE_URL = "http://localhost:8000/v1"
CODING_MODEL = "mistralai/Mistral-7B-Instruct-v0.3"
REVIEW_MODEL = "mistralai/Mistral-7B-Instruct-v0.3"
API_KEY = "EMPTY"
# Shared configuration template
def create_agent_config(model, temperature=0.3, max_tokens=2048):
return {
"config_list": [{
"model": model,
"api_key": API_KEY,
"base_url": BASE_URL,
"api_type": "openai",
}],
"temperature": temperature,
"max_tokens": max_tokens,
"timeout": 300,
"stream": True,
}
# ============================================
# AGENT DEFINITIONS
# ============================================
# Coder agent - generates initial implementations
coder = autogen.AssistantAgent(
name="SeniorDeveloper",
llm_config=create_agent_config(CODING_MODEL, temperature=0.2, max_tokens=2048),
system_message="""You are a senior software engineer with 15 years of experience.
Your task is to write production-quality Python code based on requirements.
Guidelines:
- Write complete, working implementations (not stubs)
- Include comprehensive docstrings
- Add type hints for all function signatures
- Handle edge cases and errors gracefully
- Include unit tests as part of your response
- Output code in python code blocks""",
)
# Reviewer agent - provides constructive feedback
reviewer = autogen.AssistantAgent(
name="CodeReviewer",
llm_config=create_agent_config(REVIEW_MODEL, temperature=0.4, max_tokens=1500),
system_message="""You are a meticulous code reviewer. After reviewing code, provide:
1. Overall assessment (score 1-10)
2. Specific issues found (if any)
3. Suggested improvements with code examples
4. Security concerns (if applicable)
5. Performance considerations
Be thorough but constructive. If the code is excellent, say so clearly.
Format your review in structured markdown.""",
)
# Refiner agent - improves code based on feedback
refiner = autogen.AssistantAgent(
name="CodeRefiner",
llm_config=create_agent_config(CODING_MODEL, temperature=0.1, max_tokens=2048),
system_message="""You are a code refinement specialist.
Given original code and review feedback, produce an improved version that:
- Addresses ALL issues raised in the review
- Maintains backward compatibility
- Adds any missing error handling
- Improves performance where suggested
- Output the complete refined code in python blocks""",
)
# User proxy agent
user_proxy = autogen.UserProxyAgent(
name="ProjectManager",
human_input_mode="NEVER",
max_consecutive_auto_reply=15,
code_execution_config={
"work_dir": "code_review_workspace",
"use_docker": False,
},
system_message="Project manager overseeing the code generation and review process.",
)
# ============================================
# ORCHESTRATION LOGIC
# ============================================
def run_code_review_pipeline(task_description):
"""
Orchestrates a complete code generation → review → refinement cycle.
"""
# Phase 1: Generate initial code
print("\n" + "="*60)
print("PHASE 1: Code Generation")
print("="*60)
generation_result = user_proxy.initiate_chat(
coder,
message=f"""Please implement the following requirement:
{task_description}
Provide the complete implementation with tests.""",
clear_history=True,
)
# Extract the generated code (last message from coder)
coder_response = user_proxy.last_message()
# Phase 2: Review the code
print("\n" + "="*60)
print("PHASE 2: Code Review")
print("="*60)
review_prompt = f"""Please review the following code implementation:
{coder_response.get('content', str(coder_response))}
Provide a thorough code review following your review guidelines."""
review_result = user_proxy.initiate_chat(
reviewer,
message=review_prompt,
clear_history=True,
)
reviewer_response = user_proxy.last_message()
# Phase 3: Refine based on feedback
print("\n" + "="*60)
print("PHASE 3: Code Refinement")
print("="*60)
refinement_prompt = f"""Original code:
{coder_response.get('content', str(coder_response))}
Review feedback:
{reviewer_response.get('content', str(reviewer_response))}
Please produce a refined version of the code addressing all feedback."""
refinement_result = user_proxy.initiate_chat(
refiner,
message=refinement_prompt,
clear_history=True,
)
refiner_response = user_proxy.last_message()
# Save results
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
output_dir = f"pipeline_output_{timestamp}"
os.makedirs(output_dir, exist_ok=True)
with open(f"{output_dir}/original_code.md", "w") as f:
f.write(str(coder_response.get('content', '')))
with open(f"{output_dir}/review.md", "w") as f:
f.write(str(reviewer_response.get('content', '')))
with open(f"{output_dir}/refined_code.md", "w") as f:
f.write(str(refiner_response.get('content', '')))
print(f"\nPipeline complete! Output saved to {output_dir}/")
return {
"original": coder_response,
"review": reviewer_response,
"refined": refiner_response,
"output_dir": output_dir,
}
# ============================================
# RUN THE PIPELINE
# ============================================
if __name__ == "__main__":
task = """
Create a Python class called 'RateLimiter' that implements a sliding window
rate limiting algorithm. Requirements:
1. Constructor accepts: max_requests (int), window_seconds (float)
2. Method 'is_allowed(client_id: str) -> bool' that returns True if the
client hasn't exceeded the limit in the current window
3. Thread-safe implementation using appropriate locking
4. Efficient cleanup of expired entries
5. Support for multiple concurrent clients
Example usage should be included in the tests.
"""
results = run_code_review_pipeline(task)
print("\nFinal refined code:")
print(results["refined"].get("content