fix(ai): ハルシネーション防止を一般化・N文字ルール追加・6銘柄例示

- systemInstruction をOCR専門システムとして再定義(東魁限定から一般化)
- 「ラベルにN文字なら必ずN文字のみ出力」ルールを明示化
- 東魁/男山/白鹿/久保田/白鶴/松竹梅の6銘柄例を追加
- 出力前セルフチェック手順をプロンプトに追加
- menu_pricing_screen: SharedPreferences を async/await+try-catch に修正
- version: 1.0.43+50 → 1.0.44+51
- web: eiji/maita 向け consumer APK (v1.0.44) を配置・ダウンロードページ更新

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Ponshu Developer 2026-04-23 00:26:13 +09:00
parent d72587ac19
commit 0fb4f6ea8b
6 changed files with 69 additions and 48 deletions

View File

@ -27,27 +27,26 @@ class _MenuPricingScreenState extends ConsumerState<MenuPricingScreen> {
@override @override
void initState() { void initState() {
super.initState(); super.initState();
// One-time hint for Exit button WidgetsBinding.instance.addPostFrameCallback((_) => _showExitHintIfNeeded());
WidgetsBinding.instance.addPostFrameCallback((_) { }
final prefs = SharedPreferences.getInstance();
final shown = prefs.then((p) => p.getBool('business_mode_help_shown') ?? false); Future<void> _showExitHintIfNeeded() async {
try {
shown.then((hasShown) { final prefs = await SharedPreferences.getInstance();
if (!hasShown && mounted) { final hasShown = prefs.getBool('business_mode_help_shown') ?? false;
ScaffoldMessenger.of(context).showSnackBar( if (!hasShown && mounted) {
SnackBar( ScaffoldMessenger.of(context).showSnackBar(
content: const Text('右上の×でいつでも終了できます'), SnackBar(
duration: const Duration(seconds: 3), content: const Text('右上の×でいつでも終了できます'),
action: SnackBarAction( duration: const Duration(seconds: 3),
label: 'OK', action: SnackBarAction(label: 'OK', onPressed: () {}),
onPressed: () {}, ),
), );
), await prefs.setBool('business_mode_help_shown', true);
); }
prefs.then((p) => p.setBool('business_mode_help_shown', true)); } catch (e) {
} debugPrint('Failed to load/save business_mode_help_shown: $e');
}); }
});
} }
@override @override

View File

@ -26,17 +26,32 @@ class GeminiService {
JSONを返してください JSONを返してください
## namebrandprefectureの読み取り ## namebrandprefectureの読み取りOCR厳守
3 3
-
- "name": "東魁"
- 鹿 "name": "白鹿"鹿
- "name": "久保田" 寿
- prefecture: null
##
使 - (2) "東魁"
- 鹿(2) "白鹿" 鹿
- (3) "久保田" 寿
- (2) "男山"
- (2) "白鶴"
- (3) "松竹梅"
N N
- prefecture: null
##
namebrand
-
-
##
使
- type: null - type: null
- description: type 100 - description: type 100
- catchCopy: 20 - catchCopy: 20
@ -229,10 +244,12 @@ class GeminiService {
JSONを返してください JSONを返してください
## namebrandprefectureの読み取り ## namebrandprefectureの読み取りOCR厳守
3 3
-
- "name": "東魁"
/ / 鹿鹿
N N
- prefecture: null - prefecture: null
## ##
@ -287,10 +304,15 @@ class GeminiService {
model: modelName, model: modelName,
apiKey: apiKey, apiKey: apiKey,
systemInstruction: Content.system( systemInstruction: Content.system(
'あなたは画像内のテキストを一字一句正確に読み取る専門家です。' 'あなたは画像内のテキストを一字一句正確に書き起こすOCR光学文字認識専門システムです。\n'
'ラベルに記載された銘柄名・蔵元名は絶対に変更・補完しないでください。' '【絶対的制約 — name・brand・prefecture フィールドに適用】\n'
'あなたの知識でラベルの文字を上書きすることは厳禁です。' '1. ラベルに印刷されている文字だけを出力する。ラベルにない文字を1文字も追加してはならない。\n'
'ラベルに「東魁」とあれば、「東魁盛」を知っていても必ず「東魁」と出力してください。', '2. ラベルに N 文字の銘柄名があれば N 文字のまま出力する。文字数を増やすことは禁止。\n'
'3. あなたが知っている「正式名称」「有名銘柄名」への変換・補完は禁止。\n'
' 例: 「東魁」→「東魁」(「東魁盛」禁止)、「男山」→「男山」(「男山本醸造」禁止)、\n'
' 「白鹿」→「白鹿」(「白鹿本醸造」禁止)、「久保田」→「久保田」(「久保田 千寿」禁止)\n'
'4. ラベルに都道府県名がなければ prefecture は null。銘柄名から産地を推測して埋めることは禁止。\n'
'5. 日本酒知識は description・flavorTags・tasteStats 等の推定フィールドにのみ使用すること。',
), ),
generationConfig: GenerationConfig( generationConfig: GenerationConfig(
responseMimeType: 'application/json', responseMimeType: 'application/json',

View File

@ -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 # 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 # 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. # of the product and file versions while build-number is used as the build suffix.
version: 1.0.43+50 version: 1.0.44+51
environment: environment:
sdk: ^3.10.1 sdk: ^3.10.1

View File

@ -51,7 +51,7 @@
<!-- Download Section --> <!-- Download Section -->
<section class="download"> <section class="download">
<div class="version-cards"> <div class="version-cards">
<!-- Lite Version --> <!-- Lite Version (Eiji) -->
<a href="/releases/ponshu-room-lite.apk" class="version-card" download> <a href="/releases/ponshu-room-lite.apk" class="version-card" download>
<div class="card-header"> <div class="card-header">
<span class="version-name">Lite</span> <span class="version-name">Lite</span>
@ -62,21 +62,21 @@
<path d="M12 3v12m0 0l-4-4m4 4l4-4M5 17v2a2 2 0 002 2h10a2 2 0 002-2v-2" /> <path d="M12 3v12m0 0l-4-4m4 4l4-4M5 17v2a2 2 0 002 2h10a2 2 0 002-2v-2" />
</svg> </svg>
</div> </div>
<span class="file-size">88 MB</span> <span class="file-size">90 MB</span>
</a> </a>
<!-- Pro Version --> <!-- Maita Version -->
<a href="/releases/ponshu-room-pro.apk" class="version-card pro" download> <a href="/releases/ponshu-room-maita.apk" class="version-card" download>
<div class="card-header"> <div class="card-header">
<span class="version-name">Pro</span> <span class="version-name">Maita</span>
<span class="version-badge pro-badge">Pro</span> <span class="version-badge free">Dev</span>
</div> </div>
<div class="card-icon"> <div class="card-icon">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"> <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">
<path d="M12 3v12m0 0l-4-4m4 4l4-4M5 17v2a2 2 0 002 2h10a2 2 0 002-2v-2" /> <path d="M12 3v12m0 0l-4-4m4 4l4-4M5 17v2a2 2 0 002 2h10a2 2 0 002-2v-2" />
</svg> </svg>
</div> </div>
<span class="file-size">89 MB</span> <span class="file-size">90 MB</span>
</a> </a>
</div> </div>
@ -116,7 +116,7 @@
<!-- Footer --> <!-- Footer -->
<footer> <footer>
<p>v1.0.12</p> <p>v1.0.44</p>
</footer> </footer>
</div> </div>
</body> </body>

Binary file not shown.

Binary file not shown.