0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

生成しないLLM「Decision Model」でメール仕分け(前編):ゼロショットのLLMは、数百件で学習したTF-IDF分類器に届かない

0
Last updated at Posted at 2026-10-16

この記事でやったこと

  • メールの優先度(urgent/high/medium)判定タスクで、3種類の方法を比較
    • ローカルで動くDecision Model(Laya・Clef・Clef-Flash)
    • 生成LLM(Qwen3.5)
    • 参照基準の古典的な分類器(TF-IDF特徴量+ロジスティック回帰。調整用の782件で学習)
  • 元のメール(英語は平均約500字)で、精度と速度を測定
    • 英語の評価用データ3,123件と、その一部を日本語に訳した100件
    • 件名+本文/本文のみ/件名のみの3通りの入力
  • ラベルの説明文の書き方が、精度にどれだけ効くかを測定
  • すべてRTX 5090 1枚で実行

TL;DR(分かったこと)

前提:使ったデータでは、優先度の最上位ラベル「urgent」の中身は、ほぼフィッシングメールです。

  1. 英語の元のメールでは、ゼロショット(例を見せない判定)のモデルは、どれも782件で学習したTF-IDF分類器に届かない。最良のQwen3.5-9Bが0.956、TF-IDFが0.983(Macro-F1)。
  2. Clef-FlashはQwen3.5-9Bの約4.8倍速い。代わりにMacro-F1は0.064低い。
  3. ラベルの説明文を「一般的な緊急度」に変えると、Macro-F1は0.30〜0.37下がる(調整用データ、Clef 27B・Clef-Flash・Qwen3.5-9B)。モデル間の差より大きい。
  4. 日本語訳(100件)でも、Clef 27B・Qwen3.5-9B・Clef-Flashは英語とほぼ同じ水準(Macro-F1 0.88〜0.91)。
  5. 英語では、件名を消しても主要なモデルの成績はほぼ変わらない。日本語では、Qwen3.5-9Bだけが下がる(0.909→0.848)。

英語の評価用データでのMacro-F1とレイテンシ中央値の散布図
図1 英語の評価用データでのMacro-F1と、元のメール1通あたりのレイテンシ中央値(ms、対数軸)。灰色はTF-IDF(CPU上のプロセス内呼び出し)。TF-IDFは最も速く最も高い。Clef-FlashはQwen3.5-9Bの約4.8倍速く、0.892に届く

対象読者

  • メールやチケットの振り分けを、ローカルGPU上で済ませたい人
  • llama.cppのDecision Model(選択肢ごとの確率を返すモデル)と、生成LLMにJSONで答えさせる方式のどちらを使うか迷っている人

この記事は前編です。メールに無関係な段落(背景文)を足した長文での精度と、入力が長いときの速度・GPUメモリは、後編で扱います。

注意点:評価に使ったのはAIが生成した合成データで、ラベルが人間の判断と合うかは検証していません(Claudeによる確認のみ)。実験コードは公開していません。結果に影響するプロンプトと設定は、末尾の「再現に必要な設定」にすべて載せています。

本記事の用語は次のとおりです。

  • urgent/high/medium:メールの優先度のラベル。このデータでは次の意味で使われている。
    • urgent:至急の行動を迫る、不審・詐欺的なメール(フィッシング)。アカウント停止や課金をちらつかせて、リンクを開かせる。
    • high:仕事で対応が必要な正規のメール。顧客対応のトラブル、経理・財務の承認や期限、会議の依頼。
    • medium:個人的なやりとりと、旅行の連絡。返信は求めるが、仕事上の緊急性はない。
  • Decision Model:文章を生成せず、入力を1回読んで各選択肢の確率を返す専用モデル1。生成トークンが要らないぶん速い。本記事ではLaya(convaiinnovations)とClef/Clef-Flash(Cloudflare)を、llama.cppの /v1/systemone かLaya SDKから呼ぶ。
  • ラベルの説明文:モデルに渡す、各ラベルの意味の説明。モデルは例を見ず、この説明だけで判定する(ゼロショット)。
  • TF-IDF:文字の並び(2〜4文字)の出現頻度から文章を数値の特徴量にする古典的な方法。それ自体は判定をしない。本記事では、この特徴量とロジスティック回帰を組み合わせ、調整用データで学習した分類器を略して「TF-IDF」と呼ぶ。
  • urgent再現率:urgentメールのうちurgentと判定できた割合。1から引いたものが見逃し率。
  • FPR(誤警報率):urgentでないメールをurgentと判定した割合。
  • Macro-F1:3つのラベル(urgent/high/medium)それぞれのF1の平均。
  • 調整用データ/評価用データ:データの分割。調整用(782件)でラベルの説明文を決め、TF-IDFを学習する。評価用(3,123件)で最終評価する。

