GEMINI LABJP
PRO35 — Gemini 3.5 Pro is reported to target July 17, though the date, context window, and pricing all remain unconfirmed by GoogleREBUILD — The model is said to be a ground-up rebuild after the 2.5 Pro architecture was scrapped over structural failures in recursive tool-calling and SVG generationFLASH35 — Gemini 3.5 Flash is the newest broadly available release, tuned for faster, lower-cost coding, agents, multimodal, and long-document workSAATHI — ATL Saathi, built on Gemini 3.5 Flash, begins piloting in 100 Indian schools as a 24/7 planning and training assistant for Tinkering Lab educatorsSEA — In Southeast Asia, nearly 70% of prompts arrive in native languages — Vietnam 89%, Thailand 87%, Indonesia 84% — and 3 in 4 requests come from mobileSHEETS — From July 15, AI Expanded Access licenses raise the usage limits for Fill with Gemini and the AI function in SheetsPRO35 — Gemini 3.5 Pro is reported to target July 17, though the date, context window, and pricing all remain unconfirmed by GoogleREBUILD — The model is said to be a ground-up rebuild after the 2.5 Pro architecture was scrapped over structural failures in recursive tool-calling and SVG generationFLASH35 — Gemini 3.5 Flash is the newest broadly available release, tuned for faster, lower-cost coding, agents, multimodal, and long-document workSAATHI — ATL Saathi, built on Gemini 3.5 Flash, begins piloting in 100 Indian schools as a 24/7 planning and training assistant for Tinkering Lab educatorsSEA — In Southeast Asia, nearly 70% of prompts arrive in native languages — Vietnam 89%, Thailand 87%, Indonesia 84% — and 3 in 4 requests come from mobileSHEETS — From July 15, AI Expanded Access licenses raise the usage limits for Fill with Gemini and the AI function in Sheets
Articles/API / SDK
API / SDK/2026-07-16Advanced

I Asked Gemini to Grade My App Store Screenshots. Everything Scored 78–85.

Ask Gemini Vision to grade App Store screenshots out of 100 and good candidates and deliberately broken ones both land at 78–85. Here is how I measured the judge's discrimination power, dropped absolute scoring, and rebuilt it as debiased pairwise comparison.

Gemini API187Vision4ASO2App Store6LLM-as-judgePairwise ComparisonPython38Indie Dev7

Premium Article

Before replacing all five of my App Store screenshots, I asked Gemini to grade the new set. It came back with 82. I fed it the old set as a control: 79.

Three points apart. That told me nothing, so I built a deliberately broken version — every headline erased, contrast crushed, panels shuffled — and fed that in too.

If a set with zero readable copy earns a 76, then 82 means nothing either. That was the day I stopped asking Vision for a score.

What follows is how I proved the grader was unusable at my decision granularity, and what I replaced it with. As an indie developer who has rebuilt a wallpaper app's store listing more times than I'd like to admit, this was the single change that made the tooling worth running.

The minimum setup for doubting your own grader

pip install google-genai pillow --break-system-packages
import os
import json
import statistics
from pathlib import Path
from itertools import combinations
 
from google import genai
from google.genai import types
 
client = genai.Client(api_key=os.environ["GOOGLE_API_KEY"])
 
# Verification burns a lot of calls, so Flash. Only the final head-to-head goes to Pro.
JUDGE_MODEL = "gemini-flash-latest"
 
 
def load_image(path: str) -> types.Part:
    """Load a local image into a Part, inferring MIME from the extension."""
    suffix = Path(path).suffix.lower()
    mime = {
        ".jpg": "image/jpeg",
        ".jpeg": "image/jpeg",
        ".png": "image/png",
        ".webp": "image/webp",
    }.get(suffix, "image/jpeg")
 
    with open(path, "rb") as f:
        return types.Part.from_bytes(data=f.read(), mime_type=mime)

One caveat on that model string. gemini-flash-latest is an alias, and as of July 2026 it resolves to Gemini 3.5 Flash. If you run scheduled jobs against an alias, the thing doing your grading can change underneath you without a single line of your code moving. Pin down what the alias currently points at before you trust any measurement you take today.

The grader itself is conventional. The thing under suspicion is not the function — it's the numbers coming out of it.

