機能の費用を見積もるには
AI機能の費用は、代金を払う呼び出しの数に、一回の値段をかけたものです。キャッシュは、前の答えを覚えておくので、同じ質問のたびに払う数を減らします。
コスト、トレース、信頼性のモジュールでは、キャッシュのキーの作り方と、期限の決め方を学びます。このレッスンは、さらに進んで、一週間のトラフィックでヒットを数え、一か月の費用を予測します。
このレッスンのゴール
メモ
機能の費用と、キャッシュが節約する額を、見積もることができる。
キーワード
| ことば | いみ | れい |
|---|---|---|
| キャッシュ | 代金を払った答えを覚えておく場所 | |
| ヒット | キャッシュから答えられたリクエスト | |
| ミス | キャッシュで答えられず、代金を払うリクエスト | |
| ヒット率 | ヒットだったリクエストの百分率 | 50 |
| 有効期限(ttl) | 保存した答えを使える長さ。ここではリクエストの回数で数え、ttl と書く | 5 |
| 予測 | 過去の数から、先の数を計算で求めること |
学生が入力するセルは、このプレビューでは空白です。
一回の呼び出しの費用
呼び出し一回の費用は、入力トークンに入力の値段をかけたものと、出力トークンに出力の値段をかけたものの合計です。ふつう、出力のほうが高くなります。
計算を整数にするため、「ユニット」で数えます。1 ユニットは 1000 分の 1 円です。ここでの値段は作り話です。
例: 一回の呼び出し
| 部分 | トークン | 1 トークンの値段 | ユニット |
|---|---|---|---|
| 入力 | 120 | 3 | 360 |
| 出力 | 80 | 15 | 1200 |
| 一回の合計 | 200 | 1560(1.56 円) |
学生が入力するセルは、このプレビューでは空白です。
よくある間違い
注意
キャッシュは、どの機能でも同じだけ節約すると考える人が多いです。節約できるのは、繰り返される質問の分だけです。誰も同じことを二度聞かない機能では、ほとんど節約できません。
トラフィックをキャッシュに通す
ヒット率を測るには、本物か種つきのリクエストを、順に通します。番号は 0 から数えます。ttl は、保存した答えを使えるリクエストの回数です。
保存した番号から今の番号までの差が ttl より小さければ、ヒットです。ttl 以上ならミスで、今の番号で保存し直します。ヒットは、保存した答えの期限を延ばしません。期限は自分の時計で切れるので、人気の質問でも、期限が来れば新しい答えを取ります。
例: ttl が 3 のとき
| リクエストの番号 | 質問 | 保存した番号 | 結果 |
|---|---|---|---|
| 0 | a | なし | ミス。0 で保存 |
| 1 | a | 0 | ヒット。差は 1 で、3 より小さい |
| 2 | b | なし | ミス。2 で保存 |
| 3 | a | 0 | ミス。差は 3 で、3 以上。3 で保存 |
| 4 | a | 3 | ヒット。差は 1 |
学生が入力するセルは、このプレビューでは空白です。
一週間から一か月を予測する
一週間のリクエストを足します。一か月は約 30 日なので、一か月のリクエスト数は、一週間の合計に 30 をかけて 7 で割った数です。
代金を払うのはミスだけです。100 からヒット率を引いた数をかけて 100 で割り、一回の費用をかけます。
例: 一週間が 835 リクエスト
| 手順 | 式 | 結果 |
|---|---|---|
| 一週間の合計 | 120 + 135 + 150 + 160 + 170 + 60 + 40 | 835 |
| 一か月のリクエスト | 835 × 30 ÷ 7 | 3578 |
| ヒット率 50 パーセントで払う回数 | 3578 × 50 ÷ 100 | 1789 |
| 費用(ユニット) | 1789 × 1560 | 2790840(約 2791 円) |
学生が入力するセルは、このプレビューでは空白です。
助っ人 model.js を書く(1)
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const base = process.env.AI_URL || \"http://ai:4000\";' '// 応答のトークン数。なければ null。' 'function count(data, name) {' ' return data.usage && typeof data.usage[name] === \"number\" ? data.usage[name] : null;' '}' > model.js"ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
助っ人 model.js を書く(2)
docker exec -w /home/node/project agent sh -c "printf '%s\n' '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 || 100 }),' ' signal: AbortSignal.timeout(30000),' ' });' ' if (res.ok === false) throw new Error(\"status \" + res.status);' ' const data = await res.json();' ' const text = data.choices[0].message.content;' ' return { text, inTokens: count(data, \"prompt_tokens\"), outTokens: count(data, \"completion_tokens\"), recorded: false };' ' } catch (error) {' ' console.log(\"記録済みサンプル: モデルが答えませんでした (\" + error.message + \")。\");' ' return { text: sample, inTokens: null, outTokens: null, recorded: true };' ' }' '}' 'module.exports = { ask };' >> model.js"ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
キャッシュのスクリプトを書く(1)
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const { ask } = require(\"./model\");' 'function next(seed) { return (seed * 75 + 74) % 65537; }' 'const table = [0, 0, 0, 1, 1, 2, 3, 4, 5, 6];' 'function traffic(seed, count) {' ' const out = [];' ' for (let i = 0; i < count; i++) { seed = next(seed); out.push(\"q\" + table[seed % 10]); }' ' return out;' '}' > cache.js"ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
キャッシュのスクリプトを書く(2)
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'function replay(requests, ttl) {' ' const stored = {};' ' let hits = 0;' ' requests.forEach(function (key, i) {' ' if (key in stored && i - stored[key] < ttl) hits = hits + 1;' ' else stored[key] = i;' ' });' ' return hits;' '}' 'const week = [120, 135, 150, 160, 170, 60, 40];' 'const perCall = 120 * 3 + 80 * 15;' >> cache.js"ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
キャッシュのスクリプトを書く(3)
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'async function main() {' ' const requests = traffic(7, 40);' ' console.log(\"最初の 8 件: \" + requests.slice(0, 8).join(\" \"));' ' for (const ttl of [5, 10, 20]) {' ' const hits = replay(requests, ttl);' ' const percent = Math.floor(hits * 100 / 40);' ' const month = Math.floor(week.reduce(function (a, b) { return a + b; }, 0) * 30 / 7);' ' const paid = Math.floor(month * (100 - percent) / 100);' ' console.log(\"ttl \" + ttl + \": ヒット \" + hits + \" 回、\" + percent + \" パーセント、1か月の支払いは \" + paid + \" 回、\" + paid * perCall + \" ユニット\");' ' }' >> cache.js"ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
キャッシュのスクリプトを書く(4)
docker exec -w /home/node/project agent sh -c "printf '%s\n' ' const reply = await ask([{ role: \"user\", content: \"短く一文で答えてください。キャッシュで機能が安くなることがあるのは、なぜですか。\" }], \"同じ質問には記憶から答えるので、新しい呼び出しの代金がかからないからです。\", 80);' ' console.log(\"モデル: \" + reply.text);' ' console.log(\"出力トークン: \" + (reply.outTokens === null ? \"報告なし\" : reply.outTokens));' '}' 'main();' >> cache.js"ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
キャッシュを再生する
docker exec -w /home/node/project agent node cache.jsここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
Run の出力
| 行 | 意味 |
|---|---|
| 最初の 8 件: q6 q6 q0 q2 q0 q0 q5 q0 | 種つきのトラフィック。全員で同じ |
| ttl 5: ヒット 16 回、40 パーセント、1か月の支払いは 2146 回、3347760 ユニット | 期限が短いと、ヒットが少ない |
| ttl 10: ヒット 20 回、50 パーセント、1か月の支払いは 1789 回、2790840 ユニット | 中くらいの期限 |
| ttl 20: ヒット 27 回、67 パーセント、1か月の支払いは 1180 回、1840800 ユニット | 期限が長いと、ヒットが増え、請求が減る |
| モデル: ... | ラボからの一文。モデルが答えなければ記録済みサンプル |
| 出力トークン: ... | 応答が報告した数。なければ報告なし |
学生が入力するセルは、このプレビューでは空白です。