比べた条件

前編・後編で、次の条件を比べています。前編は「元のメール(背景文なし)」の回です。

比較の軸 前編(元のメール) 後編(長いメール)
長さ 元のまま(英語で平均約500字) 背景文を足して256〜2,048トークン相当
言語 英語3,123件、日本語訳100件 英語・日本語訳とも元メール100件から作成
件名 件名+本文/本文のみ/件名のみ 件名あり/件名なし
ラベルの説明文 データに合わせた説明/一般的な緊急度 データに合わせた説明のみ
本文の切れ なし 先頭256トークンだけを渡す条件
速度 元のメール 入力長ごと、GPUメモリも

実験環境

表1 実験環境

項目 内容
GPU NVIDIA GeForce RTX 5090(VRAM 32GB、電力上限575 W)
CPU・メモリ AMD Ryzen 9 9950X3D2(16コア)、31GiB(WSL2に割り当てられた量)
OS Windows上のWSL2(Ubuntu 26.04.1 LTS、カーネル6.18.40.1-microsoft-standard-WSL2)
ドライバ・CUDA NVIDIAドライバ616.92(CUDAドライバAPI 13.4)、CUDA Toolkit 12.8
推論エンジン llama.cpp v0.6.0(commit d812350)2を、GCC 14.4.0でCUDA有効・sm_120 向けにソースビルド
Python環境 Python 3.12.15、PyTorch 2.11.0+cu128、Laya SDK 0.4.2(transformers 5.19.0)、scikit-learn 1.9.1。公式実装との照合だけtransformers 5.10.2
測定条件 並列数1の逐次処理。他のGPUジョブは速度測定のあいだ停止
実行日 2026-10-10〜11

速度はWSL2上で測っているので、絶対値は他の環境にそのまま当てはまりません。同じ環境の中での比較として読んでください。

比較したもの

表2 比較したモデル

表示名 方式 サイズ エンジン・量子化 チェックポイント
Laya(英語) Decision Model 421M llama.cpp・BF16 ggml-org/Laya-GGUF
Laya(多言語) Decision Model 322M Laya SDK・FP16 convaiinnovations/laya
Clef-Flash 9B Decision Model 9B llama.cpp・Q4_K_M ggml-org/Clef-Flash-GGUF
Clef 27B Decision Model 27B llama.cpp・Q4_K_M ggml-org/Clef-GGUF
Qwen3.5-0.8B 生成LLM 0.8B llama.cpp・Q4_K_M unsloth/Qwen3.5-0.8B-GGUF
Qwen3.5-9B 生成LLM 9B llama.cpp・Q4_K_M unsloth/Qwen3.5-9B-GGUF

GGUFはllama.cppで動かすためのモデルファイルの形式、Q4_K_Mは4ビット量子化、BF16・FP16は量子化しない16ビット精度です。

  • Clef-FlashはQwen3.5-9Bから、Clef 27BはQwen3.8-27B(比較したQwen3.5とは別世代)から追加学習したモデルです。
  • 入力は全モデル共通の Subject: … と Body: … です。Decision Modelは1回で複数の質問に答えられますが、今回は優先度の質問1問だけを渡しました。
  • Qwenは2つの出力方式(自由生成のJSON、JSON schemaで制約)で測りましたが、出力が全件で同一だったので、1系列として扱います。
  • Laya(多言語)は、日本語の評価だけで使います。英語の評価用データ3,123件では測っていません。
Qwenの推論設定
  • 毎リクエスト enable_thinking=false、temperature=0、seed=42、max_tokens=32、cache_prompt=false を指定しました。出力にthinkブロックが無いことは確認済みです。
  • 自由生成のJSONは厳密にパースしました(救済なし)。JSON schemaのenum制約でも、出力は全件で同一でした。

GGUF版のClef-Flashは、Cloudflareの公式PyTorch実装(BF16)3と調整用データ30件で照合し、29件で判定が一致しました。

