飲んだお酒を記録する AI アプリ – day 12

30 days AI challenge

本記事は 2026/02/01 ~ 2026/03/01 の間毎日 AI アプリケーション開発(AI を搭載したアプリ開発 or AI を使用した開発)をテーマに 30 days AI challenge を行う 12 日目のブログポストです。

今日のアプリは、”あのお酒おいしかったななんだっけ”、”自分が過去に飲んだ銘柄を覚えてない” という状況に陥る自分のために作った、過去に飲んだお酒を記録するアプリです。

アプリの紹介

アプリ画面は、ユーザーからのお酒情報の入力と、データを表示する画面で構成されます。

裏では SQL Server に接続しています。ユーザーが入力するとデータベースにデータを登録し、登録されたデータから SELECT クエリでデータを取得します。

このアプリの特徴は、データを登録して閲覧できるだけでなく、過去に飲んだことあるお酒の中から AI がおすすめのお酒を教えてくれます。

以下のように、ユーザーの好みや雰囲気を入力することで、おすすめのお酒を AI が教えてくれます。

お酒データを表示する際に画像データを表示したかったのですが、Bing Search も Google Search もいずれも retire 予定で AI 検索に統合されているようだったので、AI search を実装するまででもないと思い断念しました。

技術観点

技術スタックはこんな感じ。といってもこれまでのアプリはたいていこの構成です。

local での動作なので、Azure SQL ではなく、手元の PC の SQL Server を使用しています。

レイヤー技術役割
UIBlazor ServerリアルタイムインタラクティブUI
バックエンドC# (BarService)ビジネスロジック
データベースSQL Server + Microsoft.Data.SqlClientデータ永続化
AIGoogle Gemini APIお酒推奨エンジン

AI 実装箇所はこんな感じです(by Claude Haiku 4.5)。

GetAllDrinksAsync() メソッドでデータベースからすべてのデータを取得しているので、登録済みのデータを Gemini に渡すことができます。
そこからはいつも通りプロンプトで力技です。

public async Task<(Drink? drink, string reasoning)> GetRecommendedDrinkAsync(string userPrompt)
    {
        try
        {
            var allDrinks = await GetAllDrinksAsync();

            if (allDrinks.Count == 0)
            {
                return (null, "データベースにお酒が登録されていません");
            }

            // お酒情報をテキスト化
            var drinksText = string.Join("\n", allDrinks.Select(d => 
                $"- {d.Name} ({d.Type}): {d.Notes}"));

            var prompt = $@"ユーザーの以下のリクエストに基づいて、最も適切なお酒を登録済みのお酒から1つ選んでください。

ユーザーのリクエスト:
{userPrompt}

登録済みのお酒:
{drinksText}

レスポンスフォーマット:
お酒の名前: [選択したお酒の名前]
理由: [選択理由(日本語で簡潔に)]";

            var history = new List<Google.GenAI.Types.Content>();
            var response = await _geminiService.GetChatResponseAsync(prompt, history);

            // レスポンスからお酒の名前を抽出
            var lines = response.Split('\n');
            string? selectedDrinkName = null;
            string reasoning = "";

            foreach (var line in lines)
            {
                if (line.StartsWith("お酒の名前:"))
                {
                    selectedDrinkName = line.Replace("お酒の名前:", "").Trim();
                }
                else if (line.StartsWith("理由:"))
                {
                    reasoning = line.Replace("理由:", "").Trim();
                }
            }

            if (string.IsNullOrEmpty(selectedDrinkName))
            {
                return (null, "お酒を選択できませんでした");
            }

            var selectedDrink = allDrinks.FirstOrDefault(d => d.Name == selectedDrinkName);
            return (selectedDrink, reasoning);
        }
        catch (Exception ex)
        {
            _logger.LogError($"Error getting recommended drink: {ex.Message}");
            return (null, $"エラーが発生しました: {ex.Message}");
        }
    }

終わりに

データ登録、データ表示アプリはやはり画像がないと味気ない画面になってしまいますね。

画像を取得する場合は、データ登録時に画像検索 API にて対象のお酒と一致する画像を取得し、その画像データ or URL を DB に格納し、表示する方法がよさそうです(権利関係に注意ですが)。これは今後の課題で修正していきたいと思います。

これまでのアプリでも “今後修正します”, “今後機能追加します” と記載したものが多くありますが、30 days AI challenge はひとまず形のある AI でアプリを作ることが目的なので、細かい機能追加については実装できませんが、大目に見てください。

今日はここまでにします。閲覧いただきありがとうございました!

コメント

タイトルとURLをコピーしました