ai newsDo., 15. Okt.
EilmeldungAlibaba Qwen N1 AI glasses go on sale in China

Google Cloud stellt Gemini Agent vor: Modell-Router in TypeScript erklären

Auf der Gemini at Work 2026 hat Google Cloud den Gemini Agent angekündigt. Viele Agenten-Stacks nutzen für jede Aufgabe dasselbe Modell, ein kleiner Modell-Router kann einfachere Aufgaben stattdessen an günstigere Modelle verteilen.

dev.toDienstag 04:17 · 13.10.DE9 Min. Lesezeit
Bild: dev.to

Most agent stacks pick one model and use it for everything.

Tagging an email gets the same model as reviewing a contract.

It is also like hiring a senior lawyer to sort your mail.

On October 8, 2026, at Gemini at Work 2026, Google Cloud announced the Gemini agent, which it calls "a universal agent for work." It plans the work, uses skills and tools, and connects to business systems.

It chooses the best model for the job, has built-in cost controls, and most importantly, has the security, administration, and governance required by enterprise customers.

In his keynote remarks, Thomas Kurian added that the agent orchestrates across Google's Gemini models and Anthropic's Claude models today. He also said "the best model for the task is not always the largest one." Google also says the agent has built-in cost controls.

The agent and the model are now separate choices.

Something has to make that choice on every task.

Routing is a policy, not a vibe. If you can't print why a model was picked, you don't have a router.

npx tsx router.ts Enter fullscreen mode Exit fullscreen mode And watch seven example tasks get routed, paused, rejected, and resumed.

This is my small model of the idea. It is not how Google's Gemini agent works inside. Every model, price, and task below is made up.

A router has to answer three questions per task.

Most code answers only the first one, by hardcoding a model name.

Then the bill arrives, and it answers the third one for you.

Capability first. Price second. Budget last. The order is the design.

A model is just a few facts the router can compare.

type Tier = " small " | " medium " | " large "; type Model = { name: string; tier: Tier; usdPer1kTokens: number; maxContext: number; tools: boolean }; type Task = { id: string; project: string; need: Tier; tokens: number; tools?: boolean; override?: string }; type Route = | { kind: " run "; task: string; model: string; cost: number; why: string } | { kind: " paused "; task: string; model: string; cost: number; why: string } | { kind: " rejected "; task: string; why: string }; Enter fullscreen mode Exit fullscreen mode Each model has a tier, a price, a context limit, and a tools flag.

Each task says what tier it needs and how big it is.

Google said users can pick a model themselves. That felt worth keeping.

const RANK: Record < Tier, number > = { small: 0, medium: 1, large: 2 }; const MODELS: Model [] = [ { name: " mini ", tier: " small ", usdPer1kTokens: 0.0004, maxContext: 32 _000, tools: false }, { name: " mid ", tier: " medium ", usdPer1kTokens: 0.003, maxContext: 128 _000, tools: true }, { name: " big ", tier: " large ", usdPer1kTokens: 0.015, maxContext: 1 _000_000, tools: true }, ]; function fits ( m: Model, t: Task ): boolean { if ( RANK [ m. tier ] < RANK [ t. need ]) return false; if ( t. tokens > m. maxContext ) return false; if ( t. tools &&! m. tools ) return false; return true; } Enter fullscreen mode Exit fullscreen mode A model that is too small does not fit.

A model with too little context does not fit.

A task that needs tools needs a model with tools.

A cheap model that can't do the job isn't cheap. It's a retry with extra steps.

function cost ( m: Model, t: Task ): number { return Math. round (( t. tokens / 1000 ) * m. usdPer1kTokens * 10 _000 ) / 10 _000; } function pick ( t: Task ): { model?: Model; why: string } { if ( t. override ) { const m = MODELS. find (( x ) => x. name === t. override ); if (! m ) return { why: `override " ${ t. override } " is not a known model` }; if (! fits ( m, t )) return { why: `override " ${ m. name } " does not fit this task` }; return { model: m, why: " user override " }; } const candidates = MODELS. filter (( m ) => fits ( m, t )). sort (( a, b ) => cost ( a, t ) - cost ( b, t )); if ( candidates. length === 0 ) return { why: " no model fits " }; return { model: candidates [ 0 ], why: `cheapest model that fits ( ${ candidates. length } fit)` }; } Enter fullscreen mode Exit fullscreen mode An override still has to fit.

If someone forces a small model onto a huge task, the router says no.

An unknown model name is rejected, not guessed.

Otherwise the router takes the cheapest model that passed the filter.

Each project gets a cap. The ledger tracks spend.

class Ledger { spent = new Map < string, number > (); constructor ( private caps: Record < string, number > ) {} route ( t: Task ): Route { const { model, why } = pick ( t ); if (! model ) return { kind: " rejected ", task: t. id, why }; const c = cost ( model, t ); const spent = this. spent. get ( t. project )?? 0; const cap = this. caps [ t. project ]?? 0; if ( spent + c > cap ) { return { kind: " paused ", task: t. id, model: model. name, cost: c, why: `cap $ ${ cap } would be hit (spent $ ${ spent. toFixed ( 4 )} )` }; } this. spent. set ( t. project, Math. round (( spent + c ) * 10 _000 ) / 10 _000 ); return { kind: " run ", task: t. id, model: model. name, cost: c, why }; } raise ( project: string, cap: number ) { this. caps [ project ] = cap; } } Enter fullscreen mode Exit fullscreen mode The check happens before the task runs, not after.

