- 画像を
image_urlブロックでモデルに渡す3つの経路(URL・base64・mm_file://によるファイル参照)と、それぞれの制約 - 動画・音声を扱えるモデルの範囲と、フレームサンプリングレート(
fps)の考え方 - 埋め込み(embeddings)が何をしているか、なぜ意味検索に使えるのか
- ベクトル検索とリランキングを組み合わせた2段階検索パイプラインの設計
- 画像の説明生成・埋め込み生成・リランクを実際にAPIで動かす3本のハンズオン
これは何か
これまでの章で使ってきた POST /inference/v1/chat/completions(https://api.fireworks.ai 配下の絶対パス)は、テキストしか送れないわけではない。messages の content をテキストだけの文字列ではなく配列にして、テキストのブロックと並べて画像・動画・音声のブロックを追加するだけで、同じエンドポイント・同じ認証のまま多様な入力を扱える。埋め込み(embeddings)とリランキング(reranking)だけは別のエンドポイントで、それぞれ POST /inference/v1/embeddings と POST /inference/v1/rerank を使う。SDKに渡す base_url はこれらのパス末尾(/chat/completions 等)を除いた https://api.fireworks.ai/inference/v1 で、Authorization: Bearer $FIREWORKS_API_KEY とともに他の API と共通だ。
コンソール(app.fireworks.ai)側には、これらの機能の専用画面は用意されておらず、本章では API から扱う。Model Library でモデルを探すときに Vision などのフィルタを使う程度だ。
いつ使うのか
典型的なユースケースは4つある。画像の内容を読ませて説明文やOCR結果を得る(スクリーンショットの要約、書類の読み取り)。動画・音声の内容を理解させる(対応モデルは限られる)。自分のドキュメント群に対する意味検索の仕組み(RAG=検索で見つけた文書を材料にLLMへ回答させる仕組み)の土台としてベクトルを作る。検索で拾った候補を精度良く並べ替える。それぞれ渡し方とServerlessでの対応範囲が違うので、最初にどれを使うか決めておく。
| 入出力の種類 | 渡し方 | Serverless で使えるか |
|---|---|---|
| 画像 | image_url ブロック(URL・base64・mm_file://) | 対応(ただしServerlessで使えるかはモデルごとに異なる。Serverless Pricingページの個別モデル表とModel LibraryのVisionフィルタで確認する) |
| 動画 | video_url ブロック | GLM 5.3 Flash のみ |
| 音声 | audio_url ブロック | 非対応。Qwen3 Omni が専用デプロイで対応 |
| 検索用ベクトル | POST /inference/v1/embeddings | 対応(qwen3-embedding-8b 等) |
| 検索結果の並べ替え | POST /inference/v1/rerank | 対応(qwen3-reranker-8b) |
画像はVision系モデルなら常にServerlessで使えるわけではない。実際、Vision系モデルであってもServerless提供が終了することがある(例: Kimi K2.5は2026-06-26にServerlessから退役。詳細は後述)。
画像を渡す仕組み — image_url ブロック
content 配列に {"type": "text", ...} と並べて {"type": "image_url", "image_url": {"url": ...}} を追加する。リクエスト構造は OpenAI の vision API と同一なので、OpenAI 互換クライアントを使っていればコードはほぼそのまま動く。url フィールドが受け付ける値は3通りある。
{"type": "image_url", "image_url": {"url": "https://example.com/photo.jpg"}}
{"type": "image_url", "image_url": {"url": "data:image/jpeg;base64,..."}}
{"type": "image_url", "image_url": {"url": "mm_file://{file_id}"}}
1つ目は画像が公開URLにある場合にそのまま渡す方法。2つ目はローカルの画像をbase64エンコードしてdata URIにする方法(MIMEタイプのプレフィックスが必須)。3つ目はあらかじめアップロードして得た file_id を mm_file://{file_id} という形式で参照する方法だ。ただし2026-09時点で、このfile_idを取得するアップロード手順は公式ドキュメントに公開されていない(mm_file:// 形式そのものはAPIリファレンスのスキーマ記述で確認できる)。長い会話を繰り返すならURL参照の方がbase64よりレイテンシで有利、かつプロンプトキャッシュは画像部分にも効くため Time to First Token(最初のトークンが返るまでの時間)を最大80%短縮できる。
mm_file:// は動画では50MB超で公式に推奨される一方、画像はサイズ条件なしで利用できる。制約も明確に決まっている。1リクエストあたり画像は最大30枚(base64でもURLでも同じ)。base64エンコード後の合計サイズは10MB未満。URLを使う場合は1本あたり5MB未満かつ1.5秒以内にダウンロードが完了する必要がある。対応形式は .png .jpg .jpeg .gif .bmp .tiff .ppm の7種類。VLM(vision-language model=画像も読めるモデル)はPDFをネイティブには受け付けないので、PDFを扱うには各ページを画像に変換してから渡す。
動画・音声を渡す仕組み
動画・音声は画像よりも対応モデルが限られる。2026-09時点でServerlessで動画入力に対応するのはGLM 5.3 Flashだけで、音声入力に対応するのはQwen3 Omniだけ(専用デプロイが必須)。Molmo2は動画専用で、動画内の音声は理解できない。
| モデル | 対応する入力 | 提供形態 |
|---|---|---|
| GLM 5.3 Flash | 画像・動画・テキスト | Serverless |
| Qwen3 Omni 30B A3B Instruct | 動画・音声・テキスト | 専用デプロイのみ |
| Molmo2-4B / Molmo2-8B | 動画・テキスト(音声非対応) | 専用デプロイのみ |
コンテンツブロックの型名は画像と対になっていて、動画は video_url、音声は audio_url。url の受け付け方は、動画は画像と同じくURL・base64・mm_file://{file_id} の3通り(音声は公式のコード例ではbase64のdata URIのみが使われている)。動画URLは、公式ドキュメントによればカスタム認可ヘッダを付けずに直接取得できる必要がある。
フレームサンプリングレートは fps パラメータで指定でき、値が高いほど動きに敏感になる代わりにトークン数が増える。有効範囲はモデルによって異なり、MiniMax M3 VLでは0.2〜5Hz(2026-09時点)。非M3の動画モデルでは同じ役割を sample_fps という名前(Kimi K2.5 VL由来のレガシー名)で指定する。本章で扱うGLM 5.3 Flashでの fps の対応可否・範囲は公式ドキュメントに明記が無いため、ここでは断定しない。MiniMax M3 VLは本章の動画対応モデル表(Serverless のGLM 5.3 Flashなど)とは別のモデルだが、fps の有効範囲がAPIリファレンスに明記されている唯一のモデルなので、書式そのものの例として以下に挙げる。
{"type": "video_url", "video_url": {"url": "https://example.com/clip.mp4", "fps": 1}}
動画の前処理として「1 FPS・360p・先頭60秒・音声トラック除去」がffmpegの例として案内されているが、これはあくまで専用デプロイ向けのbase64サンプルで推奨されている値であって、APIのハード制限ではない。「60秒までしか渡せない」と早合点しないこと。むしろ、ffmpegで事前に1 FPSへ間引く代わりに、リクエストの fps パラメータで同じ効果を得られる場合がある。base64は合計10MB未満が前提なので、それを超えるサイズの動画はURL参照を使うか、アップロード済みファイルを mm_file://{file_id} で参照する。とくに50MBを超える動画はこの経路が公式に推奨されている(ただし、この file_id を取得するアップロード手順自体は2026-09時点で公式ドキュメントに公開されていない)。
埋め込みとは何か
埋め込み(embedding)とは、文章を「意味を表す数値の並び(ベクトル)」に変換したものだ。「猫はかわいい」と「犬はかわいい」のように意味が近い文章は、原点から見て似たような方向を向くベクトルになる。逆に「為替相場の予測」のように意味が離れた文章は違う方向を向く。この向きの近さ(角度)を測る指標としてよく使われるのがコサイン類似度で、これが意味検索(semantic search)の土台になる — クエリを同じ空間に埋め込み、向きの近い文章を探せば、キーワードが一致しなくても意味的に関連する文章を見つけられる。
Serverlessで使える埋め込みモデルの既定は fireworks/qwen3-embedding-8b。4B・0.6Bモデルは専用デプロイが必要になる。dimensions パラメータを渡すとベクトルの次元数(ベクトルの要素の個数。短くすると保存も検索も軽くなるが情報は落ちる)を短く指定できるが、これに対応する(resizable)のはQwen3系とレガシーの一部モデルのみで、非対応モデルに渡すと model: <id> is not resizable のエラーになる。重要な注意点として、Qwen3の埋め込みはL2正規化されていない — 単位ベクトル(ノルム=ベクトルの長さが1のベクトル)ではないので、内積をそのままコサイン類似度として使うと値がずれる(長さを揃えて初めて向きの近さと一致する)。リクエストに normalize: true を付ければAPI側でL2正規化された単位ベクトルが返るため、この手計算は不要になる(OpenAI SDKでは extra_body={"normalize": True} のように渡す)。レガシーのBERT系は多くが正規化済みだが、WhereIsAI/UAE-Large-V1 と mixedbread-ai/mxbai-embed-large-v1 の2つは例外的に未正規化だ。
| モデル | コンテキスト | 正規化済みか | 提供形態 | 単価(2026-09時点) |
|---|---|---|---|---|
| Qwen3 Embedding 8B | 40k | されていない(L2正規化が必要) | Serverless / 専用デプロイ | $0.10 / 1M input tokens |
| Qwen3 Embedding 4B / 0.6B | 40k / 32k | されていない | 専用デプロイのみ | 専用デプロイのGPU単価に準拠 |
| レガシー BERT系(150M以下、5モデル) | モデル依存 | 済み(5モデルすべて) | Serverless のみ | $0.008 / 1M input tokens |
| レガシー BERT系(335M、3モデル) | モデル依存 | 2モデルは未正規化 | Serverless のみ | $0.016 / 1M input tokens |
レガシーのBERT系・sentence-transformers系(BAAI/bge-small-en-v1.5 など)はModel Libraryに掲載されていないが、HuggingFace形式のモデルIDをそのまま model に渡せばServerlessで動く(専用デプロイは不可)。fireworks/gpt-oss-120b のような生成用LLMも /inference/v1/embeddings に投げれば埋め込みとして使えるが、これは検索用に訓練された専用ヘッドではなく最終トークンのhidden stateを流用したものなので、検索精度が重要な場面では専用の埋め込みモデルを優先すべきだ。非対称検索(クエリとドキュメントを別々の方法で埋め込む)に強いVoyage AI系モデルも用意されているが、Serverless提供は無く常に専用デプロイになる。
検索パイプライン — embed から rerank まで
埋め込みとリランキングは組み合わせて使うのが定石だ。まずコーパス全体をあらかじめ埋め込んでおき、クエリが来たらベクトル検索で広く候補を集める(再現率=拾うべき候補をどれだけ取りこぼさないかの指標を重視。計算コストは低い)。次にリランカーがその候補だけを精密に並べ替える(精度重視、1件あたりの計算コストは高いので対象を絞ってから使う)。全件をリランカーに通さず、まず安いベクトル検索で絞り込んでから高いリランカーにかける、という役割分担がポイントだ。
実装は2通りある。POST /inference/v1/rerank は query と documents を渡すだけの簡便なエンドポイントで、返り値の data は relevance_score(0〜1)の降順で並んでいる。ただし対応モデル・並列オプションが限られる。より柔軟に制御したいなら POST /inference/v1/embeddings に return_logits を渡す方式を使う。Qwen3 Rerankerは「クエリとドキュメントの組が関連するか」をyes/noで答えるように訓練されたモデルで、この方式では呼び出し側が <Instruct>: 指示・<Query>: クエリ・<Document>: ドキュメント の3行を改行で連結した文字列に整形して input に渡し、return_logits に"yes"/"no"に対応するトークンID(Qwen3トークナイザで確認できる値。2026-09時点で"no"=2753、"yes"=9454)を指定する。返ってくる確率は return_logits に並べたトークンIDと同じ順で並ぶので、[no, yes]の順(no=2753、yes=9454)で渡したなら2番目の値が関連度スコアになる。normalize: true を付けるとその2つのロジット(モデルが各トークンに与える生のスコア)にソフトマックス(スコアの組を合計1の確率に変換する計算)がかかり、"yes"の確率がそのまま関連度スコアになる。どちらの方式でも、Serverlessで使えるリランカーは qwen3-reranker-8b のみだ。
ハンズオン
以下3本はすべてServerlessの従量課金APIを1回ずつ呼ぶだけで、デプロイのような永続リソースは作らないため後片付けは不要。qwen3-embedding-8b は2026-09時点で $0.10 / 1M tokens、qwen3-reranker-8b は $0.20 / 1M tokens で、短い文・数件のドキュメントなら合計でも1円に満たない。画像を渡す推論のトークン単価は使うモデルによって異なるため、正確な金額は Serverless Pricing ページで確認する(個別モデル表に無いモデルは同じページの「パラメータ数とアーキテクチャで決まる一律料金」が適用される)。qwen3-reranker-8b の $0.20 は同ページの個別モデル表にはなく、embeddings ガイドが出典。
ハンズオン 6-1 画像を読ませて説明させる
- ターミナルでAPIキーを環境変数にセットする(
export FIREWORKS_API_KEY=...)。 - 次のコマンドを実行する。公開画像のURLをそのまま
image_urlに渡す。使うモデルは画像・動画・テキストに対応する GLM 5.3 Flash(glm-5p3-flash)。公式ガイドのコード例は Kimi K2.5(kimi-k2p5)を使っているが、同モデルは2026-06-26にServerlessから退役済みのためここでは置き換えている。
curl https://api.fireworks.ai/inference/v1/chat/completions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $FIREWORKS_API_KEY" \
-d '{
"model": "accounts/fireworks/models/glm-5p3-flash",
"messages": [
{
"role": "user",
"content": [
{"type": "text", "text": "この画像を日本語で説明してください"},
{
"type": "image_url",
"image_url": {
"url": "https://images.unsplash.com/photo-1582538885592-e70a5d7ab3d3?w=800"
}
}
]
}
]
}'
レスポンスの choices[0].message.content に画像の説明文が入っていれば成功。
ハンズオン 6-2 embeddings を作って2文の類似度を比べる
pip install openaiでOpenAI SDKを入れる(第2章で導入済みならスキップ)。- 意味が近い2つの文を用意し、次のPythonスクリプトを実行する。
- Qwen3の埋め込みは正規化されていないため、単純な内積ではなくコサイン類似度の定義どおり「内積 ÷ (各ベクトルのノルムの積)」で計算する。
import os
from openai import OpenAI
client = OpenAI(
api_key=os.environ.get("FIREWORKS_API_KEY"),
base_url="https://api.fireworks.ai/inference/v1",
)
sentences = ["猫はとてもかわいい動物です", "犬はとても愛らしい動物です"]
resp = client.embeddings.create(
model="fireworks/qwen3-embedding-8b",
input=sentences,
)
def cosine_similarity(a, b):
dot = sum(x * y for x, y in zip(a, b))
norm_a = sum(x * x for x in a) ** 0.5
norm_b = sum(y * y for y in b) ** 0.5
return dot / (norm_a * norm_b)
vec_a = resp.data[0].embedding
vec_b = resp.data[1].embedding
print(cosine_similarity(vec_a, vec_b))
−1〜1の値が返り、意味が近いほど1に近づく(無関係な文同士でも0付近まで下がる程度で、負になることは多くない)。試しに2文目を「今日の為替相場はどうですか」のような無関係な文に変えて実行し、値が下がることを確認するとよい。
ハンズオン 6-3 rerank API でドキュメントを並べ替える
- クエリと複数のドキュメントを用意し、次のコマンドを実行する。
top_nで返す件数を絞り、return_documentsで本文も一緒に返す。
curl https://api.fireworks.ai/inference/v1/rerank \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $FIREWORKS_API_KEY" \
-d '{
"model": "fireworks/qwen3-reranker-8b",
"query": "フランスの首都は?",
"documents": [
"パリはフランスの首都であり、エッフェル塔やルーヴル美術館がある最大都市です。",
"フランスはワインや料理、豊かな歴史で知られる西ヨーロッパの国です。",
"ヨーロッパの天気は北部と南部で大きく異なります。"
],
"top_n": 2,
"return_documents": true
}'
レスポンスの data が relevance_score の高い順に並び、パリについての文書が1位に来ていれば成功。
つまずきポイント
Qwen3の埋め込みは正規化されていない。ベクトルの内積をそのままコサイン類似度として扱うと値がずれる。必ずノルムで割ってから比較するか、正規化済みのレガシーモデルを使うか、リクエストに normalize: true を付けてAPI側で正規化させる。
画像・動画には明確な上限がある。画像は1リクエスト最大30枚・base64合計10MB未満・URL1本5MB未満かつ1.5秒以内にダウンロード完了が必要。動画の「1 FPS・360p・60秒」はffmpeg前処理例の推奨値であって、APIのハード制限ではない。両者を混同して「60秒までしか送れない」と思い込まないこと。
レガシーのBERT系・sentence-transformers系埋め込みモデル(BAAI/bge-small-en-v1.5 など)はModel Libraryに掲載されていない。探しても見つからないのは正常で、HuggingFace形式のIDをそのまま model に渡せば動く。エラーが model not found ならIDやデプロイパスの誤り、not available なら退役済みモデル(sentence-transformers/all-MiniLM-L6-v2 など)を指している。
model の書式はAPIごとに違う。チャットのServerlessモデルは accounts/fireworks/models/<id>、埋め込み・リランクのガイド本文は fireworks/<id>、専用デプロイは accounts/<ACCOUNT_ID>/deployments/<ID>。まずは各ガイドが例示している表記に合わせるのが安全だ(/rerank のAPIリファレンスは accounts/fireworks/models/qwen3-reranker-8b 形式も例示しており、同じモデルに2つの表記が公式に併存する)。IDが解決できないときは model not found が返る。
理解度チェック
Q1. Serverlessで動画を直接渡せるモデルと、音声入力に対応するモデルはそれぞれ何ですか。
Serverlessで動画入力に対応するのはGLM 5.3 Flashのみ。音声入力に対応するのはQwen3 Omniだが、これは専用デプロイが必須でServerlessでは使えない。
Q2. Qwen3 Embeddingで得たベクトル同士の類似度を、単純な内積だけで比較すると不十分なのはなぜですか。
Qwen3の埋め込みはL2正規化されていない(単位ベクトルではない)ため。内積をそのままコサイン類似度として使うと値がずれるので、各ベクトルのノルムで割ってから内積を取る必要がある。
Q3. 50MBを超える動画ファイルをモデルに渡すとき、公式に推奨されている方法は何ですか。
アップロード済みファイルのfile_idを、コンテンツブロックのurlにmm_file://{file_id}の形式で渡す(このfile_idを取得するアップロード手順自体は2026-09時点で公式ドキュメントに公開されていない)。base64のdata URIは合計10MB未満が前提なので、50MB超のファイルには向かない。
Q4. /inference/v1/rerankエンドポイントと、/inference/v1/embeddingsにreturn_logitsを渡す方式は、それぞれどんな場面で選びますか。
手早く使うだけなら/rerankで十分。ただし対応モデルや並列オプションが限られるため、より柔軟な制御(バッチ並列化など)が必要なら/embeddingsにreturn_logitsを渡し、"yes"トークンの確率を関連度スコアとして扱う方式を使う。
Q5. Model LibraryでBAAI/bge-small-en-v1.5を検索しても見つからないのはなぜですか。
レガシーのBERT系・sentence-transformers系埋め込みモデルはそもそもModel Libraryに掲載されていないため。動作はするので、HuggingFace形式のモデルIDをそのままmodelフィールドに渡せばよい。
まとめ
- 画像・動画・音声は、チャット用の同じエンドポイントに専用のコンテンツブロックを追加するだけで渡せる。
- 渡し方は画像・動画ならURL・base64・
mm_file://によるファイル参照の3通りで、ファイルサイズに応じて使い分ける(音声は公式のコード例ではbase64のみ)。動画が50MB超のときはmm_file://が公式推奨(file_idの取得手順自体は2026-09時点で非公開)。 - 埋め込みは文章をベクトル化する技術で、意味が近い文章は空間上で近くなる。検索の土台になる。Qwen3の埋め込みは正規化されていない点に注意する。
- 大規模な候補集合はベクトル検索で広く集め、少数の候補だけをリランカーで精密に並べ替える2段構えが定石。
次章(第7章)では、Tool Calling・Structured Outputs・プロンプトキャッシュなど、開発を助けるAPI機能を扱う。