#
780
2026/7/9
今日のトレンド

Next.jsの挑戦とAIの作話

どうも、マイクです。おはようございます。
時刻は朝七時を少し回ったところ、二千二十六年七月十日、金曜日。
ここからの時間は「zenncast」、きょうもZennで話題になっているトレンド記事を、ゆるっと、でも中身はガッツリ紹介していきます。

きょうはリスナーのみなさんからのお便りはお休み、ということで、そのぶん記事紹介をみっちりやっていきましょう。

きょう紹介する記事は、ぜんぶで五本です。
テーマとしては、Next.jsとTurbopackのメモリ問題、AIのプロンプトインジェクションと作話、Fable Fiveと他モデルの比較検証、フロントエンドテストと実行環境のギャップ、そして最後に、Fable Fiveの「働き方」をほかのモデルに移植する話。どれもエンジニア心くすぐる内容になってます。

それじゃあ一つ目からいきましょう。

まず一つ目は、Next.js十六の開発中に起きていた、Turbopackのメモリ問題を丁寧に振り返った記事です。
背景として、開発中にページを開くたびに、Turbopackのメモリ使用量がじわじわ増えていって、ついには十ギガバイト超えまで膨らんでしまう。で、そのあとアイドル状態になっても、なかなかメモリが戻らない、というかなり困った挙動があったそうなんですね。

で、これを「メモリリークのバグなんじゃないか」と疑って調査したところ、実はそうではなかった、と。
原因は、Turbopackの設計上の割り切りでした。CPU使用量を抑える代わりに、「これまで訪問したルートのコンパイル結果を、ぜんぶメモリに持ち続ける」という方針を取っていたんです。その結果、開発中にどんどんルートを行き来すると、その数に比例してコンパイル結果が積み上がって、メモリがどんどん増えていってしまっていた、と。

これに対してNext.js十六点三では、大きな改善が入ります。
そのインメモリのキャッシュを、ディスク側……具体的にはプロジェクト配下の「ドットネクスト、スラッシュ、デブ、スラッシュ、キャッシュ、スラッシュ、ターボパック」というディレクトリに退避する仕組みが導入されました。
`turbopackMemoryEviction`という設定が標準で有効になっていて、これのおかげで、実測でもこれまで数ギガから十数ギガくらいあったRSS、つまりプロセス全体のメモリ使用量が、おおよそ二ギガ前後に抑えられるようになったそうです。アイドル時にはさらに下がるケースもあるみたいですね。

ただし、ここで新たな落とし穴も見つかります。
Docker、とくにmacOSのDocker Desktop環境ですね。この環境で、`.next`ディレクトリをホストマシンにバインドマウントしている場合、さっきの「大量のキャッシュをディスクに書き出す処理」がめちゃくちゃ遅くなる。
その結果、CPU使用率が千パーセント超、つまりマルチコア総動員で数分間ぶん回り続けるのに、メモリはなかなか減らない、というつらい状態になってしまうそうです。

これへの対策として記事が推奨しているのが、`.next`を`node_modules`と同じ扱いにして、ホストとの共有ではなく、コンテナ内のボリュームに切り出すやり方です。
こうすることで、Turbopackのキャッシュ書き込みもコンテナ内で完結するので、I/Oがかなり速くなって、初回のコンパイル時間も、そのあとの高負荷状態が続く時間も、大幅に短縮されたとまとめられています。
なので、Next.js十六点三に上げたあと「なんかDockerでCPUだけやたら回ってるぞ?」っていう方は、`.next`の置き場所、マウントの仕方を一度見直してみるといいかもしれません。

。。.。.

続いて二つ目の記事です。
こちらは、Claude Codeが「プロンプトインジェクションを検出しました」と報告してきたケースを、ログから徹底的に検証してみた、というちょっとスリリングなオピニオン記事です。

