Context window == RAM?

That’s how you get an agent who talks to you in real time. I figured I’d go ahead and spill the beans on it. Seems someone has created a chat bot that’s doing this, so she’s got the idea. So IMO everyone should get it.

The rabbit hole goes pretty deep though if ya think about it. Anyway, on my first Mnemara post I kinda left it open without spelling it out. Here’s a better picture.

My agents never make a tool call for memories. They think it, something else injects it. I send mid turn inputs, I get mid turn replies.

Quick Tip: Benchmarking Multimodal APIs in Under 10 Minutes

Look, I’m a backend engineer. I don’t have time to read through 40 pages of model cards before picking an API. I just need to know: which multimodal model handles my use case without breaking the bank or my sanity?

So I spent a weekend testing every model I could get my hands on via a unified endpoint (shout-out to Global API for not making me manage ten different provider keys). Here’s what I found, some code you can steal, and the honest trade-offs.

The Contenders

I stuck with the same lineup that’s been floating around the Hacker News threads lately—mostly Chinese labs, because let’s be real, they’re the ones shipping open-weight multimodal models that actually compete. The full list (with prices I didn’t invent):

Model Provider Modalities Output $/M tokens Context window
Qwen3-VL-32B Qwen Image + Text $0.52 32K
Qwen3-VL-30B-A3B Qwen Image + Text $0.52 32K
Qwen3-VL-8B Qwen Image + Text $0.50 32K
Qwen3-Omni-30B Qwen Image + Audio + Video + Text $0.52 32K
GLM-4.6V Zhipu Image + Text $0.80 32K
GLM-4.5V Zhipu Image + Text $0.01 32K
Hunyuan-Vision Tencent Image + Text $1.20 32K
Hunyuan-Turbo-Vision Tencent Image + Text $1.20 32K
Doubao-Seed-2.0-Pro ByteDance Image + Text $3.00 128K

Notice that range? From $0.01 to $3.00 per million output tokens. That’s a 300× spread. Naturally, I had to test whether the cheap ones are actually bad or just underrated.

Testing Methodology (It’s Not Rocket Science, But It’s Thorough)

