Skip to main content

Someone wanted to learn this too, so Grasp built them a personal learning path.

You can keep exploring this one or create your own

Policy-Based Routing Optimization

Welcome back! In our previous lesson, we built a router that could distinguish between text and image inputs, a fundamental step in creating multimodal AI systems. That was a form of content-based routing, where the rule was static and based on the type of data in the request.

Today, we will elevate our router's intelligence by implementing policy-based routing. Instead of just looking at the content type, our router will make dynamic decisions to optimize for specific business or operational goals, such as minimizing cost, reducing latency, or selecting the most capable model for a complex task. This is a core competency for any LLM Ops engineer aiming to build efficient, scalable, and cost-effective AI services.

By the end of this lesson, you will be able to implement various routing policies to create a sophisticated, self-optimizing LLM gateway.

1. The Core Trade-Off: Cost, Latency, and Quality

The fundamental reason for policy-based routing is that not all LLM requests are created equal. Using a state-of-the-art model like GPT-4o for a simple task like formatting a list is like using a supercomputer to run a calculator—it's powerful but incredibly wasteful. Policy-based routing allows us to manage the inherent trade-offs between performance and cost.

This graph illustrates the central challenge of LLM routing: as you prioritize performance (accuracy), the total cost inevitably rises. Policy-based routing is the mechanism we use to navigate this trade-off and find the optimal operating point for our application.

The goal is to build a system that can intelligently select the "right" model for each request, maximizing value while minimizing expense.

2. Why Intelligent Routing Matters: The Business Case

Before diving into the implementation, let's understand the significant impact of intelligent routing. A well-designed router can be one of the most effective cost-saving tools in your LLM Ops toolkit.

The following video introduces RouteLLM, an open-source framework from the creators of Chatbot Arena, which exemplifies this concept.

What is an LLM Router?

Sam Witteveen's video, "What is an LLM Router?", explains the motivation and impressive results of using a dedicated routing model.

Watch the beginning of the video to grasp the potential benefits: The Problem and Solution: This section frames the problem of overspending on powerful models and introduces the concept of an LLM router that decides whether to use a cheap, fast model or a powerful, slow one. Quantifying the Impact: Pay close attention to the results. RouteLLM achieved over 85% cost savings on some datasets while retaining 95% of GPT-4's performance. These figures highlight why this is not just a theoretical exercise but a critical operational strategy.

As you can see, the savings are substantial. For any production system, this capability moves from a "nice-to-have" to a necessity for financial viability.

3. Types of Routing Policies

A "policy" is simply a rule that governs the router's decision. Your SRE background in managing complex systems with specific operational targets will be very useful here, as these policies directly map to SLOs for cost, latency, and availability.

Let's explore common routing policies using the excellent code examples from the "LLM Gateway & Routing" guide.

llm-gateway-routing | Skills Marketp... - LobeHub

This resource provides concise TypeScript examples for several key routing strategies. While the code is in TypeScript, the logic is universal and easily translatable to Python or Go.

Read the section titled "Routing Strategies". Focus on understanding the logic within each function, as they represent distinct policies: Cost-Based Routing: The routeByCost function shows how you can pre-define tiers of models (cheap, balanced, premium) and select a model based on a budget parameter. Latency-Based Routing: The routeByLatency function implements a dynamic policy. It tracks the average latency of each model and always routes the request to the currently fastest one. This is a classic adaptive system pattern. Task-Based Routing (Capability): The routeByTask function maps specific task types (e.g., coding, reasoning) to the model best suited for that task. This ensures the highest quality output for specialized requests. Hybrid Routing: The hybridRoute function combines multiple policies. It first filters models by cost and latency constraints and then selects the best one for the task from the remaining candidates. This is typical of a production-grade router.

In addition to these, a crucial policy for reliability is implementing fallbacks. The LobeHub guide also provides a clear example of a callWithFallback function that tries a chain of models until one succeeds. This is a direct application of the fallback reliability pattern you're already familiar with.

4. Practical Implementation: Two Approaches

Now, let's move from theory to practice. We'll explore two ways to implement these policies: a declarative approach using configuration and a programmatic approach using Python.

Approach 1: Declarative Policies with LiteLLM

For many use cases, especially those tied to user or team identity, you can define routing policies declaratively. LiteLLM, which we've used before, has excellent support for this through its "Teams" and "Virtual Keys" features.

This video provides a step-by-step walkthrough of setting this up.

LiteLLM Crash Course | For Complete Beginners

The "LiteLLM Crash Course" demonstrates how to enforce model access policies in a practical, hands-on way.

Watch the following segments to see how to implement a user-based routing policy: Deploying the Proxy: A quick refresher on how to get the LiteLLM proxy server running with Docker Compose. Configuring Teams and Models: This is the core of the policy definition. Observe how different models (from OpenAI, Groq) are added to the "Model Hub," and then "Teams" (e.g., Finance, HR) are created with access restricted to specific models. Creating Virtual Keys: Here, a "virtual key" is generated for the "Finance" team. This key is hard-coded to only allow access to the models assigned to that team. Testing the Policy: This is the most important part. The presenter uses the generated key to make API calls. Notice how a call to the allowed model (Groq Llama 3) succeeds, while a call to a disallowed model (GPT-4.1) correctly fails with an authorization error. This is policy enforcement in action. Finally, briefly watch the demonstration of the usage dashboard, which shows how you can track costs per team, tying directly into cost modeling.

This declarative approach is powerful because it separates policy from code and aligns perfectly with your IaC expertise. You can manage routing rules in YAML files stored in Git.

Approach 2: Programmatic Routing with Python

For more dynamic routing based on the content of the prompt, a programmatic approach offers more flexibility. You can write a Python service that inspects the prompt and applies heuristics or even a lightweight classifier model to choose the right downstream LLM.

