From b665bae95f2b7d2417fb6d7b9db361d631db022c Mon Sep 17 00:00:00 2001 From: cschlaefke Date: Thu, 23 Jul 2026 22:39:45 +0200 Subject: [PATCH] =?UTF-8?q?Diagnose-Feinschliff:=20Art-Abgleich,=20nur=20F?= =?UTF-8?q?lie=C3=9Ftext,=20konsequentes=20Du?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Die angegebene Art ist jetzt eine unbestätigte Angabe: Claude prüft selbst, welche Pflanze zu sehen ist, beurteilt bei Abweichung die Pflanze im Bild und meldet matchesSpecies=false — die App zeigt dann einen Warnhinweis im Ergebnis-Sheet. Prompt verbietet HTML/Markdown (kein
) und schreibt durchgängiges Duzen vor. Co-Authored-By: Claude Fable 5 --- functions/src/index.ts | 20 ++++++++++--- .../plants/data/plant_diagnosis_service.dart | 7 +++++ .../presentation/plant_detail_screen.dart | 28 +++++++++++++++++++ lib/l10n/app_de.arb | 6 ++++ lib/l10n/generated/app_localizations.dart | 6 ++++ lib/l10n/generated/app_localizations_de.dart | 5 ++++ 6 files changed, 68 insertions(+), 4 deletions(-) diff --git a/functions/src/index.ts b/functions/src/index.ts index a52f724..0bab641 100644 --- a/functions/src/index.ts +++ b/functions/src/index.ts @@ -131,6 +131,7 @@ async function askClaude( */ interface DiagnosisResult { healthy: boolean; + matchesSpecies: boolean; summary: string; details: string; treatment: string; @@ -168,8 +169,15 @@ export const diagnosePlant = onCall( request.data.speciesHint.slice(0, 200) : ""; + // Die Art ist nur eine UNBESTÄTIGTE Angabe des Besitzers — Claude soll + // selbst prüfen, ob das Foto wirklich diese Pflanze zeigt (der Nutzer + // kann versehentlich das Foto einer anderen Pflanze hochladen). const speciesPart = speciesHint ? - `Es handelt sich vermutlich um "${speciesHint}". ` : + `Laut Besitzer soll es sich um "${speciesHint}" handeln — verlasse ` + + "dich NICHT darauf, sondern prüfe selbst, welche Pflanze auf dem " + + "Foto zu sehen ist. Zeigt das Foto eindeutig eine andere Art, " + + "beurteile die Pflanze, die tatsächlich zu sehen ist, und nenne " + + "ihre Art im Kurzbefund. " : ""; const diagnosis = await askClaude( imageBase64, @@ -177,10 +185,14 @@ export const diagnosePlant = onCall( `der Pflanze auf dem Foto. ${speciesPart}` + "Achte auf Blattverfärbungen, Flecken, Schädlinge, Schimmel, " + "Trockenheit, Überwässerung und ähnliche Anzeichen. " + - "Antworte auf Deutsch, in einfacher, freundlicher Sprache " + - "(auch für ältere Menschen gut verständlich, keine Fachbegriffe ohne " + - "Erklärung). Antworte NUR mit JSON im Format " + + "Sprache: Deutsch, einfach und freundlich (auch für ältere Menschen " + + "gut verständlich, keine Fachbegriffe ohne Erklärung). Sprich die " + + "Person immer mit „du“ an, niemals mit „Sie“. Schreibe reinen " + + "Fließtext ohne HTML-Tags (kein
o. Ä.) und ohne " + + "Markdown-Formatierung. Antworte NUR mit JSON im Format " + '{"healthy": , ' + + '"matchesSpecies": , " + '"summary": "", ' + '"details": "<2-3 Sätze: was ist zu sehen und was ist die ' + 'wahrscheinliche Ursache>", ' + diff --git a/lib/features/plants/data/plant_diagnosis_service.dart b/lib/features/plants/data/plant_diagnosis_service.dart index ef15718..aaf87ab 100644 --- a/lib/features/plants/data/plant_diagnosis_service.dart +++ b/lib/features/plants/data/plant_diagnosis_service.dart @@ -8,6 +8,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; class DiagnosisResult { const DiagnosisResult({ required this.healthy, + required this.matchesSpecies, required this.summary, required this.details, required this.treatment, @@ -15,6 +16,11 @@ class DiagnosisResult { }); final bool healthy; + + /// false, wenn das Foto laut KI eindeutig eine andere Pflanzenart zeigt + /// als die im Profil hinterlegte (z. B. versehentlich falsches Foto). + final bool matchesSpecies; + final String summary; final String details; final String treatment; @@ -43,6 +49,7 @@ class PlantDiagnosisService { final data = response.data; return DiagnosisResult( healthy: data['healthy'] as bool? ?? false, + matchesSpecies: data['matchesSpecies'] as bool? ?? true, summary: data['summary'] as String? ?? '', details: data['details'] as String? ?? '', treatment: data['treatment'] as String? ?? '', diff --git a/lib/features/plants/presentation/plant_detail_screen.dart b/lib/features/plants/presentation/plant_detail_screen.dart index 4b7467a..5fe1e3f 100644 --- a/lib/features/plants/presentation/plant_detail_screen.dart +++ b/lib/features/plants/presentation/plant_detail_screen.dart @@ -248,6 +248,34 @@ class _DiagnosisSectionState extends ConsumerState<_DiagnosisSection> { controller: scrollController, padding: const EdgeInsets.all(24), children: [ + if (!result.matchesSpecies) ...[ + Container( + padding: const EdgeInsets.all(12), + decoration: BoxDecoration( + color: theme.colorScheme.errorContainer, + borderRadius: BorderRadius.circular(12), + ), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Icon( + Icons.swap_horiz, + color: theme.colorScheme.onErrorContainer, + ), + const SizedBox(width: 12), + Expanded( + child: Text( + l10n.diagnosisSpeciesMismatch(widget.plant.nickname), + style: theme.textTheme.bodyMedium?.copyWith( + color: theme.colorScheme.onErrorContainer, + ), + ), + ), + ], + ), + ), + const SizedBox(height: 16), + ], Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ diff --git a/lib/l10n/app_de.arb b/lib/l10n/app_de.arb index 8b801e4..aa5ba5b 100644 --- a/lib/l10n/app_de.arb +++ b/lib/l10n/app_de.arb @@ -233,6 +233,12 @@ "diagnosisTreatmentTitle": "Was du tun kannst", "diagnosisPreventionTitle": "So beugst du vor", "diagnosisDisclaimer": "Die Einschätzung kommt von einer KI und kann falsch liegen. Bei wertvollen Pflanzen im Zweifel eine Gärtnerei fragen.", + "diagnosisSpeciesMismatch": "Das Foto zeigt offenbar eine andere Pflanze als „{name}“. Die Einschätzung unten bezieht sich auf die Pflanze im Bild.", + "@diagnosisSpeciesMismatch": { + "placeholders": { + "name": { "type": "String" } + } + }, "diagnosisFailed": "Die Untersuchung hat nicht geklappt. Bitte versuche es erneut.", "saveError": "Speichern fehlgeschlagen. Bitte versuche es erneut.", "saveErrorDetail": "Speichern fehlgeschlagen ({detail}). Bitte versuche es erneut.", diff --git a/lib/l10n/generated/app_localizations.dart b/lib/l10n/generated/app_localizations.dart index 8feb669..0d46777 100644 --- a/lib/l10n/generated/app_localizations.dart +++ b/lib/l10n/generated/app_localizations.dart @@ -862,6 +862,12 @@ abstract class AppLocalizations { /// **'Die Einschätzung kommt von einer KI und kann falsch liegen. Bei wertvollen Pflanzen im Zweifel eine Gärtnerei fragen.'** String get diagnosisDisclaimer; + /// No description provided for @diagnosisSpeciesMismatch. + /// + /// In de, this message translates to: + /// **'Das Foto zeigt offenbar eine andere Pflanze als „{name}“. Die Einschätzung unten bezieht sich auf die Pflanze im Bild.'** + String diagnosisSpeciesMismatch(String name); + /// No description provided for @diagnosisFailed. /// /// In de, this message translates to: diff --git a/lib/l10n/generated/app_localizations_de.dart b/lib/l10n/generated/app_localizations_de.dart index 706d9a0..8d683af 100644 --- a/lib/l10n/generated/app_localizations_de.dart +++ b/lib/l10n/generated/app_localizations_de.dart @@ -469,6 +469,11 @@ class AppLocalizationsDe extends AppLocalizations { String get diagnosisDisclaimer => 'Die Einschätzung kommt von einer KI und kann falsch liegen. Bei wertvollen Pflanzen im Zweifel eine Gärtnerei fragen.'; + @override + String diagnosisSpeciesMismatch(String name) { + return 'Das Foto zeigt offenbar eine andere Pflanze als „$name“. Die Einschätzung unten bezieht sich auf die Pflanze im Bild.'; + } + @override String get diagnosisFailed => 'Die Untersuchung hat nicht geklappt. Bitte versuche es erneut.';