AIで画像や動画を生成していると、こんなファイルが増えていきます。
output-01.png
output-02.png
output-final.png
output-final-2.png
clip-good.mp4
clip-retry.mp4
問題はファイル名ではありません。
数日後に clip-good.mp4 を見て、
これ、どのプロンプトと参照画像で作った?
となることです。
複数の生成モデルを使う場合、この問題はさらに大きくなります。
そこで最近は、生成物だけではなく生成そのものをJobとして保存する方が扱いやすいと考えています。
最小構成のGeneration Job
まず、最低限残したい情報をTypeScriptで定義します。
type GenerationType =
| "text-to-image"
| "image-to-image"
| "text-to-video"
| "image-to-video";
type GenerationStatus =
| "queued"
| "processing"
| "completed"
| "failed"
| "review";
interface GenerationJob {
id: string;
type: GenerationType;
model: string;
prompt: string;
promptVersion: number;
references: string[];
settings: {
aspectRatio?: string;
duration?: number;
};
status: GenerationStatus;
output?: string;
createdAt: string;
}
重要なのは、output だけではなく、
- model
- prompt
- promptVersion
- references
- settings
を一緒に保存することです。
Jobを作る
簡単なfactoryを用意します。
function createJob(
type: GenerationType,
model: string,
prompt: string,
references: string[] = []
): GenerationJob {
return {
id: crypto.randomUUID(),
type,
model,
prompt,
promptVersion: 1,
references,
settings: {},
status: "queued",
createdAt: new Date().toISOString()
};
}
例えば商品画像から短い動画を作る場合、
const job = createJob(
"image-to-video",
"video-model-a",
"Slow camera push-in. Keep the product design unchanged.",
["product-front.png"]
);
のようになります。
これだけでも、生成後に入力条件を追跡できます。
Promptを上書きしない
個人的に避けたいのが、再生成するたびに古いpromptを消してしまう設計です。
例えば、
v1: Slow camera movement
v2: Slow camera push-in
v3: Slow camera push-in, keep product unchanged
と変化した場合、最終版だけ残すと「なぜv3になったのか」が分かりません。
そこでRevisionを別にします。
interface PromptRevision {
version: number;
prompt: string;
reason: string;
createdAt: string;
}
保存例:
{
"version": 3,
"prompt": "Slow camera push-in, keep product unchanged.",
"reason": "Previous output changed product details.",
"createdAt": "2026-09-18T10:30:00Z"
}
reason を残すのがポイントです。
完成したpromptよりも、何が失敗して、なぜ変更したのかの方が次回役立つことがあります。
モデルを変更してもJobを分ける
複数モデルを比較するときも、
job-101
Model A
Prompt v3
Reference A
job-102
Model B
Prompt v3
Reference A
のようにJobを分けます。
PromptとReferenceを固定すれば、モデルを変更した結果も比較しやすくなります。
逆に、
Model A + Prompt v2 + Reference A
vs.
Model B + Prompt v6 + Reference B
では、何が結果に影響したのか判断しづらくなります。
ProviderとJobを分離する
モデルが増えてきたら、アプリケーション側から特定サービスを直接呼ばないようにします。
interface GenerationProvider {
submit(job: GenerationJob): Promise<string>;
getStatus(taskId: string): Promise<GenerationStatus>;
getResult(taskId: string): Promise<string | null>;
}
各サービスはAdapterとして実装します。
Application
|
GenerationJob
|
Provider Interface
/ | \
A B C
こうしておけば、モデルやサービスを変更してもJob管理側への影響を小さくできます。
ブラウザ型ツールを使う場合も考え方は同じ
必ずしも自分でAPI連携を実装する必要はありません。
例えば DoMax AI のように複数の画像・動画生成モデルをまとめて扱える環境を使う場合でも、
Task
↓
Model
↓
Prompt
↓
References
↓
Output
↓
Review
という情報を残しておくと、生成結果を後から整理しやすくなります。
ここで重要なのは「モデルをたくさん使うこと」ではなく、どの条件からその結果が生まれたのか追跡できることです。
completed と approved は分ける
もう1つ分けておきたいのが、
生成成功 = 公開可能
ではないという点です。
例えば状態を、
queued
↓
processing
↓
completed
↓
review
↓
approved
と分離できます。
completed はシステム上の成功。
approved は人間による確認済み。
商品画像なら形状や色、動画なら不自然なフレーム、さらに使用した参照素材の権利などもReview段階で確認します。
まとめ
AI画像・動画生成では、完成ファイルだけを保存すると制作過程が消えてしまいます。
最低でも、
model
prompt
prompt version
references
settings
output
review status
を1つのGeneration Jobとして関連付けておくと、後から再現しやすくなります。
特に複数モデルを使うようになると、「どのモデルが一番良いか」よりも、
どの入力条件で、どのモデルを使い、なぜその結果を採用したのか
を残せる仕組みの方が重要になってきます。
生成AIを単発の「Generateボタン」ではなく、追跡可能なJobとして扱う。
小さな実装ですが、生成回数が増えるほど効いてくる設計だと思います。