Fragments of verbose memory

冗長な記憶の断片 - Web技術のメモをほぼ毎日更新

Jun 29, 2026 - 日記

Ornith-1.0-35B-FP8をGX10で動かす: vLLMでagentic coding向け35B MoEをローカルAPI化

Ornith-1.0-35B-FP8をGX10で動かす: vLLMでagentic coding向け35B MoEをローカルAPI化

以前、ASUS Ascent GX10(NVIDIA GB10 / DGX Spark相当)でQwen3.6-27BをvLLMから動かす記事 を書きました。

今回はその続きとして、Ornith-1.0-35B-FP8 をGX10上のvLLM で起動し、OpenAI互換API として使えるところまで確認しました。単に「35Bモデルが載った」という話ではなく、agentic coding向けのreasoning modelとして、262k context、tool calling、opencode providerで使う前提の記録です。

結論から言うと、deepreinforce-ai/Ornith-1.0-35B-FP8 はGX10上で262k context構成まで起動できました。最終的には--gpu-memory-utilization 0.80--kv-cache-dtype fp8--max-num-batched-tokens 4096の組み合わせで、CUDA graph capture込みで動いています。

Ornith-1.0-35B-FP8とは

Ornith-1.0-35B-FP8は、Deep Reinforce AI が公開しているagentic coding向けのreasoning modelです。

モデルIDは以下です。

1
deepreinforce-ai/Ornith-1.0-35B-FP8

主な特徴は以下です。

  • ベースはQwen 3.5 MoE
  • 35B MoEモデル
  • FP8量子化済み
  • ライセンスはMIT
  • agentic codingに特化
  • reasoning<think>ブロックを使うreasoning model
  • tool calling対応を前提に使える

特に興味があったのは、Terminal-BenchSWE-bench Verified のような、エージェント的なコーディング作業に近いベンチで強い点です。

公式ページに掲載されているベンチマーク値は以下です。ここではあくまで公式値として扱います。

Benchmark Ornith Qwen3.5-35B Qwen3.6-35B Gemma4-31B
Terminal-Bench 2.1 (Terminus-2) 64.2 41.4 52.5 42.1
SWE-bench Verified 75.6 70.0 73.4 52.0
NL2Repo 34.6 20.5 29.4 15.5

本格的な品質比較はまだしていません。今回は「起動できるか」「OpenAI互換APIで使えるか」「tool callingとreasoningが壊れていないか」を確認し、最後に軽い速度測定だけ載せています。

今回の構成

今回はGX10上のvLLMをOpenAI互換APIサーバとして立て、手元のクライアントやopencodeから叩ける構成にしました。

APIエンドポイントは以下です。

1
http://gx10-3cd9:8000/v1

構成はシンプルです。

