From 3c3458ffb85738623568fe01a6bfc8e2ddb0ead3 Mon Sep 17 00:00:00 2001 From: Ponshu Developer Date: Wed, 15 Apr 2026 15:52:50 +0900 Subject: [PATCH] fix: restore detail spec inference while keeping name/brand/prefecture OCR-strict - name, brand, prefecture: still OCR-strict (no completion/inference) - alcoholContent, polishingRatio, tasteStats etc: restored from label + sake knowledge - Overly strict 'no knowledge at all' rule was causing all detail specs to return null - Sync fallback prompt with same policy - Bump version 1.0.36+43 -> 1.0.37+44 Co-Authored-By: Claude Sonnet 4.6 --- lib/services/gemini_service.dart | 88 ++++++++++++++++++-------------- pubspec.yaml | 2 +- 2 files changed, 50 insertions(+), 40 deletions(-) diff --git a/lib/services/gemini_service.dart b/lib/services/gemini_service.dart index d53858f..70020b1 100644 --- a/lib/services/gemini_service.dart +++ b/lib/services/gemini_service.dart @@ -23,33 +23,41 @@ class GeminiService { Future analyzeSakeLabel(List imagePaths, {bool forceRefresh = false}) async { // クライアント側プロンプトでスキーマの一貫性を保証 const prompt = ''' -あなたは「ラベルの文字を読むだけ」の光学文字認識(OCR)システムです。 -日本酒の知識・データベース・推測は一切使わないでください。 +あなたは日本酒ラベル解析の専門家です。 +添付画像から情報を読み取り、以下のJSONを返してください。 -## nameとbrandの絶対ルール -- ラベルに印刷されている文字を「写真に写っている通り」に出力する -- 文字の補完・完成・訂正は禁止(例: ラベルに「東魁」→ name は「東魁」、「東魁盛」に変えてはいけない) -- ラベルに「白鹿」→「白鹿」(「白鹿本醸造」などに変えない) -- ラベルに「久保田」→「久保田」(「久保田 千寿」などに変えない) -- 「もしかして○○のことかな」という判断は禁止。見えた文字だけ。 +## 【絶対ルール】name・brand・prefectureの読み取り +これら3フィールドのみ、ラベルに印刷された文字を一字一句そのまま出力してください。 +- あなたの知識でラベルの文字を補完・訂正・変更することは禁止 +- ラベルに「東魁」→ "name": "東魁"(「東魁盛」に変えない) +- ラベルに「白鹿」→ "name": "白鹿"(「白鹿本醸造」に変えない) +- ラベルに「久保田」→ "name": "久保田"(「久保田 千寿」に変えない) +- prefecture: ラベルに都道府県名が書かれていればそのまま出力、書かれていなければ null + (銘柄名から産地を推測して埋めることは禁止) -## prefectureのルール -- ラベルに都道府県名が書かれていれば、その文字をそのまま出力する -- 書かれていない場合は null(銘柄名から推測して埋めることは禁止) - -## description・tasteStats・flavorTagsのルール -- これらはラベルに書かれている情報と、type(特定名称)から推定してよい -- ただし nameで読み取った銘柄名の「知名度・評判・産地イメージ」は参照しないこと -- tasteStatsは1〜5の整数(ラベルに根拠がなければ中間値の3を使う) +## その他のフィールド(推定・推定可) +以下はラベルから読み取れる情報+日本酒の一般知識を使って推定してください。 +- type: ラベルに書かれた特定名称(純米大吟醸など)。なければ null +- description: ラベル情報と type から推定した味・特徴の説明(100文字程度) +- catchCopy: 20文字以内のキャッチコピー +- flavorTags: 味のタグ(フルーティー・辛口・華やか など) +- tasteStats: 1〜5の整数。ラベルや type から推定。不明なら 3 +- alcoholContent: ラベルに記載があれば読む。なければ type から一般的な値を推定(例: 純米大吟醸→15.0) +- polishingRatio: ラベルに記載があれば読む。なければ type から推定(例: 大吟醸→50) +- sakeMeterValue: ラベルに記載があれば読む。なければ推定 +- riceVariety: ラベルに記載があれば読む。なければ null +- yeast: ラベルに記載があれば読む。なければ null +- manufacturingYearMonth: ラベルに記載があれば読む。なければ null +- confidenceScore: 画像の鮮明度・情報量から 0〜100 で評価 ## 出力形式 以下のJSONのみ返す(説明文不要): { - "name": "ラベルに写っている銘柄名の文字(一字一句そのまま)", - "brand": "ラベルに写っている蔵元名の文字(一字一句そのまま)", - "prefecture": "ラベルに書かれた都道府県名(なければnull)", + "name": "ラベルに写っている銘柄名の文字(一字一句そのまま・補完禁止)", + "brand": "ラベルに写っている蔵元名の文字(一字一句そのまま・補完禁止)", + "prefecture": "ラベルに書かれた都道府県名(なければnull・推測禁止)", "type": "特定名称(ラベルから読む。なければnull)", - "description": "ラベルの情報とtypeから推定した説明文(100文字程度)", + "description": "ラベル情報とtypeから推定した説明文(100文字程度)", "catchCopy": "20文字以内のキャッチコピー", "confidenceScore": 80, "flavorTags": ["フルーティー", "辛口"], @@ -229,26 +237,33 @@ class GeminiService { const primaryModel = 'gemini-2.5-flash'; const fallbackModel = 'gemini-2.0-flash'; - // Prepare Prompt (customPrompt が null のケースは通常発生しないが念のため同じ内容を保持) + // customPrompt は analyzeSakeLabel から常に渡される。null になるケースは通常存在しない。 final promptText = customPrompt ?? ''' -【絶対ルール】ラベルに印刷されている文字を一字一句そのまま読み取ること。 -あなたの学習データ・知識でラベルの銘柄名や蔵元名を補完・変更・推測することは厳禁。 -例: ラベルに「東魁」とだけ書かれていれば "name" は「東魁」(「東魁盛」ではない) +あなたは日本酒ラベル解析の専門家です。 +添付画像から情報を読み取り、以下のJSONを返してください。 ---- +## 【絶対ルール】name・brand・prefectureの読み取り +これら3フィールドのみ、ラベルに印刷された文字を一字一句そのまま出力してください。 +- あなたの知識でラベルの文字を補完・訂正・変更することは禁止 +- ラベルに「東魁」→ "name": "東魁"(「東魁盛」に変えない) +- prefecture: ラベルに都道府県名が書かれていればそのまま出力、なければ null(推測禁止) -あなたは日本酒の専門家(ソムリエ)です。 -添付の画像(日本酒のラベル)を解析し、以下のJSON形式で情報を返してください。 +## その他のフィールド(推定可) +ラベル情報+日本酒の一般知識を使って推定してください。 +- tasteStats: 1〜5の整数。不明なら 3 +- alcoholContent・polishingRatio: ラベルに記載があれば読む。なければ type から推定 +## 出力形式 +以下のJSONのみ返す(説明文不要): { - "name": "ラベルに書かれた銘柄名をそのまま(変更・補完禁止)", - "brand": "ラベルに書かれた蔵元名をそのまま(変更・補完禁止)", - "prefecture": "都道府県名", - "type": "特定名称(純米大吟醸など)", - "description": "味や特徴の魅力的な説明文(100文字程度)", - "catchCopy": "短いキャッチコピー(20文字以内)", + "name": "ラベルに写っている銘柄名の文字(一字一句そのまま・補完禁止)", + "brand": "ラベルに写っている蔵元名の文字(一字一句そのまま・補完禁止)", + "prefecture": "ラベルに書かれた都道府県名(なければnull・推測禁止)", + "type": "特定名称(ラベルから読む。なければnull)", + "description": "ラベル情報とtypeから推定した説明文(100文字程度)", + "catchCopy": "20文字以内のキャッチコピー", "confidenceScore": 80, - "flavorTags": ["フルーティー", "辛口", "華やか"], + "flavorTags": ["フルーティー", "辛口"], "tasteStats": {"aroma":3,"sweetness":3,"acidity":3,"bitterness":3,"body":3}, "alcoholContent": 15.0, "polishingRatio": 50, @@ -257,11 +272,6 @@ class GeminiService { "yeast": "きょうかい9号", "manufacturingYearMonth": "2023.10" } - -★必須ルール: -- "name"と"brand"はラベルから目視で読んだ文字のみ。知識での変更は絶対禁止。 -- tasteStatsは必ず1〜5の整数で埋めること。 -- 不明な値は null または合理的な推測値。 '''; // Prepare Content parts (画像バイト読み込みは一度だけ) diff --git a/pubspec.yaml b/pubspec.yaml index 9b4ae1d..182ae08 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -16,7 +16,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html # In Windows, build-name is used as the major, minor, and patch parts # of the product and file versions while build-number is used as the build suffix. -version: 1.0.36+43 +version: 1.0.37+44 environment: sdk: ^3.10.1