HOW-TO · RAG
How to Design Specialized Agent Roles
Target environment
Ubuntu 24.04 · Ollama 0.4.x
PREREQUISITES
Multi-agent system architecture planned, Python 3.10+
What this does
Specialized agent roles divide responsibilities across agents with distinct capabilities, prompts, and tools — enabling each agent to excel at a specific domain while the system handles complex multi-step tasks.
Steps
- Define role boundaries. Each role has a clear responsibility, tool set, and prompt.
from dataclasses import dataclass, field
@dataclass
class AgentRole:
name: str
description: str
system_prompt: str
tools: list = field(default_factory=list)
max_consecutive_failures: int = 3
RESEARCHER = AgentRole(
name="researcher",
description="Searches and gathers information from the web and documents",
system_prompt="You are a research specialist. Find relevant, accurate information.",
tools=["search_web", "fetch_page", "query_database"]
)
ANALYST = AgentRole(
name="analyst",
description="Analyzes data and produces insights",
system_prompt="You are a data analyst. Interpret data and create summaries.",
tools=["calculate", "analyze_data", "create_chart"]
)
WRITER = AgentRole(
name="writer",
description="Produces well-formatted final output",
system_prompt="You are a technical writer. Produce clear, well-structured content.",
tools=["format_text", "check_grammar", "export_document"]
)
- Build a role-based agent factory. Instantiate agents from role definitions.
class AgentFactory:
def __init__(self, llm_client, tool_registry):
self.llm = llm_client
self.tool_registry = tool_registry
def create_agent(self, role: AgentRole) -> dict:
return {
"role": role,
"llm": self.llm,
"tools": [self.tool_registry.get(t) for t in role.tools],
"state": {
"conversation_history": [],
"failures": 0,
"current_task": None
}
}
- Implement role-specific tool permissions. Restrict which tools each role can access.
class ToolGate:
def __init__(self):
self.permissions = {}
def grant(self, role: str, tool_name: str):
if role not in self.permissions:
self.permissions[role] = set()
self.permissions[role].add(tool_name)
def can_call(self, role: str, tool_name: str) -> bool:
return tool_name in self.permissions.get(role, set())
# Usage
gate = ToolGate()
gate.grant("researcher", "search_web")
gate.grant("writer", "export_document")
# analyst cannot call search_web or export_document
- Configure escalation paths. When an agent cannot complete its task, it escalates to another role.
ESCALATION_MAP = {
"researcher": "analyst", # researcher passes findings to analyst
"analyst": "writer", # analyst passes insights to writer
"writer": None # writer is the final step
}
def determine_next_role(current_role: str, task_result: dict) -> str | None:
return ESCALATION_MAP.get(current_role)
- Test role isolation. Verify agents cannot access tools outside their role.
assert not gate.can_call("writer", "search_web")
assert gate.can_call("researcher", "search_web")
Verification
python -c "
from dataclasses import dataclass
@dataclass
class Role:
name: str
desc: str
r = Role(name='test', desc='A test role')
print(r.name)
# Expected: test
"
Common failures
- Overlapping responsibilities. Two roles can handle the same task, causing confusion and redundant work. Define clear boundaries with no overlap.
- Role isolation bypass. An agent with free-form prompts can ignore its role and call any tool. Enforce tool access at the infrastructure level, not by convention.
- Missing escalation path. When a role fails, the task stalls. Always define a fallback or escalation for every role.
- Version mismatch - The installed package or runtime differs from the command shown; check the version first and rerun the smallest verification command.
- Local environment drift - Another service, virtual environment, model, or path is being used; print the active binary path and configuration before changing the guide steps.
Related guides
- How to Build Multi-Agent Orchestration System
- How to Implement Agent-to-Agent Communication