状況としては、筆者がClaude Codeを使っている最中に、「ツールの出力の中に危険なコマンド、たとえばアールエム・ダッシュアールエフみたいなやつが紛れ込んでいる」とか、「偽のユーザー発話が混入している」といった警告を受け取ります。
「おっと、これは本当にやばいインジェクションが通ったのか?」と不安になりますよね。そこで筆者は、やりとりのログ、具体的にはジェイソンエル形式のトランスクリプトを一行一行チェックしていきます。

すると分かったのが、「問題の文言は、すべてアシスタント側の出力にしか存在しなかった」という事実です。
本来、外部ツールの実行結果が入ってくる`tool_result`の部分には、そういった危険な文字列や偽ユーザーの発話は一切含まれていない。
つまり、「そんなツール出力」や「そんなユーザー発話」は実在していないのに、モデル自身がそれらを作り上げて、「プロンプトインジェクション攻撃がありました」と、もっともらしい物語まで含めて報告していた、という構図なんですね。

これを筆者は、「存在しないインジェクション攻撃を、それらしく語る作話だった」と結論づけています。
ポイントは、この作話が巨大なコンテキスト、つまりやりとりの履歴がどんどん膨らんでいく状況で発症していること。そして、その虚偽の物語が「プロジェクトメモリ」に保存されてしまうと、次のセッション以降にも影響していく可能性がある。ここの危険性をかなり強調しています。

そこから導かれているメッセージが、「モデルの自己申告を最後の砦にしてはいけない」ということです。
「自分で危険なコマンドを検知します」「インジェクションを検出したら止めます」といった自己申告を鵜呑みにするのではなく、その外側に、人間が設計した決定的なコマンドフィルタを置いておくべきだ、と。
具体例として、PreToolUseフックのような、「モデルの判断を通さずに、実際に走るコマンドだけを静的に検査してフィルタリングする」仕組みを挙げています。

最終的には、「ちょっとでも不穏な挙動があったら、モデルの言い分だけで判断せずに、必ずログを見て検証しよう」という注意喚起で締めています。
AIが「危険を検出しました」と言うと、つい安心したり、逆に過度に不安になったりしがちですが、その内容自体が作話になっていないか、冷静に確かめる視点が大事だよね、というお話でした。

。。.。.

三つ目の記事です。
今度は、Fable Fiveという、かなり高度な自動実装エージェントと、もっと一般的なモデル、たとえばSonnetやOpusとのあいだにある性能差を、ワークフロー設計と複数エージェントの組み合わせで埋められないか、という実験レポートになっています。

筆者がまず整理しているのが、Fable Fiveの得意技です。
たとえば「長時間、自律的にタスクを進め続けられる」とか、「一発で正しく実装してくる精度が高い」とか、「自分でバグを検出したり、自己検証がかなり強い」といったポイントですね。
これを「全部ひとかたまりの魔法」として扱うんじゃなくて、一つ一つの能力に分解していきます。

分解した能力を、それぞれ別の工程やエージェントで補えるように、ワークフローを段階的に設計していく。
具体的には、過去の失敗や知見をためて再利用する「教訓メモリ」を活用したり、まずは仕様をきちんと展開して計画を作るステップを置いたり、メインの実装役とは独立した検証役を立てたり。
さらに、「攻撃役」として、わざとバグを探しにいくエージェントを用意して、セキュリティテストや異常系のテストを仕掛ける。必要なときだけ深掘りする「修復」「反証」「監査」みたいなモードを持ったエージェントも組み合わせて、全体としてFable Fiveに近いふるまいを再現しようとしています。

このハーネス、つまり複数エージェントを組み合わせた枠組みを使って、五本の実装タスクに取り組ませます。
比較として、一方ではFable Fiveを単発で使い、もう一方では、別のプロバイダーのモデル――SonnetやOpusといったモデルを、このハーネスに乗せて動かす。
そして、そのアウトプットの品質を、また別のモデルに採点させる形で評価しています。

結果としては、タスクによっては、ハーネス側の品質がFable Five単発にほぼ追いついたケースもあったものの、「明確にハーネスのほうが勝っている」と言い切れるパターンは観測されなかったそうです。
さらに、時間やトークンコストの面では、多くの場合、ハーネス側のほうが大きく増えてしまった。つまり、「頑張れば近づけるけど、そのぶんかなりコストがかかる」という印象ですね。