def score_absolute(image_paths: list[str], app_name: str, market: str = "United States") -> dict:
    """Grade a screenshot set from 0-100 (the conventional absolute approach)."""
    parts = [
        types.Part.from_text(
            f"""You are an App Store optimization expert.
Evaluate the following screenshot set.
 
App: {app_name}
Target market: {market}
 
Return exactly this JSON:
{{
  "overall_score": <integer 0-100>,
  "message_clarity": <0-100>,
  "visual_hierarchy": <0-100>,
  "reason": "one sentence justifying the score"
}}"""
        )
    ]
    for i, p in enumerate(image_paths):
        parts.append(types.Part.from_text(f"Screenshot {i + 1}:"))
        parts.append(load_image(p))
 
    resp = client.models.generate_content(
        model=JUDGE_MODEL,
        contents=parts,
        config=types.GenerateContentConfig(
            response_mime_type="application/json",
            temperature=0.2,
        ),
    )
    return json.loads(resp.text)

Measure the noise first: ten runs, one image

You cannot judge a grader from a single output. Send the same input repeatedly and find out how much the answer moves on its own.

def measure_noise(image_paths: list[str], app_name: str, n: int = 10) -> dict:
    """Grade identical input n times and measure the spread."""
    scores = []
    for i in range(n):
        result = score_absolute(image_paths, app_name)
        scores.append(result["overall_score"])
        print(f"  run {i + 1}: {result['overall_score']}")
 
    return {
        "scores": scores,
        "mean": statistics.mean(scores),
        "stdev": statistics.stdev(scores) if len(scores) > 1 else 0.0,
        "range": max(scores) - min(scores),
    }

At temperature=0.2 I expected near-identical numbers. On my set — five portrait screenshots, wallpaper category — here is what came back.

MetricMeasured
Mean81.4
Standard deviation2.3
Min–max78–85 (7-point spread)

Seven points of movement on an unchanged image. Which means the 3-point gap between my new set and my old set was sitting entirely inside the noise floor.

Dropping to temperature=0 only narrowed the spread to 3 points. Multimodal inputs carry variance in tiling and tokenization that temperature does not reach. This is not a problem you turn down the temperature to fix, so I stopped trying.

Thank you for reading this far.

Continue Reading

What follows includes implementation code, benchmarks, and practical content we hope you'll find useful. This site runs without ads — server and development costs are supported entirely by members like you. If it's been helpful, we'd be truly grateful for your support.

WHAT YOU'LL LEARN
A repeatable way to decide whether an LLM judge is usable at all: measure run-to-run noise, then divide your signal gap by it
Ablation code that deliberately degrades your own screenshots to test what the grader can and cannot detect
Full pairwise comparison implementation with order-swap debiasing, plus win-rate ranking and a token cost breakdown
Secure payment via Stripe · Cancel anytime

Unlock This Article

Get full access to the rest of this article. Buy once, read anytime. This site is ad-free — your support goes directly toward keeping it running.

or
Unlock all articles with Membership →
Share

Thank You for Reading

Gemini Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • Copy-paste ready implementation code
  • New advanced guides published daily
  • $5/mo or $10 for lifetime access
View Membership →

Related Articles

API / SDK2026-04-04
Automating App Store Reviews with Gemini API and App Store Connect API: Implementation Notes from Running 50M-Download Apps
Implementation notes for combining Gemini API and App Store Connect API to handle review sentiment analysis, reply drafting, competitor monitoring, and weekly ASO reports in Python. Includes lessons learned from running indie apps with over 50 million cumulative downloads.
API / SDK2026-07-09
Google Sheets API × Gemini API: A Python Data Pipeline — No Apps Script Required
Learn how to build a fully Python-based pipeline that reads data from Google Sheets, processes it with Gemini API, and writes results back — without touching Apps Script. Covers service account auth, structured output, and rate limit handling.
API / SDK2026-06-29
Guarding Gemini API Responses in CI: Snapshot and Semantic Regression Testing
How to defend non-deterministic Gemini API responses with pytest snapshot tests plus embedding-based semantic regression detection — including CI wiring, separating flakiness from real regressions, and snapshot-update governance, all in working code.
📚RECOMMENDED BOOKS
Build a Large Language Model (From Scratch)
Sebastian Raschka
LLM Dev
Prompt Engineering for LLMs
Berryman & Ziegler
Prompting
AI Engineering
Chip Huyen
AI Eng
* Contains affiliate links
See all →