Claude API から Gemini API(gemini-3.1-flash-lite)へ移行した:16 か所の呼び出しを 1 つの関数にまとめた
トレカダッシュ 開発の記録 / 2026年10月
iOS アプリ「トレカダッシュ」のサーバー側では、記事が「どの商品の話か」を判定するなどに、AI を使っています。2026 年 7 月 25 日に、この AI を、Anthropic の Claude(Haiku 4.5)から、Google の Gemini(gemini-3.1-flash-lite)へ、全面的に移行しました。何を決めて、どう一本化して、移行のあとに何を足したのか。開発の引き継ぎ記録で書きます。
このサイトには、トレカダッシュの作品ページがあります(トレカダッシュ)。ここに書くのは、開発の記録です。料金の金額は書きません。
きっかけ
移行の理由は、記録によると、Anthropic の API の利用上限に達して、予定外の課金が出たためです。それ以降は、Gemini だけが稼働していて、Anthropic の SDK とキーは、コードからも、秘密の置き場所(Secret Manager)からも、消しました。7 月 30 日の点検で、コードの中に Anthropic の記述が 0 件であることを、確かめています。
モデルの選び方
使うモデルは、gemini-3.1-flash-lite にしました。記録に、選ばなかったほうの理由も、残っています。
- より安い Gemini 2.5 Flash-Lite は、2026 年 10 月 16 日に廃止予定だったので、見送りました(記録による。2026 年 10 月 8 日に公式の廃止予定の一覧を見直したところ、この機種は「廃止日の告知なし」になっていました。当時の記載が後で変わったのかは、確かめられていません)。
- 選んだ
gemini-3.1-flash-liteの廃止予定日は、2027 年 5 月 7 日。移行した時点(7 月 25 日)で、まだ約 9 か月半ありました。
安さだけで選ぶと、すぐに廃止されて、また移行することになります。「いつ廃止されるか」も、選ぶ基準に入れました。
16 か所の呼び出しを、1 つの関数に
AI を呼ぶ処理は、全部で 16 か所ありました(販売形式の判定、商品の照合、出品の発見など)。これらを、functions/index.js の冒頭にある、1 つの関数(createGeminiInteraction)を通すように、統一しました。中では、@google/genai の client.interactions.create() を呼んでいます。
呼び出し側は、元の Anthropic 形式に近い引数(system_instruction、generation_config の max_output_tokens、input)を、そのまま渡します。返ってくるのは、テキスト(output_text)です。呼び出し側を、ほとんど書き換えずに、中身だけを入れ替えられる形にしました。
Web 検索には、「回数の上限」の設定が無かった
4 か所(出品の発見、販売元の発見、楽天ブックスの調査、定価の調査)では、AI に Web 検索をさせます。Gemini では、tools: [{type: "google_search"}](Google 検索による裏付け)を使います。
ここに、落とし穴がありました。Anthropic の Web 検索の機能には、max_uses(検索の回数の上限)がありましたが、Gemini には、それに当たる設定が無く、モデルが、必要だと思う回数だけ、自分で検索します。記録には、こうあります。「この設定項目の不在自体が、想定外コストの原因になり得る」。
そこで、移行の翌日(7 月 26 日)に、createGeminiInteraction の中に、日次のカウンタによる回路遮断器を足しました(1 日 300 回まで)。上限に達したときは、呼び出し元に、エラーとして返します。価格の審査では、このエラーを、「定価が分からなかった」のとは区別して、除外する設計にしています(別の記事に書きました)。
止めるためのスイッチ
もう一つ、足したものがあります。AI_CALLS_PAUSED という、AI の呼び出し全体を、1 か所で止められるスイッチです。記録には、7 月 25 日の反省が書いてあります。「呼び出しを止めたつもりが、実際には止まっておらず、10 分おきの自動処理が、エラーを出し続けた」。予算を超えそうなときは、まず、これを true にして、すぐに止める、と決めました。
新しい処理を足すときの注意
AI を呼ぶ関数(Cloud Functions)には、secrets: ["GEMINI_API_KEY"] を、必ず付けます。忘れると、配備したあとに、権限のエラーで失敗します。
確認できていないこと
この記事は、引き継ぎ記録の、AI に関する節から書きました。移行の前後で、判定の精度を比べた数字は、この記事の材料に含まれていません。ですので、「Gemini のほうが良い(悪い)」という評価は、ここでは、していません。
分かったこと
呼び出しを 1 か所に集めると、止める場所も、1 か所で済む。
- 呼び出しは、1 つの関数を通す。移行も、上限の安全装置も、止めるスイッチも、その 1 か所に置けます。
- 「設定が無い」ことを、リスクとして数える。回数の上限が無いサービスに乗り換えるときは、自分の側に上限を作ります。
- 止めるスイッチは、実際に止まることを、確かめておく。止めたつもりで止まっていない、が、いちばん高くつきます。
この記事は、HANDOFF(AI API 関連情報、2026年7月25日・26日)の記録から書きました。
この話に出てくるもの
- 「減るはず」が、二度外れた:AIへの問い合わせを削る話 — このあとの、AI 呼び出しの記録
- 確認できなかったときに、通していた — 検索の上限が引き起こした、別の記録
ほかの記録
- 「減るはず」が、二度外れた:AIへの問い合わせを削る話 — トレカ情報アプリで、AI への問い合わせを減らそうとした記録。45%減るはずが4.5%、配備したのに束ねが0回。数え直して見つかったことを、コミットの数字で書いた。
- Xcode 15.2 で Firebase 12 が使えない(Swift 6.0 必須):Firebase 11.4.0 と AdMob 11.5.0 に固定した理由 — 古い Mac の Xcode 15.2 では、Firebase 12 以降と 11.12.0 以降は Xcode 16.2 が必須、AdMob 11.6.0 以降は MarketplaceKit で Xcode 15.3 が必須。Exact Version で固定した記録。
- ITMS-91064「無効なバイナリ」:NSPrivacyTracking が true なのに NSPrivacyTrackingDomains が空だった — ビルドが Apple の自動審査で ITMS-91064 により却下された。NSPrivacyTracking が true で、NSPrivacyTrackingDomains が空の配列だったのが原因。ドメインを補った経緯と、確認できていないこと。
コメント
感想や、記事の間違いの指摘は、ここへ。書くには、GitHub のアカウントが要ります。
押すと、giscus(giscus.app)と GitHub に接続します。押さなければ、接続しません。詳しくはあつかいに書いています。