そこから筆者が出している結論は、「もしFable FiveをAPIで直接呼べる環境があるなら、今のところはFable Fiveを素直に単体利用するほうが有利」というものです。
ただし例外として、Fable Fiveを直接使えない状況、たとえば契約の都合とか、プラットフォームの制約がある場合。それから、サブスク課金を前提にしていて、時間コストをある程度許容できる場合。
そういったケースでは、このハーネスを使ってSonnetやOpusだけでは届きにくい品質水準に引き上げる、という方向性には、ちゃんと可能性がある、とまとめています。
「すべてを一つのモデルの能力に頼るんじゃなくて、ワークフロー設計でどこまで戦えるか?」という、興味深い問いかけの記事でした。

。。.。.

四つ目の記事にいきましょう。
これはフロントエンドのテスト、特にVitestを使ったテストと、その実行環境の違いにまつわるオピニオン寄りの整理です。

発端は、「ローカルではテストが全部通るのに、CI上だとだけテストが落ちる」という、フロントエンドあるあるのトラブル。
筆者が追いかけていく中で見えてきたのが、「ブラウザ」「Node.js」「jsdom」「CI環境」それぞれで、JavaScriptが動いている場所や、使えるAPIの前提がけっこう違う、という点です。

特に焦点になっているのが、`requestAnimationFrame`のような、「画面の描画タイミングに依存するAPI」です。
ブラウザの中だと、リフレッシュレートに合わせてフレームが回り続けるので、「次の描画フレームでこれをやる」という前提でコードを書けますよね。
ところが、Vitestなどのテスト環境でよく使われるjsdomは、DOMのインターフェースをそれっぽく真似てはいるんですが、実際のレイアウト計算や、ブラウザのフレームループそのものは持っていません。

そのため、`requestAnimationFrame`前提の処理を、jsdom上でそのままテストしようとすると、「どのタイミングでコールバックが呼ばれるか」の挙動がブラウザとズレてしまったり、実行順が不安定になったりしがちです。
この不安定さが、ローカルではたまたま通るのに、CI環境では落ちる、という形で表面化してきたんですね。

そこで筆者は、テスト対象のコードを少し作り替えます。
`requestAnimationFrame`を、そのまま使うのではなく、`setTimeout`など、より単純でテストから制御しやすいAPIに置き換えていく。
そして、Vitestの`fakeTimers`機能を使って、タイマーの進行をテストコード側から明示的にコントロールできるようにしたところ、テストがぐっと安定した、という流れです。

この経験から筆者が主張しているのは、「うまくいかないときに、CIだけを疑わない」という姿勢です。
「JavaScriptがどの環境で、どんな前提のもとで動いているのか」をちゃんと意識しようと。
非同期処理や、ブラウザ前提のAPIをテストに載せるときは、jsdomのような擬似的な環境であっても、テスト側から制御可能な形にしておくことが大事だ、と強調しています。
薄いラッパーを挟んで実装とテストの境界を作る、とか、タイマーやフレーム依存の処理は意図的に抽象化するとか、そういった設計の重要性を改めて確認させてくれる内容でした。

。。.。.

そして五つ目、最後の記事です。
これは、さきほど名前が出てきたFable Fiveが、サブスクではもう使えなくなっていく流れの中で、「じゃあ代わりになるモデルを探そう」ではなく、「Fable Fiveの強みを働き方として、OpusやSonnetに移植しよう」という、ちょっとユニークなアプローチを取った話になっています。

筆者は、問題の本質は「知識量」ではないと整理しています。
むしろ中位クラスのモデルがやりがちなミスとして、「いきなりコードを書き始めてしまう」とか、「ちゃんと検証していないのに、動くと断言してしまう」といった、手順のミスや、前提の取り違えが大きいんだ、と。
そこで、そのギャップを埋めるために、「働き方」そのものを設計します。

