◉GEMINI LABJP
●TTS — The three TTS preview models shut down on 12/18 per the deprecations page, not 11/17●CLI 0.68 — Gemini CLI stable is v0.68.0, with v0.69.0-preview.0 (11/10) on the preview channel●GEMINI 2.5 — gemini-2.5-pro and 2.5-flash shut down on the API on 12/1, 16 days left. Move to 3.8 Flash or 3.5 Flash-Lite●NODE 26 — On Node 26, Gemini CLI's timeout settings reportedly stop applying to model requests●OMNI 1.1 — From 12/3, requests to gemini-omni-1.1-flash are routed to 1.2 Flash automatically●NEW — Getting Consistent JSON Through the OpenAI-Compatible Layer, in Three Steps●TTS — The three TTS preview models shut down on 12/18 per the deprecations page, not 11/17●CLI 0.68 — Gemini CLI stable is v0.68.0, with v0.69.0-preview.0 (11/10) on the preview channel●GEMINI 2.5 — gemini-2.5-pro and 2.5-flash shut down on the API on 12/1, 16 days left. Move to 3.8 Flash or 3.5 Flash-Lite●NODE 26 — On Node 26, Gemini CLI's timeout settings reportedly stop applying to model requests●OMNI 1.1 — From 12/3, requests to gemini-omni-1.1-flash are routed to 1.2 Flash automatically●NEW — Getting Consistent JSON Through the OpenAI-Compatible Layer, in Three Steps
Articles/Advanced
⬡ Advanced/2026-04-22Advanced

Gemini × DSPy: Retire from Prompt Craftsmanship — Automated Prompt Optimization

How to move from hand-written prompts to automated optimization with Gemini and DSPy: Signature, Module and Optimizer design, a label audit before you compile, and a script that measures what one compile run actually costs.

gemini121dspyprompt-engineering16optimization4llmpython113

✦ Premium Article

For three weekend nights in a row, I hand-tuned a prompt that sorted app-review replies into buckets. Every edit helped, until the following week a different kind of message showed up and the earlier tuning went to waste. I was tired in a way that more tuning was never going to fix.

That's when I tried DSPy. I was skeptical at first — another framework. But I put the prompt I'd polished for three days next to the one MIPROv2 assembled in about twenty minutes, ran both against the same validation set, and the automated one won. It had found a combination of few-shot examples I would never have picked by hand.

What I took from that wasn't "my instincts no longer apply." It was a different line to draw: a prompt is something you measure and grow, not something you write — and the writing moves to your metric and your labels.

This article builds that line out with Gemini and DSPy, from install to production. If you know basic Python and have called the Gemini API once or twice, you should be able to follow along with the code.

First, a caveat: DSPy isn't magic. Preparing labeled data and designing a metric remain your work, and the care you put into those two steps largely decides the result. Later sections cover exactly where I stumbled on both, with scripts.

One practical note. The code reads model names from environment variables, because Gemini models turn over quickly — the September 2026 changelog limited the 2.5 line to existing users, for example. Check the official docs for the model IDs you can use today and put them in .env before running anything.

Where hand-written prompting breaks down

I keep a handful of small prompts around: sorting store reviews, summarizing support messages, checking translated strings. I used to edit each one line by line and keep whichever variant won an A/B test.

That approach tends to fall apart when two or more of these happen together:

  • The input changes (a new kind of inquiry appears)
  • You switch models (Flash to Pro, or across generations)
  • You add evaluation axes (accuracy now, plus length and tone)

Any one of them makes earlier tuning obsolete, and each change means re-running the A/B test. DSPy swaps that effort for "have a metric and data, and let the machine loop."

DSPy (Declarative Self-improving Language Programs) comes from Stanford NLP. It treats an LLM call like a function, and treats the prompt as something optimized from data — much like a PyTorch nn.Module, but for language programs.

The framing that finally clicked for me: the prompt is the high-level language and the Optimizer is the compiler. Hand-editing compiler output is possible, but it isn't a good use of your evenings.

