Files
zeekayandHanzo Dev b79b939f11 We are based in San Francisco
Every author block and the authorship standard said Los Angeles; Hanzo has
been in San Francisco since at least 2016. The five catalogue PDFs whose
sources changed are rebuilt so papers.hanzo.ai says so too.

Co-authored-by: Hanzo Dev <[email protected]>
2026-09-24 16:14:51 -07:00

548 lines
29 KiB
TeX

\documentclass[11pt]{article}
\usepackage{../shared/hanzocover}
\usepackage[utf8]{inputenc}
\usepackage[T1]{fontenc}
\usepackage{amsmath,amssymb}
\usepackage{graphicx}
\usepackage{booktabs}
\usepackage{xcolor}
\usepackage{listings}
\input{../shared/lstlang}
\usepackage{tikz}
\usetikzlibrary{shapes,arrows.meta,positioning,fit,backgrounds,calc}
\usepackage[hidelinks]{hyperref}
\definecolor{codegreen}{rgb}{0,0.6,0}
\definecolor{codegray}{rgb}{0.5,0.5,0.5}
\definecolor{codepurple}{rgb}{0.58,0,0.82}
\definecolor{backcolour}{rgb}{0.96,0.96,0.94}
\definecolor{hzblue}{rgb}{0.12,0.30,0.55}
\definecolor{hzslate}{rgb}{0.22,0.25,0.30}
\definecolor{hzgreen}{rgb}{0.10,0.45,0.25}
\definecolor{hzred}{rgb}{0.60,0.16,0.16}
\lstdefinestyle{mystyle}{
backgroundcolor=\color{backcolour},
commentstyle=\color{codegreen},
keywordstyle=\color{codepurple},
numberstyle=\tiny\color{codegray},
stringstyle=\color{codegreen},
basicstyle=\ttfamily\footnotesize,
breakatwhitespace=false,
breaklines=true,
captionpos=b,
keepspaces=true,
numbers=none,
showspaces=false,
showstringspaces=false,
showtabs=false,
tabsize=2,
frame=single,
rulecolor=\color{codegray}
}
\lstset{style=mystyle}
\title{Hanzo Router: Memory-Aware, Local-First LLM Routing with a Learned SLO-Constrained Policy}
\author{Hanzo AI Research\thanks{Corresponding author: [email protected]} \\
\textit{Hanzo Industries Inc (Techstars '17)} \\
San Francisco, California \\
\texttt{https://hanzo.ai}}
\date{Hanzo Research}
\begin{document}
\hanzocoverpage
\twocolumn[
\begin{@twocolumnfalse}
\maketitle
\begin{abstract}
\noindent
Frontier language models are priced for their hardest queries, but a
production request stream is dominated by its easiest ones: short chats,
classification, formatting, and boilerplate code that a small local model
answers as well as a frontier API. Paying the frontier price on every token
is the single largest avoidable cost in an LLM product. We describe
\emph{Hanzo Router}, the request router that \texttt{hanzo-node} uses to place
each request across a swappable pool of local and cloud models. The design
decomplects \emph{mechanism} from \emph{brain}. The mechanism
(\texttt{hanzo-router}) is pure logic over value inputs: a model registry, a
memory snapshot, the set of already-loaded models, and a per-request
service-level objective; it prefers, in order, reusing a model already
resident (zero marginal cost), loading a local model that fits available
memory, then the cheapest usable cloud model. The brain is a swappable policy
behind a one-method seam: a rule-based cold-start policy, a learned bilinear
utility with per-user online adaptation (\texttt{enso}), or a small trainable
routing encoder (\texttt{zen-router}). We give the SLO-constrained objective
the learned policy optimizes, $\mathrm{quality}-\lambda\,\mathrm{cost}-\mu\,\mathrm{latency}$
under hard feasibility ceilings; the memory-aware fit check that makes
local-first placement deterministic and unit-testable; the online LinUCB loop
that personalizes routing from observed rewards; and a usage-plane signal that
biases routing away from providers near their rate limits. We do not claim a
measured benchmark saving. Instead we give a transparent, itemized cost model
that goes beyond API token prices: for a mixed workload whose simple traffic
is served locally for the price of electricity (not a rented GPU) and whose
medium traffic drops to a cheap cloud tier, the arithmetic yields roughly a
90\% reduction in total AI spend---token cost, avoided cloud-GPU rental,
consolidated per-seat subscriptions, and rate-limit overage---in line with
the published routing and cascade literature.
\end{abstract}
\vspace{1.5em}
\end{@twocolumnfalse}
]
\section{Introduction}
\label{sec:intro}
A frontier language model is priced for the worst case. Its per-token rate has
to cover the queries that genuinely need a trillion-parameter model: multi-step
reasoning, long-context synthesis, hard code generation. But the request stream
of a deployed product does not look like that benchmark. It is dominated by
easy traffic: a one-line clarification, a yes/no classification, a JSON reformat,
an autocomplete, a boilerplate function. On that traffic a 4B model running on a
laptop is indistinguishable from a frontier API to the end user, and it costs
only electricity per token---about two orders of magnitude below frontier API
pricing (\S\ref{sec:cost})---on hardware already paid for.
Routing every request to the frontier model therefore spends the frontier price
on a stream whose median query does not need it. This is the single largest
avoidable line item in an LLM product's bill, and it is avoidable precisely
because the mismatch is structural: the price is set by the tail, the volume is
in the head. A router that sends each request to the cheapest model that can
still serve it converts that structural mismatch into a cost saving, without a
user-visible quality regression, provided it can tell the two kinds of request
apart and provided it has somewhere cheap to send the easy ones.
This paper describes the router Hanzo runs in production, \texttt{hanzo-router},
and its learned policy \texttt{enso}. The contribution is not a new model; it is
an architecture that makes cheap-when-you-can, frontier-when-you-must routing a
first-class, testable, learnable component of the serving stack. We make four
claims. First, the routing \emph{mechanism} should be pure logic over value
inputs, with no hardware access of its own, so that placement is deterministic
and unit-testable (\S\ref{sec:mechanism}). Second, local-first placement is a
memory-fit problem: given a snapshot of available memory and the set of models
already resident, the router reuses, loads, or falls through to cloud by a
single ordered walk of preferences (\S\ref{sec:mechanism}). Third, the
\emph{policy} that ranks models is a swappable brain behind a one-method seam,
so a rule guess, a learned bilinear utility, and a trainable encoder are
interchangeable and compose (\S\ref{sec:brain}, \S\ref{sec:learning}). Fourth,
the saving is real but must be stated honestly: we give a transparent cost model
(\S\ref{sec:cost}) rather than a fabricated benchmark, and anchor the magnitude
in the published routing literature (\S\ref{sec:related}).
\section{The Mechanism: Memory-Aware, Local-First Placement}
\label{sec:mechanism}
The mechanism is the crate \texttt{hanzo-router}. It is deliberately dumb about
hardware and clever about values. Following the Rich Hickey discipline of
separating a decision from the place it runs~\cite{hickey_simple},
\texttt{hanzo-node} gathers the facts the decision needs into plain values and
hands them to a pure function; the router touches no device and opens no socket,
so the same inputs always produce the same decision and every path is a unit
test.
\input{figures/decision}
\subsection{The pool as a registry of value rows}
Every servable model is one \texttt{ModelCard}: a canonical id, a
\texttt{Backend} that is either \texttt{Local\{est\_bytes\}} (served by a local
\texttt{hanzo-engine}, with the resident footprint the router fits against
memory) or \texttt{Cloud\{provider\}}; the set of \texttt{Task}s the model is
preferred for; its maximum context; a vision flag; and a relative
\texttt{cost\_per\_1k}. Adding a model is adding a row. Tasks are a coarse
taxonomy---\texttt{Code}, \texttt{Reasoning}, \texttt{Math}, \texttt{Creative},
\texttt{Vision}, \texttt{LongContext}, \texttt{CheapChat}, \texttt{General}---used
only to rank quality within a modality pool; modality (text, vision, image,
audio, video) selects the pool itself. The two axes are orthogonal, so one
router serves chat, vision, and media generation by matching pools.
A request is classified to a task by a \texttt{Classifier}. The default
\texttt{Heuristic} is keyword-, length-, and media-based: fenced code or words
like ``bug'' and ``refactor'' route to \texttt{Code}; ``prove'', ``theorem'',
``integral'' route to \texttt{Math}; a prompt over 32k tokens is
\texttt{LongContext}; a prompt of a handful of tokens is \texttt{CheapChat};
everything else is \texttt{General}. The heuristic is a seam, not a commitment:
the \texttt{Classifier} trait lets a learned model produce the same
\texttt{Task} value without the policy changing, which is exactly where
\texttt{zen-router} (\S\ref{sec:brain}) plugs in.
\subsection{Memory as a value, and the fit check}
The one physical fact the router needs is how much memory it can spend. That,
too, is a value: a \texttt{MemSnapshot} carrying \texttt{available\_bytes},
\texttt{total\_bytes}, and a \texttt{unified} flag, filled by \texttt{hanzo-node}
from the same \texttt{MemoryUsage}/\texttt{DeviceMemory} signal the engine's
auto device-mapper reads. The usable budget for resident weights is a fraction
of available memory,
\begin{equation}
\mathrm{budget} = \mathrm{available\_bytes}\times f,
\end{equation}
and a local model of footprint $b$ fits iff $b \le \mathrm{budget}$. The default
fraction mirrors the engine's own policy: $f=0.85$ on unified-memory devices
(Apple Silicon, GB10/DGX-Spark, Strix Halo), where weights, KV cache, and
activations share one pool, and $f=0.7$ on a discrete GPU, where a reserve is
kept for activations and KV. The operator can override $f$; nothing else about
the fit check is tunable, because nothing else needs to be.
\subsection{The ordered walk}
Given a classified task, the registry, a memory snapshot, and the set of
currently-running model ids, the decision (\texttt{Policy::select}) is a single
ordered walk of the task's candidate models. Candidates are assembled in
preference order: the policy's explicit \texttt{prefer[task]} list first, then
models that explicitly advertise the task, then \texttt{General} catch-all
models last---so a task specialist outranks a generalist even if the specialist
is in the cloud. The walk then applies one rule:
\begin{enumerate}
\item \textbf{Reuse.} If any usable candidate is local and already resident in a
connectable engine, serve it. This is the zero-marginal-cost path and it wins
outright, even over a smaller model that would also fit, because a load already
paid for is cheaper than any load not yet paid for.
\item \textbf{Load local, or fall through.} Otherwise walk the candidates in
preference order. The first that is a local model whose footprint \emph{fits}
available memory is loaded and served; the first that is a usable cloud model is
routed to. A higher-preference local model that does not fit is skipped, and the
walk falls through to the next preference---which may be cloud. This is
literally ``run it locally if the RAM is there, else the next best wherever it
is.''
\item \textbf{NoFit.} If nothing is resident, nothing local fits, and no cloud
model is usable, the router returns \texttt{NoFit} and the caller errors rather
than silently degrading.
\end{enumerate}
Usability filters a candidate out before it is considered: a vision request
requires \texttt{vision}; a request needing $k$ tokens of context requires
$\texttt{max\_context}\ge k$; and a cloud model priced above the policy's
\texttt{cost\_ceiling} is refused. The whole of \texttt{select} is a few dozen
lines and every branch above is a named test in the crate---reuse-over-reload,
fall-to-cloud-when-local-does-not-fit, vision-requires-a-vision-model,
cheap-chat-prefers-cheapest-cloud, and no-fit.
\section{The Brain: A Swappable Policy Behind One Seam}
\label{sec:brain}
Placement is mechanism; \emph{which model is best for this request and this
user} is policy, and policy is where learning belongs. The two are joined by a
single trait, \texttt{RoutePolicy}, with one method: given a request, the user
it is for, an SLO, and the registry, return a \texttt{Route}---a model, an effort
\texttt{Level} (\texttt{Fast}/\texttt{Balanced}/\texttt{Max}), a modality, and a
confidence. Everything the mechanism owns---the registry, the SLO gate,
placement, dispatch---sits on one side of the seam; everything the brain owns
sits on the other. Three brains implement the seam and interchange freely.
\paragraph{The rule-based cold-start policy.} \texttt{hanzo-router}'s own
\texttt{Policy} implements \texttt{RoutePolicy}: it classifies with the
heuristic, walks candidates in preference order, and returns the first usable
one at \texttt{Balanced} with a fixed low confidence---enough to act on, low
enough that a learned policy knows it is an un-personalized guess. This is the
floor: a fresh deployment routes sensibly before any eval data exists, and any
learned brain delegates to it when it has no profiles for a pool.
\paragraph{The learned utility (\texttt{enso}).} The production brain is
\texttt{enso}, six orthogonal pieces with one concern each: a featurizer
(request $\to$ feature vector $x$), a profile table (eval rows $p$ per
$(\text{model},\text{level},\text{modality})$), a learnable bilinear utility, a
two-tier safety guard, an SLO-constrained selector, and a learner
(\S\ref{sec:learning}). It scores every candidate profile by a bilinear form and
picks the SLO-feasible argmax, falling back to the rule policy on cold start or
an empty pool.
\paragraph{The trainable encoder (\texttt{zen-router}).} At the classifier and
featurizer seams sits an optional 0.6B routing model,
\texttt{zen-router}~\cite{zenrouter}, with task, route, and feature heads. It is
supervised from eval and usage-ledger data and refined with GRPO~\cite{grpo} on
the same objective the selector optimizes, $\mathrm{quality}-\lambda\,\mathrm{cost}-\mu\,\mathrm{latency}$.
Because it produces exactly the \texttt{Task} and feature values the existing
seams already consume, it drops in without touching the mechanism or the
selector---a learned front-end for a policy whose shape does not change.
\section{The SLO Objective and Online Adaptation}
\label{sec:learning}
\subsection{Bilinear utility}
\texttt{enso}'s model of quality is deliberately a single matrix. For a request
feature vector $x\in\mathbb{R}^{D}$ and a profile vector $p\in\mathbb{R}^{K}$,
the utility is the bilinear form
\begin{equation}
u(x,p) = x^{\top} W p, \qquad W\in\mathbb{R}^{D\times K}.
\end{equation}
With the task one-hot in $x$ and quality-by-task in $p$, $W$ learns exactly the
task-to-quality alignment routing needs, and stays one matrix an online learner
can update convexly. A richer head---an MLP over $\phi=\mathrm{vec}(xp^{\top})$,
a low-rank neural encoder---attaches at this same seam by replacing the dot with
a network, without touching any other piece; the linear head is what keeps the
online learner convex and the offline fit closed-form.
\subsection{The SLO-constrained decision}
The per-request service-level objective is a value the caller sets---the
operator's budget for \emph{this} request, distinct from the user's learned
taste. It carries hard ceilings ($\texttt{max\_latency\_ms}$,
$\texttt{max\_cost}$, $\texttt{min\_quality}$; zero disables a ceiling) and two
soft weights ($\lambda=\texttt{lambda\_cost}$, $\mu=\texttt{mu\_latency}$). Over
the profiles in the request's modality pool that satisfy the ceilings, the
selector takes
\begin{equation}
\arg\max_{p\,:\,\text{feasible}(p)}\; u(x,p) - \lambda\,\widehat{c}(p) - \mu\,\widehat{\ell}(p),
\end{equation}
where $\widehat{c}$ and $\widehat{\ell}$ are normalized cost and latency and
feasibility is
\begin{equation}
\ell(p)\le \texttt{max\_latency\_ms}\;\wedge\; c(p)\le \texttt{max\_cost}\;\wedge\; q_t(p)\ge \texttt{min\_quality}.
\end{equation}
The hard ceilings gate; the soft weights trade. A cost-saver deployment raises
$\lambda$; a latency-first deployment raises $\mu$; a quality floor is enforced,
not traded, by \texttt{min\_quality}. Confidence is the squashed margin to the
runner-up, so a close call is reported as a close call. The same
\texttt{feasible} predicate gates the learned selector and the ground-truth
oracle, so the two are compared on identical constraints.
\subsection{Online adaptation}
Offline, the base $W$ is a closed-form ridge fit~\cite{ridge} of observed eval
quality on the bilinear feature $\phi=\mathrm{vec}(xp^{\top})$. Online, each user
carries a contextual bandit---LinUCB~\cite{linucb}---whose parameter
$\theta_u$ starts at a Gaussian prior centered on the base $W$ and drifts toward
that user's taste as outcomes arrive. The serving hot path is greedy on
$\theta_u$; the exploration bonus is computed only while learning. The feedback
is one call,
\begin{equation}
\texttt{observe}(u, x, p, r),
\end{equation}
recording a reward $r$ for the chosen $(\text{model},\text{level},\text{modality})$;
the Sherman--Morrison update keeps $A^{-1}$ and $\theta_u$ current in
closed form, and $\lVert\theta_u - W\rVert$ is the readable size of a user's
personalization. Because the reward is linear in $\phi$, the LinUCB core is
realizable and converges. Research-frontier variants---a low-rank neural
$\Delta W_u$, self-adaptive expert vectors---attach at exactly this update seam
and are deliberately not faked in the shipped code.
\section{Usage-Plane Signals as Decode Masks}
\label{sec:usage}
Cost and memory are not the only constraints on placement; provider rate limits
are a third. Hanzo's usage plane (\texttt{@hanzo/usage}) tracks, per provider,
the current rate-limit window and spend against it. A snapshot of that state is
a value the router can read like any other: a provider near its requests- or
tokens-per-minute ceiling is temporarily down-weighted or removed from the
usable set, exactly as an over-budget cloud model is removed by the cost
ceiling. Framed against the learned policy, a saturating provider acts as a
decode mask over the candidate set---it does not change what the ideal model is,
it changes which models are available to serve right now---so routing degrades
gracefully around a throttled provider instead of failing requests against it.
The signal is advisory and orthogonal: it composes with the memory fit check and
the SLO gate rather than replacing either.
\section{Cost Model}
\label{sec:cost}
We do not report a measured benchmark saving, because we have not run a
controlled A/B at the scale that would make such a number honest. What we can do
is state the arithmetic transparently, so the reader can substitute their own
workload mix and prices and see the saving fall out. The saving is not a
property of any model; it is a property of the workload's task distribution
against a price schedule.
\subsection{Per-request token economics}
Take a stream of one million requests, each averaging 1{,}000 input and 300
output tokens. A frontier model priced at \$3.00 per million input tokens and
\$15.00 per million output tokens costs, per request,
$\tfrac{1000}{10^6}\!\cdot\!3.00 + \tfrac{300}{10^6}\!\cdot\!15.00 = \$0.0075$.
A cheap cloud tier priced at \$0.15/\$0.60 per million costs $\$0.00033$ per
request---about $1/23$ of the frontier rate. A local zen model served by
\texttt{hanzo-engine} carries \emph{no per-token API charge}: the marginal API
cost is \$0 (its electricity cost is a compute line, accounted separately in
\S\ref{sec:compute}). Segment the stream by what each request needs: 70\% simple
(chat, classification, formatting) $\to$ local; 20\% medium (general, routine
code) $\to$ cheap cloud; 10\% hard (reasoning, long context) $\to$ frontier.
Table~\ref{tab:cost} works the API spend.
\begin{table}[t]
\centering
\footnotesize
\setlength{\tabcolsep}{4pt}
\begin{tabular}{@{}lrlrr@{}}
\toprule
Segment & Share & Routed to & \$/req & Routed \$ \\
\midrule
Simple & 70\% & local (zen) & 0.00000 & 0 \\
Medium & 20\% & cheap cloud & 0.00033 & 66 \\
Hard & 10\% & frontier & 0.00750 & 750 \\
\midrule
\textbf{Routed total} & & & & \textbf{816} \\
\textbf{All-frontier} & 100\% & frontier & 0.00750 & \textbf{7{,}500} \\
\bottomrule
\end{tabular}
\caption{Per-token API spend per one million requests. Baseline routes
everything to the frontier model (\$7{,}500); the router serves simple traffic
locally (no per-token API charge), drops medium traffic to a cheap tier, and
reserves the frontier model for the 10\% that needs it (\$816). Prices and mix
are inputs, not measurements; substitute your own.}
\label{tab:cost}
\end{table}
The routed stream costs \$816 in API spend against \$7{,}500 all-frontier, a
$1 - 816/7500 = 89.1\%$ reduction. The ``up to 90\%'' headline is exactly this
arithmetic, and it moves with the mix as expected: at an 80/15/5 split the saving
is 94\%, and a stream with no simple traffic saves nothing. The lever is the
share of requests that do not need the frontier model, which in real assistant
and agent traffic is the majority.
\subsection{Compute cost: local is cheap, not free}
\label{sec:compute}
Serving locally is not free---it draws power on hardware that is already
amortized. A small model (0.6B--24B) on consumer hardware draws on the order of
50\,W sustained and decodes roughly 25 tokens/s, i.e. about 2\,J per token. At
the U.S.\ average residential rate of $\approx\$0.15$/kWh~\cite{eia}, a request
of $\sim$1.3k tokens costs
\begin{equation*}
\tfrac{2\,\text{J}\times 1300}{3.6\times10^6\,\text{J/kWh}}\times \$0.15 \approx \$0.0001,
\end{equation*}
about one one-hundredth of a cent, and roughly two orders of magnitude below the
frontier API's $\sim\$0.006$/1k blended. These are order-of-magnitude estimates
(device wattage, throughput, and tariff are all inputs, not measurements); the
point is that the local token bill effectively disappears and the residual cost
is the \emph{amortized hardware}, which the developer's machine already exists to
provide.
\paragraph{Versus rented GPU.} The alternative way to get zero-per-token
economics is to self-host the open model on a \emph{rented} cloud GPU. One
always-on entry-GPU instance at $\approx\$0.80$/hr is $\approx\$580$~per month; a
redundant pair doubles it. Local-first routing onto laptops and workstations the
team already owns avoids that rental entirely---the compute the organization has
already bought becomes the local tier.
\paragraph{The fleet angle.} Owned compute can be pooled. A device that
registers into Hanzo Cloud as a run-for-pay node runs the open
\texttt{hanzoai/cloud} binary and serves an organization's \texttt{/v1} traffic
through the same metering plane, turning idle compute into capacity instead of
buying more cloud~\cite{runforpay}. Served work is priced cost-plus a fixed
resell margin (25\% over measured wholesale) and metered by the commerce ledger,
and 25\% of that served revenue is paid to the OSS contributors whose code ran
it. For an organization, the practical effect is that spare capacity on the
fleet offsets cloud spend rather than sitting idle.
\subsection{Total cost of ownership: a 20-person team}
\label{sec:tco}
API tokens are one line of the bill. Table~\ref{tab:tco} totals the monthly cost
of a 20-person engineering team running the one-million-request workload above,
against the status quo of a frontier-default API plus per-seat assistant
subscriptions. Beyond tokens, three further costs move: \emph{per-seat
subscriptions} collapse when org login shares one metered account through the
usage plane instead of 20 individual seats; \emph{provider overage} disappears
when rate-limit-aware routing shifts load before a window exhausts, so no
burst/overflow tier or wasted reset credit is bought; and \emph{operations}
simplify to one metering/billing plane (BillingGate over the commerce ledger)
instead of reconciling a stack of per-provider invoices. The consolidation
itself is priced as a small platform fee on routed usage---illustratively
1\%---which we include as a cost so the total is not flattered.
\begin{table}[t]
\centering
\footnotesize
\setlength{\tabcolsep}{4pt}
\begin{tabular}{@{}lrr@{}}
\toprule
Monthly cost line (20-person team) & Status quo & Router \\
\midrule
API token spend (Table~\ref{tab:cost}) & 7{,}500 & 816 \\
Local-tier compute (electricity) & 0 & 70 \\
Per-seat assistant subs ($20\times\$20$) & 400 & 0 \\
Provider overage / burst tiers & 150 & 0 \\
Routing + metering fee (1\% of routed usage) & 0 & 8 \\
\midrule
\textbf{Monthly total (metered bill)} & \textbf{8{,}050} & \textbf{894} \\
\bottomrule
\end{tabular}
\caption{Total monthly AI cost for a 20-person team on the
Table~\ref{tab:cost} workload. The metered bill falls from \$8{,}050 to \$894,
an $\approx 88.9\%$ reduction. \emph{Additionally, not summed above:} to run the
local tier on a rented GPU instead of owned hardware would cost
$\approx\$580$~per month---avoided entirely by local-first placement (the status-quo
column bills frontier API, not self-hosting, so the rental is a separate avoided
alternative). All figures are labeled assumptions, not measurements.}
\label{tab:tco}
\end{table}
The metered bill drops from \$8{,}050 to \$894, an $\approx 88.9\%$ reduction,
and local-first additionally avoids the $\approx\$580$~per month a rented GPU would
cost to serve the same local tier. The headline is the same as
Table~\ref{tab:cost}'s 89.1\% because API tokens dominate the bill; the point of
the itemization is that the other lines---compute, seats, overage, ops---move in
the \emph{same} direction, so the total saving is not an artifact of token prices
alone. This is the economics the cascade and routing literature reports
(\S\ref{sec:related}); our contribution is the mechanism that realizes it
deterministically and the policy that learns where the line between ``simple''
and ``hard'' actually falls.
\section{Related Work}
\label{sec:related}
LLM routing and cascading are an active area, and the cost economics above are
consistent with its published results. FrugalGPT~\cite{frugalgpt} cascades from
cheap to expensive models with a learned scorer that decides when a cheap
answer suffices, reporting large cost reductions at matched quality. RouteLLM
learns a binary strong/weak router from preference data and reports frontier-level
quality at a fraction of the cost by sending only the queries that need it to the
strong model~\cite{routellm}. Hybrid LLM~\cite{hybridllm} routes between a small
and a large model on a quality-aware threshold, and AutoMix~\cite{automix} mixes
models with self-verification to decide escalation. Tabi~\cite{tabi} is a
multi-level inference system that serves small models first and escalates
selectively. Our objective, $\mathrm{quality}-\lambda\,\mathrm{cost}-\mu\,\mathrm{latency}$,
is the natural continuous generalization of these binary escalation rules to a
pool of many models across local and cloud backends, and the per-user LinUCB
loop~\cite{linucb} adds online personalization that cascade work generally does
not. What is distinctive here is not the routing idea but the engineering
posture: a mechanism that is pure over value inputs and memory-aware, so that
local-first placement is deterministic and testable, and a policy seam that
lets a rule, a bilinear utility, and a trainable encoder interchange without a
rewrite.
\section{Limitations}
\label{sec:limits}
The honest scope is narrow in three places. First, the profile table that the
selector ranks over is populated from real eval data when a bench's JSONL is
available and from clearly-labeled synthetic tuples otherwise; until per-pool
eval data lands, the learned policy's absolute rankings are only as good as
those synthetic profiles, which is why the rule-based cold-start policy remains
the floor. Second, aggressive downgrading risks a quality regression on requests
the classifier misjudges as simple; the \texttt{min\_quality} ceiling is the
guard---it is enforced, not traded, so a request with a quality floor is never
served by a model below it, and a misclassified-hard request is caught by the
floor rather than by the soft cost weight. Third, the cost model is arithmetic,
not a measurement: it shows the shape of the saving under stated prices and a
stated mix, and a deployment's real saving depends on its own traffic. We report
the model rather than a benchmark precisely so that the claim cannot be mistaken
for one we did not run.
\section{Conclusion}
Frontier pricing is set by the hardest query; production volume is in the
easiest. Hanzo Router turns that structural mismatch into a cost saving by
placing each request on the cheapest model that can still serve it: reuse a
resident local model for free, load a local model that fits memory, and reserve
the cloud---and the frontier model within it---for the traffic that needs it.
The mechanism is pure logic over values and memory-aware, so placement is
deterministic and unit-tested; the brain is a swappable policy behind one seam,
so a rule guess, a learned bilinear utility with online per-user adaptation, and
a trainable routing encoder interchange and compose. The SLO objective
$\mathrm{quality}-\lambda\,\mathrm{cost}-\mu\,\mathrm{latency}$ makes the
cost/quality/latency trade explicit and per-request, with hard ceilings that
gate and a quality floor that is enforced rather than bargained away. The saving
is not magic and we do not dress it as a benchmark: it is the arithmetic of not
paying the frontier price on a stream whose median request never needed it.
{\small
\bibliographystyle{plain}
\bibliography{references}
}
\end{document}