OnDevice Kit

あらゆる LLM 機能を、
すべての iPhone で。

テキストからのデータ抽出、分類とタグ付け、要約、返信の下書き、音声メモのタスク化、チャット。OnDevice Kit はこうした LLM 機能を iOS アプリの中で動かします。サーバーも API 料金も不要で、データは端末の外に出ません。

Apple Intelligence が使える端末ではそれが、使えない端末では llama.cpp で動くローカルモデルが答え、最後の砦として独自ルールが控えます。回答の形を一度定義すれば、どの層が答えても同じ Codable 値が返ります。

Swift 6、iOS 17 以降、macOS 14 以降に対応。買い切り、ソースコード全公開。私たち自身の 2 つのアプリから切り出したコードです。

ユースケースを試す
LetterReader.swift
import OnDeviceKit

struct Letter: Codable, Sendable {
    var sender: String
    var action: String
    var deadline: String
    var steps: [String]
}

let schema = Schema("Letter") {
    Property("sender", .string, "Who sent it, short")
    Property("action", .choice(["none", "pay", "reply", "sign"]))
    Property("deadline", .date, "Only if the letter names one")
    Property("steps", .list(.string, max: 4), "Tiny steps")
}

// Downloads once, in the background, checked against its SHA-256
let store = ModelStore(.lfm2_2_6B)

let assistant = Assistant<Letter>(
    schema: schema,
    instructions: "Read the letter. Use only facts from it.",
    model: store.readyFile,
    rules: { text in Letter.guess(from: text) }
)

let (letter, tier) = try await assistant.run(scannedText)
// tier: .apple, .local or .rules
スキャンした郵便物

City Energy より 2 回目のご案内です。請求書 4711 のお支払いを確認できておりません。2026 年 10 月 30 日までに 84.20 EUR を、参照番号 4711 にてお振り込みください。期限を過ぎた場合は延滞料が発生します。

実行する端末
  1. Apple Intelligence Foundation Models、iOS 26 スキップ: 非対応の端末
  2. ローカル GGUF モデル Metal 上の llama.cpp、GBNF 文法 回答
  3. 独自ルール キーワード、正規表現 不要
letter, tier: .local
{
  "sender": "City Energy",
  "action": "pay",
  "deadline": "2026-10-30",
  "steps": [
    "Transfer 84.20 EUR",
    "Use reference 4711",
    "Pay before 30 October"
  ]
}

出力はイメージです。実際の文面はモデルと指示によって変わりますが、データの形は変わりません。

作れるもの

「テキストを入れて答えを得る」と説明できる機能なら、端末内で動きます。回答を Schema として記述すれば、どの層もその形どおりに返します。構造が不要ならフリーテキストをストリーミングすることもできます。

  • 書類を読む

    手紙、請求書、契約書、カメラで撮ったレシートから、差出人、金額、日付、次にやることを取り出します。

    .date, .number, .choice
  • 分類とタグ付け

    メッセージの振り分け、メモへのラベル付け、フィードバックや日記のトピックと感情の判定。

    .choice, .list(.string)
  • 音声を構造化

    Speech フレームワークの文字起こしから、タスク、買い物リスト、手順ガイドを作ります。

    .list(.object(Task))
  • 要約

    長いメール、チャットのスレッド、1 ページ分のメモを、落ち着いた 2 文にまとめます。

    .string
  • 返信の下書き

    お客さまへの最初の返信、丁寧なお断り、短い返信候補 3 つ。

    .list(.string, max: 3)
  • フォーム入力

    住所、予約確認メール、履歴書を貼り付けるだけで、フォームの各項目を埋めます。

    .object(Address)
  • 書き換え

    短いテキストを、やさしい言葉づかい、より親しみやすいトーン、短い文、別の言語に書き換えます。

    .string
  • チャット

    回答をトークン単位でストリーミングするプライベートなアシスタント。任意の GGUF モデルと、そのモデル固有のチャットテンプレートで動きます。

    LocalLLM.stream

小さなオンデバイスモデルが苦手なこと:非常に長い文書(数ページなら問題ありませんが、本 1 冊は無理です)、与えられていない世界の知識、長い推論の連鎖。テキストを渡し、そのテキストについて尋ねる使い方なら、驚くほど良い結果が出ます。

パッケージの中身

4 つの Swift モジュール、サンプルアプリ、テスト、スクリーンショット生成ツール。オンデバイス AI アプリに必ず必要で、誰も 2 度は書きたくない部分をまとめました。

Schema