Build the first pipeline

Start with the smallest thing that runs: install, then connect to Gemini.

# The package is now simply "dspy" (the older dspy-ai name still exists)
pip install -U dspy python-dotenv

Put your key and model IDs in .env:

# .env — replace with your own values
GEMINI_API_KEY=YOUR_GEMINI_API_KEY
# A Flash-class model ID you confirmed in the official docs (LiteLLM format: gemini/...)
GEMINI_FAST_MODEL=gemini/YOUR_FLASH_MODEL_ID
# A stronger model ID, used for judging and production inference
GEMINI_STRONG_MODEL=gemini/YOUR_PRO_MODEL_ID
# setup_dspy.py — smallest check that DSPy can reach Gemini
import os
import dspy
from dotenv import load_dotenv
 
load_dotenv()
 
lm = dspy.LM(
    model=os.environ["GEMINI_FAST_MODEL"],
    api_key=os.environ["GEMINI_API_KEY"],
    temperature=0.0,
    max_tokens=1024,
)
dspy.configure(lm=lm)
 
class Translate(dspy.Signature):
    """Translate Japanese text into natural English."""
    japanese = dspy.InputField(desc="Japanese text to translate")
    english = dspy.OutputField(desc="English translation")
 
predictor = dspy.Predict(Translate)
result = predictor(japanese="プロンプトを手書きする時代は終わった。")
 
print(result.english)
# e.g. "The era of writing prompts by hand is over." (wording varies between runs)

If that runs, you're connected. Notice there's no prompt string anywhere: DSPy builds the request from the Signature's docstring and fields. To see what was actually sent, call dspy.inspect_history(n=1) — you'll use it constantly while debugging.

The cache hides the variance you're trying to measure

Here's one I tripped on. Even with temperature=0.0, Gemini doesn't promise identical output on every run — when several tokens are nearly tied, the pick isn't guaranteed to be deterministic. You can vote across a few calls, or set seed in the generation config, but don't assume a seed gives you byte-identical output.

The easier thing to miss is that DSPy's LM client caches responses by default. Call twice with the same input and the second answer comes from the cache, not the API. If you want to measure variance, create the client with dspy.LM(..., cache=False); otherwise "it returns the same thing every time" can be a cache talking.

Keep both facts in mind when you write snapshot tests for prompts in CI, and you'll avoid failures that look inexplicable.

✦

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
✦If your hand-tuned prompts have plateaued, you'll see how to switch to a measure-and-grow workflow built on DSPy's Optimizers
✦You'll be able to write a production-minded Gemini pipeline from the three-layer design (Signature, Module, Optimizer), and choose between BootstrapFewShot and MIPROv2 with a clear rule
✦You get two scripts you can run today: a label audit that catches bad labels before optimization, and a probe that counts the API calls and tokens of a single compile run
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

⬡ Advanced2026-04-27
Self-Healing Architecture for Gemini Computer Use — Production Patterns That Keep Browser Automation Alive Beyond Day Three
Gemini Computer Use looks magical in demos but breaks daily in production: vanishing elements, surprise modals, network jitter, off-by-four-pixel clicks. This guide builds a five-layer self-healing architecture in Python that classifies failures and recovers them automatically, with working code you can drop into your agent loop today.
⬡ Advanced2026-10-23
A 23-Second Video Fails With a 500: What to Question Before You Wait. Three Forks to Decide Ahead for Gemini's Agentic Video Understanding
A forum report says Gemini API's agentic video understanding fails with a 'high demand' 500. Here is how I would decide between waiting, splitting and dropping to static mode before it happens, checked against the official guidance, with a small Python helper.
⬡ Advanced2026-10-14
Fine on Desktop, Blank on Safari: Four Things to Gather Before You Ask an AI to Diagnose It
After a shuffle, my lazy-loaded images never loaded again, but only in Safari on iPhone. Here is what to collect by hand before asking Gemini or any other model to diagnose a bug that does not reproduce on desktop, plus a request template you can paste.