Qwen3.6の冗長な思考出力を構造化する

目標

Qwen3の思考プロセスを「目標→状態→アルゴリズム→境界条件→検証→コード」のフォーマットで出力させる。

背景

Qwen3などの思考モデルは、通常<think>...</think>タグ内に自由形式の推論過程を出力する。この自由形式をGBNF(GGML BNF)構文で制約することで、以下のような構造化された出力を実現可能:

<think>
GOAL: Pythonで二分探索を実装
STATE: ソート済み配列、目的値のインデックス返却
ALGO: ツーポインタ法、中点比較
EDGE: 空配列・目的値不存在・重複要素
VERIFY: 境界テストケース実行
</think>

def binary_search(arr, target):
    # コード実装

利点

  • 思考過程の機械解析・検証可能
  • GOAL/STATE等のメタ情報抽出によるログ・監査
  • モデルの過剰思考・脱線防止

1. llama.cppの実装方法

GBNF定義ファイル

think.gbnfの作成:

root  ::= think code
think ::= "<think>\nGOAL: " line "STATE: " line "ALGO: " line "EDGE: " line "VERIFY: " line "</think>\n\n"
line  ::= [^\n]+ "\n"
code  ::= [\x09\x0A\x0D\x20-\x7E\u3000-\u303F\u4E00-\u9FFF\uFF00-\uFFEF]+

文字セット説明

  • \u4E00-\u9FFF:漢字領域
  • \u3000-\u303F:CJK句読点
  • \uFF00-\uFFEF:全角文字

実行方法

コマンドライン

llama-cli -m Qwen3.6-35B-A3B.gguf \
  -n 1024 \
  --grammar-file think.gbnf \
  -p "Pythonで二分探索を実装し日本語コメント付き"

API経由

curl -s 'http://localhost:8080/completion' \
  -H 'Content-Type: application/json' \
  -d '{
    "prompt": "Pythonで二分探索を実装",
    "n_predict": 1024,
    "grammar": "root ::= think code\nthink ::= \"<think>\\nGOAL: \" [^\\n]+ \"\\nSTATE: \" [^\\n]+ \"\\nALGO: \" [^\\n]+ \"\\nEDGE: \" [^\\n]+ \"\\nVERIFY: \" [^\\n]+ \"\\n</think>\\n\\n\"\ncode ::= [ -~\\u3000-\\u303f\\u4e00-\\u9fff\\uff00-\\uffef]+"
  }'

2. vLLMの実装方法

起動設定

vllm serve Qwen3.6-35B-A3B \
  --structured-outputs-config '{"enable_in_reasoning": true}'

APIリクエスト

curl -s 'http://localhost:8000/v1/chat/completions' \
  -X POST \
  -H 'Content-Type: application/json' \
  -d '{
    "model": "qwen",
    "messages": [
      {"role": "user", "content": "Pythonで二分探索を実装"}
    ],
    "max_tokens": 1024,
    "chat_template_kwargs": {"enable_thinking": true},
    "structured_outputs": {
      "grammar": "root ::= think code\nthink ::= \"<think>\\nGOAL: \" [^\\n]+ \"\\nSTATE: \" [^\\n]+ \"\\nALGO: \" [^\\n]+ \"\\nEDGE: \" [^\\n]+ \"\\nVERIFY: \" [^\\n]+ \"\\n</think>\\n\\n\"\ncode ::= [ -~\\u3000-\\u303f\\u4e00-\\u9fff\\uff00-\\uffef]+"
    }
  }'

3. 実装比較表

項目 llama.cpp vLLM
パラメータ名 grammar structured_outputs.grammar
思考プロセス制約 デフォルト全域対応 enable_in_reasoning: trueが必要
JSONエスケープ \n直接記述 JSON内で\\n必要

4. よくある問題と解決

  • grammar無効化:enable_in_reasoning: true設定漏れ
  • トークン不足:max_tokensを1024以上推奨
  • 正規表現のパフォーマンス:item{0,5}を使用して指数関数的展開を回避

5. 日本語ラベル拡張版

root  ::= think code
think ::= "<think>\n目的: " line "状態: " line "アルゴリズム: " line "境界: " line "検証: " line "</think>\n\n"

6. 実装フローまとめ

  1. .gbnfファイル作成
  2. llama.cppはそのまま利用可能
  3. vLLMは起動時に--structured-outputs-config指定
  4. max_tokensを十分確保

タグ: Qwen3.6 GBNF llama.cpp vLLM 構造化出力

5月31日 21:47 投稿