この記事のゴール
オープンウェイトLLMを本番投入する全体像で「自前運用なら vLLM」「原価を決めるのは量子化と稼働率」と書きました。本稿はその具体例の決定版です。題材は Qwen3-8B-AWQ——アリババ Qwen チームの 8B モデルを AWQ で 4bit に量子化した、Apache-2.0 のオープンウェイトです。
このモデルが面白いのは、**「小さくて安いのに“考える”」**という一点に尽きます。AWQ で重みが約 1/3 に圧縮されるので 24GB の GPU 1枚に載り、しかも 思考(reasoning)モードと高速対話モードを 1 モデルで切り替えられる。つまり「o1 系のような段階推論を、自分のサーバで、安く」が現実になります。
ゴールは、vllm serve を打てることではなく、公式ドキュメントに忠実なまま、どの場面でどう使い、何を外すと品質が崩れるかまで分かった状態になることです。コードはすべて手元で動く形で示します。
信頼性の開示:GPU で LLM を本番運用する難しさ(VRAM・スループット・回復性)は、動画AIローカライズ基盤で実際に踏んだ領域です。本稿のスペック・コマンド・サンプリング値は、すべて公式一次情報——Qwen3-8B-AWQ モデルカード・Qwen 公式ドキュメント・vLLM ドキュメント——に基づきます。VRAM とスループットの実数は環境依存・要ベンチであることを明記します。
30秒の結論:いつ Qwen3-8B-AWQ を選ぶか
| Qwen3-8B-AWQ(4bit) | Qwen3-8B(FP16/BF16) | クローズドAPI | |
|---|---|---|---|
| 重みの VRAM | ≈6GB(要実測) | ≈16GB | 0(自前不要) |
| 載るGPU | 24GBクラス1枚(L4 / A10 / RTX 4090 等) | 24GB〜 | — |
| 思考モード | あり(切替可) | あり | モデル依存 |
| ライセンス | Apache-2.0(商用可) | Apache-2.0 | 利用規約 |
| データ主権 | 自前で完結 | 自前で完結 | 外部送出 |
| 運用 | 自前(本記事) | 自前 | ほぼゼロ |
本質はこの一行:Qwen3-8B-AWQ は「思考できる小型モデルを、データを外に出さず、1枚のGPUで安く回す」ための選択肢です。
- ✅ 向く:オンプレ/VPC内での推論、機微データを外に出せない案件、RAG やエージェントの“推論役”を原価最適化したい、検証〜小規模本番を 1 GPU で回したい。
- ❌ 向かない:とにかく最高品質が要る(より大きいモデルへ)/運用ゼロで始めたい(まずはAPI)。判断軸の全体像はAPI vs セルフホストの損益分岐に揃えてください。
Qwen3-8B-AWQ とは(公式スペックを正確に)
まず公式の事実を、盛らずに置きます(出典:モデルカード)。
| 項目 | 値 |
|---|---|
| 総パラメータ | 8.2B(うち非埋め込み 6.95B) |
| レイヤー数 | 36 |
| アテンション | GQA:Q ヘッド 32 / KV ヘッド 8 |
| ネイティブ文脈長 | 32,768 トークン |
| 拡張文脈長 | 131,072 トークン(YaRN 使用時) |
| 量子化 | AWQ 4bit |
| ライセンス | Apache-2.0 |
| 引用 | Qwen3 Technical Report(arXiv:2505.09388) |
公式は、Qwen3 が推論能力で過去の QwQ・Qwen2.5 系を上回り、創作・ロールプレイ・指示追従での人間嗜好との整合性も向上したと述べています(具体スコアは技術報告書を参照)。本稿はベンチ数値の主張ではなく、**「公式仕様どおりに本番で動かす」**ことに集中します。
🔎 派生に注意:Hugging Face には
Qwen/Qwen3-8B(フル精度)、Qwen/Qwen3-8B-FP8、Qwen/Qwen3-8B-Base、コミュニティの MLX 版などが並びます。本稿が扱うのは公式Qwen/Qwen3-8B-AWQ(4bit)です。GQA は Q32/KV8——KV ヘッドが少ない設計は、KVキャッシュが軽く、同時実行を載せやすい=セルフホスト向きという含意があります。
AWQ(4bit量子化)とは:なぜ“1枚のGPU”に載るのか
AWQ = Activation-aware Weight Quantization。公式は「ハードウェアに優しい低ビット重み量子化」と説明します。重要なのは activation-aware——「出力に効く重みほど壊さない」ように量子化するため、4bit でも品質劣化を抑えられます。AutoAWQ の実装では FP16 比で約3倍のメモリ削減・約3倍の高速化が示されています(AWQ ドキュメント)。
VRAM の概算で“なぜ1枚に載るか”が腑に落ちます。
FP16 の重み ≈ 8.2B × 2 byte ≈ 16.4 GB
AWQ 4bit ≈ 8.2B × 0.5 byte ≈ 4.1 GB(+ スケール/ゼロ点で実測 6GB 前後)
つまり重みは概ね 6GB 前後。残りの VRAM を **KVキャッシュ(同時実行と文脈長を決める)**に回せます。24GB のGPU 1枚なら、重み 6GB + KV 余裕十分、という構図です(実際の同時実行数とスループットは必ず負荷試験で確定してください)。
| 量子化 | 重みVRAM | 品質 | 主用途 |
|---|---|---|---|
| FP16/BF16 | ≈16GB | 基準 | 余裕がある時の最高品質 |
| AWQ 4bit | ≈6GB | 実用上わずかな劣化 | 1GPUセルフホスト・原価最適化 |
| FP8 | ≈8GB | ほぼ無劣化 | 対応GPU(H100等)で速度重視 |
💡 使い分け:H100 のような FP8 対応GPUがあるなら FP8 も有力です(Llama を FP8 でサーブする回参照)。一方、L4 / A10 / RTX 4090 のような 24GB GPU で“とにかく載せて安く回す”なら AWQ 4bit が王道です。AWQ・GPTQ・FP8・GGUF の選び分けは量子化方式の選び方で詳説しています。
キラー機能:ハイブリッド思考(thinking / non-thinking)
Qwen3 最大の差別化が、1つのモデルで「考える」と「即答する」を切り替えられることです。
- 思考モード(thinking):
<think> … </think>の中で段階推論してから答える。数学・コード・複雑な判断に強い。 - 非思考モード(non-thinking):推論ブロックを出さず即答。分類・抽出・定型対話など速度と原価が効く用途向け。
切り替えは2通り(公式仕様)
- ハードスイッチ:
apply_chat_template(..., enable_thinking=True/False)(API ではchat_template_kwargs)。 - ソフトスイッチ:
enable_thinking=Trueの状態で、ユーザー発話に/think//no_thinkを書くとターン単位で上書きできる。
モード別の推奨サンプリング(外すと壊れる)
公式がモードごとに別のサンプリングを指定している点が要注意です。
| パラメータ | 思考モード | 非思考モード |
|---|---|---|
| Temperature | 0.6 | 0.7 |
| TopP | 0.95 | 0.8 |
| TopK | 20 | 20 |
| MinP | 0 | 0 |
| greedy decoding | 禁止 | — |
⚠️ 公式の明確な警告:思考モードで greedy decoding(temperature=0 相当)を使ってはいけません。無限ループ・繰り返し・性能劣化を招きます。「reasoning は決定的に出したいから temp=0」は逆効果です。
出力長の目安も公式にあります:ほとんどのクエリは 32,768 トークン、数学やプログラミングのような複雑問題は 38,912 トークンまで見込む。思考モードは答えの前に長く“考える”ため、max_tokens をケチると結論に到達する前に切れます。
動かす①:transformers で最短確認(開発用)
まずはローカルで最小確認。AWQ 重みの実行には awq カーネルが要るので、autoawq を入れます(本番は後述の vLLM 推奨)。
# pip install -U transformers accelerate autoawq
from transformers import AutoModelForCausalLM, AutoTokenizer
model_name = "Qwen/Qwen3-8B-AWQ"
tokenizer = AutoTokenizer.from_pretrained(model_name)
model = AutoModelForCausalLM.from_pretrained(model_name, torch_dtype="auto", device_map="auto")
messages = [{"role": "user", "content": "9.11 と 9.9 はどちらが大きい?理由も。"}]
# enable_thinking=True で <think>...</think> による段階推論を有効化(ハードスイッチ)
text = tokenizer.apply_chat_template(
messages, tokenize=False, add_generation_prompt=True, enable_thinking=True,
)
inputs = tokenizer(text, return_tensors="pt").to(model.device)
# 思考モードの公式推奨サンプリング(greedy は使わない)
generated = model.generate(
**inputs, max_new_tokens=32768,
temperature=0.6, top_p=0.95, top_k=20,
)
output_ids = generated[0][len(inputs.input_ids[0]):].tolist()
print(tokenizer.decode(output_ids, skip_special_tokens=True))
開発の単機・お試しはこれで十分。ただし transformers の generate は連続バッチを持たないため、本番の同時多数リクエストには向きません。そこで vLLM に移ります。
動かす②:vLLM で本番サーブ(OpenAI互換)
本番は vLLM。連続バッチと PagedAttention で GPU を遊ばせず、OpenAI 互換エンドポイントを出すので、アプリ側のコードを変えずに向け先だけ差し替えられます(基盤づくりの全体はvLLM 本番セルフホスト運用記に集約)。
# Qwen3-8B-AWQ を OpenAI 互換でサーブ。思考の分離パースを有効化
vllm serve Qwen/Qwen3-8B-AWQ \
--reasoning-parser qwen3 \
--max-model-len 32768 \
--gpu-memory-utilization 0.90 \
--port 8000
--reasoning-parser qwen3:vLLM 0.9.0 以降の指定。<think>ブロックを本文と分離し、レスポンスにreasoning_contentフィールドを足してくれる。古い vLLM では--enable-reasoning --reasoning-parser deepseek_r1を使います。--max-model-len 32768:ネイティブ上限。実際に必要な長さに絞るのが原価とレイテンシの鉄則(青天井にしない)。--gpu-memory-utilization 0.90:確保率。上げすぎ=OOM、低すぎ=同時実行が減る。
立ち上がれば、OpenAI クライアントでそのまま叩けます。思考のオン/オフは extra_body.chat_template_kwargs.enable_thinking で制御します。
from openai import OpenAI
client = OpenAI(api_key="EMPTY", base_url="http://localhost:8000/v1")
resp = client.chat.completions.create(
model="Qwen/Qwen3-8B-AWQ",
messages=[{"role": "user", "content": "在庫が負になりうる箇所をこのSQLから指摘して"}],
temperature=0.6, top_p=0.95, extra_body={
"top_k": 20,
"chat_template_kwargs": {"enable_thinking": True}, # 思考モード
# 量子化モデルで“終わらない繰り返し”が出たら presence_penalty を 0〜2 で(推奨 1.5)
"presence_penalty": 1.5,
},
)
msg = resp.choices[0].message
print("思考:", getattr(msg, "reasoning_content", None)) # <think> の中身(ログ/監査用)
print("回答:", msg.content) # ユーザーに返す最終回答
reasoning_content(思考過程)と content(最終回答)が構造的に分かれるのが本番で効きます。ユーザーには content だけ返し、reasoning_content は監査・評価用に内部で扱う——この分離が後述の型安全・可観測性の土台になります。
長文脈:YaRN で 32K → 131K(必要な時だけ)
ネイティブは 32,768。それ以上の文脈が要るときだけ YaRN で拡張します。CLI でも宣言できます。
vllm serve Qwen/Qwen3-8B-AWQ \
--reasoning-parser qwen3 \
--rope-scaling '{"rope_type":"yarn","factor":4.0,"original_max_position_embeddings":32768}' \
--max-model-len 131072
config.json に同等を書く形(factor:4.0, original_max_position_embeddings:32768)も公式に示されています。
⚠️ 常時 YaRN を有効化しない。公式は「必要な時だけ」と明言しています。短い入力にまで長文脈スケーリングを掛けると、短文の品質が落ちる副作用があります。32K で足りるなら YaRN は外す——「念のため最大」は逆効果です。長文脈ほど KVキャッシュが膨らみ VRAM とレイテンシも増えるので、
max-model-lenは実需に合わせて設定してください。
ツール呼び出し / エージェント
Qwen3 は関数呼び出し(tool use)に対応します。vLLM ではフラグで有効化します。
vllm serve Qwen/Qwen3-8B-AWQ \
--reasoning-parser qwen3 \
--enable-auto-tool-choice \
--tool-call-parser hermes
あとは OpenAI 互換の tools を渡せば、モデルが必要に応じて関数呼び出しを返します。エージェント的なツール連携を組むなら、公式の Qwen-Agent(ツール定義とパースのテンプレートが揃う)を使うと、tool-call の取り回しを自前で書かずに済みます。引数のZod検証・反復上限・冪等な副作用まで含む安全なループの実装はQwen3 のエージェント化で具体化します。設計の勘所——「LLMに任せる判断と決定的コードに寄せる実行を分ける」——はツール使用・関数呼び出しの設計に揃えてください。
どの場面で使うか:型安全な本番クライアント(応用)
ここからが応用です。Qwen3-8B-AWQ の強みは「思考できる」「安い」「自前」。これを型安全・冪等・回復性で包むと、本番で稼げるコンポーネントになります。代表的な応用は、機微データを外に出さない自前RAGです。
設計方針(CLAUDE.md の原則どおり、SRP / KISS / 型安全境界を効かせます):
- モードを難度でルーティング——簡単なタスクは非思考(速い・安い)、難所だけ思考(原価最適化の本丸)。
- 思考過程と回答を分離——
reasoning_contentはログ、contentだけ検証して返す。 - 構造化出力は境界で Zod 検証——LLM 出力は外部入力。
parseで不正を弾く(型安全の規律)。生成段階で不正JSONを作らせない guided decoding との二段構えは型安全な構造化出力へ。 - タイムアウト+冪等キャッシュ——同じ入力を二度生成しない=原価も安定。
// lib/qwen-client.ts — 思考/非思考をルーティングし、出力を境界で型検証する薄いクライアント
import OpenAI from "openai";
import { z } from "zod";
import { createHash } from "node:crypto";
const client = new OpenAI({
baseURL: process.env.QWEN_BASE_URL, // 例: http://qwen-internal:8000/v1(privateで公開しない)
apiKey: "internal",
timeout: 60_000, // 思考モードは長い。短すぎる timeout は“正常な熟考”を切る
});
/** タスク難度 → モードと公式推奨サンプリングを決める(SSoT) */
const MODE = {
fast: { enable_thinking: false, temperature: 0.7, top_p: 0.8, top_k: 20 },
think: { enable_thinking: true, temperature: 0.6, top_p: 0.95, top_k: 20 },
} as const;
type Difficulty = keyof typeof MODE;
/** 期待する構造化出力。LLMの“それっぽい文字列”を信用せず、ここで弾く */
const RiskFinding = z.object({
hasRisk: z.boolean(),
severity: z.enum(["low", "medium", "high"]),
reason: z.string().min(1),
});
type RiskFinding = z.infer<typeof RiskFinding>;
const cache = new Map<string, RiskFinding>(); // 実運用は Redis 等に置換
/** 入力が同じなら生成も同じ(冪等)。連打・リトライ・重複依頼でコストを無駄にしない */
const keyOf = (prompt: string, d: Difficulty) =>
createHash("sha256").update(`qwen3-8b-awq:${d}:${prompt}`).digest("hex");
export async function assessRisk(prompt: string, difficulty: Difficulty = "think"): Promise<RiskFinding> {
const key = keyOf(prompt, difficulty);
const hit = cache.get(key);
if (hit) return hit;
const m = MODE[difficulty];
const resp = await client.chat.completions.create({
model: "Qwen/Qwen3-8B-AWQ",
messages: [
{ role: "system", content: 'JSONのみで返答: {"hasRisk":bool,"severity":"low|medium|high","reason":string}' },
{ role: "user", content: prompt },
],
temperature: m.temperature,
top_p: m.top_p,
response_format: { type: "json_object" },
presence_penalty: 1.5, // 量子化モデルの繰り返し対策(公式推奨レンジ 0〜2・OpenAI互換)
// vLLM拡張はトップレベルで送る。Node SDKは未知キーも本文へ転送し、spreadなので型エラーも出ない。
// ※ Python SDK の extra_body は TS SDK には存在しないので使わない。
...{ top_k: m.top_k, chat_template_kwargs: { enable_thinking: m.enable_thinking } },
});
// content だけを検証対象に。reasoning_content(思考過程)は監査ログへ(PIIは載せない)
const raw = resp.choices[0]?.message.content ?? "{}";
const finding = RiskFinding.parse(JSON.parse(raw)); // 不正な形なら throw → 上位で握る
cache.set(key, finding);
return finding;
}
このクライアントは、“思考するLLM”の出力を決して鵜呑みにしない点が肝です。reasoning_content は人間の監査と評価のために残し、ユーザーに返すのは Zod を通った型だけ。さらに enable_thinking をタスク難度で出し入れすることで、簡単8割を非思考で安く・難所2割だけ思考で正確に——という原価設計が、1つのモデルで成立します。
本番の作り込み(重複は避け、要点だけ)
可観測性・オートスケール・グレースフルドレイン・回復性・ネットワーク隔離は、モデルが Qwen でも Llama でも同じ作法です。詳細は重複させず、vLLM 本番セルフホスト運用記に委ねます。Qwen3-8B-AWQ で特に効くポイントだけ:
- 可観測性:vLLM の
/metrics(Prometheus)でnum_requests_waiting/gpu_cache_usage_perc/ TTFT を監視。思考モードは出力が長くなりがちなので、reasoningのトークン消費を用途別に可視化すると原価の当たりがつきます(OpenTelemetry の相関設計)。 - 回復性:自前ノードの障害を全体障害にしない。タイムアウト+リトライ+フォールバック。8B が落ちたら別ノード/上位モデルへ逃がす(リトライ・サーキットブレーカー)。
- セキュリティ:vLLM の OpenAI 互換サーバはそれ自体に強い認証がない。インターネットに直接晒さない。private VPC に置き、前段の API Gateway で認証・レート制限・監査。プロンプト本文(PII)はログに残さない——そもそも「外に出さない」がセルフホストの動機です。
ハマりどころ & 公式ベストプラクティス
公式ドキュメントから、外すと品質が崩れるものを抜き出します。
- 🔴 思考モードで greedy decoding 禁止。temp=0 にしない。繰り返し・無限ループの原因。
- 🟠 量子化モデルの繰り返し対策に
presence_penalty ≈ 1.5(レンジ 0〜2)。ただし上げすぎると稀に言語混在やわずかな性能低下があるため、出たら効かせる程度に。 - 🟠 マルチターンで履歴に思考内容(
<think>)を残さない。次ターンの入力には最終回答だけを積む。思考を積み続けると文脈が汚れ、品質・原価とも悪化。 - 🟠 出力長を絞りすぎない。思考は答えの前に長い。通常 32,768 / 複雑問題 38,912 トークンを目安に
max_tokensを確保。 - 🟢 数学は出力フォーマットを指定:「Please reason step by step, and put your final answer within \boxed{}.」で最終解が機械抽出しやすくなる。
- 🟢 YaRN は必要な時だけ。常時有効化は短文の品質を落とす。
- 🟢 AWQ は
transformersだと awq カーネル依存。本番スループットは vLLM(連続バッチ)に寄せる。
よくある質問(FAQ)
Q. Qwen3-8B-AWQ と FP8 版、どちらを使う? A. 24GB級の汎用GPU(L4/A10/RTX 4090)で“載せて安く回す”なら AWQ 4bit。H100 など FP8 対応GPUで速度最優先なら FP8。重みは AWQ≈6GB / FP8≈8GB / FP16≈16GB が目安です(要実測)。
Q. “思考”は常にオンにすべき? A. いいえ。分類・抽出・定型対話は非思考(速い・安い)で十分。数学・コード・複雑な判断だけ思考に。タスク難度でルーティングするのが原価設計の本丸です(本文のクライアント例)。
Q. greedy(temp=0)で再現性を出したいのですが?
A. 思考モードでは公式が禁止しています。決定的な出力が要るなら、reasoning ではなく最終回答のスキーマ(Zod)と冪等キャッシュで再現性を担保してください。サンプリング自体を殺すのは逆効果です。
Q. 何 GPU 要りますか? A. まず 1 枚。重み≈6GB なので 24GB 1枚で動きます。同時実行とスループットは KVキャッシュ次第なので、負荷試験で飽和点を測り、そこから台数を逆算します。
Q. Ollama でも動く? A. ローカル開発の単機お試しには手軽です。ただし本番の高スループット(連続バッチ)は vLLM。開発は手軽な方、本番は vLLM、と使い分けます。
Q. ライセンスは商用利用できる? A. Apache-2.0 です。重みを所有し、改造し、自社環境で動かせます。Llama の「700M MAU 制限/Built with Llama 表記」のような制約は無く、ライセンス面はよりシンプルです(クローズドAPIとの違いはピラー記事)。
まとめ
Qwen3-8B-AWQ は、「思考できる小型モデルを、データを外に出さず、1枚のGPUで安く回す」という、これまで両立しにくかった要件を素直に満たします。
- AWQ 4bit で重み ≈6GB → 24GB GPU 1枚に載る(Apache-2.0・商用可)。
- ハイブリッド思考を難度でルーティング——簡単は非思考で安く、難所は思考で正確に。
- vLLM で OpenAI 互換サーブ(
--reasoning-parser qwen3)、reasoning_contentで思考と回答を分離。 - 公式サンプリングを厳守(思考: 0.6/0.95/20、greedy禁止、presence_penalty≈1.5、履歴に思考を残さない)。
- 出力は境界で型検証(Zod)+冪等キャッシュ。本番の作法はvLLM 運用記に集約。
オンプレ/VPC内で「思考するLLM」を、可観測・回復性・型安全まで含めて構築します。GPU 本番運用の実績をご覧のうえ、モデル選定・原価設計・セルフホスト移行までご相談ください。一人 × 生成AIで、速く・安く・安全に。
出典・公式リソース
- Qwen/Qwen3-8B-AWQ — Hugging Face モデルカード — スペック・モード切替・推奨サンプリング・ベストプラクティス
- Qwen 公式ドキュメント(vLLM デプロイ) — reasoning-parser・tool-call・YaRN フラグ
- Qwen 公式ドキュメント(AWQ 量子化) — AWQ の仕組みと使い方
- vLLM 公式ドキュメント — サーバ・メトリクス・量子化
- QwenLM/Qwen3(GitHub) — リファレンス実装
- Qwen3 Technical Report(arXiv:2505.09388)
※ スペック・フラグ・サンプリング推奨は更新されます。VRAM とスループットは環境依存・要ベンチです。実装前に一次情報と自社ベンチで必ず確認してください。