OnDeviceLLM

欲しい JSON を一度記述するだけです。Kit はそれを llama.cpp 用の GBNF 文法にコンパイルするので、デコードが制約され、小さなモデルでも壊れた JSON を出力できません。同時に Apple の Foundation Models 用の DynamicGenerationSchema にもコンパイルします。

フィールド型:.string、.choice、.integer(0...3)、.number、.boolean、.date、.list(_, max:)、ネストした .object。

GBNF で有効な JSON を強制する仕組み

schema.gbnf (excerpt)
root ::= "{" ws "\"sender\":" ws str "," ws "\"action\":" ws root-action-choice "," ws "\"deadline\":" ws date "," ws "\"steps\":" ws root-steps-list ws "}"
root-action-choice ::= "\"none\"" | "\"pay\"" | "\"reply\"" | "\"sign\""
root-steps-list ::= "[" ws "]" | "[" ws str ( ws "," ws str ){0,3} ws "]"
date ::= "\"\"" | "\"" [0-9]{4} "-" [0-9]{2} "-" [0-9]{2} "\""
ws ::= [ \t\n]{0,8}

LocalLLM

OnDeviceLLM

  • モデルファイルに格納されたチャットテンプレートを使うので、LFM2、Qwen、Gemma、Llama、Phi がプロンプトを調整せずに動きます。
  • n_batch = n_ctx を設定するため、長いプロンプトで llama.cpp の GGML_ASSERT(n_tokens_all <= cparams.n_batch) による強制終了が起きません。
  • クラッシュする代わりに、生成前に promptTooLong を投げます。
  • ストリーミングは文字単位で行います。「ä」や「漢」の途中で終わるトークンは、残りが届くまで待ちます。

n_batch クラッシュの原因を解説

LocalLLM
let llm = try LocalLLM(model: url)              // loads the GGUF once

// Free text
let summary = try await llm.generate([
    .system("Summarize in two calm sentences."), .user(email)
])

// Chat, streamed token by token
for try await piece in llm.stream(history + [.user(question)]) {
    answer += piece
}

// Structured, forced into your schema
let recipe = try await llm.generate(Recipe.self, schema: recipeSchema,
                                    instructions: "...", input: pastedText)

Assistant

OnDeviceLLM

[.apple, .local, .rules] の順に試し(順序は変更可能)、最初にデコードできた回答を、それを返した層と一緒に返します。Example を 1〜2 個渡してください。良い例が 1 つあれば、1 ページ分のルールよりも多くのことを 2B モデルに教えられます。

ModelCache.shared は呼び出しをまたいで 1 つのモデルをメモリに保持し、unload() でメモリ警告時に解放します。

フォールバック付きの Foundation Models

Assistant
let assistant = Assistant<Note>(
    schema: Note.schema,
    instructions: "Turn the text into a calm, clear note.",
    examples: [Note.example],
    order: [.apple, .local, .rules],
    model: store.readyFile,
    rules: Note.guess
)

if await assistant.currentTier() == nil { showDownloadPrompt = true }
let (note, tier) = try await assistant.run(input)

ModelStore

OnDeviceModels

  • バックグラウンドの URLSession を使用。アプリを閉じてもダウンロードを続け、次回起動時に再接続します。
  • URL を順番に試します。まず自前のミラー、次に Hugging Face。HTTP エラーや壊れたファイルの場合は次の URL に進みます。
  • SHA-256 をメインスレッド外で検証し、ファイルを iCloud バックアップの対象から外します。

大きなモデルのバックグラウンドダウンロード

ModelStore
@State private var store = ModelStore(
    .lfm2_2_6B.mirrored(first: URL(string: "https://your.cdn/lfm2.gguf")!)
)

switch store.state {
case .notDownloaded:      Button("Download (1.6 GB)") { store.download() }
case .downloading(let p): ProgressView(value: p)
case .verifying:          Text("Checking …")
case .ready:              Text("Ready")
case .failed(let reason): Text(reason)
}

FreeQuota、DailyPass、Unlock

OnDeviceStore

キーチェーンに保存するので、再インストールしてもリセットされない無料枠。1 日 1 回のパス。Transaction.updates を監視する StoreKit 2 の非消耗型アイテム。承認と購入のリクエスト、ファミリー共有、返金に対応し、最後の状態をキャッシュするので、起動時にペイウォールが一瞬表示されることもありません。

再インストールしても消えない無料トライアル

OnDeviceStore
let quota = FreeQuota("summaries", free: 3)          // Keychain
@State var unlock = Unlock("com.you.app.unlimited")  // StoreKit 2

