Ornith-1 把代理寫碼變成伺服器
我把 Ornith-1 的 README 拆成一份可直接照抄的代理寫碼伺服器模板,重點是輸出格式、工具呼叫、推理欄位和部署參數。

我把 Ornith-1 的 README 拆成一份可直接照抄的代理寫碼伺服器模板,重點是輸出格式、工具呼叫、推理欄位和部署參數。
我用一堆代理寫碼模型一陣子了,最煩的從來不是分數表,而是接起來那一段鬼打牆。模型看起來很會講,真的一上線就開始亂:工具呼叫格式對不上、推理內容被塞進同一包文字、client 端讀不到該讀的欄位,最後你只好在日誌裡面撈半天,像在找一根掉進海裡的針。
我就是被這種破事搞到有點火大,才去看 Ornith-1 的 GitHub 倉庫。它吸引我的不是那些漂亮表格,而是 README 寫得很直白:怎麼 serve、要用什麼 parser、sampling 怎麼設、工具怎麼接。這不是單純模型卡,這比較像一份部署作戰手冊。倉庫來自 deepreinforce-ai,模型家族名稱是 Ornith-1.0。
先別把它當聊天玩具
訂閱 AI 趨勢週報
每週精選模型發布、工具應用與深度分析,直送信箱。不定期,不騷擾。
不會寄垃圾信,隨時可取消。
「Ornith-1.0 是一個推理模型;預設情況下,assistant 回合會先出現一段 reasoning block,再給最終答案。」
翻譯一下就是:它不是設計成「你丟一句,它吐一段純文字」那種傻瓜流程。它期待的是結構化對話,assistant 的輸出本來就分成推理和答案兩條線。README 還直接說,伺服端應該把推理內容拆到 reasoning_content,工具呼叫則要用 OpenAI 風格的 tool_calls。

我以前踩過同一種坑:client 只看最終答案,就把推理痕跡吃掉;client 只看推理欄位,又拿不到乾淨答案。結果你以為模型不穩,其實是你整個協定讀錯。Ornith-1 這個做法比較老實,它不是假裝一切都是同一包字串,而是把兩種輸出明講出來。對代理工作流來說,這很重要,因為真正壞掉的通常不是模型能力,而是接縫。
實操上,我會把它當成雙通道輸出來設計:一條是推理流,一條是給使用者看的答案。你在做產品,就分開存;你在做評測,就明確指定哪個欄位算分;你在做工具代理,就先確認 parser 真的吃得懂它的格式,再拿去給使用者。不要等到上線後才發現,原來你只是把協定誤差包裝成 AI 問題。
還有一個很現實的點:prompt 不要硬扭模型原生格式。README 說 assistant 會先開一段 reasoning block,那你的模板、chat wrapper、log schema 就都應該照這個假設去設計。你如果硬把它塞回「純文字聊天」的老框架,最後只會得到一堆看似正常、其實對不上號的輸出。
sampling 參數不是裝飾品
「建議 sampling 參數:temperature=0.6、top_p=0.95、top_k=20;若要重現論文/表格結果,請用 temperature=1.0。」
也就是說,作者其實很清楚地告訴你:這裡有兩種模式,一種是日常使用,一種是重現 benchmark。很多人看到這種說法會直接跳過,然後回頭抱怨模型在自己機器上怎麼跟展示不一樣。拜託,差很多好嗎。
我看過太多 coding model 被誤會的案例。有人直接照抄 benchmark 設定,結果輸出變得很硬、很怪,然後就說模型不行。但 benchmark 的參數常常是為了可比性,不是為了你日常真的好用。Ornith-1 的 README 這點寫得很清楚:預設建議是 temperature=0.6,如果你是要對表格,才切到 temperature=1.0。這種提醒我其實很買單,因為它把「可用」和「可比」分開了。
實操寫法很簡單:先決定你在優化什麼。如果是互動式寫碼助手,就先用建議值;如果你是要驗證 README 裡的數字,就切到 benchmark 模式。結果一旦漂移,先別急著罵模型,先查 temperature、top_p、top_k、parser、context window。老實講,很多時候是我自己設錯,不是 checkpoint 有問題。
- 日常產品行為:先用建議的 sampling 值。
- 重現分數:只在你真的要對 README 的數字時切到 benchmark 設定。
- 每次評測都把 decode config 記下來,不然之後根本比不了。
真正值錢的是伺服器配方
vllm serve $MODEL --served-model-name Ornith-1.0 --tensor-parallel-size 8 --host 0.0.0.0 --port 8000 --max-model-len 262144 --gpu-memory-utilization 0.90 --enable-prefix-caching --enable-auto-tool-choice --tool-call-parser qwen3_xml --reasoning-parser qwen3 --trust-remote-code
翻譯一下就是:這份 repo 不是只丟你一個 checkpoint,它直接把伺服器合約也寫給你了。這不是「模型在這裡,剩下你自己想辦法」。它是「你要怎麼把它包成 OpenAI 相容服務、上下文多長、parser 用哪個、工具呼叫怎麼解析,我都先寫給你」。對代理模型來說,這件事超重要,因為 runtime 和 model 只要有一邊格式不對,整個系統就會像在講不同語言。

