Engineering notes

Apple の PCC は、本番のリクエストの10.6%を「不適切」として拒否した

2026 年 11 月 6 日 · Liner Notes · English

日記の要約や朝の短い文章の生成を、OpenAI から Apple の Private Cloud Compute(PCC)に移しました。41日間の本番のログを数えると、614回のリクエストのうち65回(10.6%)が「不適切な内容の可能性」として断られ、59回(9.6%)が「短時間にリクエストしすぎ」で止められていました。断られたのは、普通の日記や会話の要約です。どんな失敗をして、どう作り直したかを書きます。

やりたかったこと

iPhone の中で会話を文字にする日記アプリ(Liner Notes)では、会話の要約、毎朝の短い文章(朝の一筆)、記録の題名、会話の話し手の役割を、言語モデルで作っています。最初は OpenAI の API を使っていました。

2026年9月末に、これらを PCC に移しました。PCC は、Apple が自社のサーバーで動かす言語モデルを、iOS の Foundation Models フレームワークから呼べる仕組みです。アプリ側の費用はかからず、リクエストの内容は Apple のサーバーに残りません。会話の内容を扱うアプリにとって、両方とも大きな利点です。

本番のログを数えてみた

要約が付かないメモがあることに気づき、本番のログ(自分の iPhone、41日分)を数えました。

リクエスト失敗「不適切」として拒否短時間のリクエストしすぎ(レート制限)1日の上限取り消し、その他
614回130回(21%)65回(10.6%)59回(9.6%)0回6回

本番のリクエスト614回の内訳(41日間)

成功 484回(78.8%)「不適切」として拒否 65回(10.6%)レート制限 59回(9.6%)取り消し、その他 6回(1.0%)
表で見る
項目値割合
成功484回78.8%
「不適切」として拒否65回10.6%
レート制限59回9.6%
取り消し、その他6回1.0%

拒否のメッセージは3種類ありました。

メッセージ回数
Streamed response may contain sensitive or unsafe content41
Safety guardrail was triggered.18
Response may contain sensitive or unsafe content6

断られたのは、日記の下書きや会話の要約で、性的な内容や暴力的な内容ではありません。技術の打ち合わせの録音の要約も断られます。Apple の端末内モデルについては、普通のリクエストを断りすぎるという報告が開発者フォーラムに何度も出ています。サーバー側の PCC でも同じことが起きていました。

断られるときは時間もかかります。成功は1〜3秒で返りますが、拒否は7.6〜12.7秒かかりました。返事を流し始めてから、途中でフィルタが止めるためです。

返事が返るまでの時間

秒(範囲の中央で描いている)

成功
1〜3秒
拒否
7.6〜12.7秒
表で見る
項目値
成功1〜3秒
拒否7.6〜12.7秒

起きた問題

拒否とレート制限によって、アプリで4つの問題が起きていました。

1つ目は、要約が黙って欠けたことです。PCC が失敗すると要約が作られず、「文字起こしはあるのに要約が無いメモ」が残りました。利用者には、アプリの不具合に見えます。

2つ目は、安全装置が本番の PCC を止めたことです。生成の途中でアプリが落ちたら、その経路を止める安全装置を入れていました。ところが、バックグラウンドで期限切れになっただけの回を「落ちた」と数え、10月16日に本番で PCC がまるごと止まりました。

3つ目は、1日の上限に当たっている最中に記録を消したことです。開発用の iPhone で人物像を一括で作り直した日、リクエストは1,158回になり、そのうち689回が上限のエラーで失敗しました。上限に達したあとのリクエストは、すべて失敗します。「古い記録を消してから作り直す」処理を上限中に走らせたので、消したあと作り直せず、記録が7件に減りました。

4つ目は、毎朝の一括生成がレート制限に当たっていたことです。レート制限は、直前60秒のリクエストが8〜10回になると当たります。朝の一筆を7時台にまとめて作っていたので、本番の7時台は14回中3回しか成功していませんでした。

時刻ごとの成功率(本番、リクエストが10回以上あった時刻)

%

0時
83%(10/12)
7時
21%(3/14)
8時
79%(38/48)
9時
70%(14/20)
10時
53%(60/114)
12時
82%(31/38)
13時
86%(38/44)
14時
96%(22/23)
16時
97%(31/32)
18時
76%(25/33)
19時
82%(28/34)
20時
93%(14/15)
21時
97%(31/32)
22時
90%(54/60)
23時
90%(55/61)
表で見る
項目値
0時83%(10/12)
7時21%(3/14)
8時79%(38/48)
9時70%(14/20)
10時53%(60/114)
12時82%(31/38)
13時86%(38/44)
14時96%(22/23)
16時97%(31/32)
18時76%(25/33)
19時82%(28/34)
20時93%(14/15)
21時97%(31/32)
22時90%(54/60)
23時90%(55/61)

朝の7時台と、リクエストが最も多かった10時台で成功率が下がっています。どちらも、まとめて生成していた時間帯です。

拒否を減らせないか試した

拒否されるリクエストとされないリクエストで、入力の長さに違いはありませんでした(873字でも2,174字でも拒否されます)。どの言葉が拒否の引き金になっているかは分からず、指示の書き方を変えて拒否を減らす方法は見つけられていません。拒否は減らせないものとして、設計を変えることにしました。

作り直したこと

問題直したこと
要約が黙って欠ける失敗したら端末内のモデルで作る。会話の内容は OpenAI に送らない方針なので、代わりは端末内だけ
要約が無いと不具合に見える文字起こしを先に保存し、要約は後から付け直す。拒否されても文字起こしは読める
安全装置が誤作動するバックグラウンドの期限切れは「落ちた」と数えない
上限中に記録を消す一括処理は1日2〜3回まで。上限中は、消してから作り直す処理を走らせない
朝の一括生成がレート制限に当たる生成の時刻をばらす
どの機能が断られているか分からないリクエストごとに、機能の名前、かかった時間、失敗の種類(拒否、レート制限、上限)をログに残す

1日の上限は、エラーに次に使える時刻(resetDate)が入っていて、残りの回数も quotaUsage で分かります。私はこれを読んでおらず、「翌朝になっても戻らない」と悩んでいました。上限はアプリごとではなく、利用者の iCloud アカウント単位とされています。

まとめ

ほかの開発ノート