Enhancement: Refined Phase 1 prompt for less granular features & updated migration guide with troubleshooting tips
This commit is contained in:
@@ -1,21 +1,21 @@
|
|||||||
# Migration Guide: Google AI Builder Apps -> Local Docker Stack
|
# 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.
|
> **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.
|
**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.
|
**Grundsatz:** "Minimalset & Robustheit". Wir bauen keine aufgebl.hten Container, und wir verhindern Timeouts und fehlende Abh.ngigkeiten proaktiv.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 1. Vorbereitung & Abhängigkeiten (Common Pitfalls)
|
## 1. Vorbereitung & Abh.ngigkeiten (Common Pitfalls)
|
||||||
|
|
||||||
Bevor Code kopiert wird, müssen die Grundlagen stimmen.
|
Bevor Code kopiert wird, m.ssen die Grundlagen stimmen.
|
||||||
|
|
||||||
### 1.1 Package.json Check
|
### 1.1 Package.json Check
|
||||||
Generierte Apps haben oft kein `express`, da sie keinen Server erwarten.
|
Generierte Apps haben oft kein `express`, da sie keinen Server erwarten.
|
||||||
* **Aktion:** Öffne `package.json` der App.
|
* **Aktion:** "."ffne `package.json` der App.
|
||||||
* **Prüfung:** Steht `express` unter `dependencies`?
|
* **Pr.fung:** Steht `express` unter `dependencies`?
|
||||||
* **Fix:**
|
* **Fix:**
|
||||||
```json
|
```json
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -34,22 +34,22 @@ Docker kann keine einzelne Datei mounten, wenn sie auf dem Host nicht existiert.
|
|||||||
```
|
```
|
||||||
|
|
||||||
### 1.3 Vite Base Path (White Screen Fix)
|
### 1.3 Vite Base Path (White Screen Fix)
|
||||||
Wenn die App unter einem Unterverzeichnis (z.B. `/gtm/`) läuft, findet sie ihre JS/CSS-Dateien nicht, wenn Vite Standard-Pfade (`/`) nutzt.
|
Wenn die App unter einem Unterverzeichnis (z.B. `/gtm/`) l.uft, findet sie ihre JS/CSS-Dateien nicht, wenn Vite Standard-Pfade (`/`) nutzt.
|
||||||
* **Datei:** `vite.config.ts`
|
* **Datei:** `vite.config.ts`
|
||||||
* **Fix:** `base` auf `./` setzen.
|
* **Fix:** `base` auf `./` setzen.
|
||||||
```typescript
|
```typescript
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
base: './', // WICHTIG für Sub-Pfad Deployment
|
base: './', // WICHTIG f.r Sub-Pfad Deployment
|
||||||
// ...
|
// ...
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
### 1.4 Python Dependencies & Shared Libraries (Critical Pitfall)
|
### 1.4 Python Dependencies & Shared Libraries (Critical Pitfall)
|
||||||
Das Projekt nutzt ein zentrales `helpers.py`, das von mehreren Services geteilt wird. Dies führt oft zu `ModuleNotFoundError`, da eine kleine App (wie `gtm-architect`) nicht alle Bibliotheken benötigt, die in `helpers.py` importiert werden (z.B. `gspread`, `pandas`).
|
Das Projekt nutzt ein zentrales `helpers.py`, das von mehreren Services geteilt wird. Dies f.hrt oft zu `ModuleNotFoundError`, da eine kleine App (wie `gtm-architect`) nicht alle Bibliotheken ben.tigt, die in `helpers.py` importiert werden (z.B. `gspread`, `pandas`).
|
||||||
|
|
||||||
* **Fehler:** `ModuleNotFoundError: No module named 'gspread'`
|
* **Fehler:** `ModuleNotFoundError: No module named 'gspread'`
|
||||||
* **Ursache:** Die `gtm-architect/requirements.txt` enthält `gspread` nicht, aber `helpers.py` versucht es zu importieren.
|
* **Ursache:** Die `gtm-architect/requirements.txt` enth.lt `gspread` nicht, aber `helpers.py` versucht es zu importieren.
|
||||||
* **Fix (in `helpers.py`):** Machen Sie "exotische" Importe optional. Dies ist die robusteste Methode, um die Kompatibilität zu wahren, ohne die `requirements.txt` kleiner Apps aufzublähen.
|
* **Fix (in `helpers.py`):** Machen Sie "exotische" Importe optional. Dies ist die robusteste Methode, um die Kompatibilit.t zu wahren, ohne die `requirements.txt` kleiner Apps aufzubl.hen.
|
||||||
|
|
||||||
```python
|
```python
|
||||||
# Beispiel in helpers.py
|
# Beispiel in helpers.py
|
||||||
@@ -60,20 +60,20 @@ Das Projekt nutzt ein zentrales `helpers.py`, das von mehreren Services geteilt
|
|||||||
GSPREAD_AVAILABLE = False
|
GSPREAD_AVAILABLE = False
|
||||||
gspread = None # Wichtig, damit Referenzen nicht fehlschlagen
|
gspread = None # Wichtig, damit Referenzen nicht fehlschlagen
|
||||||
```
|
```
|
||||||
* **Fix (in `requirements.txt`):** Stellen Sie sicher, dass die für die App **unmittelbar** benötigten Bibliotheken vorhanden sind. Für `gtm-architect` sind das:
|
* **Fix (in `requirements.txt`):** Stellen Sie sicher, dass die f.r die App **unmittelbar** ben.tigten Bibliotheken vorhanden sind. F.r `gtm-architect` sind das:
|
||||||
```text
|
```text
|
||||||
google-generativeai
|
google-generativeai
|
||||||
requests
|
requests
|
||||||
beautifulsoup4
|
beautifulsoup4
|
||||||
```
|
```
|
||||||
|
|
||||||
### 1.5 Python Syntax & F-Strings
|
### 1.5 Python Syntax & F-Strings
|
||||||
Multi-Line Prompts können in Docker-Umgebungen zu **sehr hartnäckigen Syntaxfehlern** führen, selbst wenn sie lokal korrekt aussehen.
|
Multi-Line Prompts k.nnen in Docker-Umgebungen zu **sehr hartn.ckigen Syntaxfehlern** f.hren, selbst wenn sie lokal korrekt aussehen.
|
||||||
|
|
||||||
* **Das Problem:** Der Python-Parser (insbesondere bei `f-strings` in Kombination mit Zahlen/Punkten am Zeilenanfang oder verschachtelten Klammern) kann Multi-Line-Strings (`f"""..."""`) falsch interpretieren, was zu Fehlern wie `SyntaxError: invalid decimal literal` oder `unmatched ')'` führt, auch wenn der Code scheinbar korrekt ist.
|
* **Das Problem:** Der Python-Parser (insbesondere bei `f-strings` in Kombination mit Zahlen/Punkten am Zeilenanfang oder verschachtelten Klammern) kann Multi-Line-Strings (`f"""..."""`) falsch interpretieren, was zu Fehlern wie `SyntaxError: invalid decimal literal` oder `unmatched ')'` f.hrt, auch wenn der Code scheinbar korrekt ist.
|
||||||
|
|
||||||
* **ULTIMATIVE LÖSUNG (Maximale Robustheit):**
|
* **ULTIMATIVE L.SUNG (Maximale Robustheit):**
|
||||||
1. **Vermeide `f"""` komplett für komplexe Multi-Line-Prompts.** Definiere stattdessen den Prompt als **Liste von einzelnen String-Zeilen** und füge sie mit `"\n".join(prompt_parts)` zusammen.
|
1. **Vermeide `f"""` komplett f.r komplexe Multi-Line-Prompts.** Definiere stattdessen den Prompt als **Liste von einzelnen String-Zeilen** und f.ge sie mit `"\n".join(prompt_parts)` zusammen.
|
||||||
2. **Nutze die `.format()` Methode oder f-Strings in EINZEILIGEN Strings** zur Variablen-Injektion. Dies trennt die String-Definition komplett von der Variablen-Interpolation und ist die robusteste Methode.
|
2. **Nutze die `.format()` Methode oder f-Strings in EINZEILIGEN Strings** zur Variablen-Injektion. Dies trennt die String-Definition komplett von der Variablen-Interpolation und ist die robusteste Methode.
|
||||||
|
|
||||||
```python
|
```python
|
||||||
@@ -88,23 +88,23 @@ Multi-Line Prompts können in Docker-Umgebungen zu **sehr hartnäckigen Syntaxfe
|
|||||||
full_prompt = sys_instr + "\n\n" + prompt
|
full_prompt = sys_instr + "\n\n" + prompt
|
||||||
```
|
```
|
||||||
|
|
||||||
* **Versionierung für Debugging:** Um sicherzustellen, dass die korrekte Version des Codes läuft, füge Versionsnummern in die Start-Logs des Node.js Servers (`server.cjs`) und des Python Orchestrators (`gtm_architect_orchestrator.py`) ein.
|
* **Versionierung f.r Debugging:** Um sicherzustellen, dass die korrekte Version des Codes l.uft, f.ge Versionsnummern in die Start-Logs des Node.js Servers (`server.cjs`) und des Python Orchestrators (`gtm_architect_orchestrator.py`) ein.
|
||||||
* `server.cjs`: `console.log(`... (Version: ${VERSION})`);`
|
* `server.cjs`: `console.log(`... (Version: ${VERSION})`);`
|
||||||
* `gtm_architect_orchestrator.py`: `print(f"DEBUG: Orchestrator v{__version__} loaded ...")`
|
* `gtm_architect_orchestrator.py`: `print(f"DEBUG: Orchestrator v{__version__} loaded ...")`
|
||||||
|
|
||||||
* **Signaturen prüfen:** Shared Libraries (`helpers.py`) haben oft ältere Signaturen. Immer die tatsächliche Definition prüfen!
|
* **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`).
|
* 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
|
### 1.6 Pitfall: Veraltete API-Nutzung & Bibliotheksnamen
|
||||||
**ACHTUNG:** Dies ist eine der häufigsten Fehlerquellen bei der Migration älterer KI-Skripte.
|
**ACHTUNG:** Dies ist eine der h.ufigsten Fehlerquellen bei der Migration .ltterer 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`.
|
* **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`
|
* **Installation (`requirements.txt`):** `google-generativeai`
|
||||||
* **Import (z.B. in `helpers.py`):** `import google.generativeai as genai`
|
* **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.
|
* **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'`
|
* **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.
|
* **L.sung:** Der Code MUSS auf die moderne `GenerativeModel`-API umgestellt werden. Siehe **Appendix A.4** f.r ein Code-Beispiel.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -124,14 +124,14 @@ const port = 3005; // ANPASSEN!
|
|||||||
|
|
||||||
app.use(express.json({ limit: '50mb' }));
|
app.use(express.json({ limit: '50mb' }));
|
||||||
|
|
||||||
// 1. Statische Dateien: Robustheit für Docker (Flat) vs. Local (Nested)
|
// 1. Statische Dateien: Robustheit f.r Docker (Flat) vs. Local (Nested)
|
||||||
const distPath = path.join(__dirname, 'dist'); // Docker Standard
|
const distPath = path.join(__dirname, 'dist'); // Docker Standard
|
||||||
const isProduction = fs.existsSync(distPath);
|
const isProduction = fs.existsSync(distPath);
|
||||||
const staticDir = isProduction ? distPath : __dirname;
|
const staticDir = isProduction ? distPath : __dirname;
|
||||||
console.log(`[Init] Serving static files from: ${staticDir}`);
|
console.log(`[Init] Serving static files from: ${staticDir}`);
|
||||||
app.use(express.static(staticDir));
|
app.use(express.static(staticDir));
|
||||||
|
|
||||||
// 2. Python Pfad: Robustheit für Sideloading
|
// 2. Python Pfad: Robustheit f.r Sideloading
|
||||||
let pythonScriptPath = path.join(__dirname, 'mein_orchestrator.py'); // ANPASSEN!
|
let pythonScriptPath = path.join(__dirname, 'mein_orchestrator.py'); // ANPASSEN!
|
||||||
if (!fs.existsSync(pythonScriptPath)) {
|
if (!fs.existsSync(pythonScriptPath)) {
|
||||||
pythonScriptPath = path.join(__dirname, '../mein_orchestrator.py');
|
pythonScriptPath = path.join(__dirname, '../mein_orchestrator.py');
|
||||||
@@ -147,7 +147,7 @@ app.get('*', (req, res) => {
|
|||||||
res.sendFile(path.join(staticDir, 'index.html'));
|
res.sendFile(path.join(staticDir, 'index.html'));
|
||||||
});
|
});
|
||||||
|
|
||||||
// 5. Timeout-Härtung (CRITICAL!)
|
// 5. Timeout-H.rtung (CRITICAL!)
|
||||||
const server = app.listen(port, () => {
|
const server = app.listen(port, () => {
|
||||||
console.log(`Server listening on ${port}`);
|
console.log(`Server listening on ${port}`);
|
||||||
});
|
});
|
||||||
@@ -177,7 +177,7 @@ RUN npm run build
|
|||||||
FROM python:3.11-slim
|
FROM python:3.11-slim
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
# Node.js installieren (für Server Bridge, optimierte Methode)
|
# Node.js installieren (f.r Server Bridge, optimierte Methode)
|
||||||
RUN apt-get update && \
|
RUN apt-get update && \
|
||||||
apt-get install -y --no-install-recommends curl ca-certificates && \
|
apt-get install -y --no-install-recommends curl ca-certificates && \
|
||||||
curl -fsSL https://deb.nodesource.com/setup_20.x | bash - && \
|
curl -fsSL https://deb.nodesource.com/setup_20.x | bash - && \
|
||||||
@@ -196,7 +196,7 @@ COPY --from=frontend-builder /app/dist ./dist
|
|||||||
|
|
||||||
# Python Logic & Shared Libs
|
# Python Logic & Shared Libs
|
||||||
COPY mein_orchestrator.py .
|
COPY mein_orchestrator.py .
|
||||||
COPY helpers.py .
|
COPY helpers.py .
|
||||||
COPY config.py .
|
COPY config.py .
|
||||||
COPY market_db_manager.py .
|
COPY market_db_manager.py .
|
||||||
|
|
||||||
@@ -208,18 +208,18 @@ CMD ["node", "server.cjs"]
|
|||||||
|
|
||||||
## 4. Docker Compose & Mounts (WICHTIGER PITFALL)
|
## 4. Docker Compose & Mounts (WICHTIGER PITFALL)
|
||||||
|
|
||||||
**WARNUNG: Lokale Dateien überschreiben den Container-Code!**
|
**WARNUNG: Lokale Dateien .berschreiben den Container-Code!**
|
||||||
|
|
||||||
Wenn Sie `volumes` für das Sideloading verwenden (wie unten gezeigt), werden die Dateien aus Ihrem lokalen Verzeichnis direkt in den Container geladen. Das bedeutet: **Wenn Sie nicht `git pull` auf dem Host-System ausführen, bevor Sie den Container neu bauen, wird der Container weiterhin den alten, lokalen Code ausführen.**
|
Wenn Sie `volumes` f.r das Sideloading verwenden (wie unten gezeigt), werden die Dateien aus Ihrem lokalen Verzeichnis direkt in den Container geladen. Das bedeutet: **Wenn Sie nicht `git pull` auf dem Host-System ausf.hren, bevor Sie den Container neu bauen, wird der Container weiterhin den alten, lokalen Code ausf.hren.**
|
||||||
|
|
||||||
**Workflow:**
|
**Workflow:**
|
||||||
1. Änderungen im Git-Repository pushen (oder von einem Agent pushen lassen).
|
1. "."nderungen im Git-Repository pushen (oder von einem Agent pushen lassen).
|
||||||
2. **`git pull` auf dem Host-System ausführen.** (Dieser Schritt ist entscheidend!)
|
2. **`git pull` auf dem Host-System ausf.hren.** (Dieser Schritt ist entscheidend!)
|
||||||
3. `docker-compose up -d --build <service-name>` ausführen.
|
3. `docker-compose up -d --build <service-name>` ausf.hren.
|
||||||
|
|
||||||
Beim Sideloading müssen **alle** Abhängigkeiten gemountet werden, nicht nur das Hauptskript.
|
Beim Sideloading m.ssen **alle** Abh.ngigkeiten gemountet werden, nicht nur das Hauptskript.
|
||||||
|
|
||||||
**Wichtig:** Der Pfad zu `server.cjs` ändert sich durch die "Flat Structure" im Dockerfile!
|
**Wichtig:** Der Pfad zu `server.cjs` .ndert sich durch die "Flat Structure" im Dockerfile!
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
my-new-app:
|
my-new-app:
|
||||||
@@ -239,6 +239,9 @@ Beim Sideloading müssen **alle** Abhängigkeiten gemountet werden, nicht nur da
|
|||||||
environment:
|
environment:
|
||||||
- PYTHONUNBUFFERED=1
|
- PYTHONUNBUFFERED=1
|
||||||
- DB_PATH=/app/mein_projekt.db
|
- DB_PATH=/app/mein_projekt.db
|
||||||
|
environment:
|
||||||
|
- PYTHONUNBUFFERED=1
|
||||||
|
- DB_PATH=/app/mein_projekt.db
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -265,11 +268,12 @@ Achtung beim Routing. Wenn die App unter `/app/` laufen soll, muss der Trailing
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
||||||
## Checkliste vor dem Commit
|
## Checkliste vor dem Commit
|
||||||
|
|
||||||
- [ ] `express` in `package.json`?
|
- [ ] `express` in `package.json`?
|
||||||
- [ ] `vite.config.ts` hat `base: './'`?
|
- [ ] `vite.config.ts` hat `base: './'`?
|
||||||
- [ ] `requirements.txt` enthält die korrekten (minimalen) Dependencies?
|
- [ ] `requirements.txt` enth.lt die korrekten (minimalen) Dependencies?
|
||||||
- [ ] `server.cjs` hat Timeouts (>600s)?
|
- [ ] `server.cjs` hat Timeouts (>600s)?
|
||||||
- [ ] `docker-compose.yml` mountet auch `helpers.py` und `config.py`?
|
- [ ] `docker-compose.yml` mountet auch `helpers.py` und `config.py`?
|
||||||
- [ ] Leere `.db` Datei auf dem Host erstellt?
|
- [ ] Leere `.db` Datei auf dem Host erstellt?
|
||||||
@@ -277,93 +281,69 @@ Achtung beim Routing. Wenn die App unter `/app/` laufen soll, muss der Trailing
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
||||||
## Appendix A: GTM Architect Fixes & Gemini Migration (Jan 2026)
|
## Appendix A: GTM Architect Fixes & Gemini Migration (Jan 2026)
|
||||||
|
|
||||||
### A.1 Problemstellung
|
### A.1 Problemstellung
|
||||||
- **SyntaxError bei großen Prompts:** Python-Parser (3.11) hatte massive Probleme mit f-Strings, die 100+ Zeilen lang waren und Sonderzeichen enthielten.
|
- **SyntaxError bei gro.en Prompts:** Python-Parser (3.11) hatte massive Probleme mit f-Strings, die 100+ Zeilen lang waren und Sonderzeichen enthielten.
|
||||||
- **Library Deprecation:** `google.generativeai` hat Support eingestellt? Nein, aber die Fehlermeldung im Log deutete auf einen Konflikt zwischen alten `openai`-Wrappern und neuen Gemini-Paketen hin.
|
- **Library Deprecation:** `google.generativeai` hat Support eingestellt? Nein, aber die Fehlermeldung im Log deutete auf einen Konflikt zwischen alten `openai`-Wrappern und neuen Gemini-Paketen hin.
|
||||||
- **Lösung:**
|
- **L.sung:**
|
||||||
1. **Prompts ausgelagert:** System-Prompts liegen jetzt in `gtm_prompts.json` und werden zur Laufzeit geladen. Kein Code-Parsing mehr notwendig.
|
1. **Prompts ausgelagert:** System-Prompts liegen jetzt in `gtm_prompts.json` und werden zur Laufzeit geladen. Kein Code-Parsing mehr notwendig.
|
||||||
2. **Native Gemini Lib:** Statt OpenAI-Wrapper nutzen wir jetzt `google.generativeai` direkt via `helpers.call_gemini_flash`.
|
2. **Native Gemini Lib:** Statt OpenAI-Wrapper nutzen wir jetzt `google.generativeai` direkt via `helpers.call_gemini_flash`.
|
||||||
3. **Config:** `gtm-architect/Dockerfile` kopiert nun explizit `gtm_prompts.json`.
|
3. **Config:** `gtm-architect/Dockerfile` kopiert nun explizit `gtm_prompts.json`.
|
||||||
|
|
||||||
### A.2 Neuer Standard für KI-Apps
|
### A.2 Neuer Standard f.r KI-Apps
|
||||||
Für zukünftige Apps gilt:
|
F.r zuk.nftige Apps gilt:
|
||||||
1. **Prompts in JSON/Text-Files:** Niemals riesige Strings im Python-Code hardcoden.
|
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. Siehe **Appendix A.4** für die korrekte Implementierung.
|
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!
|
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)
|
### 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.
|
- **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:**
|
- **Log-Analyse:**
|
||||||
```
|
```
|
||||||
ERROR:helpers:Fehler beim Gemini-Flash-Aufruf: module 'google.generativeai' has no attribute 'Client'
|
ERROR:helpers:Fehler beim Gemini-Flash-Aufruf: module 'google.generativeai' has no attribute 'Client'
|
||||||
```
|
```
|
||||||
- **Untersuchung:**
|
- **Untersuchung:**
|
||||||
1. Die erste Annahme, es handle sich um einen falschen Modellnamen (`404 NOT_FOUND`), erwies sich als irreführend.
|
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.
|
2. Die Analyse des `helpers.py`-Skripts zeigte, dass der Code versuchte, eine `genai.Client`-Klasse zu verwenden.
|
||||||
- **Schlussfolgerung & Lösung:**
|
- **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.
|
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`):**
|
**Die Korrektur (implementiert in `helpers.py`):**
|
||||||
1. **Konfigurieren des API-Schlüssels:** `genai.configure(api_key="IHR_KEY")`
|
1. **Konfigurieren des API-Schl.ssels:** `genai.configure(api_key="IHR_KEY")`
|
||||||
2. **Instanziieren des Modells:** `model = genai.GenerativeModel('gemini-1.5-flash-latest')`
|
2. **Instanziieren des Modells:** `model = genai.GenerativeModel('gemini-1.5-flash-latest')`
|
||||||
3. **Aufrufen der Generierung:** `response = model.generate_content(...)`
|
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.
|
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)
|
### 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**.
|
Um die korrekte Implementierung zu standardisieren, hier der "Gold-Standard" Wrapper, wie er in `helpers.py` verwendet wird. Dieser Code ist die Vorlage f.r alle zuk.nftigen Gemini-Aufrufe.
|
||||||
|
|
||||||
```python
|
```python
|
||||||
import google.generativeai as genai
|
import google.generativeai as genai
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
# Cache für den Modellnamen
|
# Annahme: logger und _get_gemini_api_key() sind definiert
|
||||||
_CACHED_MODEL_NAME = None
|
# Annahme: Ein @retry_on_failure Decorator existiert
|
||||||
|
|
||||||
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
|
@retry_on_failure
|
||||||
def call_gemini_flash(prompt, system_instruction=None, temperature=0.3, json_mode=False):
|
def call_gemini_flash(prompt, system_instruction=None, temperature=0.3, json_mode=False):
|
||||||
"""
|
"""
|
||||||
Robuster Wrapper mit dynamischer Modellwahl.
|
Spezifische Funktion f.r Gemini 1.5 Flash Aufrufe mit System-Instruction Support.
|
||||||
|
Verwendet die korrekte `GenerativeModel` API.
|
||||||
"""
|
"""
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
# HAS_GEMINI Check hier weggelassen f.r die Lesbarkeit
|
||||||
|
|
||||||
api_key = _get_gemini_api_key()
|
api_key = _get_gemini_api_key()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
# 1. API-Schl.ssel konfigurieren
|
||||||
genai.configure(api_key=api_key)
|
genai.configure(api_key=api_key)
|
||||||
|
|
||||||
|
# 2. Generierungs-Konfiguration definieren
|
||||||
generation_config = {
|
generation_config = {
|
||||||
"temperature": temperature,
|
"temperature": temperature,
|
||||||
"top_p": 0.95,
|
"top_p": 0.95,
|
||||||
@@ -373,21 +353,51 @@ def call_gemini_flash(prompt, system_instruction=None, temperature=0.3, json_mod
|
|||||||
if json_mode:
|
if json_mode:
|
||||||
generation_config["response_mime_type"] = "application/json"
|
generation_config["response_mime_type"] = "application/json"
|
||||||
|
|
||||||
# Dynamisch das richtige Modell wählen
|
# 3. Modell instanziieren (mit System-Instruktion)
|
||||||
model_name = _get_best_flash_model(api_key)
|
|
||||||
|
|
||||||
model = genai.GenerativeModel(
|
model = genai.GenerativeModel(
|
||||||
model_name=model_name,
|
model_name="gemini-1.5-flash-latest", # Eine stabile, spezifische Version verwenden
|
||||||
generation_config=generation_config,
|
generation_config=generation_config,
|
||||||
system_instruction=system_instruction
|
system_instruction=system_instruction
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Der Prompt kann ein String oder eine Liste von Teilen sein
|
||||||
contents = [prompt] if isinstance(prompt, str) else prompt
|
contents = [prompt] if isinstance(prompt, str) else prompt
|
||||||
|
|
||||||
|
# 4. Inhalt generieren
|
||||||
response = model.generate_content(contents)
|
response = model.generate_content(contents)
|
||||||
|
|
||||||
return response.text.strip()
|
return response.text.strip()
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Fehler beim Gemini-Flash-Aufruf: {e}")
|
logger.error(f"Fehler beim Gemini-Flash-Aufruf: {e}")
|
||||||
|
if "API_KEY_INVALID" in str(e) or "403" in str(e):
|
||||||
|
raise ValueError(f"Invalid API Key: {str(e)}")
|
||||||
raise e
|
raise e
|
||||||
```
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. Troubleshooting & Lessons Learned (Jan 2026)
|
||||||
|
|
||||||
|
Diese Fehler traten bei der GTM Architect Migration auf und m.ssen zuk.nftig vermieden werden.
|
||||||
|
|
||||||
|
### 7.1 Database Initialization (`OperationalError: no such table`)
|
||||||
|
* **Problem:** Neue SQLite-Datenbanken (`.db` Files) werden zwar als leere Dateien via `touch` erstellt, enthalten aber keine Tabellen. Der Python-Code st.rzt ab, wenn er versucht, in eine nicht existierende Tabelle zu schreiben.
|
||||||
|
* **L.sung:** Die Initialisierungs-Logik (z.B. `python gtm_db_manager.py init`) MUSS beim Start des `server.cjs` aufgerufen werden. Verlasse dich nicht darauf, dass der User dies manuell tut.
|
||||||
|
* **Best Practice:** In `server.cjs`:
|
||||||
|
```javascript
|
||||||
|
const dbScript = path.join(__dirname, 'gtm_db_manager.py');
|
||||||
|
require('child_process').spawn('python3', [dbScript, 'init']);
|
||||||
|
```
|
||||||
|
|
||||||
|
### 7.2 Dependency Injection (`ModuleNotFoundError`)
|
||||||
|
* **Problem:** Wenn eine `requirements.txt` nachtr.glich hinzugef.gt oder ge.ndert wird, reicht ein einfacher Container-Neustart nicht aus. `pip install` l.uft normalerweise nur w.hrend der Build-Phase.
|
||||||
|
* **L.sung:**
|
||||||
|
1. **Immer Rebuild:** `docker-compose up -d --build <service>`.
|
||||||
|
2. **Startup-Check:** F.r maximale Robustheit kann der `CMD` im Dockerfile so angepasst werden, dass er `pip install` vor dem Server-Start ausf.hrt: `CMD ["/bin/bash", "-c", "pip install -r requirements.txt && node server.cjs"]`.
|
||||||
|
|
||||||
|
### 7.3 Python Indentation (`IndentationError`)
|
||||||
|
* **Problem:** Beim Copy-Paste von Code in `config.py` entstehen oft versehentliche Einr.ckungen (Leerzeichen am Zeilenanfang) bei globalen Variablen. Dies f.hrt dazu, dass der gesamte Python-Prozess beim Import abst.rzt.
|
||||||
|
* **L.sung:** `.py` Dateien immer auf strikte Linksb.ndigkeit bei globalen Definitionen pr.fen. Tools wie `flake8` helfen, sind aber im Container oft nicht verf.gbar. Sorgfalt ist entscheidend.
|
||||||
|
|
||||||
|
```
|
||||||
@@ -99,7 +99,10 @@ def phase1(payload):
|
|||||||
prompt = f"""
|
prompt = f"""
|
||||||
PHASE 1: PRODUCT ANALYSIS & CONSTRAINTS
|
PHASE 1: PRODUCT ANALYSIS & CONSTRAINTS
|
||||||
Input: "{analysis_content}"
|
Input: "{analysis_content}"
|
||||||
Task: 1. Extract technical features. 2. Define hard constraints. 3. Check for internal portfolio conflicts (hypothetical product "Scrubber 5000").
|
Task:
|
||||||
|
1. Extract and CONSOLIDATE technical features into 8-12 high-level core capabilities or value propositions. Group minor specs (e.g., specific ports like USB/Ethernet) into broader categories (e.g., "Connectivity & Integration"). Do NOT list every single hardware spec individually. Focus on what matters for the buyer.
|
||||||
|
2. Define hard constraints (e.g., physical dimensions, max payload, environment limitations).
|
||||||
|
3. Check for internal portfolio conflicts (hypothetical product "Scrubber 5000").
|
||||||
Output JSON format ONLY: {{"features": [], "constraints": [], "conflictCheck": {{"hasConflict": false, "details": "", "relatedProduct": ""}}, "rawAnalysis": ""}}
|
Output JSON format ONLY: {{"features": [], "constraints": [], "conflictCheck": {{"hasConflict": false, "details": "", "relatedProduct": ""}}, "rawAnalysis": ""}}
|
||||||
"""
|
"""
|
||||||
log_and_save(project_id, "phase1", "prompt", prompt)
|
log_and_save(project_id, "phase1", "prompt", prompt)
|
||||||
|
|||||||
Reference in New Issue
Block a user