ブログ
Web 制作・システム開発に関する記事を公開しています。
Gemini Live API で turnComplete 後に音声が届かない
Gemini Live API の応答計測で、8 キュー連続で「転写あり・音声 0ms」という結果が出ました。原因は API ではなく、turnComplete 受信で収集を打ち切っていた自分の完了判定です。映像ストリーミング併用時は turnComplete が音声チャンクより 0.7〜1.3 秒先に届きます。turnComplete と音声途絶を合成した完了判定に直すと 8/8 で正常受信になりました。
Claude Code の Docker Sandbox で API キーが渡らない原因
Claude Code の Docker Sandboxes で、sbx secret で入れたはずの API キーが環境変数に現れない、gh CLI が一部のコマンドだけ失敗する、という 2 つの症状に当たりました。正体はどちらも、プロキシがネットワーク層で認証を注入するというこの環境の設計です。direct モードでの実用解(ホスト側に .env を置く)と、gh の更新系操作を読み戻しで検証する運用を実測ベースで書いています。
playwright install --with-deps で CI が 15 分止まる原因
GitHub Actions の playwright install --with-deps が apt ミラーの低速に当たり、数 MB のフォント 2 つの取得だけで約 15 分かかってジョブが時間切れになりました。結果表示が failure ではなく cancelled になるのも紛らわしい点です。Playwright 公式コンテナへ移行してインストールステップ自体を消した手順と、イメージタグの一致や HOME 設定などの注意点をまとめました。
SDK なしで Gemini Live API を WebSocket で直接叩く
SDK を使わず ws と https-proxy-agent だけで Gemini Live API(BidiGenerateContent)と話す計測プローブの材料一式です。setup からのメッセージシーケンス、24kHz PCM を WAV にして聴く方法、企業プロキシ+社内 CA 環境での接続、実測した接続時間とセッション原価まで載せています。
Gemini Live API の proactive audio は自発的に喋らない
proactive audio という名前から「AI が自発的に喋り出す機能」を期待して設計を組んだら、公式リファレンスの定義は「直前の入力への応答を拒否できる」、つまり黙る自由を与える機能で、向きが逆でした。自発的に喋るエージェントを作るならテキストキュー駆動が正解で、実測の初動 0.6〜1.0 秒で実用になります。2.5 Flash Live 系限定という制約も含めて整理しました。
Gemini Live API のセッションが突然切れる 3 つの制限
Gemini Live API のセッションが切れる理由は、セッション時間の上限・WebSocket 接続の寿命・ephemeral token の期限の 3 層に分かれていて、対処がそれぞれ違います。公式ドキュメントの数値を出典付きで整理し、GoAway が来ない切断への備えと、会話の切れ目まで粘ってから張り替える実装パターンをまとめました。
Gemini Live API にテキストを送っても音声が返らない原因
Gemini Live API に clientContent でテキストを送ったのに、音声もエラーも一切返ってこない症状の原因です。turnComplete: false で送ったテキストは 2 モデルとも 7 発話中 0 応答で、完全に無視されました。エラーが返らないぶん切り分けが難しく、対処は毎回 turnComplete: true で送ることです。3.1 系のドキュメント記載との食い違いも記録しています。
Gemini Live API の音声応答が 6 秒遅れる原因は thinking
Gemini Live API の native audio モデルで、テキストを送ってから音声が返るまでの初動が最大 6 秒かかっていました。原因はモデルの地力ではなく、思考モード(thinking)が既定で有効なことです。thinkingBudget: 0 を指定すると初動の中央値は 976ms、最悪値も 1.3 秒に収まりました。preview 2 モデルでの実測データ付きです。
navigator.share の判定だけでは PC でも共有シートが出る
スマホでシェアボタンを押すとログインを求められるので、共有シートに切り替えたところ、PC でも切り替わりました。Chrome・Safari・Edge はデスクトップでも Web Share API に対応しているためです。日本語で流通している判定方法は navigator.share の存在チェックですが、これは Chrome デスクトップが非対応だった頃の前提で、今は分けたい境界と一致しません。(pointer: coarse) と組み合わせて絞ります。
WordPress 移行後に本文に残った旧 URL を Portable Text から数える
WordPress から移行したあとに残る旧画像 URL を片付けようとして、公開 HTML から数えた 21 本とデータベースから数えた 58 本が食い違いました。差の正体は表示されないリンク注釈と、生 HTML ブロックの中の書き換え漏れでした。ついでに 5 記事で画像が 404 になっていたことも見つかりました。
Astro の routeRules から外してもページがキャッシュされる
エッジキャッシュから問い合わせページを外そうとして routeRules に書かずにデプロイしたら、本番では HIT が返ってきました。キャッシュの有効判定は maxAge かタグのどちらかで立ち、CMS のキャッシュヒントはタグだけを持つため、保持時間の指定が無いままキャッシュされます。外すには routeRules ではなくページ側の Astro.cache.set(false) が要ります。
Cloudflare で purge しても age が伸び続けるのは壊れていない
記事を更新してもページが変わらず、age が単調増加する。ここから「失効が壊れている」と結論しましたが、間違いでした。purge はキャッシュを消すだけで、再描画は次のアクセス時です。curl -I での確認はキャッシュから返るためサーバーに届きません。4 回続けて誤診した過程と、キャッシュのデバッグで守るべきことをまとめます。
Cloudflare で HTML がキャッシュされないのは既定の仕様
「Cloudflare を通しているからキャッシュは効いている」は静的ファイルの話でした。HTML には cf-cache-status ヘッダすら付きません。キャッシュミスではなく、判定の対象になっていない状態です。既定が拡張子ベースである理由と、Astro のサーバーレンダリングでエッジキャッシュを効かせる方法を実測とあわせて書きます。
Cloudflare Workers 無料プランで 503 が出る原因は CPU 上限
毎分 10〜31 リクエストという低負荷で 4 回に 1 回が 503。同じサイトが 1 時間 2,516 リクエストをエラーゼロで捌いた実績もあり、負荷では説明が付きません。原因は無料プランの CPU 上限 10ms でしたが、これは「1 リクエストで超えたら落ちる」という単純な効き方をしません。実測ログとあわせて機構を整理します。
Cloudflare Workers 無料プランの 3 MB 上限に数えられるもの
Cloudflare Workers の無料プランには gzip 後 3 MB というバンドルサイズ上限があります。実運用中のサイトの実測値は約 2.1 MB で、残る余裕は 900 KB ほど。画像生成ライブラリを 1 つ足すだけで +680 KB 増えて天井に届く一方、11 MB ある静的アセットは 1 バイトも数えられません。何が数えられて何が数えられないのかを実測値で整理します。
WordPress の WXR エクスポートに画像が含まれない
WordPress のエクスポート機能が吐く WXR ファイルに画像のバイナリは含まれず、旧サーバーを解約すると原本ごと消えます。移行直後は「完了に見える」罠の解説と事前チェックリスト、そして手遅れだった場合に Wayback Machine から 36 件の画像を実際に回収した手順・落とし穴・法とマナーの線引きをまとめます。
Node.js で favicon.ico を生成する。sharp は ICO を書き出せない
Node.js 画像処理のデファクトである sharp は ICO を出力できず、BMP 格納の ICO は読み込みすらできません。しかし ICO は 6 バイトのヘッダと 16 バイト × N の目次に PNG を並べるだけの単純なコンテナで、追加依存ゼロ・約 30 行で手組みできます。マルチサイズ favicon.ico を生成する実装と細部の注意をまとめます。
Google 検索結果の favicon が地球儀のままになる原因は robots.txt
favicon はブラウザのタブに表示されているのに、Google 検索結果ではデフォルトの地球儀アイコンのまま — 原因は robots.txt が favicon の配信 URL をブロックしていたことでした。ヘッドレス CMS やサイトビルダーの構成で構造的に起きるこの問題の診断手順と、静的 /favicon.ico 併設による対策をまとめます。
Astro.rewrite("/404") が 200 を返してソフト 404 になる
Astro SSR の動的ルートでエントリ未検出時に Astro.redirect("/404") すると 302、Astro.rewrite("/404") にしても HTTP ステータスは 200 になりソフト404になります。Astro が rewrite でステータスを 200 にリセットする機構(astro@7.0.7 のソース)と、404.astro 側で Astro.response.status = 404 を立てて正しく 404 を返す方法をまとめます。
WordPress 移行後に記事内の画像が 404 になるときの対処
WordPress をヘッドレス CMS へ移行したあと、本文が旧 /wp-content/uploads/ を参照したままで記事内の画像が 404 になる問題。404 の 9 割超は実体が移行済みで URL だけが古いので、本文を書き換えずキャッチオール動的ルート 1 本でファイル名からメディアを引いて 301 する設計と、WordPress 命名(-WxH サムネイル・-e 編集版)や .gitignore の落とし穴をまとめます。