スケッチから情報、そしてアプリケーションへ
TL;DR
- 伝票のようなスケッチ画像からDBスキーマを自動生成し、アプリケーション画面の一覧を経由してプロトタイプを作成する例を紹介します
- 生成したスキーマのレビューを行い、スキーマから最小限必要なアプリケーション一覧を生成します
- 最終的には作成したアプリケーション一覧に基いてプロトタイプ画面を自動生成します
はじめに
Facebookの羽生章洋さんの投稿に、一種の伝票イメージから情報の構造を考えようというものがありました。画面のUIとDBをごっちゃに考えるのではなく、まずはどのような情報が必要かを考えようという趣旨の投稿です。
ご注意)この記事は生成AIの出力を貼り付けていますのでとても長いです。
その投稿をみて、個人的な興味から、情報構造のイメージから生成AIの力を借りて、どのように情報(DB)を導けるかを試してみることにしました。なおここで利用する生成AIはChatGPTです(なお、ここではGPT-4oとo1-previewのモデルを利用します)。
その投稿では冒頭に以下のような図が掲げられていました(引用は許諾済です)。
このイメージは、「ある『時点』で『どのような情報』が発生するのか、そのときの情報の『構造』は何か」がスケッチされた図です。
注文には「注文日」があり、その中には「得意先」「明細」というブロックがあって、他に注文全体の「合計」がある。そして明細の中にはさらに入れ子になった「商品」がありそれに添えられた「数量」と「金額」があるという内容ですね。
さてここで、このスケッチをもとに、データベースの素案を作ってみることにしましょう。GPT-4o に「注文情報」の画像を直接与えて、「この情報を扱うためのデータベースのスキーマを提案して」と依頼します。
その結果返ってきた答は以下のようなものです
画像からテーブル候補を生成
<<GPT-4o による最初のスキーマここから>>
この伝票から考えられるデータベーススキーマは、注文情報に関するデータ構造を適切に表すために、以下のようなテーブル構成が考えられます。
テーブル1: `orders`(注文情報)
`order_id` (INT, PRIMARY KEY) - 注文ID
`order_date` (DATE) - 注文日
`client_name` (VARCHAR) - 得意先名
テーブル2: `order_details`(注文の明細)
`detail_id` (INT, PRIMARY KEY) - 明細ID
`order_id` (INT, FOREIGN KEY to `orders`) - 注文ID (注文情報との関連)
`product_name` (VARCHAR) - 商品名
`unit_price` (DECIMAL) - 単価
`quantity` (INT) - 数量
`amount` (DECIMAL) - 金額 (単価 × 数量)
テーブル3: `products`(商品情報) (Optional)
`product_id` (INT, PRIMARY KEY) - 商品ID
`product_name` (VARCHAR) - 商品名
`unit_price` (DECIMAL) - 単価
テーブル4: `clients`(顧客情報) (Optional)
`client_id` (INT, PRIMARY KEY) - 得意先ID
`client_name` (VARCHAR) - 得意先名
`client_address` (VARCHAR) - 得意先の住所 (もし必要なら)
リレーションシップ:
`orders` テーブルは注文全体の情報を保持します。
`order_details` テーブルは各注文に関連する商品明細を保持します。1つの注文に対して複数の商品が関連するため、`order_id` を外部キーとして使います。
`products` テーブルは商品情報を別管理する場合に利用します(必須ではありません)。
`clients` テーブルは得意先情報を別管理する場合に使えます(必須ではありません)。
この構成により、注文の管理が効率的にでき、例えば特定の得意先の注文履歴を取得したり、商品の販売状況を分析したりすることが可能です。
必要に応じてさらにフィールドやテーブルを追加できますが、上記は基本的な構成です。
<<GPT-4o による最初のスキーマここまで>>
内容を見ると、products(商品情報)とclients(顧客情報)が optional 扱いになっていますね。元の図からはこのテーブルもあり得るけれど必須ではないという扱いになっています。
まあ、そうはいってもやはりこの二つの情報はいわゆる「マスター」にして管理しやすくするのが普通ですので、これらは必須情報にして貰いましょう。引き続き GPT-4o に対して、「products と clients 必須にしてスキーマを作り直して」と依頼してみました。
最初のスキーマを改良
<<GPT-4o による2番目のスキーマここから>>
`products` と `clients` テーブルを必須のものとして取り込んだデータベーススキーマは以下のようになります。このスキーマでは、商品や得意先の情報を一元管理することができ、注文管理がより効率的になります。
テーブル1: `orders`(注文情報)
`order_id` (INT, PRIMARY KEY) - 注文ID
`order_date` (DATE) - 注文日
`client_id` (INT, FOREIGN KEY to `clients`) - 得意先ID(得意先情報との関連)
テーブル2: `order_details`(注文の明細)
`detail_id` (INT, PRIMARY KEY) - 明細ID
`order_id` (INT, FOREIGN KEY to `orders`) - 注文ID(注文情報との関連)
`product_id` (INT, FOREIGN KEY to `products`) - 商品ID(商品情報との関連)
`unit_price` (DECIMAL) - 単価
`quantity` (INT) - 数量
`amount` (DECIMAL) - 金額(単価 × 数量)
テーブル3: `products`(商品情報)
`product_id` (INT, PRIMARY KEY) - 商品ID
`product_name` (VARCHAR) - 商品名
`unit_price` (DECIMAL) - 単価
テーブル4: `clients`(得意先情報)
`client_id` (INT, PRIMARY KEY) - 得意先ID
`client_name` (VARCHAR) - 得意先名
`client_address` (VARCHAR) - 得意先の住所
リレーションシップ:
`orders` テーブルは注文全体の情報を保持し、`client_id` によって得意先(`clients` テーブル)と関連付けられます。
`order_details` テーブルは各注文の明細を管理し、`order_id` によって注文(`orders` テーブル)と、`product_id` によって商品(`products` テーブル)と関連付けられます。
`products` テーブルは商品情報を保持し、明細で利用されます。
`clients` テーブルは得意先情報を保持し、注文に関連付けられます。
データベーススキーマ図の例
orders テーブルは複数の order_details レコードを持つことができ、1つの得意先(`clients` テーブル)と関連付けられます。
order_details は各商品(`products` テーブル)と関連付けられ、その注文における個別の数量や価格を持ちます。
clients テーブルにより、注文の履歴を得意先ごとに追跡することが可能です。
この構成により、以下のようなクエリが可能になります:
得意先ごとの注文履歴
商品ごとの売上データ
注文ごとの詳細な明細分析
これで、商品情報と得意先情報を必須の要素として取り込んだデータベース設計になります。
<<GPT-4o による2番目のスキーマここまで>>
さてこのスキーマを人間が見やすくするために ERD (ER図)にして貰いましょう。GPT-4o に対して「このスキーマを Mermaid で ERD にしてください」と依頼して得た Mermaid のコードをレンダリングしたものが以下のものです。
それっぽいですね。しかし生成しただけで安心してはいけません。このスキーマに対してレビューの依頼を出しましょう。 なお、ここでは GPT-4o ではなく、o1-previewを使ってみます。
生成AIを使ったレビュー
<<o1-preview によるレビューここから>>
レビューのご依頼ありがとうございます。以下に、データベース設計の問題点と改善提案を挙げさせていただきます。
`order_details`テーブルの`amount`フィールドについて:
問題点: `amount`(金額)は`unit_price`(単価)と`quantity`(数量)から計算可能な値であり、データの冗長性を生む可能性があります。データの更新時に整合性の問題が発生するリスクもあります。
改善提案: `amount`フィールドを削除し、必要に応じてクエリ内で計算することを検討してください。もしパフォーマンス上の理由でフィールドを保持する場合は、データの整合性を保つためのトリガーやチェック制約を設定すると良いでしょう。
データ型の詳細な指定:
問題点: データ型が具体的に定義されていないため、データの制約が曖昧です。
改善提案: `VARCHAR`や`DECIMAL`のようなデータ型には、具体的なサイズや精度を指定してください(例:`VARCHAR(255)`, `DECIMAL(10,2)`)。これにより、データの一貫性と最適なストレージ使用が確保できます。
`clients`テーブルの住所情報について:
問題点: `client_address`が一つのフィールドにまとめられているため、住所情報の検索や分析が困難になります。
改善提案: 住所を`street`(番地)、`city`(市区町村)、`state`(都道府県)、`postal_code`(郵便番号)、`country`(国)などの複数のフィールドに分割することを検討してください。
価格の履歴管理:
問題点: `products`テーブルの`unit_price`は現在の価格のみを保持しており、過去の価格変更を追跡できません。
改善提案: 価格変更履歴を管理するために、`product_prices`などの新しいテーブルを作成し、`product_id`、`unit_price`、`start_date`、`end_date`を含めることを検討してください。これにより、過去の注文における正確な価格情報を保持できます。
`order_details`テーブルの主キーについて:
問題点: `detail_id`を主キーとしていますが、実際には`order_id`と`product_id`の組み合わせで一意性が保たれる可能性があります。
改善提案: もし一つの注文内で同じ商品が複数回出現しないのであれば、`order_id`と`product_id`を複合主キーとすることを検討してください。そうでない場合は、現在の`detail_id`をそのまま使用しても問題ありません。
日時データの精度:
問題点: `order_date`が`DATE`型の場合、時間情報が失われます。
改善提案: 注文の時間まで記録する必要がある場合は、`DATETIME`や`TIMESTAMP`型を使用してください。
追加情報の検討:
税金や割引の処理: 注文に対して税金や割引を適用する場合、それらを管理するフィールドやテーブルを追加すると良いでしょう。
在庫管理: 商品の在庫状況を管理する必要がある場合、在庫数を保持するフィールドや専用の在庫管理テーブルを作成することを検討してください。
注文ステータスの追跡: 注文の進行状況(例:処理中、発送済み、完了など)を管理するために、`orders`テーブルに`status`フィールドを追加することをお勧めします。
データの正規化と冗長性の排除:
問題点: データの重複や不整合が発生する可能性があります。
改善提案: 第三正規形に従ってテーブルを設計し、データの冗長性を最小限に抑えるよう努めてください。
インデックスの最適化:
改善提案: クエリのパフォーマンスを向上させるために、頻繁に検索や結合に使用されるフィールドにインデックスを設定することを検討してください。
外部キー制約の設定:
問題点: 外部キーの制約が明確でない場合、データの整合性が損なわれる可能性があります。
改善提案: 各外部キーに対して参照整合性制約を明示的に設定し、関連するテーブル間のデータの一貫性を保つようにしてください。
これらの改善点を考慮することで、データベースの信頼性、パフォーマンス、および将来的な拡張性が向上すると考えられます。
<<o1-preview によるレビューここまで>>
最初の方はまあ良い感じですが、後半になるとだんだん抽象的になってきますね。もっと具体的な指摘も欲しいところです。とはいえこれはもっとユースケースを細かく詰めてやらなければこれ以上は無理かもしれません。
(なお、o1-preview と GPT-4o のレビュー結果を見比べてみましたが、この例ではどちらかが圧倒的に勝っている感じではなく、互いに補完しあう内容のように思えました)
「改善提案」でいくつかの点が挙げられていますが、もし取り込んでも良いものがあれば、それはきちんとデータモデルに反映することにしましょう。
さて、このように折角レビューを行ってもらったのですが、ここでそれを反映すると細かくなりすぎるので、ここでは対応を行わず、レビュー対象になったスキーマをそのまま利用して、別のプロンプトを投入してみることにします。
情報モデルからアプリケーション画面一覧を生成
スキーマを与えて、この情報モデルを操作するためのアプリケーション画面の一覧を生成して貰いましょう。具体的なプロンプトは以下のものとします。
<<アプリケーション画面一覧ここから>>
1. 注文一覧画面
画面上の項目:
注文ID
注文日
得意先名
合計金額
主要な目的: 登録されているすべての注文を一覧表示し、詳細な注文情報へのアクセスや、新規注文の作成、既存注文の編集・削除を行います。
2. 注文詳細画面
画面上の項目:
注文ID
注文日
得意先名
得意先住所
注文明細一覧(商品名、単価、数量、金額)
注文の合計金額
主要な目的: 選択された注文の詳細情報を表示し、注文内容の確認や印刷、編集を行います。
3. 注文登録・編集画面
画面上の項目:
注文日(入力または選択)
得意先選択(得意先IDまたは得意先名で検索・選択)
注文明細の追加・編集機能
商品選択(商品IDまたは商品名で検索・選択)
単価(自動表示または手動入力)
数量(入力)
金額(自動計算:単価 × 数量)
主要な目的: 新規注文の登録や既存注文の内容編集を行い、データベースに保存します。
4. 得意先一覧画面
画面上の項目:
得意先ID
得意先名
得意先住所
主要な目的: すべての得意先情報を一覧表示し、詳細情報へのアクセスや、新規得意先の登録、既存得意先の編集・削除を行います。
5. 得意先詳細画面
画面上の項目:
得意先ID
得意先名
得意先住所
関連する注文履歴一覧(注文ID、注文日、合計金額)
主要な目的: 選択された得意先の詳細情報と、その得意先に関連する注文履歴を表示します。
6. 得意先登録・編集画面
画面上の項目:
得意先名(入力)
得意先住所(入力)
主要な目的: 新しい得意先の情報を登録したり、既存の得意先情報を編集してデータベースに保存します。
7. 商品一覧画面
画面上の項目:
商品ID
商品名
単価
主要な目的: すべての商品情報を一覧表示し、詳細情報へのアクセスや、新規商品の登録、既存商品の編集・削除を行います。
8. 商品詳細画面
画面上の項目:
商品ID
商品名
単価
関連する注文明細一覧(注文ID、得意先名、数量、金額)
主要な目的: 選択された商品の詳細情報と、その商品が含まれる注文明細を表示します。
9. 商品登録・編集画面
画面上の項目:
商品名(入力)
単価(入力)
主要な目的: 新しい商品の情報を登録したり、既存の商品情報を編集してデータベースに保存します。
10. 売上分析画面
画面上の項目:
期間指定(開始日、終了日)
得意先別売上一覧(得意先名、売上合計)
商品別売上一覧(商品名、売上合計、数量合計)
総売上高
主要な目的: 指定された期間内の売上データを分析し、得意先別や商品別の売上状況を把握します。
11. ログイン画面(必要に応じて)
画面上の項目:
ユーザーID(入力)
パスワード(入力)
主要な目的: システムへのアクセスを認証し、セキュリティを確保します。
12. メインメニュー画面
画面上の項目:
注文管理へのリンク
得意先管理へのリンク
商品管理へのリンク
売上分析へのリンク
主要な目的: 各機能へのアクセスを提供し、ユーザーが目的の操作を簡単に行えるようにします。
<<アプリケーション画面一覧ここまで>>
必要最小限のアプリケーションの画面を提案してくれています。プロンプトの与え方が簡素過ぎるので、項目と目的として表示はされているものの、アクションを起こすボタンやリンクなどの扱いが曖昧ですね。
この内容に関してはプロンプトを工夫してもう少し出力形式を細かく指定した方が良いでしょう。
生成したアプリケーション画面一覧からプロトタイプを生成
とりあえず上の画面一覧の1,2,3(注文一覧、注文詳細、注文登録・編集)の定義をノーコードツールの Create.xyz にコピー&ペースト与えて画面を生成してみましょう。
これは各ボタンを押すことで遷移する様子を見ることができます。これで簡単なイメージの共有などはできるかもしれません。
とりあえずのまとめ
以上、情報のスケッチ画像からのスキーマ生成、スキーマのレビュー、スキーマからのアプリケーション画面一覧の生成と、プロトタイピングによるイメージの共有について簡単に例を示しました。
ここで人間が与えたのは、最初の画像と途中のプロンプトだけです。
比較的単純なアプリケーション作成の場合、最初に情報構造をしっかりと考えることで、生成AIによる効率化が期待できます。その意味で入口の部分をステークホルダーたちがしっかり考えることに意味が出てきます。