入力が文章でないとき
マルチモーダル入力とは、リクエストに文章のほか、画像や文書が入ることです。モデルはそれを読めますが、サービスは大きなファイルを運び、種類を確かめなければならず、画像やページごとの料金もかかります。リクエストは、数百バイトから数メガバイトに増えることがあります。
ユーザーがレシートを撮り、十ページの契約書も送ります。一つのリクエストに、パートが三つあり、断られる理由も三通りあります。
このレッスンのゴール
メモ
私は、入力が画像や文書になると何が変わるかを説明できます。
キーワード
| ことば | いみ | れい |
|---|---|---|
| パート | リクエストの一部分。文章、画像、文書のどれか | |
| メディアタイプ | ファイルの形式を表す札 | image/png |
| ペイロードの大きさ | リクエストが運ぶバイト数 | |
| ページ数の上限 | 文書に許すページ数の最大 | |
| 単価 | 画像一枚、またはページ一枚の費用 |
学生が入力するセルは、このプレビューでは空白です。
ラボはテキストだけです
注意
ラボのモデルのエンドポイントは、テキストを受け取ります。Run から画像や文書は送れません。このレッスンは、レッスン用に作った記録済みのリクエストの形から始め、コードで確かめます。
リクエストは、パートのリストになる
文章だけの入力は、一つの文字列です。マルチモーダルの入力は、パートのリストで、それぞれが種類とデータを持ちます。ファイルのデータはバイトで運ばれ、多くは文字に直されるので、三分の一ほど大きくなります。
下は、このレッスンのために作った、記録済みの形です。本物の提供元は、それぞれ別の項目名を使います。必ず、その説明書を読んでください。
記録済みのリクエストの形
{ "messages": [{ "role": "user", "content": [
{ "type": "text", "text": "このレシートには何が書かれていますか。" },
{ "type": "image", "media": "image/png", "data": "<base64、412000 バイト>" },
{ "type": "document", "media": "application/pdf", "data": "<base64、880000 バイト>" }
] }] }送る前に、パートごとに確かめる
自分で決めた規則で、パートごとに確かめます。メディアタイプ、バイト数、ページ数です。合計も確かめます。画像が何枚か、全部で何バイトかです。
早めに、はっきりした理由をつけて断ります。提供元で失敗したリクエストは、アップロードの手間と、ときにはお金を、すでに使っています。
例: 上限と価格
| パート | 中身 | 確認 | 費用(単位) |
|---|---|---|---|
| 1 | テキスト、1800 文字 | ここでは上限なし | 2 |
| 2 | 画像、png、412000 バイト | 許可、5 MB 未満 | 4 |
| 3 | 画像、gif、90000 バイト | gif はリストにない | 4 |
| 4 | 文書、24 ページ | 20 ページの上限を超える | 48 |
学生が入力するセルは、このプレビューでは空白です。
費用は、画像とページごと
文章はトークンごとに払います。画像やページは、決まった額か、大きさで決まるトークン数で払うことが多いです。呼び出しの前に足し合わせておくと、予算の規則で高いリクエストを断れます。
できるなら、送る量を減らします。画像を小さくするか、必要なページだけを送ります。
よくある間違い
注意
ファイルの名前や拡張子を信じてしまう人は多いです。名前が png でも、中身は違うことがあります。サーバー側でバイトから種類を確かめ、大きさの上限も守ります。拡張子は手がかりで、事実ではありません。
モデル用の補助を書く(1)
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const base = process.env.AI_URL || \"http://ai:4000\";' 'async function ask(messages, sample, max) {' ' try {' ' const res = await fetch(base + \"/v1/chat/completions\", {' ' method: \"POST\",' ' headers: { \"content-type\": \"application/json\", authorization: \"Bearer lab\" },' ' body: JSON.stringify({ model: \"lab\", messages: messages, max_tokens: max || 200 }),' ' signal: AbortSignal.timeout(30000),' ' });' ' if (res.ok === false) throw new Error(\"status \" + res.status);' ' return (await res.json()).choices[0].message.content;' > model.js"ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
モデル用の補助を書く(2)
docker exec -w /home/node/project agent sh -c "printf '%s\n' ' } catch (error) {' ' console.log(\"記録済みサンプル: モデルが答えませんでした (\" + error.message + \")。\");' ' const list = [].concat(sample);' ' const turn = messages.filter(function (m) { return m.role === \"assistant\"; }).length;' ' return list[Math.min(turn, list.length - 1)];' ' }' '}' 'module.exports = { ask };' >> model.js"ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
形の検査を書く(1)
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const { ask } = require(\"./model\");' '// 記録済みの作り物のリクエスト。提供元ごとに、形は違います。' 'const request = { parts: [' ' { type: \"テキスト\", chars: 1800 },' ' { type: \"画像\", kind: \"png\", bytes: 412000 },' ' { type: \"画像\", kind: \"gif\", bytes: 90000 },' ' { type: \"文書\", pages: 24, bytes: 880000 },' '] };' 'const LIMITS = { images: 3, imageBytes: 5000000, pages: 20, kinds: [\"png\", \"jpeg\"] };' 'const PRICE = { image: 4, page: 2, thousandChars: 1 }; // 作り物の単位' > shapes.js"ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
形の検査を書く(2)
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'function check(req) {' ' const problems = [];' ' let images = 0;' ' let cost = 0;' ' req.parts.forEach(function (p, i) {' ' const n = i + 1;' ' if (p.type === \"テキスト\") cost += Math.ceil(p.chars / 1000) * PRICE.thousandChars;' ' if (p.type === \"画像\") {' ' images++;' ' cost += PRICE.image;' ' if (LIMITS.kinds.indexOf(p.kind) < 0) problems.push(\"パート \" + n + \": 形式 \" + p.kind + \" は許可されていません\");' ' if (p.bytes > LIMITS.imageBytes) problems.push(\"パート \" + n + \": 画像が大きすぎます\");' ' }' >> shapes.js"ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
形の検査を書く(3)
docker exec -w /home/node/project agent sh -c "printf '%s\n' ' if (p.type === \"文書\") {' ' cost += p.pages * PRICE.page;' ' if (p.pages > LIMITS.pages) problems.push(\"パート \" + n + \": ページ数が多すぎます\");' ' }' ' });' ' if (images > LIMITS.images) problems.push(\"画像が多すぎます\");' ' return { problems: problems, cost: cost };' '}' >> shapes.js"ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
形の検査を書く(4)
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const result = check(request);' 'result.problems.forEach(function (p) { console.log(\"問題:\", p); });' 'console.log(\"費用:\", result.cost, \"単位(作り物の価格)\");' 'async function main() {' ' const text = \"レシートの文字: パン 210 円、牛乳 105 円、合計 315 円。合計はいくらですか。\";' ' const sample = \"合計は 315 円です。\";' ' console.log(\"テキストだけの本物の呼び出し:\", await ask([{ role: \"user\", content: text }], sample, 40));' '}' 'main();' >> shapes.js"ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
記録済みのリクエストを検査
docker exec -w /home/node/project agent node shapes.jsここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
出力の一例
| 行 | 何を示すか |
|---|---|
| 問題: パート 3: 形式 gif は許可されていません | メディアタイプがリストにない |
| 問題: パート 4: ページ数が多すぎます | 24 ページは上限の 20 を超える |
| 費用: 58 単位 | テキスト 2、画像二枚で 8、文書 48 |
| テキストだけの本物の呼び出し | テキストの質問への短い答え。または、ラベル付きの記録済みサンプル |
学生が入力するセルは、このプレビューでは空白です。