if quota.isAvailable || unlock.isUnlocked {
    summarize(); quota.use()
} else {
    showPaywall = true
}
Button("Unlock for \(unlock.price ?? "…")") { Task { try await unlock.buy() } }
Button("Restore purchase") { Task { try? await unlock.restore() } }

App Store 用スクリーンショット

Tools/screenshots

capture.sh はアプリをビルドし、ステータスバーをきれいな 9:41 に設定して、シミュレータで全言語の全画面を撮影します。compose.py がキャプションとデバイスフレームを付け、App Store Connect が求める 2 サイズ(6.9 インチ 1320×2868、6.3 インチ 1206×2622)で書き出します。

キャプションは macOS 自身のテキストエンジンで描画するので、アラビア語、ヘブライ語、日本語、韓国語、中国語も正しく表示されます。

Terminal
PROJECT=MyApp.xcodeproj SCHEME=MyApp BUNDLE=com.you.myapp \
  ./capture.sh en de ja ar
python3 compose.py

ほかに、LFM2 2.6B をダウンロードして任意のテキストをメモに変換し、どの層が答えたか、どれだけ時間がかかったかを表示する小さな SwiftUI アプリ OnDeviceDemo と、ONDEVICE_MODEL が GGUF ファイルを指していれば実際のモデルで統合テストを実行する Swift Testing のテストスイートも含まれます。

自分たちの 2 つのアプリから切り出しました

OnDevice Kit は週末に作ったデモではありません。theyplay.games の 2 つの iOS アプリの土台となるコードで、両方で同じ部品が必要になった段階で切り出しました。

LAITTER

紙の郵便物をスキャンすると、何についての書類か、何をいつまでにすべきかがわかります。Apple Intelligence、LFM2 2.6B、ルールのいずれかで、すべて iPhone 上で読み取ります。最初の 2 通は無料(キーチェーン)、その後は買い切りでアンロックします。

Hausgeist

家の中のモノの使い方を音声で記録すると、家族やゲスト向けのわかりやすい手順になります。同じく 3 層構成で、モデルは自社サーバーから配信し、Hugging Face をフォールバックに使用。App Store のスクリーンショットは 10 言語です。

2 つのアプリから学んだことがあります。2〜3B のモデルは、指示を長くするより、プロンプトに具体例を 1 つ入れるほうがはるかに良くなります。Assistant が examples を受け取るのはそのためです。

相性の良いモデル

チャットテンプレートを持つ GGUF モデルならどれでも動きます。以下は動作を確認したモデルです。LFM2 2.6B は、サイズと SHA-256 を含む ModelSpec として最初から用意されています。

モデルサイズ(Q4_K_M)得意なことライセンス
LFM2 2.6B Liquid AI1.6 GB抽出、8 言語、高速LFM Open License(年間売上 $10M 未満は無料)
Qwen3 4B Instruct2.5 GB推論、多言語Apache 2.0
Gemma 3 1B0.8 GB古い端末での短いタスクGemma Terms of Use
Llama 3.2 3B2.0 GB英語のテキストLlama 3.2 Community License
約 6.5 秒
M シリーズ Mac で LFM2 2.6B が 6 フィールドの郵便物スキーマを埋めるまでの時間
20〜30 秒
1.6 GB のモデルの初回読み込み。以降の呼び出しではキャッシュを使用
1.6 GB
4 ビット量子化の LFM2 2.6B。バックグラウンドで一度だけダウンロードし、SHA-256 で検証

リリース前に各モデルのライセンスを確認してください。モデルを配布するのはあなたのアプリです。

無料ガイド

2 つのアプリを作る中で学んだことを、購入しなくても使えるコード付きでまとめました。

価格

支払いは一度だけ。クライアント向けに開発するアプリを含め、いくつのアプリでも使えます。

Solo

$79 買い切り

対象:開発者 1 名

  • 4 つのモジュールすべてのソースコード
  • サンプルアプリ、テスト、スクリーンショットツール
  • 無料・有料を問わずアプリ数無制限
  • 12 か月間のアップデート
Solo を購入

Team

$199 買い切り

対象:同一企業内の開発者 10 名まで

  • Solo のすべての内容
  • チーム全体で 1 ライセンス
  • クライアントワークを含めアプリ数無制限
  • 12 か月間のアップデート
Team を購入

