効果音 1万件を「かわいい順」に並び替える機能を作りました(TypeSafe Jev)
効果音・音声素材サイト 音音M材(sounds.pronama.jp) に、キーワードで並び替える機能を追加しました。
いちばんの売りは、好きな言葉をその場で入れて並び替えられることです。「かわいい」「こわい」はもちろん、「深夜ラジオっぽい」「事件が起きそう」みたいな言葉でも、いま開いている一覧の全件にその場でスコアが付いて、並び替わります。あらかじめ用意したタグを選ぶのではなく、思いついた言葉で探せます(実際にやってみた例は後ろにあります)。
実装も、この記事も Claude Code(Opus 5)が書きました。 数字はすべて実測値です。
ぱっと見の全体像
1回のリクエストに200件ぶんの質問をまとめ、必要な回数だけ同時に投げます。音そのものは渡していません
30秒でわかる要点
- 好きな言葉でその場で並び替えられます。 定型 15種のほか、自由入力もできます
- 並び替えるのは、開いている一覧(サブカテゴリ)の全件です。「動物の鳴き声」179件、「ボイス」373件のように、そのページにあるもの全部にスコアが付きます
- サイト全体では 10,828件 / 35サブカテゴリ。いちばん大きい一覧は 1,460件
- 判定に使ったのは TypeSafe の Jev。文章を作らず、型の付いた判定だけを返すモデルです
- 音は一切聴いていません。 タイトル・タグ・英語ファイル名だけで判定しています
- 速さは 606件のサブカテゴリで1.71秒(200件ずつ4リクエストに分けて同時に投げた合計)。お金は 1,460件 × 1キーワードで約2円
- 開発から公開まで、全部で約23円。 しかも、ほとんどは開発中の動作確認ぶんでした
- 計算するたびに課金が発生するので、期間限定の実験的な機能として公開しています
細かい話は下に全部書きました。興味のあるところだけどうぞ。
目次
- 使ったのは TypeSafe の Jev
- いちばん大事だった設計: 素材の情報は「質問」に入れる
- 実験 1: バッチに分けると順位は歪むか
- 実験 2: Noul と Score、どちらが並び替えに向くか
- 実験 3: 1リクエストに何件詰められるか
- 実験 4: Cloudflare の CPU 10ms に、どう収めるか
- 日本語の判定は、どのくらい当たるのか
- いくらかかるのか
- 正直な弱点
何ができるのか
用意した定型キーワードは 15種です。
かわいい / かっこいい / 元気 / 落ち着いた / こわい / おもしろい / ゆるい / はげしい / せつない / レトロ / 未来的 / あたたかい / ドラマチック / 神秘的 / にぎやか
ここに無い言葉は、自分で入れられます。 用意したタグから選ぶのではなく、探している雰囲気をそのまま言葉にして投げられるのが、この機能の狙いです。1度計算した組み合わせはキャッシュするので、2回目からはすぐ出ます。
対象はこれだけあります。
| カテゴリ | 件数 |
|---|---|
| プロ生 無料素材 | 33 |
| 暮井 慧(プロ生ちゃん) | 609 |
| HSP3 無料素材 | 52 |
| Adobe Audition | 10,134 |
| 合計 | 10,828(35サブカテゴリ) |
いちばん大きいサブカテゴリは 1,460件あります。
使ったのは TypeSafe の Jev
Jev は TypeSafe の「System One モデル」です。文章を生成せず、型の付いた判定を直接返すのが特徴で、質問の形が 3つあります。
| 形式 | 返すもの |
|---|---|
| Noul | 「はい」である確率(0〜1) |
| Choice | 選んだ選択肢+各選択肢の確率+確信度 |
| Score | 段階の確率の加重平均+確信度 |
公式ドキュメントに書かれている仕様のうち、今回に関係するのはこのあたりです。
- 1リクエストあたり 64k tokens(うち state + 最長の質問で 32k)
- レート制限は 250,000 tokens/秒、1,200リクエスト/分
- 料金は input $0.042 / 100万トークン、output は無料(あとで請求額と突き合わせたら、計算どおりでした)
- 入力はテキストのみ(画像・音声・動画は非対応)
使ったモデルは jev-latest(= Jev 1.13)です。
1つの state に対して、複数の質問を並列・独立に評価できるという性質が、今回の設計の土台になりました。公式の検証では、13問をまとめると個別に投げるより 12.2倍安く、10.0倍速いとされています。
なぜ Jev だったか
- 音そのものは渡せません。 テキストのみなので、タイトル・タグ・英語ファイル名だけが材料です
- 1万件規模に一般的な LLM を使うと、費用も時間も現実的ではありません
- 欲しいのは文章ではなく数値です。生成してから取り出す手間がありません
いちばん大事だった設計: 素材の情報は「質問」に入れる
素朴に作るなら、素材のリストをまとめて state に入れて、質問で「37番目の素材は?」と参照する形になります。
これをやると、同じ素材でもスコアが動きます。 どの素材と同じバッチに入ったかで変わってしまうのです。
そこで、state にはキーワードだけを置き、素材のタイトル・タグ・英語名は質問文の中に丸ごと書くことにしました。こうすると質問が 1つずつ自己完結するので、バッチの組み方に左右されません。
この差は、次の実験で数字になって出ます。
実験 1: バッチに分けると順位は歪むか
同じ 100件(Audition の「家庭・日用品」× キーワード「かわいい」)を 4通りで評価して比べました。
- baseline: 100件を 1リクエストで
- repeat: 同じリクエストをもう一度(素のゆらぎの基準値)
- seq: 先頭から 25件ずつ 4バッチに分割
- shuffled: シャッフルしてから 25件ずつ 4バッチに分割
方式 A(素材情報を質問文に入れる)
| 比較 | 順位相関 rho | 平均絶対差 | 最大差 |
|---|---|---|---|
| baseline vs repeat(ゆらぎの基準) | 0.988 | 0.0043 | 0.022 |
| baseline vs seq | 0.989 | 0.0040 | 0.014 |
| baseline vs shuffled | 0.990 | 0.0034 | 0.012 |
方式 B(素材リストを state に入れる)
| 比較 | 順位相関 rho | 平均絶対差 | 最大差 |
|---|---|---|---|
| baseline vs repeat(ゆらぎの基準) | 0.993 | 0.0042 | 0.016 |
| baseline vs seq | 0.953 | 0.0128 | 0.092 |
| baseline vs shuffled | 0.861 | 0.0202 | 0.164 |
上位 50件の一致率も見ました。
| seq | shuffled | |
|---|---|---|
| 方式 A | 98% | 100% |
| 方式 B | 94% | 84% |
方式 A では、分割やシャッフルによるズレが「同じリクエストを 2回投げたときのゆらぎ」と区別できません。 方式 B ははっきり劣化します。
この結果から、バッチ間でスコアを正規化する補正は入れないことにしました。 補正は仕組みが増えるわりに、効果を確かめにくくなります。
実験 2: Noul と Score、どちらが並び替えに向くか
同じ 100件で比べました。
| 値がユニークな割合 | 再現性(同一リクエスト 2回の rho) | |
|---|---|---|
| Noul | 20% | 0.978 |
| Score(6段階) | 56% | 0.988 |
Noul は 0.01 刻みに量子化されて返るので、100件だと約8割が同点になります。並び替えが目的だと、同点の中身が実質ランダムになってしまいます。
というわけで Score を採用しました。「正反対の印象」から「この上なく代表例」までの 6段階にして、Jev が返す加重平均の小数を 0〜100 に直して表示しています。
実験 3: 1リクエストに何件詰められるか
方式 A・Score 6段階で、1件あたり 231 tokens を消費しました。
| 件数 | 結果 |
|---|---|
| 150 | OK / 34,878 tokens / 1.3秒 |
| 200 | OK / 46,387 tokens / 1.4秒 |
| 250 | OK / 57,845 tokens / 0.9秒 |
| 300 | エラー(トークン上限超過) |
64k tokens の上限どおり、実質 250件が天井でした。余裕を見て 200件 / バッチにしています。バッチはブラウザーから全部まとめて並列に投げるので、バッチ数が増えても待ち時間はだいたい 1回分で済みます。
実験 4: Cloudflare の CPU 10ms に、どう収めるか
サーバー側は Cloudflare Pages Functions(Workers)で動かしています。無料プランは 1回の呼び出しあたり CPU 10ms が上限です。
| 設計 | CPU 消費 |
|---|---|
| 1回の呼び出しで全 8バッチを処理 | 11.4〜12.1ms(上限超過) |
| 1回の呼び出しで 1バッチ | 2.9〜3.4ms |
ここが直感に反していておもしろいところです。効いていたのは「Jev の応答が遅いこと」ではなく、「JSON をどれだけ組み立てて解析するか」でした。
Cloudflare のドキュメントにはこう明記されています。
Waiting on network requests (such as fetch() calls, KV reads, or database queries) does not count toward CPU time.
つまり Jev の応答を 1.4秒待っても、CPU 時間はゼロです。だから 1リクエスト = 1バッチに分けるだけで、無料プランに収まりました。
実測のレイテンシはこうなりました。
| 対象 | 時間 |
|---|---|
| 179件のサブカテゴリ(1リクエスト) | 1.09秒 |
| 606件のサブカテゴリ(4リクエストを同時に) | 1.71秒 |
キャッシュに無い、まっさらな状態での本番の実測です。
日本語の判定は、どのくらい当たるのか
ここがいちばん楽しいところです。以下はすべて実際の出力です。
プロ生 ゲーム BGM ×「落ち着いた」
- 上位: 「宿屋、ステージクリア、発見、静か、しっとり、幻想的、ハープ」87.0
- 下位: 「ホラー、恐怖、緊張感、シンセサイザー」0.2
暮井 慧 システムボイス ×「元気」
- 上位: 「さーて、作業再開ね!(Windowsのログオン)」65.8 /「きた! USBきたよ!(デバイスの接続)」65.6
- 下位: 「あー! もう限界っ!(バッテリー切れアラーム)」1.8
暮井 慧 ボイス ×「かっこいい」
- 上位: 「カッコ」69.4 /「諦めたらそこで試合終了だよ!」66.6 /「私の勝ち!」62.0
- 下位: 「がーーん 液晶割れちゃった…」21.0 /「不合格…!」19.2 /「残念~!」15.0
Audition 動物の鳴き声 ×「かわいい」
- 上位: 「アヒルの鳴き声 01」80.0 /「アヒルの鳴き声 02」79.8 /「猫の鳴き声」76.4
- 下位: 「トラの威嚇する鳴き声」0.4 /「チンパンジーの鳴き声(悲鳴)」0.4
Audition 家庭・日用品 ×「こわい」
- 上位: 「パントリーの扉をゆっくり開けるきしみ」61.8 /「地下室のドアをバタンと閉める」61.4 /「玄関の扉をきしみながら閉じる」60.4
- 下位: 「ビール瓶で乾杯」7.6
Audition 気象・天気 ×「こわい」
- 上位: 「地震、地響き、ゴゴゴゴ」69.6 /「雷、落雷」67.8
- 下位: 「雪上でジャンプ、ブーツ、ボフッ」14.8
用意していない言葉でも
ここからが、この機能のほんとうの使いどころです。定型の 15種に無い言葉を入れてみました。
Audition 気象・天気 ×「事件が起きそう」
- 1位: 「小雨、森林、ヘリコプター、警察による捜索、サイレン」88.2
- 2位: 「地震、地響き、ゴゴゴゴ、(低音増強)」82.2
- 下位: 「雪かき、歩道上の大雪をすくう」15.2
プロ生 ゲーム BGM ×「深夜ラジオっぽい」
- 1位: 「神秘、不思議、ピアノ」51.2
- 2位: 「宿屋、エレクトリックピアノ」49.0
- 下位: 「ステージクリア、金管楽器(ブラス)、明るい」4.0
「事件が起きそう」で警察の捜索音が 1位に来るのは、用意したタグを選ぶ方式では絶対に引けない探し方です。ここがいちばん、作ってよかったと思ったところでした。
繰り返しになりますが、音は一切聴いていません。 タイトルとタグだけでここまで並びます。アヒルと猫がかわいい側に、トラの威嚇が反対側に来るあたりは、なかなかのものです。
なおスコアは計算し直すたびに 1ポイント前後は動きます(実験 1 に出てくる「ゆらぎ」の分です)。順番はほとんど変わりませんが、上の数字とぴったり同じになるとは限りません。
いくらかかるのか
料金は input $0.042 / 100万トークン(output 無料)。1件あたり 231 tokens の実測から計算すると、こうなります。
| 対象 | トークン | コスト |
|---|---|---|
| 21件のサブカテゴリ × 1キーワード | 4,900 | $0.0002 |
| 179件 × 1キーワード | 41,000 | $0.002(約0.3円) |
| 606件 × 1キーワード | 140,000 | $0.006(約0.9円) |
| 1,460件 × 1キーワード | 337,000 | $0.014(約2円) |
| 全 35サブカテゴリ × 1キーワード | 2,500,000 | $0.11(約16円) |
| 全 35サブカテゴリ × 定型 15キーワード | 37,500,000 | $1.58(約240円) |
定型キーワードの全組み合わせを 1回ずつ計算し尽くしても 240円ほどです。結果はキャッシュしているので、2回目以降の同じ組み合わせは無課金です。実際の運用コストは、この表の額に近づいていきます。
開発から公開まで、全部でいくらだったか
TypeSafe の利用明細から出した実績です。
リクエスト数 : 124
input tokens : 3,621,579
output tokens : 241,126
請求額 : $0.1521(約23円)
内訳は、ほぼ全部が開発中の検証・テストです。公開したばかりなので、読んだ方が触った分はまだごくわずかでした。
規模感を並べるとこうなります。
| 比較対象 | input トークン |
|---|---|
| 開発まる1日の実績 | 3,621,579($0.1521) |
| 全 35サブカテゴリ × 1キーワードを計算し尽くす | 2,500,000($0.11) |
| 月ごとに決めている上限 | 238,095,238 |
作っていた 1日だけで、サイト全体を 1周するぶんの 1.45倍のトークンを使いました。月ごとに決めている上限に対しては 1.52% です。
意外だったのは、いちばんトークンを食べたのが、作っているあいだの動作確認だったことです。画面を直すたびに 179件の並び替え(約 41,000 トークン)を繰り返したからで、これは公開前の話です。公開したあとにどれだけ使われるかは、これから分かります。
請求額は、公称の計算どおりだった
利用明細と、公称されている料金の計算を突き合わせました。ぴったり一致します。
| ダッシュボードの表示 | 明細から計算 |
|---|---|
| Spend $0.1521 | input 3,621,579 × $0.042 / 100万 = $0.1521 |
| Tokens 3,862,705 | input 3,621,579 + output 241,126 = 3,862,705 |
| Requests 124 | 時間帯別の合計 = 124 |
output のぶんは、たしかに請求に乗っていませんでした。 公称どおりですが、実際の請求額と計算が 1 セントもずれないのは気持ちがよいものです。ついでに分かったことが 3つあります。
- ダッシュボードの「Tokens」は input と output の合計です。これに単価を掛けると、コストを 6.7% 多く見積もることになります。課金の対象は input だけです
- Score 形式なら、output は input の約6.7% に収まります
- トークン上限を超えて失敗したリクエストは、課金もリクエスト数のカウントもされません(上限を探して 300件以上を投げた 3回は、いずれも 0 でした)
検証スクリプトを回した最初の 1時間についても、スクリプトが記録したトークン数と、あとから取った利用明細を突き合わせました。
| リクエスト | トークン | 内容 |
|---|---|---|
| 4 | 24,680 | Noul / Score と、2つの設計方式の比較(30件) |
| 10 | 95,552 | バッチ分割の実験・方式 A / Score(100件) |
| 10 | 100,612 | 同・方式 B / Score |
| 10 | 65,952 | 同・方式 A / Noul |
| 3 | 139,110 | 1リクエストに詰められる件数の探索 |
| 4 | 63,928 | 別のカテゴリ × 別のキーワード |
| 41 | 489,834 | 利用明細と 1トークン単位で一致 |
この記事に出てくる「1件あたり 231 トークン」「1リクエストの上限は実質 250件」といった数字は、すべてこの明細と整合しています。
月ごとの上限を超えた場合は、新しい計算だけを止め、キャッシュ済みの結果は出し続けるようにしています。
正直な弱点
- 音そのものは判定していません。 材料はタイトル・タグ・英語ファイル名というテキストだけです
- 件数が多いと同点が増えます。 100件でユニーク率 56%、606件で 26%。同点の中の順序は、元の並び順のままです
- 素材の種類が似ているサブカテゴリでは差が付きにくいです。たとえば Audition の気象・天気 ×「こわい」は、上位が 67〜69 に固まりました
- 失敗したとき(サービス側のエラー、上限に達したときなど)は、通常の並び順に戻して控えめにお知らせします
公式ドキュメントが挙げている Jev の弱点も、あわせて書いておきます。数値計算、日付の比較、多段の推論、そして無関係な大きいコンテキストが判定を乱すこと。今回の使い方(短いテキスト 1件を 1問で判定する)は、ちょうどこの弱点を避けられる形でした。
というわけで
期間限定ですが、ぜひ触ってみてください。音音M材 のサウンド一覧から選べます。
「かわいい順」で動物の鳴き声を並べたり、「こわい順」で家庭・日用品を並べたりすると、なかなか楽しいです。