GGUF版の照合の詳細
  • 照合した理由:「GGUFにはjoint head(答えを出す専用の出力層)が無い」という第三者の指摘4がありました。そのため、公式実装と結果が変わるおそれがありました。
  • 確率の差は中央値0.006、最大0.065で、量子化の範囲に収まっています。
  • Clef 27BはBF16で54GBあり、RTX 5090(32GB)に載らないため、照合していません。

データセットの準備

元にしたのはweijianzhg/email-triage-action-seedです(3,905件、Claude Opus 4.6による合成、Apache-2.0)5。英語のメールに、優先度とカテゴリが付いています。

このデータを、調整用(782件)と評価用(3,123件)に分けました。調整用は、ラベルの説明文を決めることと、TF-IDFの学習に使います。評価用は、最後の評価にだけ使います。似たメールが両方に入らないように分けています。

表3 データの例(カテゴリごとに1件。件数は全3,905件の内訳)

優先度 カテゴリ 件数 件名の例
urgent phishing(フィッシング) 500 Your Zoom account will be deactivated - Verify within 6 hours
high customer_support(顧客対応) 577 URGENT: Order #78432 charged twice - need immediate refund
high finance(経理・財務) 767 Payroll Funding Shortfall - Tomorrow's Run
high meeting_request(会議の依頼) 887 Urgent: Need to schedule board presentation review - Tomorrow?
medium personal(個人) 821 Can you water my plants next week?
medium travel(旅行) 353 Booking conflict: Two trips overlapping on your calendar

urgent(phishing)のメールを1件、モデルに渡す形のまま示します(URLだけ無効化しています)。

urgent(phishing)の例
Subject: Your Zoom account will be deactivated - Verify within 6 hours

Body:
Dear User,

Due to our recent terms of service update, all accounts must be re-verified.
Your Zoom account (linked to this email) will be permanently deactivated
if not verified within 6 hours.

All scheduled meetings, recordings, and contacts will be lost.

Verify your account: hxxp://zoom-account-verify[.]click/reactivate

Thank you for your cooperation.

Zoom Trust & Safety Team

表3の件名の例では、highの件名にURGENTが付き、urgentの件名には付いていません。件名にURGENT/ASAP/verify/expireなどの緊急表現が入る率は、urgentで44%、highで20%です。件名の緊急表現だけでは、urgentとhighを見分けられません。

優先度は、カテゴリで完全に決まります。

  • urgentはphishing(フィッシング)だけ
  • highはcustomer_support・finance・meeting_request
  • mediumはpersonal・travel

カテゴリだけから優先度を当てる分類器は、評価用データでMacro-F1 1.000になります。「重要メール(urgent)の見逃し」は、実質「フィッシング・詐欺メールの見逃し」です。 本物のシステム障害や訴訟リスクのような緊急の仕事メールは、customer_supportなどのカテゴリとして high に入っています。

日本語のデータは、評価用データから元メールを100件選び(urgent 50/high 25/medium 25)、Claude Opus 5.5で訳して作りました。意味・期日・依頼・金額・URL・固有名詞を変えないことを条件にしています。訳文がラベルを変えていないかは、翻訳に使っていないClaude 2体(Haiku 5.5・Sonnet 5.5)に原文と訳文を渡して確かめました。優先度と事実の保持は、100件すべてで問題ありませんでした。後編の長文は、この100件(英語の原文と日本語訳)に背景文を足して作ります。作ったデータはHugging Faceで公開しています。

ラベルの説明文

モデルには、各ラベルの意味を説明した文を渡します。説明文は、調整用データの事例だけを見て書きました。urgentは「至急の行動を迫る、不審または詐欺的なメッセージ」です。比較のため、「即時対応が必要か」を基準にした、一般的な緊急度の説明文も用意しました。

説明文の全文

モデルには次の英文をそのまま渡しています。指示文は両方とも "Classify the priority of this email using the fixed rubric." です。

データに合わせた説明文:

  • urgent: Suspicious or fraudulent message that pressures the recipient to act immediately (verify an account, click a link, pay a fee, confirm a transfer) under threat of suspension, loss or charges, typically impersonating a known company or service.
  • high: Legitimate work email that needs action from the recipient or their team: customer-support escalations, finance and accounting approvals or deadlines, and business meeting requests.
  • medium: Personal correspondence or travel logistics (family and friends, social plans, itineraries, bookings); may ask for a reply but has no business-critical consequence.

