Copilot + Claude Sonnet を活用して web api を作る下準備

ここまで、3つのクライアント(React, Blazor, Kotlin)を作ってきましたが、実際のプロジェクトでは Web API も同時に作ることが多いです。既存のシステムの場合は、既に web api があるかもしれませんが。

mock のダミー画像データを作る

新人研修ではモスバーガーの画像をそのまま借用してしまっているのですが、版権的に良くないのでダミー画像を作ります。注文サイト&管理サイトの研修方式は数年間からやっていて、このダミー画像が結構面倒なのですが、いまだと OpenAI の DALL-E でできます。

当然のことながら OpenAI の API KEY が必要になりますが、そこは必要経費ということで。

# ハンバーガー注文サイト用の画像ファイルを作成する

## カテゴリ用の画像作成


```
slug title filename
-------- ------------------------------------- --------------------
"special1" "今月のお薦め" "special1.jpg"
"special3" "限定メニュー" "special3.jpg"
"special2" "ネット注文特別価格メニュー" "special2.jpg"
"main1" "メインメニュー" "main1.jpg"
"main2" "ハンバーガー" "main2.jpg"
"main3" "ホットドック" "main3.jpg"
"main4" "ソイパティ" "main4.jpg"
"sidemenu1" "サイドメニュー" "sidemenu1.jpg"
"sidemenu2" "ドリンク・スープ" "sidemenu2.jpg"
"sidemenu3" "デザート" "sidemenu3.jpg"
```

カテゴリの title に適した画像ファイルを 690x560 px で作成してください。

こんな形でドキュメントを書いて、Copilot に渡しますが、あえなく挫折。

Claude Sonnet だと画像ができないそうなので。これを ChatGPT でちまちま作ってもいいのですが、カテゴリも10種類、商品となるハンバーガー等の画像は100個ぐらいが必要になります。

なので、量産するコードをを書いて貰いましょう。

openai を使って、プロンプトを指示して、指定サイズの画像を作るコードを書いて。

ツールで使う言語は何でもいいのですが、C# のコンソールアプリにしています。

using OpenAI;
using OpenAI.Images;
using System.ClientModel;

class Program
{
    // カテゴリ情報の定義
    private static readonly Dictionary<string, string> CategoryInfo = new()
    {
        { "special1", "今月のお薦め" },
        { "special3", "限定メニュー" },
        { "special2", "ネット注文特別価格メニュー" },
        { "main1", "メインメニュー" },
        { "main2", "ハンバーガー" },
        { "main3", "ホットドック" },
        { "main4", "ソイパティ" },
        { "sidemenu1", "サイドメニュー" },
        { "sidemenu2", "ドリンク・スープ" },
        { "sidemenu3", "デザート" }
    };

    // 各カテゴリに対応する英語プロンプト
    private static readonly Dictionary<string, string> CategoryPrompts = new()
    {
        { "special1", "A beautiful and appetizing seasonal hamburger set with fresh ingredients, warm orange and yellow color scheme, professional food photography, featuring 'Monthly Recommendation' text overlay" },
        { "special3", "A premium limited edition hamburger with luxury presentation, elegant black and gold color scheme, high-end food photography, featuring 'Limited Menu' text overlay" },
        { "special2", "A special price hamburger with discount elements, eye-catching red and white color scheme, online ordering theme, featuring 'Special Online Price' text overlay" },
        { "main1", "A variety of delicious hamburgers arranged together, appetizing brown and red color scheme, main menu showcase, featuring 'Main Menu' text overlay" },
        { "main2", "A classic hamburger cross-section showing layers of bun, patty, and fresh vegetables, vibrant green, red, and brown colors, featuring 'Hamburger' text overlay" },
        { "main3", "A delicious hot dog with sausage, bun, mustard and ketchup, warm brown and red color scheme, featuring 'Hot Dog' text overlay" },
        { "main4", "A healthy soy patty burger with fresh vegetables, healthy green and brown color scheme, emphasizing healthiness, featuring 'Soy Patty' text overlay" },
        { "sidemenu1", "A variety of side dishes including french fries and onion rings, golden yellow color scheme, featuring 'Side Menu' text overlay" },
        { "sidemenu2", "Various drinks and soups with glasses, cups, ice, and steam, refreshing blue and clear color scheme, featuring 'Drinks & Soup' text overlay" },
        { "sidemenu3", "Delicious desserts including ice cream and pies, sweet pastel color scheme, happy atmosphere, featuring 'Dessert' text overlay" }
    };