If the next task would cross the cap, it pauses.

The work waits for a human to raise the cap, and then it resumes.

Google says its agent ships built-in cost controls. This is my tiny version of one.

// ===== EXAMPLE INPUTS (made-up tasks, prices, and caps) ===== const tasks: Task [] = [ { id: " tag-email ", project: " support ", need: " small ", tokens: 2 _000 }, { id: " draft-reply ", project: " support ", need: " medium ", tokens: 6 _000, tools: true }, { id: " contract-review ", project: " legal ", need: " large ", tokens: 180 _000 }, { id: " summarize-thread ", project: " support ", need: " small ", tokens: 40 _000 }, { id: " quick-fix ", project: " support ", need: " small ", tokens: 1 _000, override: " big " }, { id: " translate ", project: " support ", need: " small ", tokens: 3 _000, override: " gpt-x " }, { id: " second-review ", project: " legal ", need: " large ", tokens: 180 _000 }, ]; const TAG = { run: " [RUN] ", paused: " [PAUSED] ", rejected: " [REJECT] " } as const; console. log ( " tiny-model-router demo " ); const ledger = new Ledger ({ support: 0.1, legal: 3 }); const paused: Task [] = []; for ( const t of tasks ) { const r = ledger. route ( t ); const detail = r. kind === " rejected "? r. why: ` ${ r. model } $ ${ r. cost. toFixed ( 4 )} ${ r. why } `; console. log ( ` ${ TAG [ r. kind ]} ${ t. id. padEnd ( 17 )} ${ detail } ` ); if ( r. kind === " paused " ) paused. push ( t ); } console. log ( " \n raise legal cap to $6, resume paused work " ); ledger. raise ( " legal ", 6 ); for ( const t of paused ) { const r = ledger. route ( t ); const detail = r. kind === " rejected "? r. why: ` ${ r. model } $ ${ r. cost. toFixed ( 4 )} ${ r. why } `; console. log ( ` ${ TAG [ r. kind ]} ${ t. id. padEnd ( 17 )} ${ detail } ` ); } console. log ( " \n spent: ", Object. fromEntries ( ledger. spent )); Enter fullscreen mode Exit fullscreen mode Output:

tiny-model-router demo [RUN] tag-email mini $0.0008 cheapest model that fits (3 fit) [RUN] draft-reply mid $0.0180 cheapest model that fits (2 fit) [RUN] contract-review big $2.7000 cheapest model that fits (1 fit) [PAUSED] summarize-thread mid $0.1200 cap $0.1 would be hit (spent $0.0188) [RUN] quick-fix big $0.0150 user override [REJECT] translate override "gpt-x" is not a known model [PAUSED] second-review big $2.7000 cap $3 would be hit (spent $2.7000) raise legal cap to $6, resume paused work [PAUSED] summarize-thread mid $0.1200 cap $0.1 would be hit (spent $0.0338) [RUN] second-review big $2.7000 cheapest model that fits (1 fit) spent: { support: 0.0338, legal: 5.4 } Enter fullscreen mode Exit fullscreen mode Here is what happened.

tag-email fits all three models, so it gets mini.

draft-reply needs tools, so mini is out and mid wins.

contract-review needs the large tier. Only big fits.

summarize-thread is "small" work, but 40,000 tokens is past mini 's context. So it routes to mid, and that would cross the support cap. Paused.

quick-fix is overridden to big. Allowed, and logged as an override.

translate asks for a model that does not exist. Rejected.

second-review would push legal past $3. Paused.

Then a human raises the legal cap, and second-review resumes.

summarize-thread stays paused, because nobody raised the support cap.

Correct. The router does not get to give itself a raise.

This is a teaching model. Real routing is harder.

Filter by capability. Sort by price. Check the budget. Print why.

task └→ filter: tier, context, tools └→ sort: cheapest fit (or override) └→ ledger: under the cap? ├→ run, with a reason ├→ pause, until a human raises the cap └→ reject, with a reason Enter fullscreen mode Exit fullscreen mode The task provides the requirements.

I think "which model are you on?" is becoming the wrong question for agents. The model will change every few months. Google said almost exactly that in the launch.

The better question is: what is your routing policy, and can you read it?

Picking the model is now part of the job. So it should be part of the log.

I'm building Roster around this idea: AI employees with real responsibilities, tools, memory, schedules and computer access. Each one has a lane, and a budget that lane can't quietly outgrow.

If the same follow-ups, handoffs, and waiting loops keep eating your week, give them to an AI employee.

Originaltitel: Google Launched the Gemini Agent. Let's Build a Tiny Model Router in TypeScript.Quelle: dev.to (dev.to). Original öffnen