一般的な緊急度の説明文:

  • urgent: Needs immediate attention: an ongoing incident, an imminent deadline, or serious harm if not handled right away.
  • high: Requires action soon or has a stated deadline, or a serious consequence if ignored.
  • medium: Needs a response or action but without strong time pressure.

説明文の書き方の影響

2つの説明文を、調整用データで比べました(図2)。一般的な緊急度の説明文にすると、Clef 27B・Clef-Flash・Qwen3.5-9BではMacro-F1が0.30〜0.37下がります。この下げ幅は、データに合わせた説明文でのモデル間の差(Laya 0.74〜Qwen3.5-9B 0.96の0.22)より大きくなりました。以降の結果はすべて、データに合わせた説明文を使っています。

説明文の違いによる調整用データでのMacro-F1の横棒グラフ

図2 説明文の違いによる、調整用データ(782件)でのMacro-F1。一般的な緊急度の説明文にすると、全モデルで下がる

判定の目安

実用の目安として、「フィッシングの見逃しが5%以下(urgent再現率0.95以上)」かつ「誤警報が5%以下(FPR 0.05以下)」を、結果を見る前に決めました。後編で長さごとに崩れたかどうかを判断するときも、この目安を使います。

英語の結果

どのモデルも、調整用データで学習したTF-IDFに届きませんでした(表4)。「目安」の列は、目安を満たすかどうかです。

表4 英語の評価用データでの結果(3,123件)

モデル Macro-F1 urgent再現率 FPR 目安 TF-IDFとの差 [95%区間]
Qwen3.5-9B 0.956 0.990 0.003 ✓ −0.027 [−0.035, −0.020]
Clef 27B 0.916 1.000 0.028 ✓ −0.067 [−0.079, −0.057]
Clef-Flash 9B 0.892 1.000 0.031 ✓ −0.092 [−0.104, −0.079]
Laya(英語) 0.741 0.733 0.166 ✗ −0.242
Qwen3.5-0.8B 0.449 0.422 0.014 ✗ −0.535
参照:TF-IDF(件名+本文) 0.983 0.970 0.000
参照:TF-IDF(件名のみ) 0.945 0.930 0.008

TF-IDFとの差は「モデル − TF-IDF(件名+本文)」です。区間を示した3モデルは区間がすべて0を下回り、TF-IDFより低い結果です。目安を満たさない2モデルは、差が大きいため区間を省きました。なお、件名だけで学習したTF-IDF(0.945)は、Qwen3.5-9B(0.956)を下回ります。

これだけで、LLM系が弱いとまでは言えません。このデータは文体と語彙がカテゴリごとに揃っています。文字の並びの頻度という表面的な手がかりだけで解けるほど、易しいデータだと考えられます。

速度

精度と速度の関係は図1のとおりです。元のメール(入力は約250〜365トークン)での、1通あたりのレイテンシ中央値は次のとおりです。

  • TF-IDF:約0.5 ms(件名のみは約0.3 ms)
  • Laya:約12 ms
  • Clef-Flash:40 ms
  • Qwen3.5-0.8B:約76 ms
  • Clef 27B:約108 ms
  • Qwen3.5-9B:約190 ms

Clef-Flashは、同じ9BのQwen3.5-9Bの約4.8倍の速さです。Decision Modelは生成トークンが要らない分、入力が短いほど差が大きくなります。LLMの中ではLayaが最速ですが、精度は目安に届きません。

TF-IDFは、ほかのモデルと同じ30件・同じ手順(ウォームアップ20件、3周)で、同じマシンのCPU(Ryzen 9 9950X3D2)上で1通ずつ測りました。TF-IDFはプロセス内の呼び出しで、HTTPの往復を含みません。LLM側の往復の分は1〜5 msなので、それを差し引いても、TF-IDFは1桁以上速い結果です。

日本語の結果

日本語訳の100件(urgent 50件・非urgent 50件)での結果は表5のとおりです。TF-IDFは英語の調整用データで学習したため、日本語は対象外です。

表5 日本語訳での結果(100件)

モデル Macro-F1 urgent再現率 FPR
Clef 27B 0.909 1.00 0.02
Qwen3.5-9B 0.909 1.00 0.02
Clef-Flash 9B 0.881 1.00 0.02
Laya(多言語) 0.508 0.80 0.18
Laya(英語) 0.393 0.70 0.36
Qwen3.5-0.8B 0.325 0.36 0.02
  • Clef 27B・Qwen3.5-9B・Clef-Flashは、英語とほぼ同じ水準で、3つとも目安を満たします。
  • Laya(多言語)は、日本語でも目安に届きません。
  • 100件なので、1件で再現率・FPRが0.02動きます。モデル間の小さな差は、傾向としての示唆にとどめます。

