Files
gemma4-research/GOTCHAS.md
T
Mortdecai 438a96f235 docs: May 2026 tool-calling update + agentic retrieval landscape
Two new corpus docs plus targeted GOTCHAS corrections based on
Ollama/llama.cpp/vLLM issue tracker review, late-2025/early-2026
research papers, and convergent empirical evidence from two production
Gemma 4 agents.

New:
- CORPUS_tool_calling_2026-05.md: current state of tool calling.
  Properly characterizes the think flag (parser-side bug, not
  model-side; recipe by loop shape), surfaces ollama/ollama#15539,
  documents the "doesn't fire tools without explicit ask" research
  (Probe & Prefill, BiasBusters, When2Call), XGrammar-2.
- CORPUS_agentic_retrieval.md: post-RAG landscape. Hybrid retrieval
  + cross-encoder rerank as the settled base, CRAG-shaped flows,
  LightRAG/LazyGraphRAG, mem0 vs Letta MemFS, deep-research agents.
  Production-vs-experimental sorting for self-hosted small-model use.

Updates:
- GOTCHAS.md: the think:false rule, Vulkan unused-token loop, and
  Ollama 0.20 streaming bug all marked SUPERSEDED/FIXED/PARTIALLY
  FIXED in-place with pointers to the May update.
- README.md: indexed the two new docs.
- .gitignore: added private/ for bot-specific notes.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-25 09:51:09 -04:00

