Fix: Gemini API modernization, dynamic model selection, and config path corrections

This commit is contained in:
2026-01-03 12:19:51 +00:00
parent 844ffa3eea
commit 97ca4cd254
6 changed files with 233 additions and 75 deletions

View File

@@ -1,5 +1,7 @@
# Migration Guide: Google AI Builder Apps -> Local Docker Stack
> **WICHTIGER HINWEIS:** Der Gemini-Agent führt Code **innerhalb** dieses Docker-Containers aus. Er hat keinen Zugriff auf den Docker-Daemon des Host-Systems. Daher kann und wird der Agent **NIEMALS** in der Lage sein, Befehle wie `docker build`, `docker-compose up` oder andere Docker-Management-Aufgaben auszuführen. Diese Befehle müssen immer vom Benutzer auf dem Host-System ausgeführt werden.
**Ziel:** Standardisierter Prozess, um eine von Google AI Studio generierte React-App schnell, robust und fehlerfrei in die lokale Docker/Python-Architektur zu integrieren.
**Grundsatz:** "Minimalset & Robustheit". Wir bauen keine aufgeblähten Container, und wir verhindern Timeouts und fehlende Abhängigkeiten proaktiv.
@@ -93,6 +95,17 @@ Multi-Line Prompts können in Docker-Umgebungen zu **sehr hartnäckigen Syntaxfe
* **Signaturen prüfen:** Shared Libraries (`helpers.py`) haben oft ältere Signaturen. Immer die tatsächliche Definition prüfen!
* Beispiel: `call_openai_chat` unterstützt oft kein `system_message` Argument. Stattdessen Prompt manuell zusammenbauen (`sys_instr + "\n\n" + prompt`).
### 1.6 Pitfall: Veraltete API-Nutzung & Bibliotheksnamen
**ACHTUNG:** Dies ist eine der häufigsten Fehlerquellen bei der Migration älterer KI-Skripte.
* **Das Problem 1 (Name):** Der Name des Pakets (`google-generativeai`) stimmt nicht mit dem Import (`import google.genai`) überein, den neuere Versionen erwarten. In unserem Fall bleiben wir vorerst beim Import von `google.generativeai`.
* **Installation (`requirements.txt`):** `google-generativeai`
* **Import (z.B. in `helpers.py`):** `import google.generativeai as genai`
* **Das Problem 2 (API-Nutzung):** Älterer Code verwendet eine `genai.Client`-Klasse, die **nicht mehr existiert**. Dies führt zu einem Absturz.
* **Fehlerbild im Log:** `AttributeError: module 'google.generativeai' has no attribute 'Client'`
* **Lösung:** Der Code MUSS auf die moderne `GenerativeModel`-API umgestellt werden. Siehe **Appendix A.4** für ein Code-Beispiel.
---
## 2. Die Backend-Bridge (`server.cjs`)
@@ -277,5 +290,104 @@ Achtung beim Routing. Wenn die App unter `/app/` laufen soll, muss der Trailing
### A.2 Neuer Standard für KI-Apps
Für zukünftige Apps gilt:
1. **Prompts in JSON/Text-Files:** Niemals riesige Strings im Python-Code hardcoden.
2. **`helpers.call_gemini_flash` nutzen:** Diese Funktion ist nun der Gold-Standard für einfache, stateless Calls.
2. **`helpers.call_gemini_flash` nutzen:** Diese Funktion ist nun der Gold-Standard für einfache, stateless Calls. Siehe **Appendix A.4** für die korrekte Implementierung.
3. **JSON im Dockerfile:** Vergesst nicht, die externen Prompt-Files mit `COPY` in den Container zu holen!
### A.3 Kritisches Problem & Lösung: `AttributeError` bei Gemini API (Jan 2026)
- **Problem:** Nach der Migration auf die `google-generativeai` Bibliothek schlugen alle API-Aufrufe mit einem `AttributeError: module 'google.generativeai' has no attribute 'Client'` Fehler fehl.
- **Log-Analyse:**
```
ERROR:helpers:Fehler beim Gemini-Flash-Aufruf: module 'google.generativeai' has no attribute 'Client'
```
- **Untersuchung:**
1. Die erste Annahme, es handle sich um einen falschen Modellnamen (`404 NOT_FOUND`), erwies sich als irreführend.
2. Die Analyse des `helpers.py`-Skripts zeigte, dass der Code versuchte, eine `genai.Client`-Klasse zu verwenden.
- **Schlussfolgerung & Lösung:**
Der Fehler lag in der Verwendung einer **veralteten API-Initialisierungsmethode**. Die `google-generativeai`-Bibliothek hat die `Client`-Klasse entfernt und erfordert nun einen modernen Ansatz.
**Die Korrektur (implementiert in `helpers.py`):**
1. **Konfigurieren des API-Schlüssels:** `genai.configure(api_key="IHR_KEY")`
2. **Instanziieren des Modells:** `model = genai.GenerativeModel('gemini-1.5-flash-latest')`
3. **Aufrufen der Generierung:** `response = model.generate_content(...)`
Dieser Fix wurde in `helpers.py` umgesetzt und hat die Funktionalität des GTM Architect wiederhergestellt. Alle neuen KI-Anwendungen müssen diesem Muster folgen.
### A.4 Gold-Standard: Gemini API Wrapper (Robust & Dynamic)
Um 404-Fehler durch sich ändernde Modellnamen oder regionale Unterschiede zu vermeiden, nutzt der neue Standard eine **dynamische Modell-Ermittlung**.
```python
import google.generativeai as genai
import logging
# Cache für den Modellnamen
_CACHED_MODEL_NAME = None
def _get_best_flash_model(api_key):
"""
Ermittelt dynamisch das beste verfügbare Flash-Modell via list_models().
"""
global _CACHED_MODEL_NAME
if _CACHED_MODEL_NAME:
return _CACHED_MODEL_NAME
default_model = "gemini-1.5-flash"
try:
genai.configure(api_key=api_key)
# Suche nach Modellen mit 'flash' und 'generateContent' Support
models = list(genai.list_models())
flash_models = [
m.name.replace('models/', '')
for m in models
if 'flash' in m.name.lower() and 'generateContent' in m.supported_generation_methods
]
# Priorisierung
if "gemini-1.5-flash" in flash_models:
_CACHED_MODEL_NAME = "gemini-1.5-flash"
elif flash_models:
_CACHED_MODEL_NAME = flash_models[0]
else:
_CACHED_MODEL_NAME = default_model
return _CACHED_MODEL_NAME
except Exception:
return default_model
@retry_on_failure
def call_gemini_flash(prompt, system_instruction=None, temperature=0.3, json_mode=False):
"""
Robuster Wrapper mit dynamischer Modellwahl.
"""
logger = logging.getLogger(__name__)
api_key = _get_gemini_api_key()
try:
genai.configure(api_key=api_key)
generation_config = {
"temperature": temperature,
"top_p": 0.95,
"top_k": 40,
"max_output_tokens": 8192,
}
if json_mode:
generation_config["response_mime_type"] = "application/json"
# Dynamisch das richtige Modell wählen
model_name = _get_best_flash_model(api_key)
model = genai.GenerativeModel(
model_name=model_name,
generation_config=generation_config,
system_instruction=system_instruction
)
contents = [prompt] if isinstance(prompt, str) else prompt
response = model.generate_content(contents)
return response.text.strip()
except Exception as e:
logger.error(f"Fehler beim Gemini-Flash-Aufruf: {e}")
raise e
```