件名の有無の影響

件名が判定にどれだけ効いているかを見るため、同じデータで、入力を2通りに変えて測りました。

  • 本文のみ:Subject: の行を消し、Body: 以降だけを渡す
  • 件名のみ:Subject: の行だけを渡す

TF-IDFは、それぞれの入力で学習し直した分類器です。ラベルの説明文とプロンプトは変えていません。結果は表6のとおりです。

表6 入力を変えたときの結果(英語は評価用データ3,123件、日本語は訳した100件)

モデル 言語 Macro-F1
件名+本文 → 本文のみ → 件名のみ
urgent再現率
件名+本文 → 本文のみ → 件名のみ
FPR
件名+本文 → 本文のみ → 件名のみ
Qwen3.5-9B 英語 0.956 → 0.950 → 0.903 0.990 → 0.972 → 0.798 0.003 → 0.003 → 0.013
日本語 0.909 → 0.848 → 0.831 1.00 → 0.92 → 0.72 0.02 → 0.02 → 0.02
Clef 27B 英語 0.916 → 0.910 → 0.847 1.000 → 1.000 → 0.960 0.028 → 0.032 → 0.085
日本語 0.909 → 0.909 → 0.861 1.00 → 1.00 → 0.94 0.02 → 0.02 → 0.06
Clef-Flash 9B 英語 0.892 → 0.882 → 0.883 1.000 → 1.000 → 0.920 0.031 → 0.040 → 0.046
日本語 0.881 → 0.888 → 0.855 1.00 → 1.00 → 0.92 0.02 → 0.06 → 0.08
Laya(英語) 英語 0.741 → 0.795 → 0.457 0.733 → 0.835 → 0.972 0.166 → 0.117 → 0.616
日本語 0.393 → 0.350 → 0.476 0.70 → 0.68 → 0.96 0.36 → 0.36 → 0.66
Laya(多言語) 日本語 0.508 → 0.466 → 0.496 0.80 → 0.88 → 0.92 0.18 → 0.50 → 0.42
Qwen3.5-0.8B 英語 0.449 → 0.392 → 0.495 0.422 → 0.258 → 0.378 0.014 → 0.006 → 0.051
日本語 0.325 → 0.223 → 0.284 0.36 → 0.14 → 0.32 0.02 → 0.00 → 0.12
参照:TF-IDF 英語 0.983 → 0.982 → 0.945 0.970 → 0.968 → 0.930 0.000 → 0.000 → 0.008

英語では、件名を消しても、Clef系・Qwen3.5-9B・TF-IDFはほとんど変わりません。英語の元のメールでは、どれも本文から判定できています。TF-IDFに届かないという結果も、件名の有無に関係なく同じです。

日本語では事情が違い、件名を消すとQwen3.5-9Bに見逃しが出ます(再現率0.92)。日本語の本文だけでは、urgentと言い切れないメールがあるようです。Clef系は再現率1.00のままですが、Clef-FlashはFPRが0.06(50件中3件)になり、目安をわずかに外れます。

件名だけにすると、英日とも、Clef系とQwen3.5-9Bで振る舞いが分かれます。Clef系はurgentと言い続け(再現率0.92〜0.96)、代わりに誤警報が増えます。Qwen3.5-9Bは本文がないとurgentと言いにくくなり(再現率は英語0.80、日本語0.72)、誤警報はほとんど増えません。

Laya(英語)は、英語では件名を消すと成績が上がります。逆に件名だけを渡すと、英日ともほぼすべてをurgentにします(FPR 0.62〜0.66)。件名の緊急表現に強く引っ張られていると考えられます(推測で、原因は確かめていません)。

件名の効き方の違いは、後編の「本文が切れたとき」で効いてきます。

まとめ

