Building Agnostic Intent Matching Engines
In modern AI-driven systems, the underlying models evolve rapidly. If your application code is tightly coupled to a specific API (like OpenAI’s) or a local library (like Transformers), you face significant technical debt when you need to migrate, upgrade, or optimize costs.
To solve this, we define an Abstraction-Driven Intent Engine. By separating the Engine (the orchestrator) from the Provider (the model implementation), we create a pluggable architecture that treats AI as a swappable commodity.
The Provider Pattern
The provider pattern ensures that your business logic remains stable, even when the underlying technology stack undergoes a complete overhaul.
1. Simple Implementation: Defining the Provider Contract
We define the IntentProvider abstract base class to enforce a strict contract. Any AI implementation, whether it is a local model or a cloud-based inference endpoint, must adhere to this interface.
from abc import ABC, abstractmethod
class IntentProvider(ABC):
@abstractmethod
def classify(self, text: str) -> str:
"""Standardized interface for intent matching."""
pass
class MockProvider(IntentProvider):
def classify(self, text: str) -> str:
return "GREETING_INTENT"
2. Complex Implementation: Enterprise Fallback Chain
In production, a single model is rarely enough. We implement a CompositeProvider to orchestrate multiple models, ensuring resilience through fallback mechanisms.
class CompositeProvider(IntentProvider):
def __init__(self, primary: IntentProvider, fallback: IntentProvider):
self.primary = primary
self.fallback = fallback
def classify(self, text: str) -> str:
try:
result = self.primary.classify(text)
return result
except Exception as e:
# Fallback to a simpler, more robust model if the primary fails
print(f"Primary engine error: {e}. Switching to fallback.")
return self.fallback.classify(text)
# Orchestrating via DI
# engine = IntentMatchingEngine(provider=CompositeProvider(HeavyLLM(), FastHeuristic()))
Architectural Advantages
- Model Agnosticism: Your application logic interacts only with the
IntentProviderinterface. You can swapLlama-3forMistralor an internalXGBoostclassifier by changing only the injected class in your Kernel. - A/B Testing & Canary Deployments: By wrapping multiple providers, you can route 5% of traffic to a new model version while keeping the rest on the stable version, all without touching your core application flow.
- Testing in Isolation: By injecting a
MockProvider, you can run high-speed integration tests in your CI/CD pipeline without needing to load 40GB of model weights into GPU memory.
Production Strategy
To achieve true modularity, standardize the output schema. Ensure all providers return a consistent JSON structure—including confidence scores—so that your downstream services (like a database or an automation engine) can reliably act on the model’s output regardless of the specific provider used.
- Author: Amin Boulouma, Software Engineer
- Github source code: https://github.com/aminblm/ai_systems_design_from_scratch