347 lines
16 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Gemma 4 Gotchas & Known Issues
> Derived from Seth's production implementations (Simon, AI_Visualizer)
> and community reports. These are hard-won lessons.
>
> **2026-05-25 corrections:** several entries below were corrected by
> field evidence from two independent agents and by Ollama/llama.cpp
> issue-tracker review. See `CORPUS_tool_calling_2026-05.md` for the
> consolidated current state. The "`think: false` kills 26B" rule and
> the `<unused>` Vulkan-loop entry both need re-reading against that
> update — they are marked **SUPERSEDED** in place below.
## CRITICAL: Thinking Mode Eats Context (single-turn pipelines only)
**Severity: HIGH — causes silent failures in single-turn `/api/generate` workloads**
> **Scope update (2026-04-18):** This guidance applies to **single-turn JSON
> generation pipelines** (the AI_Visualizer shape: one call → one structured
> response). For **multi-turn tool-calling agents**, the opposite is true on
> `gemma4:26b` — see § "`think: false` Kills Gemma 4 26B in Multi-Turn
> Tool-Calling Loops" above. Don't copy this fix to an agent harness without
> testing.
Gemma 4 in Ollama 0.20+ defaults to `think: true`. When enabled in a single-turn
JSON pipeline:
- Thinking tokens go into a hidden `thinking` field, NOT `response`
- If `num_predict` is limited, thinking consumes the entire budget
- `response` comes back **empty** — no error, just silence
- On evaluative tasks, thinking inflates scores (31B scored a known-bad image 9/10 with thinking vs 7/10 without)
**Fix (for single-turn pipelines):** Always pass `think: false` in the Ollama payload.
```json
{
"model": "gemma4:26b",
"think": false,
"options": { "num_predict": 4096 }
}
```
**Do not blindly carry this to multi-turn tool-calling agents** — verified
2026-04-18 that it silent-stops 26B specifically in that context.
## CRITICAL: format=json Causes Infinite Loops
**Severity: HIGH — hangs indefinitely**
Ollama's server-side `format: "json"` enforcer causes Gemma 4 26B (Q4) to enter an infinite retry loop when the requested schema is deeply nested.
**Fix:** Never use `format: "json"`. Instead:
1. Request JSON structure in the prompt text
2. Parse client-side with regex + `json.loads` + json5 fallback
```python
# DO THIS
response = client.generate(model="gemma4:26b", prompt=prompt, format_json=False)
body = response["response"]
obj = json.loads(body[body.find("{"):body.rfind("}") + 1])
# NOT THIS
response = client.generate(model="gemma4:26b", prompt=prompt, format="json") # HANGS
```
## CRITICAL: Ollama Default Context is 2048
**Severity: HIGH — causes truncation**
Ollama defaults `num_ctx` to 2048 tokens. Gemma 4 supports 128K. If you don't override, your prompts get silently truncated.
**Fix:** Always set `num_ctx` explicitly:
```json
{ "options": { "num_ctx": 8192 } }
```
Scale to your needs: 4096 for simple tasks, 16384 for long inputs, 32768 for complex multi-turn.
## HIGH: num_predict Default is 128
**Severity: HIGH — truncates output**
Ollama defaults `num_predict` to 128 tokens. Almost any useful Gemma 4 output exceeds this.
**Fix:** Always set `num_predict` explicitly. Minimum recommended: 512. For JSON output: 2048+.
## HIGH: `think: false` Kills Gemma 4 26B in Multi-Turn Tool-Calling Loops [SUPERSEDED 2026-05-25]
**Severity: HIGH — silent agent-loop failure. Setting is what the old guidance said to do.**
> **SUPERSEDED 2026-05-25:** the symptom is real but the diagnosis was
> wrong. Per [ollama/ollama#15539](https://github.com/ollama/ollama/issues/15539),
> the failure is an **Ollama parser bug** triggered by the combination
> `system prompt + think:false + tools` — the model emits a valid tool
> call on the wire, the framework returns `tool_calls=[]` with an empty
> `content` and a stray `<channel|>` token. **The bug affects `gemma4:e4b`
> too, not just 26B MoE.** Field evidence from a production agent at
> `num_predict=1024` confirms `think: false` works fine for short tool
> arguments and shallow loops — the original bakeoff hit the deep-loop
> case where the parser fails.
>
> **Replacement rule:** see `CORPUS_tool_calling_2026-05.md` § 4 for the
> proper recipe by loop shape. Safe default for all cases:
> `think: True` + `num_predict ≥ 2048`. Diagnostic for whether you're
> hitting this bug: run a four-condition probe (system × think) at the
> same model state — if only `system + think:False` zeros tool_calls,
> you've reproduced #15539.
>
> The April-2026 content below is preserved for the historical record but
> should not be applied without first reading the May update.
Reproduced on 2026-04-18 against `gemma4:26b` via Ollama 0.20.4 on a 3090 Ti
(steel141). Contradicts the older "always think:false" guidance (see § "Thinking
Mode Eats Context" below — now scoped to single-turn pipelines only).
### The observation
At identical message state with all else equal:
| `think` setting | `eval_count` on decision turn | Agent behavior |
|---|---|---|
| `false` | **4** (silent stop, no content, no tool_calls) | Fails — zero edits emitted |
| unset (Ollama default) | 165 | Passes — emits correct edit |
| `true` | 165 | Passes — emits correct edit |
26B passes the task in 8 iterations / 12-20s on the same harness the moment
the `think` key is removed from the Ollama payload. `write_file` vs
`apply_patch` doesn't matter. Tool-response size doesn't matter.
### What I initially got wrong
The 2026-04-18 bakeoff went through two wrong hypotheses before Seth asked
"was this with think=false?" The failed-and-corrected path:
1. **"Long `write_file` argument breaks 26B"** — wrong. `apply_patch` also failed.
2. **"Large tool-response context breaks 26B"** — wrong. Truncation *did* make
26B pass (800/1200-char caps), but that's because shorter context dodged
the `think: false` side effect by coincidence of state at the decision turn.
3. **Actual cause:** `think: false` alters the decoding path in a way that makes
the 26B MoE (3.8B active params, 8-of-128 expert routing) emit near-immediate
EOS at tool-decision turns. 31B Dense and Qwen3-Coder are robust to the
flag; 26B specifically is not.
See `docs/reference/bakeoff-2026-04-18.md` § "Round 3" for full traces and the
diagnostic that isolated the flag.
### Fix
- **For multi-turn tool-calling agents, do NOT set `think: false`.** Leave it
unset (Ollama default) or `true`.
- **If your agent accumulates `thinking` field content**, prune old thinking
blobs from message history to control context growth.
- **For single-turn JSON pipelines** (the AI_Visualizer shape), the original
"always think:false" guidance still applies — see § "Thinking Mode Eats
Context" below.
- 31B Dense and Qwen3-Coder work fine either way — this gotcha is 26B-specific
on this Ollama version.
## MEDIUM: Weak at Long/Nested JSON
**Severity: MEDIUM — causes parse failures**
Gemma 4 reliably produces short JSON (5-10 fields) but struggles with:
- Deeply nested schemas (3+ levels)
- Long arrays (20+ items)
- Mixed nesting + length
**Fix:** Sequential tool calls. Break one large JSON request into multiple smaller calls:
- Instead of "generate a 50-item storyboard", do "generate items 1-5", "generate items 6-10", etc.
- Due to Gemma 4's fast speed and free local use, sequential calls are cheap
**Fallback pattern (AI_Visualizer):**
```python
for attempt in range(MAX_RETRIES):
temp = BASE_TEMP + attempt * TEMP_BUMP # 0.4 -> 0.5 -> 0.6
response = call_gemma(temperature=temp)
try:
return parse_json(response)
except JSONDecodeError:
continue
```
## MEDIUM: Identity Confusion
**Severity: MEDIUM — cosmetic but confusing**
Gemma 4 is ultra-compliant and highly capable but does not know who it is. It may:
- Claim to be a different model
- Hallucinate capabilities it doesn't have
- Respond as a generic "AI assistant" without personality
**Fix:** Explicit identity in system prompt:
```
You are [Name], a [role]. You are powered by Gemma 4.
You ONLY do [X]. You NEVER do [Y].
```
Gemma 4 does NOT need hand-holding on task execution — it's very capable.
It needs explicit instructions about identity and boundaries.
## MEDIUM: Flash Attention Hang on 31B Dense (>3-4K tokens)
**Severity: MEDIUM — hardware-specific, affects RTX 3090**
Community-reported: Flash Attention causes Gemma 4 31B Dense to hang indefinitely during prompt evaluation when the prompt exceeds ~3-4K tokens. The 26B MoE variant handles the same prompts fine — bug is specific to the Dense model.
**Source:** [ollama/ollama#15350](https://github.com/ollama/ollama/issues/15350)
**Fix:** Use 26B for long prompts, or disable Flash Attention if running 31B on affected hardware.
## MEDIUM: Tool Calling Broken in Ollama v0.20.0 Streaming [PARTIALLY FIXED]
**Severity: MEDIUM — version-specific. PARTIALLY FIXED in v0.20.6 + v0.22.1.**
As of early April 2026, Gemma 4 tool calling has issues in Ollama v0.20.0: the tool call parser fails and streaming drops tool calls entirely. Community reports include format mismatches and continuous loops in llama.cpp / LM Studio.
**Source:** [community reports](https://dev.to/dentity007/-gemma-4-after-24-hours-what-the-community-found-vs-what-google-promised-3a2f)
**Fix:** Use non-streaming for tool calls (Simon does this). Test tool calling thoroughly when upgrading Ollama versions. Seth's implementations work reliably with non-streaming tool calls.
> **2026-05-25 update:** Ollama [v0.20.6](https://github.com/ollama/ollama/releases/tag/v0.20.6)
> explicitly improved Gemma 4 tool-calling and added "improved parallel
> tool calling for streaming responses." [v0.22.1](https://github.com/ollama/ollama/releases/tag/v0.22.1)
> updated the Gemma 4 renderer for thinking + tool-calling improvements.
> Current stable is **v0.24.0** (2026-05-14). Re-test streaming on current
> versions before assuming the original bug persists. **Open bugs**
> remain — see `CORPUS_tool_calling_2026-05.md` § 1 for the current list,
> especially [#15539](https://github.com/ollama/ollama/issues/15539)
> (`system + think:false + tools` returns empty tool_calls) which is the
> most likely failure mode you'll hit on a recent Ollama.
## MEDIUM: VRAM-Hungry for Context
**Severity: MEDIUM — affects hardware planning**
Gemma 4 KV cache is large relative to competitors. Community reports: 31B at 262K context requires ~22GB just for KV cache on top of model weights. One user could only fit Gemma 3 27B Q4 with 20K context on a 5090, while Qwen 3.5 27B Q4 fit with 190K context on the same card.
**Implication:** Don't set num_ctx higher than you need. 32K is plenty for most tasks and keeps VRAM reasonable.
## MEDIUM: Safety Overfiltering
**Severity: MEDIUM — blocks benign prompts**
Strict safety alignment occasionally blocks technical, academic, or creative prompts that superficially resemble restricted categories. One user reported jailbreaks with basic system prompts.
**Fix:** Rephrase blocked prompts to avoid trigger patterns. For system prompts, avoid language that sounds like you're asking the model to bypass restrictions — just state the task directly.
## MEDIUM: KV Cache Config Bug (31B/26B ship with num_kv_shared_layers=0)
**Severity: MEDIUM — crashes on first attention forward pass**
The 31B and 26B ship with `num_kv_shared_layers = 0`, which causes `layer_types[:-0]` to collapse to zero layer slots. Crashes on first forward pass.
**Fix:** Patch the config. Check model card discussions for the exact fix.
## LOW: vLLM Triton Fallback (~9 tok/s on RTX 4090)
**Severity: LOW — vLLM-specific**
Heterogeneous attention head dimensions in Gemma 4 force vLLM to fall back to a slow Triton kernel. RTX 4090 gets ~9 tok/s instead of expected ~100+.
**Source:** [vllm-project/vllm#38887](https://github.com/vllm-project/vllm/issues/38887)
**Fix:** Use Ollama instead of vLLM for now, or wait for the fix.
## LOW: `<unused>` Token Infinite Loop (Vulkan backends) [FIXED]
**Severity: LOW — Vulkan-specific. FIXED in llama.cpp b8691+.**
Gemma 4 can generate `<unused>` or `<unused24>` tokens in an infinite loop on Vulkan backends in llama.cpp.
**Source:** [ggml-org/llama.cpp#21516](https://github.com/ggml-org/llama.cpp/issues/21516)
> **2026-05-25:** Per the
> [unsloth/gemma-4-26B-A4B-it-GGUF discussion thread](https://huggingface.co/unsloth/gemma-4-26B-A4B-it-GGUF/discussions/2),
> the `<unused49>` / channel-token flood across CUDA / ROCm / Vulkan / SYCL
> was a llama.cpp eval bug, **not** a quantization problem. Fixed in
> b8691+. Old quants work fine on patched builds.
>
> Caveat: 31B + f16/f32 vision projector still produces `<unused49>`
> floods on current builds per one report — the eval-bug fix didn't
> cover the multimodal projector path.
## MEDIUM: `google/gemma_pytorch` Abandoned for Gemma 4
**Severity: MEDIUM — wastes time on a dead-end path**
The `google/gemma_pytorch` repo (last push 2025-05-30) has zero Gemma 4 support —
its variants validator only accepts Gemma 1/2/3 IDs. Anyone pointing at it as "the
official PyTorch reference" for Gemma 4 is wrong.
**Use instead:**
- **Inference:** `huggingface/transformers` (`AutoModelForMultimodalLM`, v5.5.4+)
- **Reference impl:** `google-deepmind/gemma` (JAX/Flax)
- **Serving:** Ollama / vLLM / llama.cpp
See `tooling/google-official/gemma-pytorch/README.md` for the original repo state.
## LOW: Fine-Tuning Ecosystem Issues
**Severity: LOW — only relevant if fine-tuning**
Day-one issues for fine-tuners:
- HuggingFace Transformers didn't recognize gemma4 architecture (required install from source)
- PEFT couldn't handle Gemma4ClippableLinear (new vision encoder layer type)
- New `mm_token_type_ids` field required during training even for text-only data
- E2B/E4B show training loss of 13-15, which is normal for multimodal models (not a bug)
- **Flash Attention 2/4 incompatible:** Gemma 4's global-attention head_dim is 512;
FA2 max is 256, FA4 max is 128. Training backends fall back to SDP or Flex Attention
(Axolotl hard-codes `sdp_attention: true` for Gemma 4). Does not affect inference
runtimes that already use SDP (Ollama, vLLM).
- **Fused LoRA kernels broken** (shared-KV layers). Axolotl disables
`lora_mlp_kernel` / `qkv_kernel` / `o_kernel` for Gemma 4; Unsloth routes around it.
- **26B A4B MoE wants ≥8-bit LoRA**, not 4-bit QLoRA — MoE expert quality degrades
at 4-bit during training. Axolotl's ScatterMoE + expert-LoRA config is the only
validated 4-bit MoE path. (This caveat is **training-only**; Q4_K_M inference is fine.)
- **New tool-call / channel tokens are learned embeddings** — if fine-tuning, set
`modules_to_save=["lm_head","embed_tokens"]` + `ensure_weight_tying=True` in
`LoraConfig`, or the adapter trains against frozen random vectors for them.
See `tooling/fine-tuning/recipe-recommendation.md` for the full training path.
## LOW: Vision Validator Overrejects
**Severity: LOW — specific to evaluative vision tasks**
In AI_Visualizer, Gemma 4 vision was used to critique SDXL frames. It flagged images for motif-matching failures that humans rated as equal or better than passed images. The validator was queued for disable.
**Pattern:** Gemma 4 vision is good at description but unreliable for subjective quality scoring. Use it for "what's in this image?" not "is this image good?"
## LOW: Keep-Alive Too Short
**Severity: LOW — performance only**
Default `keep_alive` is 5 minutes. If your pipeline has gaps (e.g., waiting for SDXL generation), the model gets unloaded and reloaded (~10-30s penalty).
**Fix:** Set `keep_alive` to match your pipeline duration:
```json
{ "keep_alive": "4h" }
```
Or pin/unpin explicitly:
```python
client.generate(model="gemma4:26b", prompt="", keep_alive=-1, options={"num_predict": 0}) # pin
# ... do work ...
client.generate(model="gemma4:26b", prompt="", keep_alive=0, options={"num_predict": 0}) # unpin
```