はじめに
IBM Spyre は、IBM Power サーバーに搭載可能な AI アクセラレーターです。
PCIe 接続の AIU(AI Unit)デバイスとして認識され、vLLM のバックエンドとして大規模言語モデルの推論に利用できます。
これまでに書いたSpyre関連の記事はこちら
今回は Spyre 上で Granite 4.1 モデルを使った推論サーバーを立ち上げ、発注書のテキストからエンティティ(構造化データ)を抽出するシナリオを試してみます。
Red Hat AI Inference(RHAII) は OpenAI 互換 API として起動するため、curl や既存の OpenAI クライアントをそのまま使えるのが便利なところです。
RHAIIは、3.5のEarly access版を使用しています。
環境
- IBM Spyre アクセラレーター搭載サーバー
- モデル:
granite-4.1-8b-fp8 - コンテナランタイム: Podman
- コンテナイメージ:
registry.redhat.io/rhaii-early-access/vllm-spyre-rhel9:3.5.0-ea.1-1781067361
推論サーバーの起動
環境変数の設定
利用する AIU デバイスやモデルパス、バッチサイズなどを環境変数で定義します。
# 利用する AIU デバイスの PCIe アドレス(実際の環境のアドレスに変更)
export AIU_IDS="xxxx:xx:xx.x"
# コンテナにマウントするモデルのパス
export VLLM_MODEL_PATH=/models/granite-4.1-8b-fp8
# 使用する AIU の数(テンソル並列度)
export AIU_WORLD_SIZE=1
# モデルが処理する最大トークン長
export MAX_MODEL_LEN=3072
# 同時に処理できるリクエストの最大数
export MAX_BATCH_SIZE=16
Podman でコンテナを起動
以下の podman run コマンドで vLLM サービスを起動します。
podman run -it \
--name entity-ext \
--replace \
--device=/dev/vfio \ # AIU デバイスへのアクセスを許可
-v /home/models:/models:Z \ # モデルディレクトリをマウント
-e AIU_PCIE_IDS="${AIU_IDS}" \ # 使用する AIU の PCIe アドレスを渡す
-e VLLM_SPYRE_USE_CB=1 \ # Spyre の Continuous Batching を有効化
--pids-limit 0 \
--userns=keep-id \
--group-add=keep-groups \
--security-opt label=disable \
--memory 100G \
--shm-size 64G \
-p 0.0.0.0:8000:8000 \
registry.redhat.io/rhaii-early-access/vllm-spyre-rhel9:3.5.0-ea.1-1781067361 \
--model "${VLLM_MODEL_PATH}" \
-tp "${AIU_WORLD_SIZE}" \
--max-model-len "${MAX_MODEL_LEN}" \
--max-num-seqs ${MAX_BATCH_SIZE} \
--enable-prefix-caching
主なオプションの補足は以下のとおりです。
| オプション | 説明 |
|---|---|
AIU_PCIE_IDS |
使用する Spyre(AIU)デバイスの PCIe アドレスを指定 |
VLLM_SPYRE_USE_CB=1 |
Spyre 固有の Continuous Batching モードを有効にする |
-tp |
テンソル並列度(使用する AIU 数)を指定 |
--enable-prefix-caching |
プレフィックスキャッシュを有効にし、同じシステムプロンプトを再利用する際の効率を上げる |
起動後にログへ以下のような出力が流れれば、サービスが正常に起動しています。
INFO: Application startup complete.
INFO: Uvicorn running on http://0.0.0.0:8000
念のため curl で疎通確認しておくと安心です。
curl -s http://xxx.xxx.xxx.xxx:8000/health
{} や 200 OK が返れば問題ありません。
エンティティ抽出を試してみる
推論サーバーが起動したら、発注書テキストからのエンティティ抽出を試してみます。
プロンプト設計
システムプロンプトで JSON 形式での応答を指示します。
あなたは発注書のテキストから情報を抽出する専門家です。
与えられたテキストから以下のJSONフォーマットで情報を抽出してください。
見つからない情報は空文字または0で補完してください。
必ずJSON形式のみで返答してください。
ユーザーメッセージには、出力スキーマの例と発注書のテキストを合わせて渡します。
以下の発注書テキストから情報を抽出し、次のJSONスキーマに従って返答してください。
スキーマ例:
{
"customer_name": "顧客名",
"customer_email": "メールアドレス",
"customer_address": "住所",
"items": [{ "product_name": "商品名", "quantity": 1, "unit_price": 1000 }],
"total_amount": 1000,
"notes": "備考"
}
発注書テキスト:
発注書
発注日: 2025年7月1日
発注者: 株式会社テック商事
メール: order@example.co.jp
住所: 東京都千代田区丸の内1-2-3
品目:
- ノートPC 5台 120,000円
- マウス 10個 3,000円
合計金額: 630,000円
備考: 納期2週間以内
curl での実行
エンドポイントの URL とモデル名を変数で定義しておきます。
INFERENCE_API_BASE_URL="http://xxx.xxx.xxx.xxx:8000"
INFERENCE_API_KEY=""
INFERENCE_MODEL_NAME="/models/granite-4.1-8b-fp8"
response_format に json_object を指定することで、モデルが JSON のみを返すように制御できます。
なお、この機能はモデルによって対応が異なります。Granite 4.1 では問題なく動作しましたが、別モデルを使う場合は事前に確認してください。
curl -s -X POST "${INFERENCE_API_BASE_URL}/v1/chat/completions" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer ${INFERENCE_API_KEY}" \
-d "{
\"model\": \"${INFERENCE_MODEL_NAME}\",
\"response_format\": { \"type\": \"json_object\" },
\"messages\": [
{
\"role\": \"system\",
\"content\": \"あなたは発注書のテキストから情報を抽出する専門家です。与えられたテキストから以下のJSONフォーマットで情報を抽出してください。見つからない情報は空文字または0で補完してください。必ずJSON形式のみで返答してください。\"
},
{
\"role\": \"user\",
\"content\": \"以下の発注書テキストから情報を抽出し、次のJSONスキーマに従って返答してください。\nスキーマ例:\n{\\\"customer_name\\\":\\\"顧客名\\\",\\\"customer_email\\\":\\\"メールアドレス\\\",\\\"customer_address\\\":\\\"住所\\\",\\\"items\\\":[{\\\"product_name\\\":\\\"商品名\\\",\\\"quantity\\\":1,\\\"unit_price\\\":1000}],\\\"total_amount\\\":1000,\\\"notes\\\":\\\"備考\\\"}\n\n発注書テキスト:\n発注書\n発注日: 2025年7月1日\n発注者: 株式会社テック商事\nメール: order@example.co.jp\n住所: 東京都千代田区丸の内1-2-3\n品目:\n- ノートPC 5台 120,000円\n- マウス 10個 3,000円\n合計金額: 630,000円\n備考: 納期2週間以内\"
}
]
}" | jq '.choices[0].message.content | fromjson'
実行結果
以下のような JSON が返ってきます。
品目ごとに商品名・数量・単価が正しく分解され、合計金額・備考も含めて構造化されていることが確認できます。
{
"customer_name": "株式会社テック商事",
"customer_email": "order@example.co.jp",
"customer_address": "東京都千代田区丸の内1-2-3",
"items": [
{
"product_name": "ノートPC",
"quantity": 5,
"unit_price": 120000
},
{
"product_name": "マウス",
"quantity": 10,
"unit_price": 3000
}
],
"total_amount": 630000,
"notes": "納期2週間以内"
}
自然言語で書かれた発注書から、構造化された JSON を正確に抽出できています。
まとめ
IBM Spyre 上の vLLM を使って、発注書テキストからのエンティティ抽出を試してみました。
OpenAI 互換 API を利用しているため、response_format: json_object を指定するだけで JSON 出力を強制でき、後続のパース処理をシンプルに保てます。
プロンプトにスキーマ例を含めることで、フィールド構成が変わっても柔軟に対応できるのも利点です。
実際の業務利用に向けては、以下の点を考慮しておくと良いと思います。
Spyre の起動さえ済んでしまえば、あとは通常の OpenAI 互換クライアントと同じ感覚で使えるので、既存の処理パイプラインへの組み込みもしやすいと思います。