0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

複数モデル対応の画像編集UIをCapability Contractで組み立てる

0
Posted at

オンラインのAI画像編集UIは、最小構成なら「画像を選ぶ、指示を書く、生成する」で作れます。しかし、背景変更、不要物除去、補正、アウトペインティング、修復、スタイル変更を一つの画面で扱い、さらに複数モデルを切り替えられるようにすると、フォームの条件分岐が急速に増えます。

モデルAでは1枚入力、モデルBの合成モードでは複数枚入力、4Kは一部の組み合わせだけ、認証やクレジットが必要な操作もある、という状態です。これらをフロントエンドへ個別にハードコードすると、バックエンドの変更とUIがずれます。

本稿では、利用可能な選択肢をバージョン付きのCapability Contractとして配信し、UI・事前検証・送信・復旧を同じ概念で組み立てる方法を整理します。

前提と解決したい問題

対象は、画像をアップロードし、自然言語で編集内容を指定し、現在利用可能なモデルとオプションで非同期生成するWeb UIです。

避けたいのは次の状態です。

  • 選択できるのにサーバーでは実行できない
  • モデル変更後も無効な解像度が残る
  • 処理中にフォーム変更が実行中ジョブへ混入する
  • タイムアウト後の再送でジョブが二重作成される
  • モデル成功をそのまま「利用可能な結果」と扱う

Capability Contractの型

能力はモデル単位ではなく「モデルとモードの組み合わせ」にぶら下げます。

type CapabilityDocument = {
  version: string;
  models: Array<{
    id: string;
    modes: Array<{
      id: "single_edit" | "multi_fusion";
      minSources: number;
      maxSources: number;
      acceptedMimeTypes: string[];
      maxBytesPerSource: number;
      resolutions: Array<"standard" | "hd" | "4k">;
      aspectRatios: string[];
      requiresAuth: boolean;
      creditCost: number;
    }>;
  }>;
};

「製品のどこかに4Kがある」ことと、「現在のモードで4Kを使える」ことは別です。入力枚数、解像度、アスペクト比、認証、コストを同じ分岐へ配置すると、表示条件と送信条件を一致させやすくなります。

フォームは能力から導出する

初期表示では能力文書を取得し、有効なモデルとモードだけを選びます。親の選択が変わったときは、子の値をすべてリセットするのではなく、まだ有効な値だけ保持します。

function reconcileSelection(
  previous: EditSelection,
  capability: ModeCapability,
): EditSelection {
  return {
    ...previous,
    resolution: capability.resolutions.includes(previous.resolution)
      ? previous.resolution
      : capability.resolutions[0],
    aspectRatio: capability.aspectRatios.includes(previous.aspectRatio)
      ? previous.aspectRatio
      : capability.aspectRatios[0],
  };
}

値を置き換えた場合は、理由も表示します。黙ってフォーム全体を初期化すると、ユーザーが書いたプロンプトや選択の意図まで失われるためです。

クライアント検証とサーバー検証を分ける

クライアント側では、入力枚数、宣言されたMIME、ファイルサイズ、ブラウザでのデコード可否、画像の幅と高さを確認します。これは早いフィードバックのためです。

ただし、ファイル名やブラウザ由来のMIMEは信頼できません。サーバー側では実際の内容を識別してデコードし、圧縮サイズだけでなく総ピクセル数とデコード後のメモリ量も制限します。

さらに生成APIは、能力バージョン、モード、入力枚数、解像度、アスペクト比、認証状態を再検証し、コストを再計算します。クライアントが送る見積額は変更検知には使えても、課金根拠にはできません。

送信時に不変スナップショットを作る

送信ボタンを押した時点で、編集可能なフォームから不変のリクエストを作ります。

type EditRequest = Readonly<{
  capabilityVersion: string;
  sourceAssetIds: string[];
  prompt: string;
  model: string;
  mode: string;
  resolution: string;
  aspectRatio: string;
  quotedCreditCost: number;
}>;

アップロードと生成を分離し、生成側は検証済みのasset IDを参照します。プロンプトを修正して再実行しても、同じ画像バイトを再送する必要がありません。

送信には冪等キーも付けます。ジョブ作成後にHTTP応答だけ失われた場合、同じキーでの再送は同じjob IDを返し、二重実行を防ぎます。

loadingではなく状態機械で表す

idle -> validating_sources -> ready -> submitting
     -> processing -> reviewing -> completed
任意の活動状態 -> failed

processingはジョブが受理された状態であり、結果品質を保証しません。reviewingでは元画像、プロンプト、設定スナップショット、生成結果を並べます。文字、顔、境界、反復テクスチャ、光、構図を確認してからcompletedへ進めます。

失敗ごとに復旧操作を変える

  • 能力バージョン競合: 最新能力を取得し、画像とプロンプトを保持する
  • デコード失敗: 問題のファイルだけを差し替える
  • 認証またはクレジット不足: ジョブ投入前に止め、復帰後に再検証する
  • プロバイダーのタイムアウト: job IDを保持し、「不明」と「失敗」を区別する
  • 結果URLの期限切れ: 既存結果の認可を更新し、再生成しない
  • 品質不合格: 元リクエストを保持して新しい試行を作る

復旧可能かどうかだけでなく、失敗した段階と推奨操作をエラー構造に含めると、UIは正しい次の操作を提示できます。

制約を隠さない

対応モデルとオプションは変わり得ます。高解像度や複数画像入力は全モード共通ではなく、一部の操作には認証やクレジットが必要です。対応形式とファイルサイズにも上限があり、生成結果は必ず人が確認する必要があります。

また、プロンプト中心のオンライン編集は、精密なマスク、レイヤー合成、ピクセル単位の修正、バッチ処理をすべて置き換えるものではありません。

この設計を考える具体例として、自作に関わっているAI Photo Editorを使いました。再利用できるポイントは製品名ではなく、能力を契約として扱い、UIとサーバー検証と復旧を同じ境界にそろえることです。homepage-desktop.png

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?