Overview
Last week, we released Shift-1⌝, a frontier-quality model router trained for coding on AC2. You can try it at shift.ac.
In our experiments, Shift-1 reduces Codex spend by 55% against GPT-6 Astra, routing between Astra and GPT-6.1 Sol, while preserving SWE-bench Verified accuracy and user satisfaction. In Claude Code, Shift-1 reduces spend by 24% against Opus 5.5, routing between Opus and Sonnet 5.5, with a small hit to accuracy on SWE-bench Verified and no significant degradation in user satisfaction.
Shift-1: A First Step Toward Intelligent Per-Request Routing
Model routers direct requests to different LLMs in an effort to optimize inference cost while maintaining response quality. Model routing is a popular product offering, yet the term has become a catchall for everything from gateways built for reliability to internal multi-agent systems served through a single completions endpoint.
In order to build a smart, cheap router that is useful in our day-to-day work, we built Shift-1 with the following properties:
- Drop-in replacement model endpoint. Shift-1 works as a drop-in replacement for a single model, so it works even when we don’t control the harness. Using subagents to reduce tokens in the main agent stream is the simplest way to significantly cut agent costs, so we designed Shift-1 to complement these harness-based approaches rather than replace them.
- Per-request router. Difficulty is hard to assess upfront, and a task that initially seems easy can become challenging as the agent progresses. Per-request routing allows Shift-1 to update its routing decision while solving a task, which is not possible with per-task routing.
- Cheap and performant policy model. We aim to train small router models that keep both serving costs and latency low.
- Cost-aware. Routing decisions must account for the cost of cache misses due to switching models. Ignoring these can make routing more expensive than sending every request to the most expensive target model.
Evaluating Shift-1: Quality, Cost, & Router Behavior
Internal Coding Evaluations
We evaluate Shift-1 on thousands of coding turns from our engineers and researchers at Applied Compute. While assessing accuracy on benchmarks is easy, rigorously assessing quality on internal coding data is trickier. We choose three metrics:
- User satisfaction (shown in the Overview): A calibrated LLM judge checks whether the developer’s next message contains a
correction, are-ask, or any sign of frustration. We compare All-Astra traces and router traces in live internal A/B testing. - Rubrics: We use an LLM to identify a list of requirements from the original trace, given full context on the agent’s actions and developer reaction.
- All-Astra Passing Rubrics: We extend the baseline rubric with requirements drawn from Astra’s own behavior. An LLM judge reviews 5 All-Astra rollouts of the same turn, and any requirement that all 5 rollouts satisfy is added to the rubric. Rubrics built this way produce statistically significant score differences between Sol and Astra.
Below is an example of one of these sample rubrics, for reference:
Cost Savings Breakdown
The most significant cost savings from routing to Sol or Sonnet come from paying lower rates for cache reads, followed by uncached inputs (predominantly cache writes, in Claude Code), and finally outputs. Cache-read costs grow quadratically, since every new token in the cache is re-read on all subsequent requests. As expected, “escalation switches” from Sol to Astra end up costing us quite a bit, but “de-escalation” switches are cheap due to lower Sol costs. See Building Shift-1 below for more details on cost modeling.
In addition to headline cost and quality savings metrics, we seek to gain a better understanding of how Shift-1 works in practice, specifically when and why it makes routing decisions. Across AC’s internal coding usage, 18.4% of total LLM requests are routed to Astra, but over 25% of all turns end up routing to Astra at some point. We see healthy routing stickiness of an average of approximately 20 requests per switch. When Shift-1 switches to Astra, it typically stays there for over 6 requests, enough to resolve observed problems. Shift-1 for Claude Code has similar stickiness metrics.
Analyzing Shift-1 Routing Decisions on Internal Coding
We now investigate what typically causes Shift-1 escalation in live internal coding agent sessions, as well as what state typically looks like at de-escalation. We notice that escalation almost always occurs after specific failures or developer interactions. Environment and permissions issues seem to result in escalation most often, followed by failing tests. Interestingly, git errors also cause escalation occasionally, as these issues can potentially result in lost work. Escalation occurs frequently on plan approval, as well as after explicit developer feedback and new requests. Large features or changes may often escalate to perform heavy operations before de-escalating back to Sol.
De-escalation seems to occur in benign or simple states 91% of the time, including when waiting, polling, or performing basic git commands. De-escalation occurs after failures 9% of the time, however, which may be preemptive in some circumstances or when next steps forward are clear.
Shift-1 is generally resistant to rapid escalation in the event of single failures but responds to persistent failures with escalation much more often. In fact, as the number of consecutive failures grows, we see escalation rates increase correspondingly. On the other hand, consecutive successes alone have little correlation with de-escalation.
Shift-1’s resistance to rapid escalation on single failures is a good behavior, as this prevents switching back and forth between Sol and Astra too frequently, contributing to our cost savings. Note that “failures” are defined here as commands exiting with an error, test failure, lint failure, or other output showing failures.
Benchmark & Synthetic Data Evaluations
Shift-1 Routing Behavior on SWE-bench Verified
We evaluate Shift-1 on SWE-bench Verified, achieving equivalent performance to Astra. We define Easy tasks as tasks that both Sol and Astra solve on all rollouts, Mixed tasks as tasks that Sol and Astra each occasionally succeed or fail, and Hard tasks as tasks that neither model ever solves. We see here that Astra usage under Shift-1 increases clearly with task difficulty, as desired in a router. Shift-1 appears to have both learned to assess task difficulty fairly effectively and to react to unforeseen challenges during rollouts by introducing more or less Astra as needed.
On individual traces, Shift-1 starts traces more often on Sol, its “default” model, before introducing additional Astra. Earlier turns in benchmark rollouts generally correspond to exploration, so you can imagine Shift-1’s routing decisions adjusting as it explores and gets a better sense of the underlying problem, encountering its first challenges. By halfway through the rollout, Astra usage begins to decrease rapidly on Mixed and Easy tasks, while remaining steady on Hard tasks.
We observe that Shift-1 is “de-escalation”-biased on these tasks, as Astra usage is concentrated earlier in turns. This is a good property for a router policy, especially on single-shot task problems like SWE-bench Verified. Later requests carry more cached context and cost more, so serving them on Sol saves the most. Additionally, escalating earlier is cheaper since the cache loss is smaller.
Responding to Developer Interactions
The primary difference between benchmark tasks and live internal coding data is developer interaction. Each benchmark rollout consists of 1 user turn, whereas internal coding chats often consist of dozens. Cost dynamics change significantly between these settings due to built-up long context and the router policy must learn to respond effectively in developer interactions. Shift-1 is deliberately designed to function across user turns effectively and gains a lot of signal from developer interactions, escalating and de-escalating as needed.
We evaluated Shift-1 on synthetic developer follow-ups after initial solutions to SWE-bench tasks, including positive feedback, positive feedback with negative-sounding words, soft pushback, and strong pushback. Shift-1 responds very aggressively to negative feedback, escalating to Astra to attempt to ensure the best possible quality in subsequent turns. Shift-1 is also a good discriminator, however, and rarely escalates on our synthetic positive feedback scenarios.
In internal coding data, negativity in the last developer interaction signals 1.6x greater likelihood of negativity in the next one. Shift-1’s aggressive escalation seems to be an effort to minimize this cascade. We feel that this is generally good behavior from a routing policy, as it helps keep you from feeling stuck or frustrated on tasks as a result of being routed to a cheaper model. That being said, we may attempt to relax this aggressive escalation slightly in future model training runs to unlock even greater savings, especially when feedback requires only small changes.
Recap on Shift-1 Model Behavior
- Reacts to task difficulty and challenges in traces. Shift-1 updates its routing decision to react to both task difficulty and the challenges it encounters, while maintaining a de-escalation-biased policy shape.
- Reacts effectively to developer feedback. Shift-1 interacts with developers effectively, reacting aggressively to negative feedback and rarely escalating on positive feedback.
- Consecutive failures trigger escalation and de-escalation usually happens in benign states. Shift-1 is resistant to rapid, unnecessary escalation on single failures and usually waits to de-escalate until difficult situations are resolved.
Building Shift-1
We’ll dive into the technical details of Shift-1 itself, as well as some of the research findings and decisions that ultimately led to the creation of Shift-1, including cost modeling and data label generation for training router models.
Shift-1's Architecture
Shift-1 is a Qwen3.5-4B base trained on AC2 as a decision model on about 150,000 benchmark and internal coding agent states. Shift-1 is trained to answer 2 questions, “Is it safe to use Sol?” and “Is Astra needed?” (we find that splitting these questions up works slightly better in practice than “Safe to use Sol?” = 1 - “Astra needed?”). Shift-1 uses calibrated thresholds on decision model results along with other cost-related logic, allowing us to select good cost/accuracy tradeoffs. The AC2 platform was tremendously powerful for us in training and evaluating our decision model routers. You can read about training decision models on AC2 here⌝.
Router models often consist of either a decoder-only transformer with a classification head (i.e. a decision model) or an encoder model feeding an MLP, although there are many other possible approaches. We tried both of these solutions and others, but the decision model architecture ended up working best for us and resulted in more calibrated output distributions for routing when using Brier loss.
Modeling Cost for Routers: Simulation & Example
Building good router models requires a good understanding of the non-trivial cost dynamics at play when switching between models in a single trace. In general, there are 3 primary components of cost in an agent trace:
- Input & cache write tokens (e.g. initial prompt, tool call results)
- Output tokens (e.g. reasoning tokens, tool calls)
- Cache reads (every new token in the cache is re-read on all subsequent requests)
We’ve built a simple cost accounting model for you to play around with to get a better understanding of cost dynamics in a trace. The model is preloaded with Astra and Sol prices.
We had a few interesting takeaways from our cost modeling work that ended up being highly relevant to our training work on Shift-1:
- Context bloat is a killer — When we tried to benchmark Shift-1 on Astra/Luna, Luna would take 2.1x the number of requests to finish the turn and add 1.5x more context on average, substantially reducing routing benefits. Even when routing 35% of requests to Luna, (100x cheaper than Astra), this policy was only saving ~12% of total cost as a result of this bloat issue. Sol has no such bloat issue, leading to a better overall cost-savings profile.
- De-escalation is more efficient than escalation — Later requests are more expensive due to built-up cached tokens, so it is best to use a more expensive model first and a cheaper model second, as opposed to the other way around.
- Routing between similar cost models is often not worthwhile — Switching models incurs a cost that must be amortized over subsequent cheaper requests. As model prices converge, per-request savings shrink, requiring increasingly long stretches, or routing “stickiness”, to break even. Eventually, the required stickiness becomes impractical without sacrificing quality.
Below are 2 charts representing the components of spend on internal coding data using All-Astra.
- Billed view: This chart splits Astra’s actual bill by how each token was charged: cache reads, uncached input (new content plus prefixes re-sent after cache expiry), and output. Uncached input is approximately half of the total spend (adding up each sub-component) despite being only about 9% of total tokens and the model’s own output is only 8%, despite output tokens being priced so high.
- Lifetime view: This chart assigns cost to the content that causes it, assuming perfect caching. Tool schemas (37%, dominated by connector definitions) and tool results (37%) dominate.
Model Router Training Data Generation
Training Model Routers: Naive Label Generation Fails
Generating data to train model routers is the most important part of building a router. Per-request routing is a sequential decision problem, just like language generation. The state is the conversation so far, or some derived representation of it, and the action is which model serves the next request.
In theory, you could try every routing combination by rolling out every combination of models across a trace, requiring (# models)^(# requests) rollouts and read off the optimal policy. This is obviously not practical from a cost perspective.
We started by experimenting with per-request LLM judging: given a state, roll out the expensive model and the cheap model multiple times for 1 request, and use an LLM judge to determine at what rate the cheap model’s output matches the expensive model’s output "close enough". We defined “close enough” as making the same amount of progress toward solving the final task. This procedure ultimately failed, with labels showing no ability to predict terminal reward.
With this in mind, we focused on labels grounded in the terminal outcomes of real rollouts, while recognizing that exhaustive search wasn’t an option.
Labels are Q-values, Continuation Policy Matters
Given the above, we recognized that routers need to estimate , the expected terminal reward of serving the next request with model from state . But, is only defined once you determine what happens after that request. In other words, every label is really some for continuation policy . should incorporate success and cost, for example , where cost-to-go includes any switch cost incurred by the routing decision. Since only scales the cost term, we can estimate the two terms separately. In practice, we train our routers primarily as success predictors and layer separate cost models and policies on top (Shift-1 uses a richer tradeoff rule than a single ). This lets us sweep cost/accuracy tradeoff values, and trace out the full cost/quality frontier, without retraining.
This framing turns label generation into two choices:
- which states or prefixes appear in the data (i.e. which policy generates these states)
- which continuation policy finishes each rollout after the labeled request.
A router performs best when deployed on-policy, or under the state distribution and continuation policy its labels assumed. When the router is not deployed on-policy, the learned router inherits the bias of whatever these 2 choices assumed.
Policy iteration is the textbook answer to solving this dilemma, where we fix an initial policy , label data under it, train , and repeat. We attempted several rounds of policy iteration for training, but saw little additional gain in our trained routers versus training once on switch & commit style labels (described below). Policy iteration also requires fixing cost model/tradeoff logic in advance of having a trained policy, which limits our ability to sweep a full frontier.
Shift-1’s Training Labels: Switch & Commit
Shift-1 is trained with labels generated under a single, simple continuation policy, : after a labeled request, stay on that model until the end of the turn.
- De-escalation labels come from states where Astra has been serving the turn. From each state, we hand the rest of the turn to Sol, sample the continuation several times, and record Sol’s success rate and cost-to-go. This estimates .
- Escalation labels come from states where Sol has been serving the turn. From each state, we hand the rest of the turn to Astra and record the same quantities. This estimates .
- Stay labels come from standard all-Sol and all-Astra rollouts. At each state, the rest of the rollout estimates the value of staying on the current model, . Together with the switch-and-commit labels, this gives outcomes for both actions at every state, so the router can weigh switching against staying.
These values map directly onto Shift-1’s two questions. “Safe to use Sol?” corresponds to Sol’s pass rate from Astra states built from de-escalation and Astra stay labels. “Astra needed?” corresponds to Sol failures on tasks where Astra succeeds, built from escalation labels and Sol stay labels. These values mirror each other, since each evaluates one switch, but the states they’re conditioned on look very different, with one coming from Astra histories and the other from Sol histories. This likely explains why 2 questions work better than treating one as the complement of the other.
Switch-and-commit labels assume that after a switch, the router stays on the new model for the rest of the turn. Shift-1 is free to switch again, so these labels may seem suboptimal. We use them anyway for 2 reasons:
- Signal. A one-request deviation barely changes the terminal outcome, so its label is mostly noise relative to a pass/fail reward at the end of a long turn. Switching and committing is a much larger action, with a measurable effect on outcomes, so its labels are far more informative.
- Stickiness. Our cost modeling showed that frequent switching erodes savings, so we wanted Shift-1 to be a sticky router that rarely switches. Commit-style labels encourage exactly this, and the final router behaves accordingly (averaging 20 requests per switch), so these labels are close to on-policy in practice.
Scaling labels beyond branched rollouts
Branched continuation rollouts give the cleanest labels, but they’re quite expensive to generate at scale with frontier models. Shift-1 combines this data with cheaper sources to scale our router training dataset substantially:
- Developer feedback labels. We graded the developer’s next message across 118k internal coding turns with an LLM judge, using the same correction / re-ask / frustration criteria as our quality evaluation. This provides a turn outcome score on real traffic.
- Hindsight phase labels. For turns marked as poor, a “hindsight” LLM judge (i.e. full access to the completed turn) identifies the phase where things went wrong, turning turn-level outcomes into state-level labels. We calibrate our hindsight LLM judges against continuation labels from above so they estimate the same quantity. For “Astra needed?”, we ramp the label up gradually in the steps leading into the problematic phase, then keep it labeled as Astra throughout that phase. Recovering and routine stretches become “Safe to use Sol?” labels in the same way.
While we discovered that LLM judge labels fail when the judge is operating at the request level, LLM judge labels work effectively when the judge has full-turn hindsight and is calibrated against continuation labels. In other words, judging a single step in isolation doesn’t predict terminal outcomes, but judging where a turn went wrong with knowledge of how the turn ended works effectively.
Why Not use Policy Gradient? (For Now!)
Our approach can be described as offline, value-based reinforcement learning: we estimate -values under a simple switch-and-commit policy and act greedily on them, which amounts to one step of policy improvement over . Training our router decision model is ordinary supervised learning, but RL structure exists in how labels are defined.
We’re actively exploring improved recipes to use policy-gradient methods to train routers, but elected to pursue the above approach instead for Shift-1. Policy gradient samples one routing trajectory per rollout and learns from a single noisy terminal reward. In our setting that is expensive and high-variance, since every rollout calls frontier models and individual routing decisions have small effects on terminal outcome. Furthermore, value estimates keep cost tradeoff parameters as a dial we can tune at serving time, even on a customer-by-customer basis, rather than something baked into the policy weights.
Recap on training Model Routers
- Label generation procedure matters more than architecture. Labels must be based in terminal reward signals and selection of a continuation policy is critical in influencing the shape of learned routers.
- Make actions large enough to measure. Switch-and-commit labels carry far more signal than one-request deviations for training routers.
- Model cost explicitly. Separate success and cost estimates make the cost/quality tradeoff a dial rather than a retraining job, and make switch costs and context bloat visible to the policy.
- Calibrate cheap labels to expensive ones. Judge-based labels scale to production traffic, but only stay trustworthy when anchored to real continuation outcomes.
Moving toward Online Router Training & Beyond
Soon, customers will be able to simply point LLM request traffic at an AC2 inference endpoint and receive automatic model routing that improves over time, using online router model training. Since developer feedback and hindsight labels are easily available on all production traffic, they open a clear path to executing online router training without extensive need for replayed continuation labels.
Labels collected on traffic served by Shift-1 amount to policy evaluation. Retraining on these labels and redeploying is policy improvement, so repeating this loop is a form of approximate policy iteration, run online and at production scale. Shift-1 will continue to offer ZDR and online routing will, of course, only be available to customers without ZDR enabled.
We see intelligent routing as an important component of unlocking the power of specialized intelligence. We imagine a world in which companies and even individuals run cost-optimized swarms of agents managed by sophisticated router policies. In addition to continuing to refine and improve our recipes for training task-specific model routers for all use cases. These directions include online router training, as discussed above, online distillation of expensive model responses into cheaper target models, KV cache translation across models to eliminate switch costs, and more.
We’re just getting started with our work on model routing and will continue to release new products and research in the coming weeks and months.
Get our latest research
Product news, customer stories, and new posts, straight to your inbox.