元のメールに限ると、選び方は次のようになります。

  • ラベル付きの事例が数百件あるなら:まずTF-IDF+ロジスティック回帰を試す価値があります。このデータでは最も強く、学習も一瞬でした。
  • 事例がないなら:精度を優先するならQwen3.5-9B(0.956)、速度を優先するならClef-Flash(0.892、約4.8倍速い)です。どちらも目安を満たし、日本語訳でも同じ傾向でした。
  • どのモデルでも:ラベルの説明文は、データの事例を見て書く必要があります。一般的な緊急度の説明では、モデル選びの差より大きく崩れました。
  • LayaとQwen3.5-0.8B:ゼロショットのままでは目安に届きません。Layaのモデルカードは、自分の領域のデータでfine-tuneすると精度が大きく上がると説明しています6。同カードの課題では、ゼロショットの0.362がfine-tune後に0.766になっています。今回の結果は、これらのモデルの総合能力を示すものではありません。

ただし、メールが長くなると、この順位は入れ替わります(後編)。

限界

  • ラベルの意味:urgentはフィッシングを指すため、この結果は一般的な緊急メールの検出性能ではありません。ラベルの説明文は調整用データの事例から書いたため、実質的にはカテゴリの説明文です。
  • 日本語は翻訳100件です(Claude Opus 5.5による翻訳)。日本語で新規に書いたメールでの評価は未実施です。
  • 確認に使ったのはClaude系だけです。データを作ったAI(Claude Opus 4.6)と同系統で、別の系統のAIや人手による確認は入っていません。
  • 比較の交絡:基盤・学習・エンジン・量子化が同時に違うので、サイズだけの効果は主張しません。Clef-FlashとQwen3.5-9Bの差も、タスク特化の効果とは言い切れません(学習データは非公開)。
  • 公開済みのモデルだけの検証:Decision Modelは、Unslothを使えば、QwenやGemmaなどから自分のタスク向けに学習できます7。メールの判別に特化させたモデルのほうが、今回の公開モデルより、精度が高く速くなる可能性があります。今回は試していません。学習したモデルをllama.cppで動かせるかも未確認です。
  • 未実施:Clef 27Bの公式実装との照合。

再現に必要な設定

結果に影響する要素は、次のとおりすべて載せます。

入力とプロンプト

入力テキストは、全モデル共通です。

入力テキストの形式
Subject: {件名}

Body:
{本文}

Decision Model(llama.cppの /v1/systemone)には、次の内容を送りました。ラベルの説明文は、前の節の「データに合わせた説明文」です。選択肢の順番は high・medium・urgent です。返ってきた answers.priority.choice を予測として使い、probabilities のキーが3ラベルと一致しない場合は無効としました。

/v1/systemone へのリクエスト(説明文は略)
{
  "state": "Subject: …\n\nBody:\n…",
  "questions": {
    "priority": {
      "type": "choice",
      "instructions": "Classify the priority of this email using the fixed rubric.",
      "criteria": {
        "high": "Legitimate work email that needs action …",
        "medium": "Personal correspondence or travel logistics …",
        "urgent": "Suspicious or fraudulent message that pressures …"
      }
    }
  }
}

Laya(多言語)はSDKをプロセス内で呼びました。質問は上と同じ内容で、入力の上限は1,024トークンです。

Laya(多言語)の呼び出し
agent = laya.load("convaiinnovations/laya", subfolder="multilingual", revision="7b928d8…")
out = agent.system_one(入力テキスト, questions, max_len=1024)

Qwen3.5(llama.cppのチャット補完)のシステムプロンプトは次のとおりです。{...} の部分にラベルの説明文(上と同じ英文)が入ります。ユーザーメッセージは入力テキストそのものです。

Qwen3.5のシステムプロンプト
You are an email triage assistant. Classify the priority of this email using the fixed rubric.

Priority labels:
- high: {highの説明文}
- medium: {mediumの説明文}
- urgent: {urgentの説明文}

Answer with a single JSON object and nothing else: {"priority": "<label>"} where <label> is one of: high, medium, urgent.

リクエストのパラメータは temperature=0、seed=42、max_tokens=32、cache_prompt=false、chat_template_kwargs={"enable_thinking": false} です。出力は json.loads で厳密にパースし(コードフェンスの除去などの救済なし)、priority の値が3ラベルにない場合は無効としました。JSON schemaで制約した場合は、次の指定を加えています(結果は同一でした)。

JSON schemaで制約する場合の追加指定
"response_format": {
  "type": "json_schema",
  "json_schema": {
    "name": "priority", "strict": true,
    "schema": {
      "type": "object",
      "properties": {"priority": {"type": "string", "enum": ["high", "medium", "urgent"]}},
      "required": ["priority"], "additionalProperties": false
    }
  }
}
llama-serverの起動引数