flowchart LR
    client["curl / opencode / OpenAI互換クライアント"]
    api["vLLM OpenAI API
gx10-3cd9:8000/v1"] model["Ornith-1.0-35B-FP8
deepreinforce-ai/Ornith-1.0-35B-FP8"] gpu["ASUS Ascent GX10
NVIDIA GB10"] client --> api api --> model model --> gpu

前回のQwen3.6-27Bの記事では、主にDGX Spark / ASUS Ascent GX10上でvLLMを動かす基本構成を扱いました。今回はその上に、reasoning parser、tool call parser、262k context向けのメモリ設定、opencode provider設定を重ねています。

関連する過去記事はこちらです。

vLLMの起動コマンド(262k context)

以下は、GX10上でOrnithを262k contextでvLLM serveするためのコマンドです。Hugging Face のトークンが必要な場合に備えて、HF_TOKENを環境変数として渡しています。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
cd ~/services/spark-vllm-docker
sudo HOME=/home/tumf ./launch-cluster.sh \
  --solo --name vllm_ornith -p 8000:8000 \
  -e HF_TOKEN=<token> \
  exec vllm serve deepreinforce-ai/Ornith-1.0-35B-FP8 \
    --host 0.0.0.0 --port 8000 \
    --dtype bfloat16 \
    --max-model-len 262144 \
    --gpu-memory-utilization 0.80 \
    --kv-cache-dtype fp8 \
    --served-model-name ornith-1.0-35b-fp8 \
    --enable-prefix-caching \
    --enable-auto-tool-choice \
    --tool-call-parser qwen3_xml \
    --reasoning-parser qwen3 \
    --trust-remote-code \
    --max-num-batched-tokens 4096

重要なオプションだけ整理します。

--max-model-len 262144

今回の推奨構成では262k contextで起動しました。

agentic coding用途では、コードベース情報、tool定義、履歴、reasoning、最終回答が同じcontextに乗ります。32kでも試せますが、opencodeのようなクライアントで使うなら長いcontextを確保できるほうが安心です。

--kv-cache-dtype fp8

262k contextをGB10のメモリに収めるために使っています。

35B MoE FP8のモデル本体だけでなく、長いcontextではKV cacheが効きます。262kを狙う場合、KV cache側もFP8にしないとメモリが厳しくなります。

--gpu-memory-utilization 0.80

262k contextでは、--gpu-memory-utilizationを上げすぎるとCUDA graph capture中に落ちました。

0.85では以下のエラーが出ました。

1
cudaErrorIllegalInstruction

最初は--enforce-eagerで回避していましたが、最終的には0.80まで下げることでCUDA graph capture込みで起動できました。今回の環境では、--enforce-eagerは不要です。

--max-num-batched-tokens 4096

これも262k起動時に必要でした。

指定しない場合、Mamba cache align modeの制約で以下のようなエラーになりました。

1
block_size (2096) must be <= max_num_batched_tokens (2048)

--max-num-batched-tokens 4096に上げることで回避しています。

--enable-auto-tool-choice--tool-call-parser qwen3_xml

tool callingをOpenAI形式で返すための設定です。

OrnithはQwen系のtool calling形式を使う前提なので、vLLM側ではqwen3_xml parserを指定しました。確認時にはfinish_reason: "tool_calls"とOpenAI形式のtool_callsが返りました。

--reasoning-parser qwen3

reasoning modelとして、思考部分を分離するための設定です。

確認時には、通常のcontentとは別にreasoningフィールドへ思考が分離されました。<think>を含むモデルをOpenAI互換APIから扱う場合、この分離が効いているかはかなり重要です。

32k構成のフォールバック

262k構成が不安定な場合は、32k contextまで落とすと起動しやすくなります。まずAPI疎通やtool callingだけ確認したい場合のフォールバックです。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
cd ~/services/spark-vllm-docker
sudo HOME=/home/tumf ./launch-cluster.sh \
  --solo --name vllm_ornith -p 8000:8000 \
  -e HF_TOKEN=<token> \
  exec vllm serve deepreinforce-ai/Ornith-1.0-35B-FP8 \
    --host 0.0.0.0 --port 8000 \
    --dtype bfloat16 \
    --max-model-len 32768 \
    --gpu-memory-utilization 0.55 \
    --served-model-name ornith-1.0-35b-fp8 \
    --enable-prefix-caching \
    --enable-auto-tool-choice \
    --tool-call-parser qwen3_xml \
    --reasoning-parser qwen3 \
    --trust-remote-code

以前の下書きではこちらを主構成としていましたが、更新後は262k構成を推奨に変えています。32k構成は、切り分け用として残すのがよさそうです。

起動時間

手元の環境では、APIが使えるようになるまで約8〜10分かかりました。

内訳としては、主に以下です。

  • safetensorsの読み込み
  • KV cache初期化
  • CUDA graph capture
  • torch.compile周辺の初期化

一度起動してしまえばAPIとしては普通に使えますが、試行錯誤中はこの起動時間が地味に効きます。--gpu-memory-utilization--max-model-lenを変えながら調整すると、1回ごとに数分待つことになります。

疎通確認

まずは/v1/modelsで、served model nameが見えていることを確認します。APIサーバが起動済みで、gx10-3cd9というホスト名で到達できる前提です。

1
curl http://gx10-3cd9:8000/v1/models

期待するのは、ornith-1.0-35b-fp8がモデル一覧に出ることです。確認時には、max_model_lenとして262144の構成で起動できていました。

通常のchat completionsは以下のように確認しました。reasoning modelなので、max_tokensは短くしすぎない方がよいです。

1
2
3
4
5
6
7
8
curl http://gx10-3cd9:8000/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{
    "model": "ornith-1.0-35b-fp8",
    "messages": [{"role":"user","content":"Hello"}],
    "max_tokens": 2048,
    "temperature": 0.6
  }'

日本語応答も問題ありませんでした。実測した自己紹介プロンプトでは、completion_tokens=448reasoning_len=1304でした。

注意点として、max_tokensは最低でも2048程度を見ておくのがおすすめです。reasoning modelは、短い出力上限だと思考部分だけでトークンを使い切ることがあります。特にエージェント用途では、回答本文やtool callまで到達しないと意味がありません。

もう一つ細かい点として、自己紹介ではモデルが「Qwen」と名乗りました。Ornithとしてpost-trainされていても、自己認識ではベースモデル名が出ることがあります。モデル名を答えさせるタイプの確認だけで同一性を判断しないほうがよさそうです。

簡単な速度測定

262k context、--gpu-memory-utilization 0.80--kv-cache-dtype fp8--enforce-eagerなしの構成で、簡単に速度も見ました。

プロンプト completion tokens elapsed tok/s 備考
Reply with exactly: pong 112 2.96s 37.9 tok/s finish=stop、本文pong到達
Python素数判定コード生成 2048 54.24s 37.8 tok/s reasoningだけで2048を使い切り、本文未到達

速度自体は、今回の軽い測定では約38 tok/sでした。ただし、Ornithはreasoningが長いため、コード生成やagent用途ではoutputを4096以上にする方が安全です。短い上限だと、最終回答に到達する前にreasoningだけで使い切ります。

reasoningとtool callingで見るべき点

