Diagnose-Feinschliff: Art-Abgleich, nur Fließtext, konsequentes Du
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 <br>) und schreibt durchgängiges Duzen vor. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
18b060413b
commit
b665bae95f
6 changed files with 68 additions and 4 deletions
|
|
@ -131,6 +131,7 @@ async function askClaude<T>(
|
|||
*/
|
||||
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<DiagnosisResult>(
|
||||
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 <br> o. Ä.) und ohne " +
|
||||
"Markdown-Formatierung. Antworte NUR mit JSON im Format " +
|
||||
'{"healthy": <true wenn die Pflanze gesund wirkt, sonst false>, ' +
|
||||
'"matchesSpecies": <false NUR wenn eine Art angegeben wurde und das ' +
|
||||
"Foto eindeutig eine andere Pflanzenart zeigt, sonst true>, " +
|
||||
'"summary": "<Kurzbefund in 1 Satz>", ' +
|
||||
'"details": "<2-3 Sätze: was ist zu sehen und was ist die ' +
|
||||
'wahrscheinliche Ursache>", ' +
|
||||
|
|
|
|||
|
|
@ -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? ?? '',
|
||||
|
|
|
|||
|
|
@ -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: [
|
||||
|
|
|
|||
|
|
@ -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.",
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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.';
|
||||
|
|
|
|||
Loading…
Reference in a new issue