228 lines
9.3 KiB
PHP
228 lines
9.3 KiB
PHP
<!-- Version: 24.09.2025 22:10 -->
|
|
<!DOCTYPE html>
|
|
<html lang="de">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Prompt-Vergleich</title>
|
|
<style>
|
|
body {
|
|
font-family: sans-serif;
|
|
margin: 2em;
|
|
line-height: 1.6;
|
|
background-color: #f4f4f9;
|
|
color: #333;
|
|
}
|
|
.container {
|
|
display: flex;
|
|
gap: 20px;
|
|
margin-top: 20px;
|
|
flex-wrap: wrap;
|
|
}
|
|
.panel {
|
|
flex: 1;
|
|
min-width: 300px;
|
|
background-color: #fff;
|
|
padding: 20px;
|
|
border-radius: 8px;
|
|
box-shadow: 0 4px 8px rgba(0,0,0,0.1);
|
|
}
|
|
h1, h2 {
|
|
color: #5E8C61;
|
|
}
|
|
pre {
|
|
background-color: #eee;
|
|
padding: 15px;
|
|
border-radius: 6px;
|
|
white-space: pre-wrap;
|
|
word-wrap: break-word;
|
|
font-size: 0.9em;
|
|
}
|
|
.loading-bar {
|
|
width: 0;
|
|
height: 4px;
|
|
background-color: #5E8C61;
|
|
transition: width 0.4s ease;
|
|
margin-top: 10px;
|
|
}
|
|
.controls {
|
|
margin-bottom: 20px;
|
|
}
|
|
.controls input[type="file"] {
|
|
margin-right: 10px;
|
|
}
|
|
.controls button {
|
|
padding: 10px 15px;
|
|
background-color: #5E8C61;
|
|
color: white;
|
|
border: none;
|
|
border-radius: 5px;
|
|
cursor: pointer;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
|
|
<h1>Prompt-Vergleich</h1>
|
|
<p>Lade ein Bild hoch und vergleiche die Ergebnisse mit dem alten und dem neuen Prompt.</p>
|
|
|
|
<div class="controls">
|
|
<input type="file" id="imageInput" accept="image/*">
|
|
<button id="runTestBtn">Test starten</button>
|
|
</div>
|
|
|
|
<div id="loadingStatus" style="display: none;">
|
|
<p>Analysiere mit beiden Prompts...</p>
|
|
<div class="loading-bar"></div>
|
|
</div>
|
|
|
|
<div class="container">
|
|
<div class="panel">
|
|
<h2>Alter Prompt</h2>
|
|
<div id="oldPromptResult"></div>
|
|
</div>
|
|
<div class="panel">
|
|
<h2>Finaler Profi-Prompt (V6)</h2>
|
|
<div id="newPromptResult"></div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
const imageInput = document.getElementById('imageInput');
|
|
const runTestBtn = document.getElementById('runTestBtn');
|
|
const loadingStatus = document.getElementById('loadingStatus');
|
|
const loadingBar = loadingStatus.querySelector('.loading-bar');
|
|
const oldPromptResult = document.getElementById('oldPromptResult');
|
|
const newPromptResult = document.getElementById('newPromptResult');
|
|
|
|
const PROXY_URL = 'api_proxy.php';
|
|
|
|
const oldPrompt = `
|
|
Du bist ein liebevoller Pflanzenexperte. Analysiere das Bild dieser Pflanze. Gib mir die folgenden Informationen als sauberen JSON-String zurück, ohne zusätzlichen Text oder Markdown.
|
|
- "name": Der gebräuchliche Name der Pflanze.
|
|
- "latin_name": Der botanische (lateinische) Name der Pflanze.
|
|
- "watering_interval_days": Das Gießintervall in Tagen (nur die Zahl).
|
|
- "fertilizing_interval_days": Das Düngeintervall in Tagen (nur die Zahl).
|
|
- "care_tips": Gib mir 3 kurze und liebevoll formulierte Pflegetipps.
|
|
WICHTIGE REGELN FÜR DIE FORMARTIERUNG DER TIPPS:
|
|
1. Jeder Tipp MUSS mit einem passenden Emoji und einem Titel beginnen (z.B. "☀️ Licht:").
|
|
2. Zwischen den einzelnen Tipps MUSS sich ein doppelter Zeilenumbruch (\\n\\n) befinden.
|
|
Falls du die Pflanze nicht sicher erkennen kannst, setze den Wert für "name" und "latin_name" auf "Unbekannt".
|
|
`;
|
|
|
|
const newPrompt = `
|
|
Du bist ein kritischer Botaniker. Deine Aufgabe ist es, die Pflanze auf dem Bild wissenschaftlich und so exakt wie möglich zu identifizieren.
|
|
Gib das Ergebnis als sauberen JSON-String zurück, ohne zusätzlichen Text oder Markdown.
|
|
|
|
- "name": Der gebräuchlichste Name der Pflanze (nicht zwingend der deutsche).
|
|
- "latin_name": Der botanische (lateinische) Name der Pflanze.
|
|
- "plant_family": Die Pflanzenfamilie (z.B. "Araceae").
|
|
- "plant_genus": Die Pflanzengattung (z.B. "Monstera").
|
|
- "confidence_score": Bewerte deine Sicherheit bei der Identifizierung KRITISCH und ehrlich auf einer Skala von 0 (geraten) bis 100 (absolut sicher). Sei nicht übermäßig selbstbewusst. Ein Score von 95+ sollte nur bei perfekter Bildqualität und eindeutigen Merkmalen vergeben werden.
|
|
- "identification_notes": Gib eine sehr kurze Notiz, welche Merkmale zur Identifizierung geführt haben (z.B. "Herzförmige Blätter mit silbernen Flecken").
|
|
- "watering_interval_days": Das Gießintervall in Tagen (nur die Zahl).
|
|
- "fertilizing_interval_days": Das Düngeintervall in Tagen (nur die Zahl).
|
|
- "care_tips": Gib mir 3 kurze Pflegetipps.
|
|
WICHTIGE REGELN FÜR DIE TIPPS:
|
|
1. Formuliere die Tipps charmant aus der Ich-Perspektive der Pflanze (z.B. "☀️ Licht: Stell mich an einen hellen Ort, aber ohne direkte Mittagssonne.").
|
|
2. Jeder Tipp MUSS mit einem passenden Emoji und einem Titel beginnen (z.B. "☀️ Licht:").
|
|
3. Zwischen den Tipps MUSS sich ein doppelter Zeilenumbruch (\\n\\n) befinden.
|
|
|
|
WICHTIG: Falls dein confidence_score unter 75 liegt, setze "name", "latin_name", "plant_family", "plant_genus" auf "Unbekannt" und gib bei "identification_notes" den Grund für die Unsicherheit an (z.B. "Geringe Zuversicht aufgrund von schlechter Bildqualität.").
|
|
`;
|
|
|
|
runTestBtn.addEventListener('click', async () => {
|
|
const file = imageInput.files[0];
|
|
if (!file) {
|
|
alert('Bitte wähle zuerst ein Bild aus.');
|
|
return;
|
|
}
|
|
|
|
oldPromptResult.innerHTML = '';
|
|
newPromptResult.innerHTML = '';
|
|
runTestBtn.disabled = true;
|
|
loadingStatus.style.display = 'block';
|
|
loadingBar.style.width = '0%';
|
|
|
|
try {
|
|
const base64Image = await fileToBase64(file);
|
|
|
|
const analyzeWithProgress = async (prompt, resultElement) => {
|
|
try {
|
|
const result = await analyzeImage(base64Image, prompt);
|
|
displayResult(resultElement, result);
|
|
} catch (error) {
|
|
displayResult(resultElement, { error: error.message });
|
|
} finally {
|
|
loadingBar.style.width = (parseInt(loadingBar.style.width) + 50) + '%';
|
|
}
|
|
};
|
|
|
|
await Promise.all([
|
|
analyzeWithProgress(oldPrompt, oldPromptResult),
|
|
analyzeWithProgress(newPrompt, newPromptResult)
|
|
]);
|
|
|
|
} catch (error) {
|
|
console.error('Test failed:', error);
|
|
alert('Fehler beim Test: ' + error.message);
|
|
} finally {
|
|
runTestBtn.disabled = false;
|
|
loadingStatus.style.display = 'none';
|
|
loadingBar.style.width = '0%';
|
|
}
|
|
});
|
|
|
|
function fileToBase64(file) {
|
|
return new Promise((resolve, reject) => {
|
|
const reader = new FileReader();
|
|
reader.readAsDataURL(file);
|
|
reader.onload = () => resolve(reader.result.split(',')[1]);
|
|
reader.onerror = error => reject(error);
|
|
});
|
|
}
|
|
|
|
async function analyzeImage(base64Image, prompt) {
|
|
const requestPayload = {
|
|
"contents": [
|
|
{
|
|
"parts": [
|
|
{ "text": prompt },
|
|
{ "inline_data": { "mime_type": "image/jpeg", "data": base64Image } }
|
|
]
|
|
}
|
|
]
|
|
};
|
|
|
|
const response = await fetch(PROXY_URL, {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ payload: requestPayload })
|
|
});
|
|
|
|
if (!response.ok) {
|
|
const errorBody = await response.text();
|
|
throw new Error(`API error! status: ${response.status}, body: ${errorBody}`);
|
|
}
|
|
return response.json();
|
|
}
|
|
|
|
function displayResult(element, result) {
|
|
if (result.error) {
|
|
element.innerHTML = `<p style="color:red;">API Fehler:<br><pre>${result.error}</pre></p>`;
|
|
return;
|
|
}
|
|
try {
|
|
const textResponse = result.candidates[0].content.parts[0].text;
|
|
const jsonString = textResponse.replace(/```json|```/g, '').trim();
|
|
const plantData = JSON.parse(jsonString);
|
|
|
|
element.innerHTML = `<pre>${JSON.stringify(plantData, null, 2)}</pre>`;
|
|
} catch (e) {
|
|
element.innerHTML = `<p style="color:red;">Fehler beim Parsen der Antwort. Rohe Antwort:<br><pre>${JSON.stringify(result, null, 2)}</pre></p>`;
|
|
}
|
|
}
|
|
</script>
|
|
</body>
|
|
</html>
|
|
|