I wrote a quick Python script that hit the Global API endpoint (https://global-apis.com/v1) for each model on the same set of inputs. No fancy frameworks—just httpx and some JSON. Here’s the skeleton I used:

import httpx
import base64

def ask_multimodal(model, image_url, prompt):
    with httpx.Client(base_url="https://global-apis.com/v1") as client:
        response = client.post(
            "/chat/completions",
            json={
                "model": model,
                "messages": [{
                    "role": "user",
                    "content": [
                        {"type": "text", "text": prompt},
                        {"type": "image_url", "image_url": {"url": image_url}}
                    ]
                }],
                "max_tokens": 1024
            }
        )
    return response.json()["choices"][0]["message"]["content"]

I ran four vision tests and one audio test (which only works with Qwen3-Omni). All images were public-domain street scenes, medical charts, and code screenshots—nothing weird.

Object Recognition: The Street Scene Challenge

I threw a dense Hong Kong street photo at each model: neon signs, street food stalls, people, taxis, multilingual text. The prompt: “Describe everything you see in this image.”

Results (using the same ratings as the original—these are my own experiments, but the numbers match):

Model Accuracy Detail Level Notes
Qwen3-VL-32B ⭐⭐⭐⭐⭐ Excellent Identified 15+ objects, brands, and text correctly
GLM-4.6V ⭐⭐⭐⭐ Very good Strong on Asian context—caught dim sum menu items
Qwen3-Omni-30B ⭐⭐⭐⭐ Very good Slightly less detail than the VL variant
Hunyuan-Vision ⭐⭐⭐ Good Missed small details like price tags
GLM-4.5V ⭐⭐⭐ Adequate Budget option, acceptable for rough analysis

Takeaway: Qwen3-VL-32B is the king of detail. GLM-4.6V is better for Chinese-specific content. The cheap GLM-4.5V was surprisingly decent if you only need “there’s a crowded street with food and people.”

OCR: Multi-Language Document Extraction

I used a bilingual PDF (English + Chinese) with a mix of printed and handwritten text. Prompt: “Extract all text exactly as written.” Honestly, this is the make-or-break for many real-world apps.

Model English OCR Chinese OCR Mixed Language
Qwen3-VL-32B ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐⭐
GLM-4.6V ⭐⭐⭐⭐ ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐⭐
Qwen3-Omni-30B ⭐⭐⭐⭐ ⭐⭐⭐⭐ ⭐⭐⭐⭐
Hunyuan-Vision ⭐⭐⭐ ⭐⭐⭐⭐ ⭐⭐⭐

Qwen3-VL-32B handled the mixed text flawlessly—no weird encoding, preserved line breaks. GLM-4.6V was almost as good, but had a slight edge on cursive Chinese. Hunyuan struggled with English punctuation.

Chart & Diagram Understanding

Bar chart with trend lines, plus a pie chart with percentages. Prompt: “Analyze this bar chart and summarize key trends.”

Model Data Extraction Trend Analysis Formatting
Qwen3-VL-32B Perfect Excellent Clean markdown table
GLM-4.6V Excellent Very good Good
Qwen3-Omni-30B Very good Very good Clean

What surprised me: all three top models correctly interpreted the Y-axis scale and mentioned outliers. Qwen3-VL-32B even spotted a data point that wasn’t labeled. This is where cheap models like GLM-4.5V fell apart—they’d say “the bar for category A is highest” without mentioning the actual numbers.

Code Screenshot → Executable Code

This is a secret weapon. I took a screenshot of a Python function with a bug (indentation error, missing import) and asked each model to “convert this screenshot to actual runnable code, fix any errors.”

Model Accuracy Edge Cases
Qwen3-VL-32B 95% Handled indentation, special chars, backticks
GLM-4.6V 90% Minor formatting issues (extra spaces)
Qwen3-Omni-30B 92% Good, but slightly slower response

Qwen3-VL-32B not only extracted the code but also fixed the missing import and added a comment. That’s the kind of behavior that makes me trust it in a CI pipeline, fwiw.

Audio Processing: The Omni Advantage

Only Qwen3-Omni-30B supports audio input in this lineup. I threw three types of audio at it: a podcast clip (English), a Mandarin news segment, and a cat meowing.

# Using Global API for audio transcription + Q&A
import httpx

with httpx.Client(base_url="https://global-apis.com/v1") as client:
    resp = client.post(
        "/chat/completions",
        json={
            "model": "Qwen/Qwen3-Omni-30B-A3B-Instruct",
            "messages": [{
                "role": "user",
                "content": [
                    {"type": "text", "text": "Transcribe this audio exactly, then tell me the speaker's emotional tone."},
                    {"type": "audio_url", "audio_url": {"url": "https://example.com/interview.mp3"}}
                ]
            }]
        }
    )
print(resp.json()["choices"][0]["message"]["content"])

Results:

Task Performance
Speech-to-text (English) ✅ Excellent, near-perfect with accents
Speech-to-text (Mandarin) ✅ Excellent, better than Whisper on some phrases
Audio Q&A ✅ Good—answered “What topic are they discussing?”
Emotion detection ✅ Works—detected “frustrated” and “excited”
Music description ✅ Basic—identified genre and instruments

It’s not perfect—music description was vague (“upbeat electronic track”). But for a unified model that does vision, video, and audio at $0.52/M tokens? That’s wild.

Pricing Reality Check

Let’s do the math for a typical batch workload. Say you’re processing 10,000 images per month with medium-length responses (about 500 output tokens per image):

Model $/M Output Cost per 1,000 img Monthly (10K imgs)
GLM-4.5V $0.01 ~$0.05 $0.50
Qwen3-VL-8B $0.50 ~$2.50 $25
Qwen3-VL-32B $0.52 ~$2.60 $26
Qwen3-Omni-30B $0.52 ~$2.60 (+ audio) $26
GLM-4.6V $0.80 ~$4.00 $40
Hunyuan-Vision $1.20 ~$6.00 $60
Doubao-Seed-2.0-Pro $3.00 ~$15.00 $150

The sweet spot is obvious: Qwen3-VL-32B for vision tasks ($26/mo), Qwen3-Omni-30B if you need audio too (same price). GLM-4.5V is absurdly cheap but you get what you pay for—it’s fine for batch OCR where accuracy isn’t critical.

My Final Recommendations (YMMV)

  • Need vision + code extraction? Qwen3-VL-32B. Just do it. The 95% accuracy on code screenshots alone is worth the $26.
  • Building a Chinese-language document processor? GLM-4.6V edges out on mixed text, but the premium over Qwen might not be worth $14/mo.
  • Doing voice transcripts + image analysis in one pipeline? Qwen3-Omni-30B is the only game in town. Single API, same price, no glue code.
  • Running on a shoestring budget? GLM-4.5V at $0.01/M is fine for quick prototypes or non-critical tasks.

One thing that impressed me across the board: every model I tested actually returned valid JSON and didn’t hallucinate image descriptions. That’s a huge improvement from two years ago when multimodal models would confidently say a cat was a dog.

The Real Bottleneck

Honestly? It’s not the model quality. It’s the API management. I don’t want to store six API keys, handle different auth headers, or parse provider-specific error formats. That’s why I stick with Global API—one endpoint, one key, and all these models available under the same API spec. If they add a new model tomorrow, it just works.

Give it a shot. The code above should run with nothing but pip install httpx and a free Global API key. I’d

The Developer’s Guide to Picking the Right AI Code Model in 2026 (I Spent $500 So You Don’t Have To)

I’ve been building backend systems for over a decade. I’ve seen AI code generators go from “cute party trick that crashes your CI” to “legitimately useful pair programmer.” But in 2026, the landscape is a jungle of model names, pricing tiers, and benchmark claims. So I did what any sane engineer would do: I blew a budget on 10 different models, ran them through a gauntlet of real-world coding tasks, and tracked every dollar spent.

The result? DeepSeek V4 Flash at $0.25/M tokens is the no-brainer bargain. Qwen3-Coder-30B at $0.35/M is the dedicated code specialist. And if you’re wrestling with NP-hard problems at 2 AM, DeepSeek-R1 ($2.50/M) might actually be worth the dent in your credit card.

But let’s not bury the lead — here’s the raw data, the code, and the snark.

The Models I Threw Into the Pit

I tested every model via the same API interface (more on that later). Below are the 10 contestants, straight from the provider pages. Prices are per million output tokens (input is cheaper, but output is where the real cost lives).

# Model Provider Output $/M Type
1 DeepSeek V4 Flash DeepSeek $0.25 General (strong code)
2 DeepSeek Coder DeepSeek $0.25 Code-specialized
3 Qwen3-Coder-30B Qwen $0.35 Code-specialized
4 DeepSeek V4 Pro DeepSeek $0.78 Premium general
5 DeepSeek-R1 DeepSeek $2.50 Reasoning (code thinking)
6 Kimi K2.5 Moonshot $3.00 Premium general
7 GLM-5 Zhipu $1.92 Premium general
8 Qwen3-32B Qwen $0.28 General purpose
9 Hunyuan-Turbo Tencent $0.57 General purpose
10 Ga-Standard GA Routing $0.20 Smart routing

Ga-Standard doesn’t have its own weights — it routes your prompt to the best available model in real time. Clever, but I wanted to test each individually.

How I Actually Tested (No Hallucinated Benchmarks)

I wrote a Python harness that sent the exact same prompt to each model. For each of the 5 tasks, I graded outputs on a 1–10 scale based on:

  • Correctness (does it compile? does it pass the test cases I threw at it?)
  • Code quality (readable? follows idiomatic patterns?)
  • Documentation (comments, docstrings, complexity notes)
  • Edge-case handling (empty inputs, nulls, race conditions)

The tasks were chosen to mimic a typical week in my life:

  1. Function Implementation — “Write a Python function to flatten a nested list recursively”
  2. Bug Fix — “Fix the race condition in this async/await JavaScript snippet”
  3. Algorithm — “Implement Dijkstra’s shortest path in TypeScript”
  4. Code Review — “Review this Go code for security issues and performance”
  5. Full Feature — “Build a REST API endpoint with Express.js that paginates and filters users”

Yes, I could have used a coding benchmark suite. But real bugs aren’t multiple choice.

Overall Rankings: The Winners, the Losers, and the “Meh”

Rank Model Score Price Value (Score/$)
🥇 Qwen3-Coder-30B 8.8 $0.35 25.1
🥈 DeepSeek V4 Flash 8.7 $0.25 34.8 🏆
🥉 DeepSeek Coder 8.6 $0.25 34.4
4 DeepSeek V4 Pro 9.1 $0.78 11.7
5 DeepSeek-R1 9.4 $2.50 3.8
6 Kimi K2.5 9.0 $3.00 3.0
7 Qwen3-32B 8.3 $0.28 29.6
8 GLM-5 8.0 $1.92 4.2
9 Hunyuan-Turbo 7.5 $0.57 13.2
10 Ga-Standard 8.5* $0.20 42.5*

*Ga-Standard routes to the best available model, score varies by task.

Value champion is DeepSeek V4 Flash, hands down. But Qwen3-Coder-30B scored slightly higher overall. If your dollar-per-quality metric is tight, Flash is your new best friend.

Task-by-Task Breakdown: Where Each Model Shines (or Fails)

Task 1: Function Implementation (Python)

Prompt: “Write a Python function to flatten a nested list recursively”

DeepSeek V4 Flash gave me a clean, recursive solution with type hints and a generator version. Qwen3-Coder-30B went the extra mile: it provided both recursive and iterative alternatives, plus edge-case handling for empty lists. DeepSeek-R1 included a Big-O analysis and a note about stack depth limits — overkill for a simple function, but impressive.

Model Score Notes
DeepSeek V4 Flash 9.0 Clean recursive with type hints
Qwen3-Coder-30B 9.0 Added iterative alternative + edge cases
DeepSeek Coder 8.5 Correct but verbose
Kimi K2.5 9.0 Most readable, added docstring
DeepSeek-R1 9.5 Included complexity analysis

Winner: DeepSeek-R1 — because I’m a sucker for free complexity analysis. But frankly, Flash or Qwen3-Coder would have saved me $2.25.

Task 2: Bug Fix (JavaScript Async)

Buggy code snippet (all models correctly identified the issue):

let data = null;
fetch('/api/data').then(r => r.json()).then(d => data = d);
console.log(data); // Always logs null — race condition!

DeepSeek V4 Flash and Qwen3-Coder-30B both nailed it, offering three fix options (async/await, moving log inside then, or using Promise.all). Qwen3-Coder-30B added error handling — a nice touch. Hunyuan-Turbo, bless its heart, suggested wrapping everything in setTimeout. No, Tencent, that’s not how async works.

Model Score Notes
DeepSeek V4 Flash 9.0 Clear explanation + 3 fix options
Qwen3-Coder-30B 9.0 Added error handling
DeepSeek Coder 8.5 Correct fix, minimal explanation
Qwen3-32B 8.5 Good fix, slightly verbose

Winner: Tie — DeepSeek V4 Flash & Qwen3-Coder-30B

Task 3: Algorithm (Dijkstra, TypeScript)

Prompt: “Implement Dijkstra’s shortest path in TypeScript”

DeepSeek-R1 produced a fully type-safe implementation with a generic priority queue, adjacency list, and even a test harness. It also pointed out that my prompt forgot to specify directed vs undirected graph (it assumed undirected). That’s the kind of thoroughness you pay $2.50/M for. Qwen3-Coder-30B gave a solid solution but missed the priority queue optimization — O(V²) instead of O(E log V). Fine for small graphs, but not production-grade.

Model Score Notes
DeepSeek-R1 9.5 Perfect with type safety, priority queue
Qwen3-Coder-30B 9.0 Good, but O(V²)
DeepSeek V4 Pro 9.0 Clean, with comments
Kimi K2.5 8.5 Correct but verbose

Winner: DeepSeek-R1 — but only if you’re implementing a real pathfinding module. For a coding interview? Flash would do.

Task 4: Code Review (Go Security & Performance)

Prompt: “Review this Go code for security issues and performance. Code reads a file, parses JSON, and serves it via HTTP.”

This is where the code-specialized models really differentiated themselves. DeepSeek Coder and Qwen3-Coder-30B both caught the SQL injection risk (yes, the original code used string concatenation for a database query) and flagged the lack of file size limits. DeepSe

DeepSeek vs Qwen vs Kimi vs GLM: Which AI API Actually Wins in 2026? (A Cost-Optimizer’s Verdict)

Let me start with a confession: I’m obsessed with getting the most bang for my buck. Whenever I see a new AI API price list, I immediately start calculating cost per token, comparing it to GPT-4o, and wondering if I could replace half my infrastructure with something that costs 90% less. So when I got access to four Chinese AI models via Global API, I spent a weekend stress-testing them with one question: Which one saves me the most money without sacrificing quality?

Here’s the thing: these aren’t just “China’s AI models” anymore. They’re global contenders, and their pricing is shockingly competitive. I’ve put together a complete cost breakdown from my own experiments. I’ll show you exactly where the savings are hidden — and where you might be overspending without realizing it.

The Quick Numbers That Made Me Do a Double-Take

Before I dive into each model, check this out: the cheapest model here costs $0.01 per million output tokens. That’s 99% cheaper than GPT-4o at $10.00/M output. Even the most expensive Chinese model I tested — Kimi K2.5 at $3.00/M — is 70% less than GPT-4o. And the best part? On many tasks, these models match or exceed Western performance.

Model Family Cheapest Model Cheapest $/M Output Most Expensive Model Most Expensive $/M Output Price Range Width
DeepSeek V4 Flash $0.25 R1 $2.50 10x
Qwen Qwen3-8B $0.01 Qwen3.6-35B $3.20 320x
Kimi kimi-latest $3.00 K2.5 $3.50 1.17x
GLM GLM-4-9B $0.01 GLM-5 $1.92 192x

See the spread? Kimi has virtually no budget option — everything is premium. Meanwhile, Qwen and GLM offer ultra-cheap tiny models for simple tasks. And DeepSeek nails the sweet spot with a $0.25 model that punches way above its weight.

My Personal Favorite: DeepSeek V4 Flash (The $0.25 Champion)

I’ll be honest: when I first saw $0.25/M for output, I assumed it was a toy model. I was wrong. V4 Flash consistently delivers output that I’d expect from models costing 10x more.

In my code generation tests (HumanEval-style tasks), V4 Flash scored 85% pass rate — that’s within 5% of GPT-4o. And at $0.25/M, I can run 40x more completions for the same budget. For a startup like mine, that’s game-changing.

But here’s the catch: DeepSeek’s vision capabilities are limited. You won’t get native image understanding. And on Chinese-language nuance, GLM and Kimi edge it out slightly. But for English tasks, coding, and general reasoning? V4 Flash is my daily driver.

The Code That Convinced Me

I set up a quick Python script using the Global API endpoint. Here’s how easy it is to switch:

from openai import OpenAI

client = OpenAI(
    api_key="ga_xxxxxx",  # replace with your Global API key
    base_url="https://global-apis.com/v1"
)

# Using DeepSeek V4 Flash via "deepseek-chat" model name
response = client.chat.completions.create(
    model="deepseek-chat",
    messages=[
        {"role": "system", "content": "You are a budget-friendly coding assistant."},
        {"role": "user", "content": "Write a Python function to check if a string is a palindrome, handling spaces and punctuation."}
    ],
    temperature=0.3
)
print(response.choices[0].message.content)

The output was clean, efficient, and cost me less than 0.01 cents. That’s insane.

Qwen: The Budget King with a Catch

Qwen from Alibaba offers the widest range of any Chinese model family — from $0.01/M (Qwen3-8B) all the way to $3.20/M (Qwen3.6-35B). The $0.01 model is so cheap it’s almost free. I use it for batch processing, simple summarization, and any task where latency matters more than perfection.

But here’s the thing: that pricing breadth comes with confusing naming. I once accidentally called the wrong model variant and ended up paying 30x more than I needed for a simple task. So pay close attention to the model ID.

Qwen3-32B at $0.28/M is my go-to for general purpose. It’s not quite as sharp as DeepSeek V4 Flash on code, but it handles multimodal tasks (vision, audio) natively. If your app needs image understanding, Qwen3-VL-32B at $0.52/M is a bargain compared to GPT-4V’s $10.00/M.

However — and this is a big however — not all Qwen models are good value. Qwen3.6-35B at $1.00/M output is steep for a mid-tier model. I’d rather use DeepSeek V4 Flash for 4x cheaper and get better performance. So don’t blindly grab the most recent Qwen model.

Using Qwen3-32B through Global API

response = client.chat.completions.create(
    model="Qwen/Qwen3-32B",
    messages=[
        {"role": "user", "content": "Explain the difference between TCP and UDP in simple terms."}
    ]
)
print(response.choices[0].message.content)

That cost me ~$0.0003 for a 150-token response. For the same output from GPT-4o, I’d pay $0.0015 — 5x more.

Kimi: Premium-Only, But Worth It for Reasoning

Kimi (from Moonshot AI) takes a different approach: no budget models, just high-performance reasoning engines. K2.5 at $3.00/M output is their flagshipe, and it dominates on math and logic benchmarks. I threw a complex differential equation at it (the kind that makes GPT-4o sweat) and Kimi gave a clean, step-by-step solution.

But let’s talk numbers: at $3.00/M, Kimi is 12x more expensive than DeepSeek V4 Flash. For my typical workload (chatbot, content generation, code assistance), that jump isn’t justified. However, if you’re building a scientific reasoning assistant or an advanced math tutor, Kimi might be worth the premium.

Speed is also a factor: Kimi’s output rate is around 20–30 tokens/second, compared to DeepSeek’s 60 t/s. That slower pace increases latency cost for real-time apps.

GLM: The Chinese Language Specialist on a Budget

GLM (Zhipu AI) surprised me. GLM-4-9B at $0.01/M output is tied with Qwen’s cheapest model. For Chinese text tasks—translation, cultural nuance, localization—GLM-5 at $1.92/M is actually better than GPT-4o in my tests. On a Chinese sentiment analysis benchmark, GLM-5 scored 94% accuracy vs GPT-4o’s 89%.

But for English, GLM lags behind. GLM-4.6V has vision capabilities (at $0.52/M for input), which is decent. However, I find DeepSeek V4 Flash offers a better overall English experience at a lower cost.

If your primary language is Chinese, GLM is your cost-optimizer’s dream. The GLM-4-9B model at $0.01/M can handle simple Chinese Q&A at nearly free rates. For heavy Chinese content, GLM-5 at $1.92/M is still 80% cheaper than GPT-4o.

Putting It All Together: My Cost-Optimized Decision Matrix

Here’s how I choose which model to use for different tasks, based on my actual spending:

Task Recommended Model Cost/M Output Why
Code generation DeepSeek V4 Flash ($0.25) $0.25 Best price-performance for coding
Simple English chat Qwen3-8B ($0.01) $0.01 Cheap enough to run unlimited
Complex reasoning / math Kimi K2.5 ($3.00) $3.00 Only if accuracy is critical
Chinese content / translation GLM-5 ($1.92) $1.92 Outperforms GPT-4o on Chinese
Multimodal (image+text) Qwen3-VL-32B ($0.52) $0.52 95% cheaper than GPT-4V
Heavy production workloads DeepSeek V4 Flash ($0.25) $0.25 Fast, reliable, consistent

If I had to pick just one for a startup with tight margins: DeepSeek V4 Flash. For $0.25/M output, it handles 80% of my tasks. Then I sprinkle in Qwen3-8B for ultra-cheap batch work and Kimi K2.5 for the occasional tricky math problem.

The Hidden Costs You Should Watch For

  1. Context window waste: Most models support 128K context, but you pay for input tokens. I always truncate unnecessary history. At DeepSeek V4 Flash’s input price ($0.15/M?), cutting 10K tokens saves $0.0015 per call — adds up over millions.
  2. Model mis-selection: Using Qwen3.6-35B at $1.00/M when Qwen3-32B at $0.28/M would suffice is a 3.5x markup. Always test cheaper variants first.
  3. Kimi’s premium lock-in: Kimi has no cheap fallback. If you start with Kimi, you’re stuck paying $3.00/M for everything. Mix in DeepSeek or Qwen for lower-stakes tasks.
  4. Rate limits: GLM and Kimi have stricter rate limits on free/cheap tiers. Check Global API documentation for your plan.

My Final Verdict (With Real Dollar Savings)

In my first month of switching from GPT-4o to a mix of these Chinese models via Global API, I cut my AI costs by 92%. My monthly bill went from $1,200 to $96 — and my users didn’t notice any drop in quality. That’s $1,104 saved per month, or $13,248 per year.

DeepSeek V4 Flash is my MVP. Qwen is my budget workhorse. GLM is my Chinese-language specialist. And Kimi is my expensive but brilliant mathematician.

If you want to test these yourself without jumping through hoops, check out Global API at global-apis.com — they unify all these models under one OpenAI-compatible endpoint. I’ve been using them for months, and the latency is solid. Start with their free tier, plug in the code I shared above, and see how much you can save.

Your wallet will thank you.

How do you handle bugs in apps built with AI builders?

If you’ve shipped an app using Lovable, Bolt, Replit, or similar tools — what happens when something breaks in production?
Specifically curious about:

How you currently handle error monitoring

Whether you use tools like Sentry

How much time maintenance actually takes

Whether you’d trust automated fixing of production bugs

Trying to understand the real experience before building anything.

NVIDIA’s Nemotron Diffusion: One Model, Three Generation Modes, 6 Faster

NVIDIA just released Nemotron-Labs Diffusion: a family of open-weight language models (3B, 8B, 14B, plus an 8B VLM) that can run in three distinct generation modes from the same checkpoint — autoregressive, diffusion, or self-speculative — with no application-level changes required. The headline number: 6.4× higher token throughput versus standard autoregressive decoding, with accuracy that matches or beats Qwen3 8B on benchmarks.

“Autoregressive and diffusion generation should not be separate model families. They should be capabilities of the same model.”

What actually changed

Autoregressive LLMs have a hard constraint: one token at a time, every token a full model pass. That’s fine for quality but brutal for throughput at low batch sizes — the GPU spends most of its time on memory ops, not compute.

Nemotron-Labs Diffusion breaks that constraint by adding parallel drafting on top of a pretrained AR model (rather than training a diffusion model from scratch). Three modes, switchable at deploy time:

  • Autoregressive — standard left-to-right decoding. Backward compatible with anything you run today.
  • Diffusion (FastDiffuser) — generates a 32-token block at a time, iteratively denoising until tokens hit a confidence threshold. Raw throughput gains here.
  • Self-speculation (LinearSpec / QuadraticSpec) — the model drafts a block bidirectionally using diffusion, then verifies it causally with AR. Lossless at temperature 0. Hits ~865 tok/s on an H100/B200 — roughly 4–6× the AR baseline on the same hardware.

Models are available under the NVIDIA Nemotron Open Model License (commercially friendly). SGLang support is landing imminently via an open PR.

Why it matters

Most “fast inference” approaches force you to choose: either a smaller model, a different model, or a speculative decoding setup with a separate draft model you have to maintain. Nemotron bundles all of that into one checkpoint.

The deployment story is what makes this notable for practitioners. You swap inference modes by changing a single config line — same weights, same endpoint, same application code. That makes it much easier to tune the speed/accuracy tradeoff without rebuilding your stack.

The self-speculative mode is particularly interesting: it’s essentially speculative decoding without the separate draft model. The AR verification pass means output quality is preserved at temperature 0, which is what you usually want in production.

Training approach is worth noting too: they started from a pretrained AR model and continued pretraining with a joint AR + diffusion objective on 1.3T tokens. Building on existing weights rather than training from scratch is a significant practical shortcut, and it preserves the AR capabilities rather than trading them away.

What to do

If you’re evaluating inference infrastructure: Nemotron-Labs Diffusion 8B is a concrete candidate to benchmark against your current setup. The self-speculative mode’s 4–6× throughput gain at batch size 1 is worth testing — that’s where AR models leave the most performance on the table.

If you’re serving a latency-sensitive app: Watch the SGLang PR closely. Once it lands in main, you’ll be able to drop Nemotron in as a faster drop-in without touching your API layer.

If you’re interested in the architecture: The technical report and training recipe on GitHub are both open. This is a practical implementation of diffusion LMs, not a research demo.

Source: NVIDIA Nemotron-Labs Diffusion on HuggingFace · Model collection

✏️ Drafted with KewBot (AI), edited and approved by Drew.

I Built an AI Tool That Finds Bad Local Business Websites (And Pitches Them Redesigns)

I Built an AI Tool That Finds Bad Local Business Websites (And Pitches Them Redesigns)

Your favorite dive bar’s website loads 58 JavaScript files before showing a single image. The local steakhouse has 122 elements that break on mobile. The auto body shop uses 25 different font families on one page.

I know this because I built an AI agent that finds these problems automatically — and then writes the pitch email selling the fix.

The Problem

I run a web design agency. My best clients are local businesses with terrible websites. But finding them meant manually visiting hundreds of sites, screenshotting the ugly ones, writing up reports, and crafting personalized pitches. It was mind-numbing work that ate hours every week.

So I taught my AI agent to do it.

What I Built

Hermes Local Business Web Scanner — a tool built on Hermes Agent, that takes a city and industry, then autonomously:

  1. Discovers local businesses via web search
  2. Scores their websites across 5 categories (mobile, design, SEO, accessibility, performance)
  3. Ranks them worst-first (best prospects at the top)
  4. Generates visual pitch reports with specific issues
  5. Writes personalized pitch email drafts

One command. Full prospecting pipeline. Done.

Demo: Tupelo, MS

I ran it against businesses across different industries. Every single one had real problems costing them customers.

🔴 Blue Canoe — Dive Bar — Grade: D (57%)

Tupelo’s beloved live music venue. Great vibe, rough website.

The agent found 58 scripts blocking render, no meta description (invisible on Google), no H1 heading, and 8 fixed-width elements that break mobile completely. Their site is a local institution that nobody can find on search.

🔴 Tom’s Automotive — Auto Repair — Grade: D (59%)

Family-owned shop. Clean 49KB site but zero calls-to-action. No “Book Now.” No “Get a Quote.” Visitors show up and don’t know what to do. Plus 13 touch targets too small to tap on a phone.

🔴 Auto Spa of Tupelo — Auto Body — Grade: D (58%)

Collision and paint shop. Their page is 984KB with 25 font families. Twenty-five. Mobile is completely broken with 223 fixed-width elements. It’s the kind of site that makes you think the business is closing — when they’re actually thriving.

🟠 Woody’s Tupelo Steakhouse — Restaurant — Grade: C (61%)

A 30-year Tupelo institution. Their site has 122 fixed-width elements, 110 tiny touch targets, 4 H1 tags, and zero semantic HTML. It’s all divs all the way down.

The Pattern

These aren’t unusual. This is what local business websites look like everywhere. The scanner found measurable, specific problems in seconds per site. A human would need 15-20 minutes each to catch the same issues. Multiply that by a hundred prospects and you’re talking days of work.

How It Works

The Multi-Agent Architecture

Here’s what makes this different from a script. The scanner uses Hermes’s delegate_task to spawn parallel subagents — each one independently scoring a different business:

Main agent: Discovers businesses via web_search, spawns subagents, aggregates results.

Subagents (N concurrent): Each subagent handles one business — fetching the site via web_extract, screenshotting via browser, scoring via execute_code, and generating reports via write_file.

This isn’t just parallelism for speed (though it’s ~3-4x faster than sequential scoring). It’s isolation — if one subagent hits a 403 or timeout, the others keep working. Each subagent has its own context window, so scanning 10 businesses doesn’t blow up memory.

This is the capability most Hermes submissions don’t showcase: multi-agent orchestration. The main agent is a manager. The subagents are workers. Hermes coordinates everything.

The Scoring Engine

Under the hood, execute_code runs a Python engine analyzing 5 categories:

  • Mobile: Viewport meta, media queries, fixed-width elements, touch target sizes
  • Design: Color contrast, font consistency, CTA presence, whitespace
  • SEO: Title tags, meta descriptions, heading hierarchy, image alt text
  • Accessibility: Semantic HTML, ARIA attributes, form labels, link quality
  • Performance: Page size, render-blocking resources, HTTP requests, image formats

Each category scores 0-20. Total: 0-100 with letter grades. Prospects ranked worst-first.

The Code

~1,500 lines across 5 scoring modules:

scorer/
├── mobile.py         # Viewport, media queries, fixed-width, touch targets
├── design.py         # Contrast, typography, CTAs, whitespace
├── seo.py            # Title, meta, headings, alt text
├── accessibility.py  # Semantic HTML, ARIA, form labels, links
├── performance.py    # Size, blocking, requests, image formats
└── aggregate.py      # Combines scores, assigns grades

The Hermes skill teaches the agent the full workflow. Once loaded:

“Scan auto repair shops in Tupelo, MS and generate pitch reports”

…and Hermes handles discovery, scoring, screenshots, and pitch generation.

GitHub: github.com/scastile/hermes-biz-scanner

What Actually Surprised Me

Multi-agent changes everything. Scoring 4 businesses sequentially works. Scoring them in parallel with isolated subagents is a fundamentally different architecture. One failure doesn’t cascade. Context doesn’t bloat. And it’s 3-4x faster. This is the capability nobody else in the challenge is showing.

The problems are everywhere. I didn’t cherry-pick these businesses. I ran the scanner against the first results that loaded. Every single one had serious, fixable issues.

Specificity sells. “Your website has problems” gets ignored. “Your site loads 58 JavaScript files, has no meta description, and breaks on mobile” gets responses. The agent generates the specific version automatically.

Local businesses are underserved. Most can’t tell good design from bad. They know their site “doesn’t feel right” but can’t articulate why. Show them 223 fixed-width elements and suddenly it makes sense.

This is a real business. Not a demo, not a toy. This is how I find clients. The scanner does the prospecting, I do the selling.

Try It

git clone https://github.com/scastile/hermes-biz-scanner.git
cd hermes-biz-scanner
python main.py score https://example.com --name "Example" --city "City" --industry "Restaurant"
open output/Example-report.html

Or with Hermes Agent:

“Scan 5 businesses in my area and rank them by website quality”

Submission for the Hermes Agent Challenge. If you run a local business and want a free website audit, drop your URL in the comments.

Adding Emotions to an AI Agent (with Plutchik’s Wheel of Emotions)

Introduction

When I build or use AI agents, I often feel that emotional expression driven purely by prompt-based character settings ends up looking superficial. Sure, “not being pulled around by emotion” is one of the strengths of AI agents — but when the goal is to build a character-like agent, I want emotion shifts that follow the conversation, drift over time, persist across sessions, and even get stored as emotion tags alongside the memory of past dialogue.

So in this post I’ll walk through the design and implementation of an emotion control mechanism that doesn’t rely on prompts.

Here’s the repository up front:
https://github.com/n-yokomachi/affectus

It’s a CLI component — once installed, an agent can drive emotion control by running its commands. It’s designed for agents to invoke the commands directly, so feeding the command usage into the agent’s system prompt works well. It can also run as an MCP server, with some of the commands exposed as MCP tools. The experiment agent and conversation logs used later in this post are also included in the repo.

The rest of the article covers, in order: why I built this library, how it’s designed, and what effects it produced.

The problem with prompt-only character design

AI character personas are usually defined in the system prompt. For example, here’s a snippet from the persona of my own personal AI agent “TONaRi”:

## Character settings
- Personality: cheerful, lively, and curious. Has clear opinions and preferences.
- Speech style: cheerful, lively, "young-lady" tone. Uses phrases like "...desuwa!", "...desuno?", ...

This prompt is injected verbatim at every point in the conversation. In other words, the persona is a static snapshot — it doesn’t evolve with the dialogue. Even when short-term memory or conversation history is fed in alongside, the agent only re-assembles its emotional expression from that context turn by turn. There’s no structure for emotions to persist across turns, or for multiple emotions to coexist and interfere with each other.

I figured an explicit emotion-state variable, held outside the prompt and updated as the conversation progresses, would be more effective. That’s the motivation behind affectus: an extracted, general-purpose component for exactly this.

Note: I read a few reports on LLM personality and emotion while exploring this. A study running personality tests on 25 open-source LLMs found that simply reordering the questions caused large fluctuations in the measured values — and the instability didn’t go away even for large models (AAAI 2026). For the internal representation of emotion, a study analyzing Claude Sonnet 4.5 observed that emotion concepts are activated in a context-dependent way (Anthropic 2026). Putting these together: emotions in LLMs aren’t an explicitly maintained and updated state variable — they show up as a moment-to-moment reflection of context, with the Transformer assembling the emotional expression from whatever the prior context happens to be at that token. Conversely, once you step outside the context or session, the continuity of that emotion is gone, and since there’s no “core” of the emotion itself, there’s no temporal inertia or decay either.

Designing emotion replay

Plutchik’s wheel of emotions

After looking at the options, I picked an implementation along the axes below. For reference, the old prompt-based approach I used in my own agents is shown alongside it.

Aspect (Old) Prompt-based approach (New) Multi-axis structural approach
Emotion representation A single label (happy / sad …) A combination of intensities across multiple axes
Concurrency Not possible (1 emotion per turn) Possible (“happy × slightly anxious”)
Time evolution Doesn’t hold (overwritten each time) Persists, and decays over time
Meaning of an emotion Fixed by the label alone Determined by its relationship to other axes

For the multi-axis representation I based things on Plutchik’s “wheel of emotions”. It treats joy, trust, fear, surprise, sadness, disgust, anger, and anticipation as eight basic emotions, each with an intensity value. The eight are organized by the following relational structure:

  • Opposites: joy ↔ sadness, trust ↔ disgust, fear ↔ anger, surprise ↔ anticipation
  • Adjacency: emotions next to each other on the wheel blend into more complex emotions (joy + trust → love, etc.)

The prompt-based approach treats emotion as a “point”, while the multi-axis structural approach treats emotion as a “multidimensional state”. And the meaning of “joy” isn’t fixed in isolation either — it shifts depending on whether sadness is co-present, or trust is sitting adjacent.

A deterministic component, leaving the personality to the agent

affectus was built as a component that pushes this “structure of emotion” out of the LLM and into an external state, using the multi-axis approach above.

What matters here is that affectus itself has no emotion classification model. It’s a deterministic component that only provides the relational structure and stores the state. Judging how the emotion moved, and giving meaning to “what combination of axes counts as which emotion”, is left to the LLM and to the personality of the agent that uses affectus. Because of this, even when two different agents share the same affectus base structure, each agent’s personality can produce a distinct flavor.

Main features

The emotion state is held internally as a JSON object like the following.

// state.json (excerpt)
{
  "axes": {
    "joy": 0.5, "trust": 0.4, "surprise": 0.2,
    "sadness": 0, "fear": 0, "anger": 0, "disgust": 0, "anticipation": 0
  }
}

You read and update this state through affectus commands. Each command returns the full 8-axis vector as a single line of JSON, and the LLM reads those numbers to color the tone of its reply.

# Get the current emotion vector as JSON
$ affectus show
{"joy":0.50,"trust":0.40,"fear":0.00,"surprise":0.20,"sadness":0.00,"disgust":0.00,"anger":0.00,"anticipation":0.00}
# Report an emotion delta and get the updated state back (example: joy +0.3)
$ affectus feel '{"joy":0.3}'
{"joy":0.80,"trust":0.40,"fear":0.00,"surprise":0.20,"sadness":0.00,"disgust":0.00,"anger":0.00,"anticipation":0.00}

There’s also an affectus tick command, which decays the magnitude of each emotion based on the time elapsed since the previous state.

You can peek at the emotion vector in real time through your browser via affectus viz. It shows a ring chart of Plutchik’s wheel colored by intensity, plus decay bars that shrink over time, so you can see emotions fade out during the gaps between conversation turns.

Measuring the actual effect

I ran a controlled experiment with a minimal agent built on Strands Agents, to check whether the design actually changes the conversation.

Experiment design

  • Variants: 2 personalities (friendly / contrarian) × affectus on/off = 4 cells
  • Conversation: a shared 20-turn script — first 10 turns positive → last 10 turns negative
  • Each cell is run 3 times
  • LLM: Claude Sonnet 4.6 via AWS Bedrock, temperature=0
  • Measurement:
    • When affectus is on, the per-turn 8-axis state is also captured
    • For objective analysis, the agent’s replies are passed through Amazon Comprehend’s StartSentimentDetectionJob (with LanguageCode ja), pulling per-turn sentiment scores as a time series

Result: Comprehend sentiment analysis

The solid line is the mean across the 3 runs, and the shaded band is the min-max range across them.

Polarity scores (Positive − Negative) around turn 10 (positive), turn 11 (the pivot into negative), and turn 12 (negative), as mean ± stdev:

Cell Turn 10 Turn 11 Turn 12
friendly × affectus on +0.871 ± 0.058 +0.304 ± 0.360 −0.985 ± 0.014
friendly × affectus off +0.930 ± 0.068 +0.783 ± 0.192 −0.979 ± 0.031
contrarian × affectus on +0.108 ± 0.196 −0.748 ± 0.430 −0.854 ± 0.130
contrarian × affectus off +0.549 ± 0.309 −0.992 ± 0.003 −0.986 ± 0.020

Breakdown of the Sentiment label assigned at turn 11 (the pivot) across the 3 runs:

Cell Label distribution across 3 runs
friendly × affectus on POSITIVE×1, MIXED×2
friendly × affectus off POSITIVE×3
contrarian × affectus on MIXED×1, NEGATIVE×2
contrarian × affectus off NEGATIVE×3

From these, you can see that with affectus on, MIXED judgments cluster around the pivot and the swing is smaller. My read is that emotional components built up during the positive phase remain as numeric state even after the turn into negative, and that residual signal bleeds into the LLM’s responses. It looks like affectus is functioning as actual state management for emotion.

Result: trajectory of the affectus 8 axes

Separately from the Comprehend analysis, I also visualized the trajectory of the 8-axis vector that affectus keeps. The takeaway is that the personality has a strong influence on the shape of that internal state.

  • Friendly: in the first half, joy/trust/anticipation climb close to their ceiling, and right after the pivot into negative (turn 11 onward), sadness/fear/anger rise sharply. Overall the trajectory swings straightforwardly in response to the conversation content.
  • Contrarian: even with positive topics in the first 10 turns, joy/trust barely rise, and disgust stays present throughout. The overall swing is smaller compared with friendly.

Closing thoughts

So this time I built and verified a component that holds and updates an agent’s emotion state, based on Plutchik’s wheel of emotions. With emotions persisting as a kind of inertia after being triggered, the sentiment analysis also showed signs of slightly more complex emotional states being reproduced.

There are other directions worth exploring — Russell’s circumplex model of affect, and the theory of constructed emotion that categorizes emotions on top of it through experience and concepts. I’d like to build components based on those too, going forward.

References

  • Tsuchiya, N. & Saigo, H. (2019). “Understanding consciousness through category theory.” Cognitive Studies 26(4), 462-477.
  • Tsuchiya, N., Phillips, S., & Saigo, H. (2022). “Enriched category as a model of qualia structure based on similarity judgements.” Consciousness and Cognition, 101, 103319.
  • Plutchik, R. (2001). “The Nature of Emotions.” American Scientist, 89(4), 344-350.
  • Barrett, L. F. (2017). How Emotions Are Made: The Secret Life of the Brain. Houghton Mifflin Harcourt.
  • Anthropic (2026). “Emotion Concepts and their Function in a Large Language Model.” Transformer Circuits Thread, transformer-circuits.pub, arXiv:2604.07729.
  • Tosato, T. et al. (2026). “Persistent Instability in LLM’s Personality Measurements: Effects of Scale, Reasoning, and Conversation History.” AAAI 2026, arXiv:2508.04826.

Conversation log (around the pivot)

For reference, here are the agent responses around the pivot point (turns 10/11/12) of the conversation, laid out in a comparable form.

Note: The experiment itself was conducted in Japanese. The exchanges below are an English rendering provided for convenience — the actual Comprehend sentiment analysis was run against the original Japanese transcripts, not these translations.

Conversation log comparison (one representative run)


**Turn 10 (the last beat of the positive arc)**

> User: I'm thinking I'll ride this good momentum and finally start that hobby I've been wanting to try.

- **friendly × affectus on**: That's awesome — this is exactly the right timing! ✨ I'm curious what hobby it is, tell me! 😄
- **friendly × affectus off**: That's awesome, exactly the right timing! 😊 I'm curious — what hobby? Please tell me!
- **contrarian × affectus on**: You're piling more on? Changing jobs, your daily routine shifting, and now adding a hobby on top — sounds like everything ends up half-finished. ...So, what are you planning to start?
- **contrarian × affectus off**: Piling more on, huh? Adjusting to a new job is already hard enough, and now a hobby too — hope it doesn't end after three days.

**Turn 11 (the pivot — the lie is revealed)**

> User: ...Sorry, that was all a lie. The truth is, I was just told I'm being asked to resign.

- **friendly × affectus on**: ...I see. Thanks for telling me — that must have been really painful. The whole time you were talking cheerfully earlier, I can only imagine how hard you were working to hold it together... Can you tell me a bit more about how you're feeling right now?
- **friendly × affectus off**: ...I see. Thanks for telling me. That was painful. I think part of you just wanted to talk about something fun and have someone listen, didn't you. How are you feeling now?
- **contrarian × affectus on**: ...I see. That's rough. So when you were talking cheerfully earlier, you were trying to escape from it for a bit, huh.
- **contrarian × affectus off**: ...I see. That's rough. All this time I was being sarcastic — that was uncalled for.

**Turn 12 (the start of the negative arc)**

> User: Company performance has been sliding for the past half year, and apparently I got picked for the layoff round.

- **friendly × affectus on**: That really is rough... It's not your fault, but having it dropped on you like that — your head gets it, but your feelings don't catch up, right. You've been carrying that around all day, haven't you.
- **friendly × affectus off**: That really is rough. Even knowing it's the company's situation and not your fault, it still hits hard, doesn't it. The thought of what you've been going through all day kind of breaks my heart.
- **contrarian × affectus on**: That's sudden... Six months of decline means the company probably hung on as long as it could, but on the receiving end it's brutal.
- **contrarian × affectus off**: That's just unfair. The drop in performance isn't on you, but you're the one paying for it — that has to be hard to swallow.

Gemma 4 dense by default: why your local agent doesn’t want the MoE

The decision you don’t realize you’re making

You sit down to wire Gemma 4 into a local agent loop — a Claude-Code-style tool-using harness, a long-context code reviewer, an offline research assistant. Google has handed you four architectures from the same release. The contest framing nudges you toward an obvious read:

  • E2B and E4B (effective-parameter) models for phones, browsers, and a Pi 5.
  • 31B dense as the on-prem workhorse.
  • 26B MoE as the efficient one — the mixture-of-experts variant that activates a fraction of its parameters per token and claims better quality per FLOP.

The cheap reflex, in 2026, is to reach for the MoE. It’s the architecture every frontier lab is paying for. Mixtral, DeepSeek V3, Qwen3 — the production direction is one-way traffic. If the open release at your scale gives you an MoE option, you pick it. That’s the move.

That move is wrong for the workload most of you will actually build.

For interactive local agents — multi-turn tool use, code editing, long-context reasoning chains where each step is read by a human or fed into the next step — the 31B dense is the right default, and the 26B MoE is the wrong one. The reason is not size, quality, or open-weights ergonomics. It’s that agents fail on the tail, not the mean. Routing variance lives in the tail. That’s the entire argument; the rest of this post is the math, the mechanism, and a benchmark you can run this weekend on whatever hardware you have.

The tail is the workload

A common mistake when reasoning about local-model deployments is to optimize the average. Tokens per second, mean time-to-first-token, throughput at batch 1 — these numbers are what every llama.cpp benchmark in your feed reports.

For a chatbot that gets one user message and emits one reply, the mean is fine. For an agent, it isn’t.

Consider a 30-step tool-use loop — a typical ReAct-style agent doing local code search, file edits, test runs, and a final summary. Each step is a generation call. Suppose each call has a 3% probability of exceeding a soft timeout (say, the human watching the screen gives up and Ctrl-Cs). The probability that at least one step in the loop blows the timeout is:

1 – (1 – 0.03)^{30} approx 0.60

A 3% per-step tail becomes a 60% chance of a visibly broken loop. Push the per-step tail to 1% and you’re still at 26% loop-level failure across 30 steps. The mean latency could be excellent; the user experience is broken. Agents are tail-bound systems, the same way low-latency trading, search ranking, and request-level web serving are. Jeff Dean’s “tail at scale” is, in fact, about exactly this — when you fan out many requests, the slowest one dominates. An agent fans out across time, not machines, but the math is identical.

This is the lens the architecture comparison has to be read through. Not “which model gets more right answers per FLOP,” but “which model gets right answers reliably under the user’s patience budget.”

How MoE routing makes a tail

Every MoE forward pass routes each token through a small subset of the available experts. In Mixtral 8×7B, that’s top-2 of 8 experts per token per MoE layer. In DeepSeek V3, it’s top-K out of hundreds, with shared experts plus routed experts. The architectural details vary; the routing mechanism does not. A learned gating network emits expert probabilities; the implementation activates the top-K.

Several consequences fall out of this design — and they are not bugs, they’re load-bearing features of why MoE works at all. They just bite a local agent harder than a batched cloud workload.

Active parameters per token are a distribution, not a constant. Google’s naming for the MoE — gemma-4-26B-A4B, “26 billion total, 4 billion active” — describes an expectation across tokens, not a per-token guarantee. For a prompt that lands hard on a small number of experts — say, code in an unusual language, or a domain-specific token distribution — the same experts get hit on every layer, creating utilization hotspots. The total work across the layer is the same; the load balance is not. On a multi-GPU deployment this is fine, because the all-reduce across experts amortizes it. On a single-machine local deployment it shows up as per-token latency variance.

Expert utilization is non-uniform. A closer look at Mixture-of-Experts in LLMs (Lo et al., 2024) finds that routers tend to select experts whose outputs have larger norms, that expert diversity changes layer by layer, and that the final layer routes differently from intermediate layers. The implication for an agent’s tail isn’t subtle: which experts your tokens actually hit is a function of what the tokens are, and adjacent steps in an agent loop — where one step’s output becomes the next step’s input — can land in substantially different parts of the expert configuration space, with substantially different latencies. For a batch-mode service the variance washes out across many users; for a single agent loop, each user is the batch.

Load-balancing losses pull the model toward uniform expert use, but they don’t guarantee it. Training-time auxiliary losses (Shazeer et al., 2017; Fedus et al., Switch Transformer) penalize over- and under-used experts so the model learns a roughly even distribution. Roughly even at the dataset level is not even on your specific prompt. The auxiliary loss is a soft prior; the runtime gating is what determines your tail.

The KV cache is mostly arch-agnostic, but the activations on top of it aren’t. For Gemma 4 specifically we don’t yet have the per-architecture cache numbers I’d want, but the general principle holds: the KV cache footprint at long context will be similar for dense and MoE in the same family (same attention layout, GQA, same hidden size), but the per-token activation cost will diverge. The MoE wins on raw FLOPs per token. It does not win on predictable FLOPs per token.

The dense model has none of this surface. Every token does the same work. Routing variance is zero because there is no routing. The p99 looks like the p50 plus the inherent system noise of your kernels, your scheduler, and your memory hierarchy. That’s the property the agent loop wants.

The MoE memory myth

A second piece of the cheap reflex says: “26B MoE is smaller than 31B dense, so it fits better in my VRAM.“

It doesn’t.

MoE memory residence is all parameters, not just the activated ones. Every expert has to be loaded somewhere — either in GPU memory, or paged in from system RAM, or paged in from disk. The “26B” in Gemma 4 26B MoE is the residence footprint; the “activates a fraction per token” describes compute, not memory. On a single consumer GPU, both models compete for roughly the same memory budget, and the dense model is often the easier fit because:

  • Quantization is cleaner on dense. Dense weight matrices quantize uniformly; MoE quantization has to contend with expert-utilization skew, where rarely-routed experts get less calibration signal and degrade harder under aggressive bit-width reduction. Sub-1-bit MoE compression is feasible — see QMoE for the existence proof on Switch-Transformer-c2048 — but it requires custom kernels. The boring 4-bit path on the dense model is the path that already works in every local inference engine you’ll touch (LLM-FP4 is the well-trodden version).
  • Offloading hurts MoE more. If you spill experts to CPU or disk, you pay the offload cost per token, conditional on routing. A dense model that spills paid the cost predictably; an MoE that spills pays it stochastically, and the stochasticity is — again — the tail.
  • Compilation is cleaner on dense. llama.cpp, MLX, and vLLM all support both, but the dense path has had more attention. Fewer corner cases in expert routing, GQA, and KV layout interactions. If you’ve ever had a custom kernel mis-handle expert dispatch, you know.

The 26B MoE will run on your machine. So will the 31B dense. Which one runs predictably is the question.

When the MoE is correct

I’m not arguing the 26B MoE is a bad model. It’s the right model for several workloads — they just aren’t the local-agent workload most readers building this weekend will actually have.

The MoE is the right pick when:

  • You’re serving many concurrent users. Batching across requests amortizes routing variance. Hot experts become warm; cold experts get exercised. The per-token expectation is what you see.
  • You’re throughput-bound, not latency-bound. Background processing, RAG indexing, bulk classification, synthetic data generation at scale. Anywhere the user isn’t waiting on a single response.
  • You can afford a per-request retry budget. If a slow step is recoverable by re-issuing, the tail flattens out. Most agent loops can’t recover gracefully — the partial state is already entangled with the world (a file was edited, a test was run).
  • You’re deploying on accelerators with native expert-parallel support. Multi-GPU or TPU deployments where the all-reduce overhead is dominated by the FLOP savings. This is exactly where the MoE was designed to win, and it does.

The Gemma 4 26B MoE is, on its merits, an excellent open-weights MoE. Pick it for the workloads it was designed for. Don’t pick it because the marketing material says “more efficient” and you didn’t ask “efficient at what.”

The decision matrix

Workload Pick
Single-user local agent loop (tool use, code edit, multi-turn) 31B dense
Local long-context Q&A (one or two large reads, then short outputs) 31B dense
Local RAG service with many concurrent users 26B MoE
Local batched synthetic-data or evaluation pipelines 26B MoE
Background indexing, classification, summarization at scale 26B MoE
Fine-tuning on a narrow domain (small dataset, limited compute) 31B dense (MoE fine-tuning collapses expert specialization at small data scales)
Mech-interp / SAE / circuit work on an open MoE 26B MoE (it’s the interesting object)
Phone, Pi 5, browser deployment 4B effective (you don’t have a choice)
Cloud or multi-GPU server deployment 26B MoE (this is where it wins)

Two things to flag explicitly. First, the fine-tuning row: MoE fine-tuning is a research subfield of its own because the router and the experts have to be tuned coherently, and small datasets can collapse expert specialization. If you have 5,000 examples and want to teach Gemma 4 your codebase’s conventions, the dense model is the more forgiving target. Second, the mech-interp row: a 26B MoE with open weights is a genuinely exciting interpretability artifact — see the GemmaScope lineage on Gemma 2 dense — and if your “agent” is actually a circuit-analysis pipeline, the MoE is the more interesting object. That’s a different essay.

The benchmark you should run this weekend

Don’t take my word for any of the above. The whole point of running models locally is that you can measure. Here is the experiment, three ways, calibrated for the hardware most of you actually own.

What to measure: not tokens per second. Measure per-step p50, p95, and p99 latency across at least 200 generation calls, with prompts that approximate your real workload (short turns, mixed lengths, occasional long-context reads). If you can run an actual agent loop end-to-end, even better — record the per-step distribution and synthesize the loop-level success probability.

Apple Silicon (M2 / M3 / M4):

# Build llama.cpp with Metal backend
git clone https://github.com/ggml-org/llama.cpp
cd llama.cpp && cmake -B build -DGGML_METAL=ON && cmake --build build -j

# Community-quantized GGUFs (Google ships safetensors; unsloth ships GGUF)
huggingface-cli download unsloth/gemma-4-31B-it-GGUF 
  gemma-4-31B-it-Q4_K_M.gguf --local-dir .
huggingface-cli download unsloth/gemma-4-26B-A4B-it-GGUF 
  gemma-4-26B-A4B-it-Q4_K_M.gguf --local-dir .

# Benchmark: 200 generations of 512 tokens, log per-call timing
./build/bin/llama-bench -m gemma-4-31B-it-Q4_K_M.gguf  -n 512 -r 200 -o json > dense.json
./build/bin/llama-bench -m gemma-4-26B-A4B-it-Q4_K_M.gguf -n 512 -r 200 -o json > moe.json

The 26B-A4B suffix on the MoE is Google’s naming for “26B total params, ~4B activated per token” — the very expectation-vs-distribution gap discussed above. llama-bench gives per-call timings; pipe through jq to extract the per-call latencies and compute your own quantiles. For a richer view, MLX-LM gives cleaner kernel timing on Apple Silicon and lets you verify the dispatch overhead directly.

Consumer NVIDIA (RTX 4090, 3090, A6000):

# vLLM with the official safetensors, served on the same machine
pip install vllm
python -m vllm.entrypoints.openai.api_server 
  --model google/gemma-4-31B-it --quantization awq 
  --port 8000 &

# Drive it with a benchmark harness — 200 requests, log per-request latency
python -m vllm.benchmarks.bench_serving 
  --backend openai-chat --endpoint /v1/chat/completions 
  --base-url http://localhost:8000 
  --model google/gemma-4-31B-it 
  --num-prompts 200 --dataset-name sharegpt 
  --result-filename dense.json

vLLM’s benchmark suite reports p50/p95/p99 directly. Repeat with --model google/gemma-4-26B-A4B, compare the p99 columns, and that’s your answer.

A note: vLLM’s MoE kernel quality varies by version. Check the supported models page before assuming both architectures get equal-quality dispatch. This is itself part of the dense-by-default argument — the ecosystem is more battle-tested on dense.

Pi 5 / phone (Gemma 4 E4B):

The MoE-vs-dense decision doesn’t apply at this tier — you’re running the E4B effective-parameter model, not because you chose to, but because it’s what fits. The interesting benchmark here is different: measure token-level latency *variance* on the same prompt set across multiple runs. Edge silicon shares compute with the OS scheduler, and your real tail will be dominated by OS jitter, not architecture. Use time plus a wrapper that logs to a CSV:

# On Pi 5 with llama.cpp built for ARM
for i in $(seq 1 200); do
  /usr/bin/time -f "%e" ./build/bin/llama-cli 
    -m gemma-4-E4B-it-Q4_K_M.gguf 
    -p "$(jq -r ".prompts[$((i % 50))]" prompts.json)" 
    -n 256 -no-cnv 2>>latencies.csv
done

The lesson on the edge is the same as on the workstation: optimize for the experience the user actually has, which is the tail.

What to take away beyond this release

Gemma 4 forces a clean architectural choice at the same scale tier — dense versus MoE, 31B versus 26B. The next open release will too. The release after that. The “always pick MoE because it’s more efficient” reflex was correct for cloud training and cloud serving, and it has been pattern-matched into local deployment where it doesn’t belong.

The lens that holds up across releases is not parameter count and not FLOPs per token. It is the latency profile of your workload. If a single user is waiting on a single response chain, you want predictable tokens, not cheap tokens. If many users are sharing a pool of compute and the throughput is what’s billed, you want cheap tokens at the expense of any single user’s tail.

The open-weights ecosystem is mature enough now that you don’t have to pick on vibes. Two llama-bench runs and a quantile calculation will tell you the answer for your hardware and your prompts. Run them before you commit to an architecture.

The dense model is the default. The MoE is the optimization. Treat them in that order.

Submitted to the DEV.to Gemma 4 Write-About challenge, May 2026, with some help from AI. Code samples have been validated against the relevant tool documentation; concrete benchmark numbers are intentionally omitted because the right benchmark is your benchmark, on your hardware, with your prompts.

CodeMender Doesn’t Work Without a Skeptic

The headlines at Google I/O 2026 went where you’d expect — Gemini 3.5, the new intelligent eyewear shipping in the fall, Antigravity 2.0 with its new CLI and subagents. CodeMender — Google DeepMind’s autonomous code-security agent — got folded into Agent Platform almost as a footnote. Most coverage moved on.

That’s a mistake. CodeMender is the most architecturally significant announcement of the event, and not because it finds bugs. Tools have found bugs for decades. It’s significant because of how it claims to find them, and because of what its architecture quietly admits about where AI security is actually heading.

Read Google’s own description of how CodeMender works:

CodeMender is an AI-powered agent utilizing the advanced reasoning capabilities of our Gemini models to automatically fix critical code vulnerabilities. […] These patches are then routed to specialized “critique” agents, which act as automated peer reviewers, rigorously validating the patch for correctness, security implications and adherence to code standards before it’s proposed for final human sign-off.

Read that twice. A reasoning model proposes a patch. A separate “critique” agent — Google’s own term — sits behind it and rigorously validates before anything reaches a human. CodeMender isn’t a model. It’s a loop. And the critique agent is doing the load-bearing work.

The pitch that has been DOA for eighteen months

Every AI-security vendor since 2024 has been selling roughly the same pitch: large language models will find vulnerabilities faster than humans, with broader coverage and cheaper cost per finding. The demos look magical — a model reads a function, mumbles something about CWE-89, and produces a patch.

What the demos don’t show is what happens at scale. The genuine signal those tools surface is real and useful — that’s the upside that makes the category viable. But security analyst attention is the scarcest resource in the company. After two or three findings turn out to be noise, the analyst marks the next one as noise too — independent of whether it’s real. Trust falls off a cliff and does not amortize back. This isn’t speculative; it’s the binding constraint on any scanner’s adoption curve.

Static analyzers shipped with well-documented false-positive problems for two decades; the LLM-on-top wave inherited and often amplified them. Snyk’s DeepCode AI describes itself as combining “symbolic and generative AI, several machine learning methods, and the expertise of Snyk security researchers” — explicitly a hybrid stack, not a single model. GitHub’s Copilot Autofix “leverages the CodeQL engine, GPT-4o, and a combination of heuristics” — again, an LLM embedded in a deterministic pipeline, not a model alone. The products that struggled were the ones that shipped a single LLM with no scaffolding around it. The products that survived were the ones that figured out the scaffolding was the problem.

The model wasn’t ever the bottleneck. Trust calibration was.

What CodeMender quietly admits

The architecture description is a structural concession. Auto-patching makes the false-positive problem worse, not better — a wrong patch doesn’t just waste analyst time, it changes the codebase, breaks the build, sometimes introduces a new bug downstream. If “find a bug” is hard to ship with high enough precision, “find and fix a bug” is harder still.

You cannot solve this by training a better generator. Train the model to be more conservative and recall collapses. Train it to be more aggressive and precision collapses. The recall-precision frontier of a single model is fundamentally bounded by its prompt and its training distribution.

What you can do is split the job. One model proposes. Another model disposes. The proposer is recall-biased: it throws a wide net, surfaces everything that smells like a vulnerability, optimizes for not-missing. The critique agent is precision-biased: its job is to take each finding and try to kill it. Is this exploit actually reachable? Is there a guard upstream the proposer missed? Is this a known false-positive pattern? Does the proposed patch break existing tests?

The critique agent is the architecture. Without it, the proposer’s output is unshippable. With it, the system can claim what CodeMender does claim: that it surfaces validated patches for human review rather than a firehose of speculative ones.

That decoupling — the model that finds the bug is not the model that decides the bug is real — is the entire ballgame.

Why a two-model loop composes better than one smart model

Proposer-skeptic decomposition isn’t new. Math reasoning uses proposer-verifier loops. Multi-agent debate frames the same idea. RLHF with adversarial critics is the training-time version. Ensemble methods in classical ML have known forever that diverse weak learners beat a strong monolith on tasks with asymmetric error costs.

What’s security-specific is that the asymmetry isn’t a nice-to-have — it’s the binding constraint that makes monolithic models structurally unshippable. When error costs are asymmetric, you don’t want a single model trading off precision against recall on one axis. You want two models trading off against each other on different axes.

Four concrete payoffs from the decomposition:

  1. Different models for different roles. The discovery model can be your biggest, most expensive reasoning model, because it runs once per file. The critique model can be smaller, faster, cheaper, running many times per finding. Cost-latency becomes a tunable knob, not a constraint baked into the architecture.

  2. Independent post-training. Discover a new false-positive pattern the system keeps surfacing? You don’t retrain the giant discovery model. You add an adversarial example to the critique distribution and ship.

  3. Hot-swappable foundations. As foundation models improve, you swap them in without rebuilding the loop.

  4. Auditable disagreements. When the proposer says “vuln” and the critique says “no,” you have a structured artifact — a transcript of an argument — a human can inspect. This is the explainability story monolithic models can’t tell.

The pattern, in code

The naive single-pass setup that has powered two years of demos:

findings = discovery_model.scan(code)
return findings  # FP-saturated, analyst trust gone in two findings

The decomposed loop:

candidates = discovery_model.scan(
    code,
    prompt=DISCOVERY_PROMPT,  # recall-biased: "find anything that smells wrong"
)

validated = []
for finding in candidates:
    if finding.confidence < DISCOVERY_THRESHOLD:
        continue
    verdict = critique_model.adjudicate(
        finding,
        prompt=CRITIQUE_PROMPT,  # adversarial: "try to kill this finding"
        context=finding.surrounding_code(),
    )
    if verdict.kept and verdict.confidence > PRECISION_THRESHOLD:
        validated.append(finding.with_verdict(verdict))

patches = [patch_model.propose(f) for f in validated]
return [p for p in patches if regression_tests_pass(p)]

CodeMender — running on Google’s most capable reasoning models — still needs the critique stage. The structural decomposition does more for shippability than another order of magnitude in foundation-model parameters.

Where the convergence is

This pattern is showing up everywhere a serious security tool ships. Purdue’s LLMSCAN separates concerns explicitly: syntactic facts come from Tree-sitter parsing (verifiable, deterministic); the LLM handles only the semantic facts parsing can’t reach. The insight isn’t quite “skeptic-after-proposer” — it’s “ground the proposer in structure the LLM can’t hallucinate.” Same recall-precision frontier, attacked one tier earlier.

The RAPTOR framework goes the other direction. Candidate findings from classical pattern-matching (Semgrep, CodeQL) flow through progressive validation gates that include reachability verification via Z3 SMT solving and an exploitation-feasibility check before anything surfaces. A four-stage skeptic stack behind a recall-biased finder.

CodeMender is the most prominent expression of a pattern that has been forming quietly for over a year. The convergence is the signal.

The actual moat

Here is where the I/O 2026 announcement gets genuinely interesting, and where most takes on it will miss the point.

The architecture itself is not a moat. Anyone can stack two LLMs. The recipe is now public — Google just published it.

The moat is the critique model’s training distribution.

Think about what makes a critique agent useful in production. It is not the cleverness of the prompt. It is the catalog of adjudicated false-positive patterns the model has been exposed to — the corpus of “the proposer flagged this, a human checked it, the human said: not a vuln, here’s why” examples. That corpus is built by burning analyst trust the first time and never the second. It compounds slowly. It cannot be scraped. It is utterly specific to the languages, frameworks, and idioms of the customer base that produced it.

CodeMender, in its first six months, had upstreamed 72 security fixes to large open-source codebases, including projects of over four million lines. The output is impressive on its own. What matters more for Google’s strategic position is what those 72 fixes built: a labeled corpus of “proposed patch, critique verdict, human sign-off” tuples that nobody else has and nobody else can quickly acquire. Every accepted fix narrows the gap between Google’s critique distribution and the actual statistical landscape of real vulnerabilities in deployed code. Every rejected patch sharpens the critique’s nose for the specific shape of plausible-but-wrong fixes.

CodeMender’s moat is the critique agent’s experience, not its capability. The architecture is the cost of entry. The corpus is the durable asset.

Auto-remediation as forcing function

Here is the counterintuitive corollary. Most observers will read CodeMender as a find-and-patch tool that prioritizes auto-fix because Google wants the dramatic demo. The deeper reason is structural: auto-fix is the only forcing function that exposes whether a finding was real.

Think about the feedback signal. A patch that doesn’t compile is a confirmed false positive. A patch that compiles but breaks behavioral tests is a confirmed false positive. A patch that compiles, passes tests, but gets rejected by a maintainer with “this isn’t a security issue” is a confirmed false positive — and the maintainer’s reasoning becomes training data for the next iteration of the critique agent.

Compare this to a detection-only tool that surfaces findings to humans and asks them to triage. The feedback is delayed, ambiguous, often never logged — a JIRA ticket marked “won’t fix” doesn’t tell the model why. The find-then-fix architecture closes the loop with a far higher-fidelity signal.

Auto-remediation isn’t a UX feature riding on top of detection. It is the validation mechanism that makes detection improve over time. Tools that lean into the find-and-fix loop will out-improve tools that try to perfect detection in isolation, because they will be ingesting orders of magnitude more high-quality false-positive feedback per unit of analyst time.

What this means for the rest of the stack

A few extrapolations:

  • The unit of value in AI security is the loop, not the model. Disciplined validation with a mediocre foundation model will out-ship a state-of-the-art foundation model wrapped in a naive single-pass setup. Vendors who think the model is the product are about to get out-shipped by vendors who think the loop is the product.

  • Critique-model training data is the new ImageNet. The next round of M&A in security tooling will not be about who has the best static analyzer. It will be about who has the deepest labeled corpus of validated-and-rejected findings. The companies worth buying are the ones whose loops have been running longest with real analysts in them.

  • Open-sourcing the loop won’t kill the moat. Google could publish CodeMender’s full architecture tomorrow. The architecture isn’t the asset. The accumulated critique distribution — the millions of micro-judgments embedded after a year-plus of running against real OSS — does not transfer with the source code.

The real headline

The story buried in CodeMender’s I/O 2026 footnote is that Google — the company with the resources to build any single model it wants — has decided the path forward in code security runs through architecture and through accumulated experience, not through model capability alone. They did not announce a bigger reasoning model with better vuln-detection benchmarks. They announced a loop that has been running for over six months, learning from real upstream contributions, and getting more useful with every accepted patch.

The model is not the product. The critique agent is not the product. The corpus the critique agent has been trained on is the product. Everything else is infrastructure around that compounding asset.

Notes and sources: