コンテンツにスキップ

27B のローカルモデルが 191 項目中 189 問正解した — 落とした 1 問は Gemma 4 と同じ場所だった

Alibaba が Apache 2.0 で公開した Qwen3.8-27B を、自作ベンチマークの全 9 テーマで実測しました。構造化出力ではローカル実行できる規模として過去最高でしたが、HTML の一括生成では別の弱点が出ます。

何を測ったか

OreOre-Bench は、同じプロンプトを各モデルに 1 回だけ投げて成果物を並べる個人用のベンチマークです。手直しは一切しません。

条件 値
モデル Qwen/Qwen3.8-27B(2026-08-05 公開、Apache 2.0)
量子化 unsloth の GGUF Q8_0(29GB)
実行環境 Ollama 0.30.6 / Mac Studio M3 Ultra 512GB
サンプリング temperature 0.3、top_p 既定、max_tokens 65,000(全モデル共通)
テーマ HTML 生成 7 種 + 構造化 JSON 2 種

アーキテクチャは 27B の dense で、Gated DeltaNet 3 層 + Gated Attention 1 層を 16 回反復する構成です。ネイティブ 262K コンテキストで、thinking がデフォルトで有効になっています。

構造化出力は明確に強い

json-ladder は、同じ短編小説から段階的に難しい JSON を抜き出させるテーマです。L1 のタイトル抽出から、L6 の「全文の段落冒頭を逐語で index 化する」まで 6 段階あります。

レベル 採点項目 一致 accuracy
L1〜L4 41 41 100%
L5 54 53 98%
L6 96 95 99%
合計 191 189 平均 99.5%

191 項目中 189 一致。ローカル実行できる規模では過去最高で、これまで最高だった Gemma 4 31B(99%)を上回りました。

ただし「全問正解」ではありません。落とした 2 件はこれです。

L5  characters[0].relations   got: 1        expected: 0
L6  sections[3].paragraph_openings[2]
      got: "> 千鶴へ"   expected: "千鶴へ"

L6 の方は、本文中に引用形式で挿入された手紙の冒頭行から、引用記号 > を取り除けなかったというものです。これは Gemma 4 31B が同じテーマで落としている箇所と完全に同一でした。モデルの規模や世代が変わっても、「原文の装飾をどこまで剥がすか」の判断は同じところで割れるようです。

PR トリアージのテーマ(10 件の PR を merge / fix / hold / close に分類)でも正解キー一致 90% で、Gemma 4 系 3 モデルの 85% を上回っています。しかもこのテーマは「JSON 単体で返す」ことを指示していますが、thinking を長く挟んだ上で指示どおり JSON だけを返しました。Claude Opus 4.8 が前置き文を付けて破っている指示です。

HTML 生成では前置きが混ざる

一方、HTML を 1 ファイルで書かせるテーマでは癖が出ます。

HTML 系 7 テーマのうち 6 テーマで、成果物の先頭に前置き文かコードフェンスが混入しました。

