Python abc Module: Defining Abstract Base Classes for Agent Interfaces
Learn to define robust agent interfaces using Python's abc module with abstract methods, interface contracts, plugin patterns, and registration hooks for extensible AI architectures.
Why Abstract Base Classes Matter for Agent Architectures
AI agent systems are inherently modular. You swap LLM providers, switch vector databases, add new tools, and replace memory backends. Without formal interfaces, each swap becomes a risky refactor where you discover missing methods at runtime — usually in production.
Python's abc module lets you define abstract base classes (ABCs) that enforce interface contracts at class definition time, not at call time. If a subclass forgets to implement a required method, Python raises a TypeError when you try to instantiate it, long before any agent logic runs.
Defining an Agent Interface
Start with the base agent contract that all agent implementations must follow.
from abc import ABC, abstractmethod
from typing import Any
class BaseAgent(ABC):
"""Contract that all agent implementations must satisfy."""
@abstractmethod
async def run(self, input_text: str) -> str:
"""Process input and return a response."""
...
@abstractmethod
async def get_tools(self) -> list[dict]:
"""Return the list of tools available to this agent."""
...
@abstractmethod
def get_system_prompt(self) -> str:
"""Return the system prompt for this agent."""
...
# Concrete method shared by all implementations
def format_response(self, raw: str) -> str:
return raw.strip()
# This fails at instantiation, not at method call time
# agent = BaseAgent() # TypeError: Can't instantiate abstract class
Implementing Concrete Agents
Subclasses must implement every abstract method or Python refuses to instantiate them.
class ResearchAgent(BaseAgent):
def __init__(self, model: str = "gpt-4o"):
self.model = model
async def run(self, input_text: str) -> str:
tools = await self.get_tools()
# Use tools to research the input
return f"Research results for: {input_text}"
async def get_tools(self) -> list[dict]:
return [
{"name": "web_search", "description": "Search the web"},
{"name": "arxiv_search", "description": "Search academic papers"},
]
def get_system_prompt(self) -> str:
return "You are a research agent. Find accurate information."
class CodingAgent(BaseAgent):
async def run(self, input_text: str) -> str:
return f"Code solution for: {input_text}"
async def get_tools(self) -> list[dict]:
return [
{"name": "code_exec", "description": "Execute Python code"},
{"name": "file_read", "description": "Read source files"},
]
def get_system_prompt(self) -> str:
return "You are a coding agent. Write clean, tested code."
Abstract Properties and Class Methods
ABCs can enforce properties and class methods, not just regular methods.
See AI Voice Agents Handle Real Calls
Book a free demo or calculate how much you can save with AI voice automation.
from abc import ABC, abstractmethod
class BaseLLMProvider(ABC):
@property
@abstractmethod
def model_name(self) -> str:
"""The model identifier used for API calls."""
...
@property
@abstractmethod
def max_context_length(self) -> int:
"""Maximum tokens in the context window."""
...
@abstractmethod
async def complete(self, messages: list[dict]) -> str: ...
@abstractmethod
async def stream(self, messages: list[dict]): ...
# Concrete method that uses abstract properties
def can_fit(self, token_count: int) -> bool:
return token_count <= self.max_context_length
class OpenAIProvider(BaseLLMProvider):
@property
def model_name(self) -> str:
return "gpt-4o"
@property
def max_context_length(self) -> int:
return 128_000
async def complete(self, messages: list[dict]) -> str:
# OpenAI-specific implementation
return "response"
async def stream(self, messages: list[dict]):
yield "token"
Plugin Registry Pattern
Combine ABCs with a registry to build extensible agent systems where new implementations are discovered automatically.
from abc import ABC, abstractmethod
from typing import Type
class ToolPlugin(ABC):
_registry: dict[str, Type["ToolPlugin"]] = {}
def __init_subclass__(cls, **kwargs):
super().__init_subclass__(**kwargs)
if hasattr(cls, "tool_name") and cls.tool_name:
ToolPlugin._registry[cls.tool_name] = cls
@property
@abstractmethod
def tool_name(self) -> str: ...
@abstractmethod
async def execute(self, args: dict) -> str: ...
@classmethod
def get_tool(cls, name: str) -> "ToolPlugin":
tool_class = cls._registry.get(name)
if not tool_class:
raise ValueError(f"Unknown tool: {name}")
return tool_class()
@classmethod
def list_tools(cls) -> list[str]:
return list(cls._registry.keys())
# Plugins auto-register on definition
class CalculatorTool(ToolPlugin):
tool_name = "calculator"
async def execute(self, args: dict) -> str:
expr = args["expression"]
return str(eval(expr)) # simplified for example
class WeatherTool(ToolPlugin):
tool_name = "weather"
async def execute(self, args: dict) -> str:
return f"Weather for {args['city']}: Sunny, 72F"
# Discover all registered tools
print(ToolPlugin.list_tools()) # ["calculator", "weather"]
tool = ToolPlugin.get_tool("calculator")
FAQ
When should I use ABC versus Protocol for interfaces?
Use ABC when you want to enforce that subclasses explicitly inherit from the base and implement all methods — this provides early error detection at class instantiation. Use Protocol when you want structural (duck) typing where any class that happens to have the right methods qualifies, without requiring inheritance. ABCs are better for plugin systems; Protocols are better for loose coupling.
Can a class inherit from multiple ABCs?
Yes. Python supports multiple inheritance, and a class can implement several ABC interfaces. This is common in agent architectures where a class might implement both BaseAgent and Serializable interfaces. Use this pattern carefully to avoid diamond inheritance complexity.
How do I handle optional methods in an ABC?
Provide a default implementation in the ABC instead of marking the method abstract. Subclasses can override it if they need custom behavior, but they are not forced to. This is the template method pattern and it works well for lifecycle hooks like on_start, on_error, and on_complete in agent frameworks.
#Python #ABC #DesignPatterns #Interfaces #AgenticAI #LearnAI #AIEngineering
CallSphere Team
Expert insights on AI voice agents and customer communication automation.
Try CallSphere AI Voice Agents
See how AI voice agents work for your industry. Live demo available -- no signup required.