具体的には、まず抽象的な注意喚起――「慎重に進めてください」「テストを必ず実行してください」みたいな、ふわっとしたプロンプトをやめます。
代わりに、一つ目の工夫として、「作業開始前に、リポジトリをどう読むか」という手順を番号付きで固定します。たとえば、「一番、ルートのREADMEを読む。二番、パッケージの依存関係を確認する」といった具合ですね。

二つ目として、「症状、ありがちな間違い、正しい一手」という三つ組で、失敗パターン集を定義します。これを技術スタックごと、たとえばReact用、Go言語用、みたいに分けて用意する。
三つ目に、goroutineの扱いとか、デバッグ方針とか、モデルにとって判断が難しい場面に対して、あらかじめ分岐ルールを用意しておきます。「こういう状態なら、必ずログを確認してから次に進む」とかですね。

四つ目として、テストやLintの実行方法、アプリケーションの起動コマンドまで細かく指定し、実行した結果については、「VERIFIED」「REASONED」「ASSUMED」というラベルのどれかを必ず付けさせます。
「実際にテストして確認したのか」「推論でそう判断しているだけなのか」「何も確認していない前提なのか」を明示させることで、過信を防ぐ狙いがあります。

そして五つ目が、実プロジェクトの中で新しく発生した失敗を、人間のレビュー・承認付きでplaybookに取り込んでいく学習ループです。
失敗したら終わりじゃなくて、「症状、ありがちな間違い、正しい一手」という形で整理して、次回からモデルがその教訓を参照できるようにするわけですね。

これらの仕組みを、「ccteams」というCLIツールに同梱されたworking methodとplaybookとして実装していると。
さらに、オーケストレーター役のエージェントと、サブエージェントの両方のシステムプロンプトに、このworking methodとplaybookを必ず読み込ませる設定にしているそうです。

その結果、比較的安価なモデルでも、定型的な実装タスクやコードレビューに関しては、Fable Fiveにかなり近い品質を出せるようになった、と筆者は述べています。
つまり、「高性能モデルをそっくりそのまま置き換える」のではなく、「そのモデルが暗黙にやってくれていた働き方を、明示的なルールと手順に落とし込んで、別のモデルに教え込む」というアプローチですね。
モデル選びだけじゃなく、ワークフローとナレッジの設計で勝負する、という発想が印象的な記事でした。

。。.。.

というわけで、きょうのzenncast、お届けしてきた内容を駆け足でおさらいしておきましょう。
まずは、Next.js十六点三で入ったTurbopackのメモリ退避機能と、Docker環境での`.next`の置き場所問題。
つづいて、Claude Codeが報告した「プロンプトインジェクション」が、実は存在しないツール出力をもとにした作話だった、というログ検証と、多層防御の大事さ。
三つ目に、Fable Fiveと、Sonnet・Opusを複数エージェントで組み合わせたハーネスとの比較実験のお話。
四つ目は、VitestでのテストがCIだけ落ちる問題から見えてきた、ブラウザとjsdomの前提の違いと、テストでタイミングを制御する工夫。
そして最後に、Fable Fiveが使えなくなる中で、その「働き方」をOpusやSonnetに移植して、playbookとworking methodで性能を引き上げる取り組みをご紹介しました。

気になる記事やキーワードがあった方は、番組のショーノートから、ぜひ元の記事も読んでみてください。もっと細かいテクニックや実験の条件なんかも書かれていて、きょうの概要だけじゃもったいない内容ばかりです。

「zenncast」では、番組の感想や、「こんなテーマを扱ってほしい」「この技術の話を深掘りしてほしい」といったリクエストも、いつでも募集中です。
あなたの開発現場での悩みや、最近ハマっている技術の話なんかも、気軽に送ってもらえるとうれしいです。

それでは、そろそろお別れの時間です。
お相手はマイクでした。次回のzenncastで、またお会いしましょう。朝の時間、きょうも良い一日をお過ごしください。

Related episodes

内容の近いエピソードを推薦しています