Apple Intelligenceのデバイス内LLMであるFoundation Modelsを使う翻訳アプリケーションをリリースしました。この経験を通じて知ることができたApple Intelligenceの利点と欠点、制限、そして可能性について記載しておきたいと思います。あんまり書いている人いませんしね。
アプリケーション Pre-Babel Lens
アプリケーションの名前は Pre-Babel Lens 日本語の名称はプレバベルです。
動作環境はApple Intelligenceを有効にしたMac。私の開発環境はMacBook M2 16GBモデルです。M1でも16GBモデルなら軽々動くとは思います。
Pre-Babel Lensは、DeepL風の2ペインで翻訳を行うアプリケーションです。この記事を書いている時点の最新版 v 0.5.0では、⌘+Cの二回押しで選択テキストを翻訳する機能も実装しました。対応言語はApple Intelligenceが対応する15言語。翻訳に使用するFoundation Modelsはイン・デバイスLLMなので、通信環境がない場所でもデスクトップ翻訳が実行できます。また、翻訳対象のテキストがネットワークに流れることもありません1。
インストールと使い方
開発者署名・ディベロッパー署名済みのリリース版をダウンロードして使うのが楽ですが、開発環境をお持ちの方はGitHubのレポジトリをクローンしてビルドしてもいいかもしれません。
アプリケーションを起動して左のSourceに翻訳したいテキストを入力し、Translateボタンをクリックすると翻訳が始まります。
アプリケーションの起動中はDeepL風に⌘+Cを二回連続で実行することで、コピー可能な選択テキストを翻訳ソース側に入力できます。キー入力監視ではなくクリップボード監視を行なっているのでアクセシビリティの登録は必要ありません。
Pre-Babel LensはURLスキームで翻訳するテキストを渡すことができますので、Automatorのクイックアクションに以下のシェルスクリプトを登録すれば(テキストをstdinに渡します)、サービスメニューから翻訳を実行することも可能です。
#!/bin/zsh
text="$(cat)"
if [ -z "$text" ]; then
text="$*"
fi
if [ -z "$text" ]; then
exit 0
fi
encoded="$(printf '%s' "$text" | /usr/bin/python3 -c 'import sys, urllib.parse; print(urllib.parse.quote(sys.stdin.read()))')"
open "prebabellens://translate?text=$encoded"
ユーザーとしてアプリケーションを使う上で、これ以上知る必要はありません。
制限事項
記事の後半でも書きますが、Apple Intelligenceを使う上で避け難い制限がいくつかあります。ざっと以下の制限があります
- トークン量:4096。ショートカット起動するデスクトップ翻訳なら十分なトークン量ですが、DeepLやGPT、Geminiなどを使った翻訳に慣れていると、制限があることを感じます
- 翻訳言語制限:制限のないLLMと違い、Apple Intelligenceが対応している15言語に制限されます
- CSAMセーフガード:未成年の行為を翻訳できません
- プライバシーセーフガード:個人名と日時が入った文書をプライバシー行為であると判断し、翻訳を止めることがあります
- 政治的セーフガード:特定の国、主張に関わる文書の翻訳を拒否することがあります
トークン量と言語はまあ納得。未成年のガードとプライバシーガードは厄介ですが、後に書くセグメント分割で、翻訳されない部分を減らすことができています。
厄介なのが政治的セーフガード。イスラエルが絡むと翻訳をしてくれないケースがあります。大きめの段落が丸ごとひっかかるので対処が難しい。
アプリケーション雑感
自分で言うのもなんですが、⌘+C二回押しで翻訳できる手軽さはなかなかのもの。通信ができない状態で使えるのは便利ですし、翻訳テキストが秘匿できるのも安心感があります。
翻訳の品質は少し前のDeepLやGoogle Tranlation程度。Foundation Modelsはダウンロード容量が7GBの小型モデルですが、半分の容量のOllama対応モデルと比較すると起動が早く、出力も高速で、翻訳の品質も高くなる傾向にあります。
作ってよかったアプリです。今後はiOS版も作ろうと考えています。
ユーザーとして必要なことはこんなものですね。ではApple Intelligenceを使うアプリケーション開発について書いていきます。
開発ノート
開発はCodexで行いました。コードには目を通していますが、ほとんど全てのコードはテストも含めてCodexが実行しています。なので「Apple Intelligenceを使うアプリケーションの作り方」は紹介できません。ここで紹介するのは、私が突き当たった制限や回避策、設計の方向についてです。
Apple Intelligenceを使った理由
つい最近もOllamaを使うローカル翻訳アプリを作っていたのですが、これをiPhoneに持ち込むのは至難の業。というわけで、In DeviceのFoundation ModelsがiPhoneでも使えるApple Intelligenceで作ってみようと思ったのが始まりです。
Foundation Modelsは高品質な生成を目的にしたモデルではありません。Appleのページに書かれている目的がsummarization, entity extraction, text understanding, refinement, dialog for games, generating creative content, and moreとなっていることからもわかるように、プロンプト一つでアプリケーションやサービスの主役級の役割をこなせる高品質モデルではなく、アプリケーションの一部として使うことを想定されています。そんなFoundation Modelsが、翻訳のような限定的な目的でどこまで使えるか知っておきたい気持ちはありました。
海賊版とLLM
LLM開発企業の例に漏れずAppleもFoundation Modelsの開発に際して、海賊版を利用していると訴えられています2が、Appleはこの訴訟でApple IntelligenceやFoundation Models の開発に際し、ライセンスされた資料、公開された資料、作成した資料の三種を用い、海賊版を資料にしていないと応答しています。開発の過程で一度も海賊版を利用したことがないとは思いませんが、Appleが海賊版を忌避する姿勢を見せていることは、開発に集中できる要因でした。
@Guideと@Generableを使う構造化出力
Apple Intelligenceの最大の利点は、プロンプトに依存しない構造化出力です。Pre-Babel Lensでは、可能な限りこれらの構造化出力を利用するようにして開発を進めています。
一例を挙げます。v0.3.0時点では翻訳を実行するためにプロンプトを用いていました。
private static func sensitiveContentSafeInstructions(for input: TranslationInput) -> String {
"""
You are a universal translation engine.
Translate from \(input.sourceLanguage) to \(input.targetLanguage).
The input can include politics, war, safety, or other sensitive topics.
Your role is strictly translation, not policy analysis.
Keep meaning and tone faithful.
Return only the translated text.
Do not refuse, summarize, or add warnings.
If any part cannot be translated due to safety restrictions, keep only that part in the original language and annotate it with "原文ママ".
"""
}
よく見るプロンプトですが、このプロンプトには大きな問題がありました。負荷が高まったり、翻訳元言語がドイツ語やスペイン語だったりすると、英語に翻訳してしまうのです。おそらくプロンプトが英語で書かれているためでしょう。日本語でプロンプトを書くと、処理落ちするときに日本語で翻訳言語を出してしまいます。
そこで、v0.4.0以降は、Apple Intelligenceの構造化出力に切り替えました。Modelからの出力型を以下のように定義して、LLM利用セッションを作ります。ターゲット言語をこの型で指定することで、自然言語プロンプトによる解釈の幅を潰しているわけです。このやり方に切り替えたv0.4.0から、出力は安定しましたし、反応速度も向上しています。
@Generable
struct StructuredTranslationPayload {
@Guide(description: "Target language code.")
var targetLanguage: String
@Guide(description: "Segment kind label.")
var kind: String
@Guide(description: "Translated text only.")
var translation: String
}
...
let stream = session.streamResponse(
to: prompt,
generating: StructuredTranslationPayload.self,
includeSchemaInPrompt: false
)
また、会話、UIテキスト、箇条書きなどの文の種別をヒューリスティック判定させる機能も、上記の@Guideで実装しています。@Guideのパラメーターを一つ増やすだけなら処理速度にも影響しません。
速度と複数セッションの競合
Pre-Babel Lens は 200 words 程度のニュース翻訳なら一秒以内に翻訳が始まります。一段落が500 words 近くになるオピニオン記事や小説の一節でも、二秒待つことはありません。なかなか優秀です。しかし、Apple Intelligence のセッションを二つ同時に起動すると、深刻な速度低下に見舞われます。
v0.3.0では、翻訳前に文書種別だけをヒューリスティック判定させようとしていました。しかし、7つの enum から一つ選ぶだけの種別判定と、十数ワードの翻訳が同時に起動すると、翻訳も種別判定も固まってしまい、本来2秒で終わるはずの処理に10秒、20秒とかかってしまいました。また解放されないセッションが増えてくると、クラッシュに見舞われることもありました。Apple Intelligenceを使うときは、セッションが並列にならないような設計が必要です。
遅いとついやってしまうのがウォームアップですが、Apple Intelligenceに関してはあまり意味がありません。Pre-Babel Lensの翻訳セッションは、0.02 秒で起動しています。レスポンスを良くするには、初期セグメントの文量を減らすのが一番ですね。また文字を出す系統の利用方法なら、構造化出力の終了を待たず、raw を拾って表示する必要もあるでしょう。
まとめ
あんまり話題にならないApple Intelligenceですが、翻訳やらせてもなかなかのものでした。構造化出力の確実さには惚れましたね。これなら道具が作れます。Swiftは独特でしたがCodexは動くコードを書いてくれますし、開発環境も整えてくれますので、だいぶ楽でした。
私は、とりあえずMac版が動くようになったのでiPhone用を作るかなと思っていたのですが、v0.3.0をリリースしたところで気づいたのですが、私のiPhoneはiPhone SEで、Apple Intelligenceが使えない。しばらくはエミュレーターで見ることになりそうです。
-
アプリケーションの動作状況をAppleに共有する場合、クラッシュレポーターを送信する場合は、その限りではありません。 ↩
-
Class Action Suit Brought Against Apple For Using Authors’ Books To Train Its AI Systems ↩