我很喜歡它同時給了 vLLM 和 SGLang 的範例。這代表作者不是只想秀一個 demo,而是真的預期大家會在不同 stack 上落地。它也把那些很無聊、但最容易出事的參數講清楚:tensor parallelism、host/port、記憶體利用率、prefix caching、parser 選擇。無聊是好事,無聊代表你不太會半夜被 prod 叫醒。
實操上,我會先選一條路鎖死。你本來就用 vLLM,就先從 vLLM 開始;你的 infra 比較偏 SGLang,就直接走 SGLang。不要在還沒搞懂 parser 和 reasoning hook 之前,亂混兩邊的設定。還有,對外提供內部 API 時,model 名稱最好保持穩定,因為下游工具很愛把它硬寫死,等你改名再來哭,通常都來不及。
256K context window 也是很明顯的訊號。這表示它不是只做短 prompt 的玩具,而是想處理長而髒的寫碼流程。這會改變我對記憶、檢索、任務切分的做法。上下文這麼大,我就可以塞更多 repo 內容、更多 issue 討論、更多 agent 歷史進去,不用一開始就急著把外部記憶系統拉上來救火。
Dense 跟 MoE 不是只有大小差別
Ornith-1.0 提供 dense 9B,另外還有兩個 Mixture-of-Experts 模型(35B、397B)。所有 checkpoint 都暴露相同的 OpenAI 相容介面,並支援 256K(262,144 token)上下文;dense 9B 可跑在單張 80GB GPU 上,而 MoE checkpoint 則需要在多 GPU 節點上用 tensor parallelism 切分。
也就是說,這份 repo 想把部署變成一個很務實的選擇題,不是那種「反正越大越好」的幻想。9B 是給想自己真的跑起來的人;35B 和 397B 則是給有多卡資源、而且知道自己為什麼要花這筆錢的團隊。
我以前被很多模型頁面陰過,常常只寫「小模型」「大模型」,但根本沒講清楚硬體需求。這裡就直接講白:dense 9B 可以塞進單張 80GB GPU,較大的 MoE 版本要做 sharding。README 也提到 bf16、FP8、GGUF 這些 variant,這種細節其實就是決定一個模型是「週末玩玩」還是「真的能上線」的分水嶺。
實操寫法是:先用你現有的機器去對應 checkpoint,不要反過來。你只是想原型驗證,就選 9B;你有單機但 VRAM 緊,就先試 FP8;如果你打算走本機部署,像 llama.cpp 或 Ollama 這種路線,就直接看 GGUF。不要因為表格上最大那顆看起來很香,就硬買硬體。
- Dense 9B:最適合本機測試和小規模調整。
- 35B / 397B MoE:你有多 GPU 才考慮,而且要真的需要那個品質。
- FP8 / GGUF:當記憶體壓力比極致精度更重要時,這些版本比較實際。
Benchmark 表要看註腳,不然就是看熱鬧
* Terminal-Bench 2.1(Terminus-2):用 Harbor/Terminus-2 framework 評估,parser=json,temperature=1.0,top_p=1.0,128K context window。每次 run 4 小時 timeout、32 CPU cores、48GB RAM,平均 5 次。 * SWE-bench Verified / Pro / Multilingual:OpenHands harness,temp=1.0,top_p=0.95,256K context window。 * SWE Atlas QnA / RF / TW:mini-SWE-agent harness,temp=1.0,top_p=0.95,128K context window,平均 5 次。
翻譯一下就是:分數跟 harness 綁得很死。這不是「A 模型贏過 B 模型」這麼簡單,而是「A 在這組工具、這組 decode、這個 timeout、這個 context budget 底下表現比較好」。你如果把這些條件拿掉,根本不是同一場比賽。
我很欣賞 README 願意把評測機器講這麼細,因為不然 benchmark 很容易變成表演。模型也許真的強,但 harness 一換,結果就會變。某個 coding agent 在一個 setup 拿到不錯的分數,不代表你把 parser、temperature 或 timeout 改掉後還一樣。這不是作弊,這就是現實。
實操上,我看任何 agent benchmark 之前,會先抄四件事:harness、decode 設定、context 長度、平均方式。這四個如果沒寫清楚,那個數字參考價值就很有限。你如果自己要發評測,也請把這些註腳一起放,不然分數再漂亮,我也不太信。
這份 repo 還有一個很誠實的地方:它沒假裝一套評測可以吃遍所有任務。Terminal-Bench、SWE-bench、NL2Repo、ClawEval、SWE Atlas 都是不同處理方式。這才對,因為不同任務本來就會打到不同失敗模式。你如果硬把它們混成一個總分,最後通常只是對某個 benchmark 過擬合,然後自以為進步。
OpenAI 相容介面就是最好入口
from openai import OpenAI client = OpenAI(base_url="http://localhost:8000/v1", api_key="EMPTY") response = client.chat.completions.create( model="Ornith-1.0", messages=[{"role": "user", "content": "Write a one-line Python lambda that squares a number."}], temperature=0.6, top_p=0.95, max_tokens=1024, ) message = response.choices[0].message
也就是說,這份 repo 想讓 adoption 變得很無聊。只要你的 client 已經會講 OpenAI 風格的 chat completions,你就不用再多裝一套奇怪的 SDK 才能試模型。你只要把 base URL 指到本機服務,接下來就能繼續做事。
這件事比聽起來更重要。我真的受夠那些「技術上開源、操作上很煩」的模型釋出。API 形狀如果夠熟悉,整合成本就會小很多。如果 response 還能把 reasoning_content 跟 content 分開,那就更好,因為你的 app 可以自己決定要展示、儲存還是遮掉哪一段。
實操寫法:你的整合層最好吃 base URL 和 model name,不要硬綁某家 vendor SDK。這樣你之後要換成本機服務測試,也不用整個應用重寫。如果你在做內部代理平台,這就是該有的抽象層。模型應該是可替換的,整個 app 不是。
我也注意到 README 提到 streaming 和 tools,雖然截圖裡沒把完整範例貼完,但這已經夠說明它不是只想做一次性 completion。你如果真的要拿來做寫碼流程,這就是 demo 跟可用系統的差別。
可抄的模板
# Ornith-1 代理寫碼伺服器模板
## 1) 先選 checkpoint
- 本機單卡測試:deepreinforce-ai/Ornith-1.0-9B
- 多卡正式部署:deepreinforce-ai/Ornith-1.0-35B 或 deepreinforce-ai/Ornith-1.0-397B
- 記憶體比較緊:優先找 FP8 版本
- 本機量化推理:用 GGUF 版本搭配 llama.cpp 或 Ollama
## 2) 先把 sampling 分成兩種
- 日常互動:
- temperature=0.6
- top_p=0.95
- top_k=20
- 重現 benchmark:
- temperature=1.0
- top_p=1.0 或 README 指定值
## 3) 保留模型原生輸出結構
- 推理內容和最終答案分開存
- 預期 assistant 先出 reasoning block
- 工具呼叫要當成結構化事件,不要當純文字
- 在 app 裡把 `reasoning_content` 和 `content` 分開處理
## 4) 用 OpenAI 相容服務包起來
- 優先用 vLLM 或 SGLang
- 對外暴露 `/v1`
- served model name 固定成 `Ornith-1.0`
- tensor parallelism 要跟 GPU 數量對齊
- context 長度可支援時就設到 262144
## 5) 可直接貼的 vLLM 起手式
MODEL=deepreinforce-ai/Ornith-1.0-9B
vllm serve $MODEL \
--served-model-name Ornith-1.0 \
--host 0.0.0.0 --port 8000 \
--max-model-len 262144 \
--gpu-memory-utilization 0.90 \
--enable-prefix-caching \
--enable-auto-tool-choice \
--tool-call-parser qwen3_xml \
--reasoning-parser qwen3 \
--trust-remote-code
## 6) 可直接貼的 Python client
from openai import OpenAI
client = OpenAI(base_url="http://localhost:8000/v1", api_key="EMPTY")
response = client.chat.completions.create(
model="Ornith-1.0",
messages=[
{"role": "system", "content": "請精準回答,需要時就呼叫工具。"},
{"role": "user", "content": "寫一個簡短的 Python 函式,判斷質數。"},
],
temperature=0.6,
top_p=0.95,
max_tokens=1024,
)
message = response.choices[0].message
print("reasoning:", getattr(message, "reasoning_content", None))
print("answer:", message.content)
## 7) 可直接貼的評測註記
- 記錄完整 checkpoint 名稱
- 記錄 serving stack 版本
- 記錄 reasoning 和 tool call parser
- 記錄 temperature、top_p、top_k、max_tokens、context length
- 記錄 harness 與 timeout
- 沒有對齊這些欄位,就不要直接比分數如果是我明天要導入 Ornith-1,我會先留這份在旁邊。不是那張 benchmark 表,也不是那句很會講的宣傳詞,而是那些很無聊但最救命的部署筆記、parser 規則和 sampling 預設。這些才是決定它到底有沒有用的地方。
這份 repo 最有價值的地方,就是它沒有逼我自己從零猜作者的意思。它把模型怎麼跑、怎麼接、怎麼重現,講得夠直接。這種文件,我看得下去,也比較願意真的拿去上線。
來源致謝:原始內容來自 deepreinforce-ai/Ornith-1 GitHub 倉庫;我上面寫的拆解與模板是依據 README 重寫整理。另有參考 vLLM、SGLang、llama.cpp 與 Ollama 的官方文件。