Ornithをagentic coding用途で使うなら、単にテキストが返るだけでは足りません。

見るべき点は大きく2つです。

  1. reasoningが本文と分離されること
  2. tool callingがOpenAI互換形式で返ること

reasoningについては、レスポンス内のreasoningフィールドに思考部分が分離されることを確認しました。これにより、クライアント側ではユーザーに見せる本文と、内部的に扱う推論内容を分けやすくなります。

tool callingについては、関数呼び出しが必要なプロンプトを投げた際に、以下のような状態を確認しました。

  • finish_reason"tool_calls" になる
  • OpenAI形式の tool_calls が返る
  • tool nameとargumentsがクライアント側で解釈できる

この2点が通ると、opencodeやOpenAI互換クライアントから「ローカルのagentic coding model」として扱いやすくなります。

opencode provider設定例

opencode から使う場合は、OpenAI互換providerとして設定できます。ここではAPIキー認証なしのローカルvLLMを想定し、apiKeyにはEMPTYを入れています。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
"ornith-gx10": {
  "npm": "@ai-sdk/openai-compatible",
  "name": "Ornith 1.0 35B FP8 vLLM",
  "options": {
    "baseURL": "http://gx10-3cd9:8000/v1",
    "apiKey": "EMPTY"
  },
  "models": {
    "ornith-1.0-35b-fp8": {
      "name": "Ornith 1.0 35B FP8",
      "tool_call": true,
      "reasoning": true,
      "limit": {
        "context": 262144,
        "output": 4096
      }
    }
  }
}

ポイントは、tool_callreasoningを有効にしておくことです。

今回のvLLM起動側でも、--enable-auto-tool-choice--tool-call-parser qwen3_xml--reasoning-parser qwen3を指定しています。サーバ側とクライアント側の両方で、tool callingとreasoningを意識した設定にしておく必要があります。

試行中に失敗した構成

今回の一番の学びは、35B MoE FP8でも「モデルがメモリに載るか」だけでは足りない、という点です。長いcontext、KV cache、CUDA graph capture、既存プロセスの残存まで見る必要がありました。

試行中に失敗した主な構成は以下です。

試行 エラー 原因
--gpu-memory-utilization 0.85(前プロセス残存時) Free memory (72.57 GiB) < desired (103.38 GiB) 前回のvLLM EngineCoreがGPUメモリを占有したまま
--max-model-len 262144 / --gpu-memory-utilization 0.85 / --enforce-eagerなし cudaErrorIllegalInstruction CUDA graph capture中にクラッシュ。--gpu-memory-utilization 0.80で解消
--max-model-len 262144--max-num-batched-tokensなし) block_size (2096) must be <= max_num_batched_tokens (2048) Mamba cache align modeの制約

特に最初のFree memory < desiredは、純粋にOrnithの設定だけが原因ではありませんでした。前回のvLLMプロセスがGPUメモリを握ったままだったため、先に全プロセスを確実に止める必要がありました。

GB10で35B MoE FP8を載せる際の注意点

最終的に、262k contextの推奨構成は以下です。

1
2
3
4
--max-model-len 262144
--gpu-memory-utilization 0.80
--kv-cache-dtype fp8
--max-num-batched-tokens 4096

この設定で、起動と疎通確認はできました。

一方で、以下はまだ試行余地があります。

  • prefix cachingの効果
  • opencode実タスクでの安定性
  • Qwen3.6-27BやGemma4-31Bとの実作業比較
  • 262k contextを使い切るような長いagentic codingタスクでの挙動

特にagentic coding用途では、単発のベンチ値だけでなく、長い編集・複数tool call・失敗からの復帰が重要です。このあたりは別途、実タスクで試したいところです。

まとめ

Ornith-1.0-35B-FP8をGX10上のvLLMで起動し、OpenAI互換APIとして使えるところまで確認しました。

確認できたことは以下です。

  • deepreinforce-ai/Ornith-1.0-35B-FP8をvLLMで起動できた
  • APIはhttp://gx10-3cd9:8000/v1で疎通
  • served-model-nameornith-1.0-35b-fp8
  • max_model_len262144で起動できた
  • 起動には約8〜10分かかった
  • 日本語チャット応答はOK
  • reasoningフィールドへの分離を確認
  • OpenAI形式のtool callingを確認
  • opencode providerとして設定可能
  • 簡易測定では約38 tok/sだった
  • 262k contextでは--gpu-memory-utilization 0.80--kv-cache-dtype fp8--max-num-batched-tokens 4096が重要だった

個人的には、Ornithは「ローカルでagentic coding用のreasoning modelを試す」対象としてかなり面白いです。特に、OpenAI互換API、reasoning分離、tool callingが通るところまで確認できたのは大きいです。

一方で、35B MoE FP8をGB10で動かす場合、起動オプションの詰めは必要です。まずは今回の262k構成を基本にし、GPUメモリが足りない場合や起動が不安定な場合は32kフォールバックへ落とす、という運用がよさそうです。

参考リンク