uzu を自アプリに組み込む前に読む、Rust 製ローカル推論エンジンの輪郭
A high-performance inference engine for AI models
ひと目でわかる
- これは何?
- trymirai/uzu は Rust で書かれた推論エンジンで、Rust・Python・Swift・TypeScript の4言語バインディングを同梱する。モデル識別子の書式、Engine と Session の分離、そしてモデル取得が engine.download に切り出されている設計を、README から読み取れる範囲で整理する。
- 誰に向いている?
- uzu が向くのは、Apple 端末上でモデルを同梱または初回取得させ、Rust あるいは Swift から推論を呼びたいアプリ開発者だ。逆に、サーバ側で複数モデルを切り替えながら同時実行数を捌く構成や、Metal 以外のアクセラレータを前提にした構成には、README からは根拠が見つからない。
- 商用利用できる?
- できます。MIT は寛容なライセンスで、著作権表示とライセンス表示を残せば、使用・改変・販売が可能です。
- 今もメンテナンスされている?
- されています。直近 1 日以内に新しいコミットがあります。
- 何の言語で書かれている?
- 主に Rust です(GitHub の言語統計による)。
回答はプロジェクトの GitHub データ(最終同期:2026年9月15日)と当サイトの分析に基づくもので、法的助言ではありません。
オープンソース詳細解説
uzu が埋めようとしているのは「推論コスト」ではなく「アプリ内推論の配線コスト」
README の冒頭は、uzu を high-performance inference engine と位置づけ、その利点として zero latency、full data privacy、no inference costs の3語を並べている。ここで注目したいのは性能の話ではなく、3語がすべて配布と実行場所の問題だという点である。クラウド API を呼ばなければ通信の往復は消え、入力は端末外に出ず、トークン課金も発生しない。つまり uzu が解こうとしているのは、モデルを速く走らせることそのものより、モデルをアプリのバイナリか端末ストレージに置いて呼び出すまでの配線である。
対象読者は README の構成から推測できる。Rust、Python、Swift、TypeScript の4つの折りたたみセクションが同じサンプルを並べ、Swift 側には Package.swift と iOS / macOS のバッジが付く。デスクトップとモバイルのネイティブアプリ、あるいはそのバックエンドを同じエンジンで揃えたい開発者が想定されている。逆に、Web ブラウザだけで完結させたい場合や、GPU クラスタを自前で抱える構成は、この README の射程外に見える。
Engine と Session を分け、モデル取得を download に切り出す API 設計
4言語のサンプルは同じ順序をたどる。EngineConfig を作り、Engine を生成し、engine.model(識別子) でモデルを引き当て、engine.download(model) で取得し、engine.chat(model, ChatConfig) でセッションを得て、session.reply(messages, ChatReplyConfig) で応答を受け取る。
設計上の判断が2つ読み取れる。ひとつは Engine と Session の分離で、Engine はモデルの解決とダウンロードを担い、Session が会話状態を持つ。Rust 版では engine.chat が model を引数に取るため、同一 Engine から複数モデルのセッションを並べられる形になっている。もうひとつは、ダウンロードが推論呼び出しと別のメソッドに切られている点だ。Python と Swift と TypeScript では download が非同期イテレータを返し、update.progress を読んで進捗を表示する。Rust 版は while let Some(update) = downloader.next().await というループで、同じ進捗値を 0.0 から 1.0 の範囲で返す。
モデル識別子は alibaba:qwen3.5:0.8b:mirai:mirai-m:4 というコロン区切りの文字列で、4言語すべてで同一の値が使われている。README には各セグメントの意味の説明がない。おそらく提供元、モデル名、パラメータ規模、配布元、量子化またはバリアントの指定と読めるが、これは推測であって、裏付けは README にはない。
ChatMessage は役割ごとのコンストラクタで組み立て、reasoning と text を別々に取り出す
メッセージは ChatMessage::system() と ChatMessage::user() という役割別のコンストラクタに with_text を繋いで作る。Rust では ChatMessage::system().with_text("You are a helpful assistant".to_string())、Swift では ChatMessage.system().withText(text: ...) のように、言語ごとの命名規約に合わせた形になっている。
応答側の構造のほうが判断材料として大きい。session.reply は replies の配列を返し、その最後の要素の message から reasoning() と text() を別々に取り出す。Rust 版は reply.message.reasoning().unwrap_or_default() と reply.message.text().unwrap_or_default() を続けて出力する。つまり推論過程のテキストと最終回答のテキストが別フィールドとして保持される設計だ。reasoning を返さないモデルでは空文字列になる。
ここから実装上の制約がひとつ見える。アプリ側で「思考過程は見せるが保存はしない」といった出し分けをする場合、フィールドが分かれているぶん扱いやすい。一方で、reply が配列である理由や、複数要素が返る条件は README に書かれていない。ストリーミングで断片が積まれるのか、複数候補が返るのかは、この資料からは判断できない。
導入手順はパッケージマネージャ単位で完結するが、Rust だけは git 依存
Python は uv add uzu==0.5.26、TypeScript は pnpm add @trymirai/uzu@0.5.26、Swift は Swift Package Manager で .package(url: "https://github.com/trymirai/uzu.git", from: "0.5.26") を dependencies に書く。いずれもバージョンを固定した形が README に示されている。
Rust だけは様子が違う。README の例は crates.io ではなく git を指す。uzu = { git = "https://github.com/trymirai/uzu", branch = "main", package = "uzu" } という形で、branch = "main" を指定している。バージョン番号ではなくブランチを追う設定なので、Cargo.lock を更新するたびに取得されるコミットが変わりうる。再現性を重視するなら、README の例をそのまま使うのではなく rev でコミットを固定する必要がある。この点は README 自身が注意を促しているわけではなく、記述から読み取れる帰結である。
もうひとつ、バッジのリンク先が示す事実として、Python・TypeScript・Swift のバインディングは crates/legacy/uzu/bindings 配下に置かれている。パスに legacy が含まれる以上、これらが現行の主要開発対象ではない可能性がある。バージョン番号が 0.5.26 でそろっている点だけを見て、4言語が等しく保守されていると考えるのは早い。
Apple のユニファイドメモリ前提という制約と、そこから外れる構成
README の特徴リストに Utilizes unified memory on Apple devices とある。これは利点の列挙の一項目であって、対応プラットフォームの一覧ではない。だが、Swift バインディングが iOS と macOS を対象にしていること、トピックに metal が入っていることを合わせて読むと、このエンジンの中心的な実行経路が Apple の GPU とユニファイドメモリにあることは疑いにくい。
ここが採用判断の分かれ目になる。ユニファイドメモリを活かす設計は、CPU と GPU の間でモデルの重みをコピーしない前提で成り立つ。その前提が崩れる環境、たとえば discrete GPU を積んだ Linux ワークステーションや、複数プロセスで1つのモデルを共有したいサーバでは、同じ効率は期待できない。README には Metal 以外のバックエンドに関する記述が見当たらないため、他プラットフォームでの実行可否そのものを、この資料から断定することはできない。
もうひとつの限界はモデルの入手経路だ。engine.download がエンジンの API として存在するということは、モデルの重みがアプリのバイナリに同梱されているのではなく、実行時に取得される経路が想定されている。オフライン前提の配布や、取得元を自社管理下に置きたい要件では、この取得レイヤをどう扱うかを先に決めておく必要がある。README はダウンロード先のパスも、キャッシュの破棄方法も示していない。
candle や llama.cpp との違いは、抽象度をどこに置くかにある
Rust でモデルを動かす選択肢として candle がある。candle はテンソル演算とニューラルネットワークの部品を提供するライブラリで、モデル定義や重みの読み込み、トークナイザの接続は利用者側が書く。uzu はその層をすでに内側に取り込んでおり、利用者が触るのは Engine、model 識別子、ChatMessage、ChatReplyConfig という会話レベルの型になる。同じ Rust でも、candle は部品、uzu はアプリ向けの窓口だという違いがある。
llama.cpp との違いは別の軸にある。llama.cpp は GGUF 形式のモデルファイルを自分で用意してパスを渡す運用が中心で、モデルの選択と配置は利用者の責任になる。uzu は engine.model(識別子) と engine.download という2段階で、識別子の解決と取得をエンジン側の責務にしている。モデル管理をエンジンに寄せるか、ファイル管理として利用者に残すかという設計判断の差である。
ただし、この比較は README に書かれた uzu の API 形状から読み取れる範囲の話であって、推論速度やメモリ使用量の比較ではない。どちらが速いかをこの資料から論じることはできない。uzu の README はベンチマーク数値を一切載せていない。
0.5.x 系のリリース間隔と、legacy ディレクトリが示す保守の重心
リポジトリ情報によれば、0.5.23 が 2026-09-03、0.5.25 が 2026-09-04、0.5.26 が 2026-09-06 に公開され、最終 push は 2026-09-09 となっている。3日から2日おきにパッチ版が出ている計算で、活発に動いている時期だと読める。ただし 0.5.x というマイナー番号が続いている以上、API が安定した 1.0 に到達しているわけではない。EngineConfig::default() や ChatConfig::default() といった既定値に依存したコードは、マイナー更新で引数の形が変わると壊れる可能性がある。
保守コストの見積もりで最も効くのは、先に触れた crates/legacy/uzu/bindings というパスだ。Python、TypeScript、Swift のバインディングが legacy の下にまとめられている一方、リポジトリのトピックには tts が含まれている。README のサンプルはテキストチャットのみで、音声合成の API は一切出てこない。トピックと README の間にこのずれがあるため、音声合成を目的に uzu を見ているなら、README だけでは判断できず、docs.trymirai.com 側を確認する必要がある。
ライセンスは MIT で、リポジトリ情報と README のバッジが一致している。MIT は著作権表示とライセンス文の保持を条件に、改変と再配布を許す。ただし、uzu 本体が MIT であっても、engine.download で取得するモデルの重みには別のライセンスが適用されるのが通例で、その条件は README には書かれていない。アプリに同梱して配布する形態を取るなら、モデル側の条件を別途確認する必要がある。ここは法務判断の領域なので、具体的な可否は自組織の法務に確認してほしい。
編集部の結論
uzu が向くのは、Apple 端末上でモデルを同梱または初回取得させ、Rust あるいは Swift から推論を呼びたいアプリ開発者だ。逆に、サーバ側で複数モデルを切り替えながら同時実行数を捌く構成や、Metal 以外のアクセラレータを前提にした構成には、README からは根拠が見つからない。導入前に確認すべきは3点ある。第一に、engine.model に渡す識別子 alibaba:qwen3.5:0.8b:mirai:mirai-m:4 の各セグメントが何を表すかがドキュメント側で説明されているか。第二に、engine.download が返す進捗オブジェクトのキャンセル手段がバインディングごとに存在するか。第三に、crates/legacy/uzu/bindings 配下というディレクトリ名が示すとおり、Python・TypeScript・Swift のバインディングが legacy 扱いである点が、今後の更新でどう扱われるかだ。この3点が自チームの要件と噛み合わないなら、uzu の採用は保留したほうがよい。
コミュニティノート