The following resource provides a complete, practical Python script for this.

How to Build LLM Deployment Architecture

This article from OneUptime includes an excellent section on intelligent routing with a full Python example.

Focus on the section "Intelligent Request Routing." Architecture: First, look at the flowchart under "Request Routing and Load Balancing." It clearly visualizes how the API Gateway's "Model Router" sits in front of a pool of models of varying sizes. Code Example: Now, study the router.py script. This is a fantastic real-world example. Notice these key components: ModelTier enum and ModelEndpoint dataclass: These structures elegantly define the available models and their associated metadata, including cost_per_1k_tokens. estimate_complexity function: This function implements the core routing policy. It uses simple keyword-based heuristics to classify a prompt as FAST, BALANCED, or QUALITY. route_request function: This is the main entry point that calls estimate_complexity to select an endpoint and then proxies the request. It also enriches the response with routing metadata, including an estimated cost—crucial for observability.

This Python-based approach gives you maximum control and is ideal for implementing complex, content-aware routing logic. Given your background, you can see how this script could be deployed as a containerized service within a Kubernetes cluster, acting as the central brain of your LLM gateway.

Your Turn: Extend the Routing Policy

Let's apply what you've learned. Your task is to modify the logic from the router.py script.

Imagine your team wants to optimize costs further by introducing an ultra-fast, cheap model for very simple, well-defined tasks like extraction or translation.

Task (5-10 minutes):

  1. Define a new ModelTier called UTILITY.
  2. Add a new ModelEndpoint to the ENDPOINTS dictionary for this tier. You can name the model 'distil-llama-3b', give it a URL, and assign it a very low cost_per_1k_tokens (e.g., 0.00005).
  3. Modify the estimate_complexity function to route requests to this new UTILITY tier. Your new policy should be: if the prompt contains keywords like "translate," "extract," "list," or "format," it should return ModelTier.UTILITY. The previous FAST tier should now be used for summarization tasks, and BALANCED remains the default.

Write down the modified Python code for the ModelTier enum, the ENDPOINTS dictionary, and the estimate_complexity function.

Solution

Here's one possible implementation of the changes:




# router.py (modified sections)

class ModelTier(Enum):
    """Model tiers based on capability and cost."""
    UTILITY = "utility" # New tier for simple tasks
    FAST = "fast"
    BALANCED = "balanced"
    QUALITY = "quality"




# Define available model endpoints
ENDPOINTS = {



    # New endpoint for the UTILITY tier
    ModelTier.UTILITY: ModelEndpoint(
        name="distil-llama-3b",
        url="http://vllm-distil-llama-3b:8000/v1",
        tier=ModelTier.UTILITY,
        max_tokens=4096,
        cost_per_1k_tokens=0.00005,
    ),
    ModelTier.FAST: ModelEndpoint(
        name="llama-8b",
        url="http://vllm-llama-8b:8000/v1",
        tier=ModelTier.FAST,
        max_tokens=8192,
        cost_per_1k_tokens=0.0001,
    ),
    ModelTier.BALANCED: ModelEndpoint(
        name="llama-70b",
        url="http://vllm-llama-70b:8000/v1",
        tier=ModelTier.BALANCED,
        max_tokens=8192,
        cost_per_1k_tokens=0.001,
    ),
    ModelTier.QUALITY: ModelEndpoint(
        name="llama-405b",
        url="http://vllm-llama-405b:8000/v1",
        tier=ModelTier.QUALITY,
        max_tokens=8192,
        cost_per_1k_tokens=0.01,
    ),
}

def estimate_complexity(prompt: str) -> ModelTier:
    """Estimate the complexity of a prompt to route to appropriate model."""
    prompt_lower = prompt.lower()
    



    # Complex reasoning tasks need the largest model
    complex_keywords = [
        "analyze", "compare", "explain in detail",
        "step by step", "reasoning", "prove",
        "code review", "architecture", "design"
    ]
    if any(kw in prompt_lower for kw in complex_keywords):
        return ModelTier.QUALITY
        



    # New rule for simple utility tasks
    utility_keywords = ["translate", "extract", "list", "format", "convert"]
    if any(kw in prompt_lower for kw in utility_keywords):
        return ModelTier.UTILITY




    # Modified rule for fast tasks (now focused on summarization)
    fast_keywords = ["summarize"]
    if any(kw in prompt_lower for kw in fast_keywords):
        return ModelTier.FAST




    # Default to balanced
    return ModelTier.BALANCED

Conclusion

In this lesson, you've moved from simple content-based routing to sophisticated, policy-based routing. This capability is the linchpin of an efficient and scalable LLM serving infrastructure.

Key Takeaways:

  • The Goal: Policy-based routing aims to intelligently manage the trade-off between cost, latency, and quality by selecting the most appropriate model for each request.
  • Policy Types: Policies can be based on static attributes (like user identity or team), dynamic metrics (like model latency), or request content (like task complexity).
  • Implementation Methods:
    • Declarative (IaC-friendly): Use tools like LiteLLM to define policies in configuration files, ideal for rules based on teams, users, or budgets.
    • Programmatic (Flexible): Write custom logic (e.g., in Python) to implement complex, content-aware policies based on heuristics or machine learning classifiers.
  • Architectural Role: The router acts as an intelligent gateway or reverse proxy, sitting between your clients and your pool of diverse LLM endpoints.

In our next lesson, we will zoom in on the financial aspect of this system. Now that you know how to route requests based on cost, you'll learn how to identify the key cost drivers and build a model to calculate critical metrics like dollars-per-request and dollars-per-successful-task.

Can't find a good explanation? Sign up and we'll make it for you

Sign up