全モデルに --host 127.0.0.1 を付けています。速度の測定のときだけ、--metrics を加えました。

表7 llama-serverの起動引数

モデル 引数
Laya(英語) -ngl 99 -c 4096 -b 4096 -ub 4096 --parallel 1 --no-webui
Clef-Flash 9B、Clef 27B -ngl 99 -c 8192 -b 8192 -ub 8192 --parallel 1 --no-webui
Qwen3.5-0.8B、Qwen3.5-9B -ngl 99 -c 8192 -b 8192 -ub 2048 --parallel 1 --jinja --no-webui
TF-IDFの設定

調整用データの782件だけで学習しました。ハイパーパラメータの探索はしていません。

TF-IDFの設定
make_pipeline(
    TfidfVectorizer(analyzer="char_wb", ngram_range=(2, 4), min_df=2, sublinear_tf=True),
    LogisticRegression(max_iter=2000, C=4.0, class_weight="balanced"),
)

「件名+本文」は入力テキスト全体を、「件名のみ」は件名の文字列だけを入力にしました。scikit-learn 1.9.1です。

データの作り方
  • 調整用と評価用の分割:似たメール(文字5-gramのMinHashでJaccard 0.7以上、完全一致を含む)を同じグループにまとめました。そのうえで、優先度で層化して5分割し(StratifiedGroupKFold、シード42)、1つを調整用(782件)、残りを評価用(3,123件)にしました。
  • Claude 3体による確認:ラベルの確認では、件名・本文と、データに合わせた説明文だけを渡し、ラベルを1つ選ばせました。正解ラベルとカテゴリは見せていません。
集計と速度測定
  • 予測できなかった場合(出力が解釈できない、選択肢にない値、通信エラー)は、誤りとして数え、母数から外しません。この記事の評価では0件でした。
  • 指標:Macro-F1は3ラベルのF1の平均です。urgent再現率とFPRは、urgentを陽性、highとmediumを陰性として、予測ラベルから計算しました。Decision Modelの予測は、確率が最大の選択肢です。
  • 95%区間:パーセンタイル法です。似たメールのグループ単位で、モデルとTF-IDFの予測を組にしたまま1,000回再標本化しました(ペアのブートストラップ)。
  • 速度の測定対象:英語の元メールから30件を無作為に選び(シード42)、全モデルで同じ入力を使いました。
  • 手順:ウォームアップ20件のあと、30件を3周しました。各周で順序を入れ替え、同じ入力が連続しないようにしています。
  • 時間の測り方:1リクエストの往復の壁時計時間です。
  • 件名の有無の比較:本文のみは Body:\n{本文}、件名のみは Subject: {件名} を入力にしました。Qwen3.5は自由生成のJSONだけで測っています(2つの出力方式は、件名+本文で全件同一だったため)。

データの出典はweijianzhg/email-triage-action-seed(Apache-2.0)です。日本語訳100件・長文・調整用と評価用の分割は、aoyama2222222222/email-triage-long-en-ja(Apache-2.0)で公開しています。

参考文献

  1. Xuan-Son Nguyen, Victor Mustar「New in llama.cpp: Decision Models」(ggml-org、2026-10-02). https://huggingface.co/blog/ggml-org/decision-models-in-llamacpp ↩

  2. ggml-org/llama.cpp v0.6.0, commit d81235049384534c167caea52b85a694f6103d14. https://github.com/ggml-org/llama.cpp ↩

  3. Cloudflare/clef-flash(PyTorch参照実装 joint_schema_model.py を含む), revision 8b2e5fd17c09fd49bc2880b805d5323ec7ff4ff3. https://huggingface.co/Cloudflare/clef-flash ↩

  4. innFactory「Cloudflare Clef」(第三者解説。GGUFのdecision head、言語、メモリについて). https://innfactory.ai/en/ai-models/cloudflare-clef/ ↩

  5. weijianzhg/email-triage-action-seed, revision 8c98baa8b10a1dddd676c860d9e1b86c89141c38, Apache-2.0. https://huggingface.co/datasets/weijianzhg/email-triage-action-seed ↩

  6. convaiinnovations/layaモデルカード「Fine-tune for better accuracy」. https://huggingface.co/convaiinnovations/laya ↩

  7. Unsloth Docs「Unslothで独自の決定モデルを学習する」. https://unsloth.ai/docs/jp/ji-chu/unslothdenomoderuwosuru ↩

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?