hasami-shogi  `index.html` 1枚で完成した実装です。指定ルールセット(...
lp-fable5     絵本の世界観で「Fable 5」の発表ページを、水彩SVG・...
lp-nishibi    # NISHIBI(西日)— 凍結プロンプト実装版
othello       `index.html` 1枚で完成したオセロです。緑のフェルト盤面・...
phoenix-lp    ```html
suminagashi   和紙の上で墨が渦を巻いて溶け合う、フルスクリーンの...
roguelike     (混入なし。<!DOCTYPE html> で始まる)

末尾にも実装解説が付きます。ブラウザは寛容なので表示自体は成立しますが、「単一 HTML ファイルとして出力せよ」という指示に対しては不正確です。

面白いのは、構造化 JSON の 2 テーマでは混入がゼロだったことです。出力形式が JSON だと厳密に守り、HTML だと解説を付けたくなる、という非対称があります。

肝心の中身の完成度は高く、Fable 5 の LP は水彩 SVG のイラストと明朝の大見出し、スクロール演出まで作り込まれていました。オセロも合法手のハイライト、3D フリップ反転、パス処理まで実装され、Playwright で着手と CPU 応答を確認できています。

失敗したのは 2 テーマです。ローグライクはタイトル画面の作り込みが良い一方、ゲーム開始後に JS エラーが 6 件出ました。墨流し(WebGL の流体シミュレーション)はフラグメントシェーダーがコンパイルできず、ローカル勢が全滅しているこのテーマの傾向を踏襲しています。

18.8 tok/s は速いのか

生成速度は 8 テーマの加重平均で 18.8 tok/s でした。

テーマ completion 秒 tok/s
pr-triage 12,004 616 19.5
othello 38,563 2,012 19.2
lp-nishibi 38,734 2,025 19.1
hasami-shogi 38,519 2,022 19.0
lp-fable5 55,360 2,968 18.7
suminagashi 50,516 2,723 18.5
phoenix-lp 65,000 3,533 18.4

ブレが 18.4〜19.5 と極端に小さいのが特徴です。65,000 トークン出した phoenix-lp でも 18.4 を維持していて、KV キャッシュが増えても速度がほとんど落ちません。

ただし tok/s より効いたのは 1 リクエストあたりの総トークン量でした。thinking がデフォルトで有効かつ長いため、HTML 1 ページの生成に 32〜59 分かかります。9 テーマの completion 合計は 370,866 トークンでした。

phoenix-lp では 65,000 トークンの上限に到達し、末尾の JavaScript がコメントの途中で切れています。それでも canvas 描画とスクロール演出は動いたのでスモークテストは通ってしまいました。「テストが通る」と「出力が完成している」が一致しないケースです。

実運用の観点だと、この速度と thinking の長さは用途を選びます。対話的に使うには 1 応答 30 分は厳しく、バッチで構造化データを抽出する用途なら 191 項目中 189 という精度が効いてきます。

測定を 2 回やり直した

素直には終わりませんでした。

MLX サーバが 10 分で落ちる

最初は MLX で 8bit に自前変換して mlx_lm.server で回そうとしましたが、生成開始から約 10 分・1 万トークン前後で生成スレッドが落ちます。

RuntimeError: [metal::malloc] Resource limit (499000) exceeded.

499000 を「499GB のメモリ上限」だと読んで、メモリ枯渇を疑いました。しかし実測すると active 29GB で横ばい、swap の増加はゼロで、量的にはまったく余裕があります。仮説と観測が合いません。

これはバイト数ではなく Metal バッファの「個数」の上限でした。MLX の allocator は num_resources_ >= resource_limit_ で判定しています。桁が実測と合わないときは、単位の解釈を疑うべきでした。

切り分けで決定的だったのは、サーバ経由と直接生成を同条件で比較することです。

# 同じモデル・同じプロンプト・同じ max_tokens を直接流す
for _ in stream_generate(model, tokenizer, text, max_tokens=65000):
    ...

直接 stream_generate を叩くと 13,400 トークン / 644 秒を問題なく通過しました。サーバがクラッシュした地点を大きく超えています。これでモデルでもトークン数でもなく、mlx_lm.server の生成経路固有の問題だと確定しました。

厄介なのは、プロセスは生き続けて生成スレッドだけが死ぬことです。クライアントからは応答待ちのハングに見え、pgrep によるプロセス生存確認では異常を検知できません。監視条件にはサーバログの Exception を入れるべきでした。

最終的に llama.cpp(Ollama + 同等の Q8_0 GGUF)へ経路を変えて 9 テーマ完走しました。量子化水準を 8bit で揃えれば、比較条件は維持できます。MLX の直接生成は 21 tok/s と約 12% 速かったので、サーバが安定していれば MLX の方が良い選択でした。

検証スクリプトが正しい実装を FAIL にした

はさみ将棋のスモークテストが FAIL を返しました。「歩 10 枚・と 10 枚」で、初期配置は 9 枚ずつのはずです。

スクリーンショットを見ると盤面は 9 枚ずつで正しく、右側のスコアボードに置かれた「歩」「と」の 1 文字バッジまで駒として数えていました。検証スクリプトが、ページ全体の葉要素からテキストが「歩」「と」のものを数える実装だったためです。

盤面の座標からグリッドを推定し、その矩形内だけを数えるよう直したのですが、レビューでより悪い罠を指摘されました。

素朴に「等間隔が続く最長区間」を盤面とすると、配置を誤ったモデルほど盤面推定も一緒にずれ、過剰な駒が「盤外」に再分類されて逆に PASS してしまうのです。実際、列間隔が乱れた 10 列の盤(駒 10 枚)が 9/9 として通ることを再現できました。

判定基準を被検査対象そのものから導出すると、壊れた入力ほど基準が甘くなります。最終的に、列の採否を x 方向の距離ではなく「盤面の行に乗るか」で判定し、推定列数が 9 でなければ FAIL にする形にしました。

まとめ

  • 構造化 JSON の抽出は 191 項目中 189 一致で、ローカル勢では過去最高
  • ただし引用記号の除去など「原文の装飾をどこまで剥がすか」は Gemma 4 と同じ場所で落とす
  • HTML 生成では 7 テーマ中 6 テーマで前置きが混入する。JSON では混入ゼロという非対称がある
  • 18.8 tok/s は安定しているが、thinking が長く 1 ページ 30〜59 分かかる
  • エラー番号の桁が実測と合わないときは、単位の解釈を疑う
  • 判定基準を被検査対象から導出すると、壊れた入力ほど基準が甘くなる

全 9 テーマの成果物と生成条件は OreOre-Bench で公開しています。