決済と請求書の発行は Polar が行います。価格は USD で、該当する場合は付加価値税(VAT)が加算されます。12 か月の経過後も、それまでに受け取ったすべてのバージョンを引き続き使用できます。ライセンスにより、Kit を自分のアプリで使用・改変できます。ソースコードの再販売や公開は認められません。llama.cpp は MIT ライセンスで、各モデルにはそれぞれ独自のライセンスがあります。

よくある質問

具体的に何が手に入りますか?

OnDevice Kit の Swift ソースコード一式(OnDeviceLLM、OnDeviceModels、OnDeviceStore の各モジュールと、それらをまとめる OnDeviceKit)、サンプルアプリ OnDeviceDemo、テストスイート、スクリーンショットツールです。Xcode にローカルパッケージとして追加する Swift パッケージの形で提供します。llama.cpp は Swift Package Manager 経由で取り込まれます。リリース b11887 に iOS シミュレータ対応を加えただけの無改変ビルドで、バージョンとチェックサムで固定しています。

ライセンスで何が許可されていますか?

無料・有料を問わず、クライアント向けに開発するアプリも含め、自分のアプリでいくつでも Kit を使用・改変できます。Solo は開発者 1 名、Team は同一企業内の開発者 10 名までが対象です。ソースコードを単体で、またはテンプレート、スターターキット、講座の一部として再販売、サブライセンス、公開することはできません。コンパイル済みのアプリは問題ありません。アップデートは 12 か月間含まれ、それまでに受け取ったすべてのバージョンは引き続き使用できます。

iOS シミュレータで動きますか?

動きます。llama.cpp の公式リリースには iOS シミュレータ用のスライスがないため、Kit では同じリリースをシミュレータ対応込みでビルドした無改変版をリンクしています。シミュレータではローカルモデルが CPU で動くため、2〜3B のモデルだと数秒ではなく 1〜2 分かかります。UI の開発には十分ですが、実際の速度は実機で確認してください。シミュレータで Apple Intelligence を使うには、Mac 側で Apple Intelligence をオンにする必要があります。

ローカルモデルはどの iPhone で動きますか?

LFM2 2.6B(1.6 GB)のような 4 ビットの 2〜3B モデルには、RAM 6 GB 以上の iPhone が必要です。iPhone 13 Pro と Pro Max、iPhone 14 以降が該当します。それより古い端末では、Gemma 3 1B(0.8 GB)のような 1B モデルで短いタスクをこなせますし、ルール層はどの端末でも動きます。Apple Intelligence には、iOS 26 を搭載した iPhone 15 Pro 以降で、機能がオンになっていることが必要です。利用できない理由は AppleModel.unavailableReason で確認できます。

モデルをダウンロードするアプリは App Review を通りますか?

自分たちのアプリでは次のようにしています。審査メモに「アプリはリクエストに応じて言語モデルをダウンロードし、端末内で実行する」と記載する。ダウンロードを任意にするか、オンボーディングで説明する。ダウンロード前にサイズを表示する。設定画面からモデルのライセンスにリンクする。詳しくはドキュメントを参照してください。

データが端末の外に出ることはありますか?

ありません。Kit はどこにもデータを送信せず、アナリティクスも含みません。ネットワーク通信は、設定した URL からのモデルのダウンロードだけです。購入は通常どおり Apple の StoreKit を経由します。

モデルのライセンスの責任は誰にありますか?

あなたにあります。モデルを配布するのはあなたのアプリだからです。モデルは Kit に含まれません。LFM2 は LFM Open License(年間売上 $10M 未満なら商用利用無料)、Qwen3 は Apache 2.0、Gemma と Llama にはそれぞれ独自の利用規約があります。リリース前に、選んだモデルのライセンスを必ず読んでください。

開発に何が必要ですか?

Xcode 26 と Swift 6 です。アプリのターゲットは iOS 17 以降、macOS 14 以降にできます。iOS 26 と macOS 26 では、Apple Intelligence が利用可能な場合に自動的に使われます。OnDeviceKit ですべてをまとめて取り込むことも、必要なモジュールだけを使うこともできます。

返金はできますか?

購入後すぐにソースコード一式を受け取れるため、購入の取り消しはできません。ただし、お使いの環境で Kit が説明どおりに動作しない場合は、14 日以内に info@theyplay.games までメールでご連絡ください。問題の解決をお手伝いし、解決できない場合は返金します。

土台づくりは任せて、AI 機能に集中しよう。

スキーマ、3 層構成、モデルのダウンロード、無料枠、アンロック、スクリーンショット。すべてを、あなたが所有する 1 つのパッケージで。