    static async Task Main(string[] args)
    {
        // OpenAI API キーを環境変数から取得
        var apiKey = Environment.GetEnvironmentVariable("OPENAI_API_KEY");
        if (string.IsNullOrEmpty(apiKey))
        {
            Console.WriteLine("エラー: OPENAI_API_KEY 環境変数が設定されていません。");
            Console.WriteLine("使用方法: set OPENAI_API_KEY=your_api_key_here");
            return;
        }

        var client = new OpenAIClient(apiKey);

        // 出力ディレクトリを作成
        var outputDir = Path.Combine(Directory.GetCurrentDirectory(), "generated_images");
        Directory.CreateDirectory(outputDir);

        Console.WriteLine("ハンバーガー注文サイト用画像生成ツール");
        Console.WriteLine("========================================");

        if (args.Length == 0)
        {
            Console.WriteLine("使用方法:");
            Console.WriteLine("  すべてのカテゴリ画像を生成: dotnet run all");
            Console.WriteLine("  特定のカテゴリ画像を生成: dotnet run <slug>");
            Console.WriteLine("  カスタムプロンプトで生成: dotnet run custom \"<prompt>\" <filename>");
            Console.WriteLine();
            Console.WriteLine("利用可能なカテゴリ:");
            foreach (var category in CategoryInfo)
            {
                Console.WriteLine($"  {category.Key} - {category.Value}");
            }
            return;
        }

この手のやつは、ちまちま本に書いた覚えがあるのですが、AI に書いて貰うほうが断然楽です。どうせ使い捨てツールなので、中身はどうということはありません。

実行すると、こんな感じで画像を作ってくれます。

商品画像のほうは量が多いのでデータベースから読み込むようにしてもいいのですが、これも使い捨てツールなので商品画像の作成機能を追加してしまいます。

同じパターンで、商品画像の作成も追加して。

互換性もなんのその、豪快にメニューを書き変えて「カテゴリ画像」と「商品画像」の両方が生成できるようにしてくれます。

        Console.WriteLine("ハンバーガー注文サイト用画像生成ツール");
        Console.WriteLine("========================================");

        if (args.Length == 0)
        {
            Console.WriteLine("使用方法:");
            Console.WriteLine("  すべてのカテゴリ画像を生成: dotnet run category-all");
            Console.WriteLine("  特定のカテゴリ画像を生成: dotnet run category <slug>");
            Console.WriteLine("  すべての商品画像を生成: dotnet run product-all");
            Console.WriteLine("  特定の商品画像を生成: dotnet run product <slug>");
            Console.WriteLine("  カスタムプロンプトで生成: dotnet run custom \"<prompt>\" <filename>");
            Console.WriteLine();
            Console.WriteLine("利用可能なカテゴリ:");
            foreach (var category in CategoryInfo)
            {
                Console.WriteLine($"  {category.Key} - {category.Value}");
            }
            Console.WriteLine();
            Console.WriteLine("利用可能な商品:");
            foreach (var product in ProductInfo)
            {
                Console.WriteLine($"  {product.Key} - {product.Value}");
            }
            return;
        }

生成中です。

ひとまず、これで版権をクリアした画像の作成が完了。

テーブル定義

DDL定義(テーブル定義)を作成しておきます。

会社でデータベース設計書などを作るときは Excel を使うことが多いと思うのですが、おそらく ddl.md というドキュメントファイルを作って、create table の定義を並べておいたほうが AI に理解しやすいです。いったん、MySQL Workbeanch とか、SQL Server Management Studio などを使ってテーブル作成した後にスクリプトに落とし込んでもいいでしょう。

# DDL 定義

## categories

カテゴリのテーブル定義です。

```sql
CREATE TABLE categories (
id INTEGER PRIMARY KEY AUTOINCREMENT,
slug varchar(255) NOT NULL UNIQUE,
title varchar(255) NOT NULL,
description TEXT,
image varchar(255),
sortid INTEGER NOT NULL DEFAULT 0,
display INTEGER NOT NULL DEFAULT 1,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
deleted_at TIMESTAMP NULL DEFAULT NULL,
);
```
## products

商品のテーブル定義です。

```sql
CREATE TABLE products (
id INTEGER PRIMARY KEY AUTOINCREMENT,
category_id INTEGER,
slug varchar(255) NOT NULL UNIQUE,
name varchar(255) NOT NULL,
description TEXT,
image varchar(255),
price REAL NOT NULL,
sortid INTEGER NOT NULL DEFAULT 0,
display INTEGER NOT NULL DEFAULT 1,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
deleted_at TIMESTAMP NULL DEFAULT NULL,
FOREIGN KEY (category_id) REFERENCES categories(id)
);
```

## orders

注文のテーブル定義です。

```sql
CREATE TABLE orders (
id INTEGER PRIMARY KEY AUTOINCREMENT,
order_number varchar(10) NOT NULL,
total_price REAL NOT NULL,
total_quantity INTEGER NOT NULL,
status INTEGER NOT NULL DEFAULT 0,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
deleted_at TIMESTAMP NULL DEFAULT NULL,
);
```

## order_products

注文と商品を関連付ける中間テーブルの定義です。

```sql
CREATE TABLE order_products (
id INTEGER PRIMARY KEY AUTOINCREMENT,
order_id INTEGER NOT NULL,
product_id INTEGER NOT NULL,
price REAL NOT NULL,
quantity INTEGER NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
deleted_at TIMESTAMP NULL DEFAULT NULL,
FOREIGN KEY (order_id) REFERENCES orders(id),
FOREIGN KEY (product_id) REFERENCES products(id)
);
```

コードファースト形式の場合、マイグレーションのコードをプログラムに書くことになるのですが、これは言語やフレームワークに依存してしまいます。Ruby, Laravel, C# などそれぞれの言語でしか使えない形式になってしまうので、CREATE TABLE のように SQL のままのほうがよいと思います。たぶん、各言語で書いたとしても別の言語に AI がコンバートしてくれるだろうから、大丈夫だとは思うのですが。

OpenAPI定義を書く

web api の定義として、RESTful で書いていくか、OpenAPI で書いていくか、ルーティングをどう記述していくかが問題になると思いますが、ここでは OpenAPI定義を直接書いていきます。

この部分も諸々仕様書に書き起こすこともできるのですが、現状だと

  • テーブル定義から OpenAPI 定義を書き出す
  • OpenAPI 定義から設計書等へ書き出す

のように、リバース型式でドキュメントを書いたほうが楽です。

db/ddl.md のテーブル定義を参考にして、
mos-api.yaml に CURD を呼び出せる openapi を書き出して。

このように DDL の定義から CRUD 形式を一発で作成してくれます。

openapi: 3.1.1
info:
title: Mos API
description: Mos API
version: 1.0.0
contact:
name: moonmile solutions
servers:
- url: "http://localhost:8000/mos/api"
paths:
# Categories CRUD
/categories:
get:
summary: Get all categories
operationId: getCategories
parameters:
- name: display
in: query
description: Filter by display status
required: false
schema:
type: integer
enum: [0, 1]
responses:
'200':
description: Successful response
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/Category'
post:
summary: Create a new category
operationId: createCategory
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/CategoryCreate'
responses:
'201':
description: Category created successfully
content:
application/json:
schema:
$ref: '#/components/schemas/Category'
'400':
description: Bad request
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
...

ハンバーガー注文サイトの場合は、注文 /orders など、いくつかの web api を用意することがあるのですが、ひとまず Copilot で mos-api.yaml を書いて貰います。

vscode でプレビューもできるし、実際にここから web api を呼び出すこともできます。

参照

moonmile/mos-ai-sample: opilot + Claude Sonnet 4 でハンバーガー注文サイト作成のサンプル https://github.com/moonmile/mos-ai-sample

  • src/tools/make-image ダミー画像の作成ツール
  • src/db/ddl.md テーブル定義
  • src/webapi/mos-api.yaml OpenAPI 定義

カテゴリー: 開発 パーマリンク