株式会社RevCommのブログ - TECH PLAY

TECH PLAY

株式会社RevComm

株式会社RevComm の技術ブログ

190

はじめに 1. 注目した次世代ツールの魅力 1.1 Oxlint ── 「今すぐ、設定ゼロで爆速化」 1.2 Biome ── 「これ 1 つで全てが片付く、究極の一本化」 1.3 Deno ── 「セットアップ不要、最高にクリーンな開発体験」 1.4 Ezno ── 「型チェックまで Rust 化する、超未来のコンパイラ」 2. Vue特有の壁: の静的解析問題 3. チームでの議論:「設定ファイル2つは本当に必要か?」 4. 結果 5. 今後の展望 まとめ はじめに フロントエンドエンジニアをしている伊藤と申します。 昨今は、Rust 製のツールが数多く生み出されており、Linter もその波に乗っていると思います。 弊社でも移行をしたブログを書いており、是非下記の記事も読んでみてください。 https://tech.revcomm.co.jp/ai-lint-formatter-oxc 史上稀に見る大波が来ている時代に、私も颯爽と波に乗ろうとしました。 そしてどういった結論に至ったかご紹介させていただきます。 注: 本記事の検証は2026年9月10日時点、Oxlint v1.82.0 / Biome v2.5.12 で行っています。この領域は変化が速いため、お読みいただく時点では状況が変わっている可能性があります。 1. 注目した次世代ツールの魅力 1.1 Oxlint ── 「今すぐ、設定ゼロで爆速化」 最大の魅力:既存のプロジェクトにノーリスクで追加できる メリット ESLint の 50〜100 倍高速 : 数万行のコードも一瞬でスキャンが終わります。 設定ファイルが不要 : インストールして実行するだけで、プロ級のバグ検出ルールが最初から動きます。 既存の ESLint と併用可能 : 重い処理(バグチェック)は Oxlint に任せ、複雑なカスタムルールだけ ESLint に残す「いいとこ取り」ができます。 1.2 Biome ── 「これ 1 つで全てが片付く、究極の一本化」 最大の魅力:Linter と Formatter が融合したオールインワン構造 メリット ESLint と Prettier の多くの用途を一本化できる : 個別にプラグインを組み合わせる煩わしさから解放されます(ただし後述の通り、Vue の <template> 部分は現状 ESLint との併用が前提になる場面が残っています)。 設定の衝突ゼロ : Linter と Formatter が同じ思想で作られているため、「Linter と Prettier のルールがバッティングして動かない」という定番のストレスがありません。 圧倒的な構文解析の正確さ : エラーがあっても壊れたコードを器用にパースし、親切なエラーメッセージを出してくれます。 1.3 Deno ── 「セットアップ不要、最高にクリーンな開発体験」 最大の魅力:最初からすべてが揃っている「環境」そのもの メリット : ツール選定やインストールの手間がゼロ : deno lint と deno fmt が最初から内蔵されています。 package.json に大量の依存関係を書く必要がありません。 標準で Web 標準に準拠 : ブラウザと同じ API をそのままサポートしているため、環境ごとの差異に悩まされません。 Node.js との互換性も強力 : 近年は Node.js のパッケージもそのまま動くため、敷居が非常に低くなっています。 注意: Deno は Linter 単体のツールではなく、ランタイムとツールチェーンそのものです。既存の Node.js プロジェクトに deno lint だけを差し込むのではなく実行環境ごと移行する話になるため、Oxlint / Biome / Ezno とは比較の土俵・移行規模が異なる点に注意してください。 1.4 Ezno ── 「型チェックまで Rust 化する、超未来のコンパイラ」 最大の魅力:JavaScript の限界を超える、圧倒的に賢く高速な型解析 メリット : TypeScript(tsc)より遥かに高速 : 現在、フロントエンド開発で一番時間がかかる「型チェック」のプロセスを Rust で爆速にします。 型推論がスマート : コードの型をより深く、正確に推論してくれるため、開発者が型定義を細かく書く手間を減らせます。 バグを未然に防ぐ先進性 : 副作用(Side Effects)の検知など、従来の TypeScript よりもさらに一歩進んだ安全なコード検証が可能です。 注意: ただし Ezno は本記事執筆時点(2026年9月)でまだ feature-complete ではなく、tsc との完全互換を目指しているわけでもありません。既存の大規模プロジェクトの型チェックをそのまま置き換えられる段階ではなく、将来性に注目したい実験的プロジェクトという位置付けです。 どれも高速性を売りにしており、React では非常に乗り換えやすく恩恵も受けられます! 実際に私もローカルでBiomeの設定を試してみましたが、かなり早かったです。 ではそのまま導入して、Rust の恩恵を受けようとしました。 ただ、Vue になると少し話が変わってくるんです・・・ 2. Vue特有の壁: の静的解析問題 Vue は、HTML5 の標準仕様である <template> を、独自の便利な形に拡張して使っています。 要素に v-if や v-for を付けたり、 <template> を .vue ファイルの枠組みとして使ったりしています。 普段の開発をしている中で、 v-if や v-for を使わないケースはほぼないと思います。 当然ですが、それらの値もチェックしてくれると思っていました。 ただ、 <template> 内は、Oxlint, Biome ともにチェックしてくれないんです。 実際に最新版(Oxlint v1.82.0 / Biome v2.5.12、2026年9月時点)で試した結果を、以下のサンプルコードとあわせてご紹介します。 <template > < div > <!-- ❌ 1. 壁:Linterがここを見ないため、存在しない変数(タイポ)をスルーする --> < p > {{ userNema }} </ p > <!-- ❌ 2. 壁:Linterがここを見ないため、「下で使われている」と認識できない --> < p > {{ unusedMessage }} </ p > </ div > < / template> <script setup lang="ts"> import { ref } from 'vue' const userMessage = ref ( 'こんにちは' ) // 🔴 <script> 内は見ているので、「定義したのに使われていない!」と怒られる // (実際には上の template で使いたいのに、Linterが template を読めないせいで孤立する) const unusedMessage = ref ( 'これはエラーになります' ) < / s cript> Oxlint(v1.82.0) デフォルトでも vue-plugin オプションを付けても、このサンプルに対しては何も警告を出しませんでした。 userNema のタイポも unusedMessage の誤検知も発生しない代わりに、テンプレート側の静的解析自体がまだ行われていない、というのが実態でした。 Biome(v2.5.12) デフォルト設定ではスクリプト側の noUnusedVariables ルールが .vue ファイルにも適用され、 userMessage・unusedMessage の両方を「未使用」としてエラーにします( unusedMessage は実際にはテンプレート側で使われているので、これは誤検知です)。 ただし html.experimentalFullSupportEnabled を有効にすると、Biomeはテンプレート内での参照を認識できるようになり、 unusedMessage の誤検知は解消されました(本当に未使用な userMessage は引き続き正しく検出されます)。 一方で、フラグを有効にしてもテンプレート内の userNema のようなタイポを検出するルールはまだ無く、不正な変数参照を見逃す問題は解消されません。 上記の検証からわかる通り、テンプレート内の静的解析はRust製Linter単体ではまだ完結できません。Biomeは実験的フラグ付きで一部の誤検知を解消できるようになってきていますが、テンプレート内のタイポ検出のような踏み込んだチェックまでは、依然として eslint-plugin-vue に頼る必要があります。 つまり、必要なVue固有のルールと各ツールの対応状況次第では、OxlintまたはBiomeとESLintを併用する必要があるということです。 3. チームでの議論:「設定ファイル2つは本当に必要か?」 Rust製Linter を導入するか、一旦は ESLint のままにするか。 どちらを選択するか検討するため、メリット・デメリットを出してみました。 メリット 圧倒的な高速化 : 従来のJavaScriptなどで書かれたLinter(ESLintなど)に比べ、数倍〜数十倍高速に動作します。大規模なコードベースでも ミリ秒単位 で処理が終わるため、CI/CDの実行時間を大幅に短縮し、開発者の待ち時間をほぼゼロにします。 開発の快適化 : 保存時の自動整形や pre-commit フックでの実行が劇的に速くなります。 簡単な導入 : Rust製ツールはコンパイル済みのシングルバイナリとして配布されることが多いため、環境構築やCIでのセットアップが非常にシンプルになります。 デメリット 二重管理 :(一番の懸念) Rust製フロントエンドLinter(BiomeやOxcなど)は、 .vue ファイルの構文(HTMLテンプレートやVue固有の構文ルール)を完全にサポートしていません。 そのため、Vue固有のルール( vue/valid-template-root など)をチェックするには、結局従来の ESLint(eslint-plugin-vue) を残さなければならなくなります TypeScript/JavaScript部分: 爆速のRust製Linter(Biomeなど)でチェック Vueファイル・テンプレート部分: 従来のESLintでチェック プラグインエコシステムの未熟さ : 歴史のあるESLintなどのように、コミュニティが作った無数のサードパーティ製プラグインを自由に追加することが難しい場合があります。独自のルールを追加したい場合のハードルが高めです。 ルールや構文の追従ラグ: 言語(特にJavaScript/TypeScriptやPython)の新機能や新しい構文が登場した際、Rust側でのパーサ(構文解析器)の対応にわずかなタイムラグが生じることがあります。 4. 結果 結果は、デメリット部分の二重管理の懸念部分が大きく、現状のESLint(eslint-plugin-vue)で管理する方を選びました。 手元の計測で速度については、申し分ないことが立証済みでした。 導入するメリットも大いに感じているものの、2つのLinterを併用する点を上回るほどではないと判断しました。 5. 今後の展望 Biome は既に html.experimentalFullSupportEnabled フラグで Vue SFC の実験的サポートを始めており、Oxlint も vue-plugin オプションなどVue向けの機能を増やしてきています。 「サポートされるのを待つ」というよりも、「実験的サポートがどこまで安定するか・テンプレート内のタイポ検出のような踏み込んだチェックまでカバーされるか」を継続的にウォッチしていく、というのが今の立ち位置です。 各プロジェクトの現状を、ご紹介いたします。 Biome v2.4(2026年2月)で Vue・Svelte・Astro 対応を実験的機能からプロダクション標準に近づける取り組みが発表されました。既存のリンティングルールを強化し、HTMLライクな言語でも機能するようにする方針です。 https://biomejs.dev/ja/blog/roadmap-2026/#2026-roadmap Oxlint 公式ドキュメント上の制限の記載に、Vue も含まれており、改善してくれるはずだと思います。そして、改善のための issue があり開発が進められているのがわかります。 https://github.com/oxc-project/oxc/issues/23207 https://oxc.rs/compatibility.html そしてなんといっても、Oxlint の開発を支援しているのが Vue の開発者である Evan You さんが立ち上げた VoidZero という会社なんです。 現在のフロントエンドの「ツールの断片化(Linter、Formatter、テストツール、ビルドツールがバラバラで設定が面倒かつ遅い)」という課題を根本から解決するためVite+(ヴィートプラス)が開発され、その中に Oxlint も含まれています。 Vue の開発者が、Vue を見放すはずがないんです!弊社が Oxlint にするのも近いですね! まとめ Rust 製のツールは非常に高速で、すぐにでも乗り換えたいと思ってしまいます。 ただ、メリット・デメリットや今後の展望を踏まえて、検討するのが大事だなと今回の移行作業を通じて感じました。 どのツールにするかもしっかり検討したいです。 AI で開発を行うと、簡単に実行できるため、検討するのをサボりがちになってしまいます。 そういう意味でも今回はいい勉強になったと思います。 お読みいただきありがとうございました。 次は、Oxlint に移行した話でお会いしましょう。
Introduction This article is based on the original Japanese version: https://tech.revcomm.co.jp/report-pyconjp-2026 . We attended PyCon JP 2026, held from August 21 to 22, 2026. 2026.pycon.jp Our backend engineer, Rei Suyama, gave a talk at the conference. This year, RevComm also participated as a Gold Sponsor of PyCon JP 2026. In this post, we'll look back on the event and share some of our highlights. Looking Back on the Talk A Deep Dive into ASGI Middleware: Mastering Simple yet Profound ASGI Middleware Overview: A solid understanding of ASGI is essential for developing full-fledged web applications in Python. In this session, we focused on ASGI middleware, which could be said to encompass nearly all of the mechanisms behind ASGI itself. By understanding how ASGI middleware works and exploring the many ways it can be used, we learned how to put it to effective use. Speaker: Rei Suyama Reflections on the Talk This wasn't my first time speaking at PyCon JP, but I was still so nervous right before the talk that my colleagues at the sponsor booth kept telling me, "You've been looking pretty restless all day!" 😄 Fortunately, I had practiced the presentation many times right up until the last minute, so everything went smoothly overall. After the talk, some attendees told me, "It was really interesting—I learned a lot of things I didn't know before." Hearing that made me glad once again that I had decided to give the talk. My talk was scheduled for the afternoon of the second day. If I get another chance to speak at PyCon JP, I'd love to have an earlier time slot—ideally before the party! 😄 Running the Sponsor Booth This year, we had a booth at PyCon JP as a sponsor. Thank you very much to everyone who stopped by! PyCon JP 2026 RevComm Booth At the RevComm booth, we displayed videos and panels designed to give visitors a visual overview of the features of MiiTel, our product developed and provided by RevComm. Just before PyCon JP 2026, on August 13, the book Practical Introduction to Python Web Development: Building Web APIs and Asynchronous Processing with FastAPI, written by our speaker Rei Suyama, was also published. We had copies available at our booth as well. If you're interested in developing web applications with FastAPI or Python, we hope you'll check it out! gihyo.jp Sessions That Left an Impression Python and language learning: How did Python help me get the JLPT N1 This talk by Jose covered his 10-year journey from coming to Japan as an English-speaking student to eventually passing the JLPT N1 while leading a Japanese-speaking engineering team. With Japanese language proficiency becoming increasingly important for engineers from overseas working in Japan, particularly in light of recent changes to visa requirements, I found the session especially timely and meaningful. What impressed me most was how Jose connected his own language-learning experience directly to his expertise as a software engineer. Rather than relying on conventional textbooks or repetitive memorization, he demonstrated how Python and the latest LLMs can be used to create learning tools tailored to his individual needs. He explained that he previously wrote web scrapers to collect learning materials from sources such as news websites. Today, however, LLMs make it possible to generate this kind of content on demand. I was also impressed by how he used open-source libraries such as genanki for creating flashcards and onsei for pronunciation analysis to build a personalized learning environment around his own weaknesses—all through programming. The talk changed the way I think about language learning. It showed me that programming skills can be used to "hack" the complex process of learning a new language. Beyond the specific techniques introduced in the talk, I came away thinking that Python could enable much more creative approaches to all kinds of language-learning goals. 2026.pycon.jp Retry Is Not a Strategy: Classifying and Recovering from AI Agent Failures in Python When developing LLM agents, we often have to deal with silent failures whose causes are difficult to identify, as well as unintended loops. Cyrus Mante's talk addressed exactly these kinds of challenges. Rather than simply retrying from the beginning and hoping that "it'll work this time," Cyrus introduced an open-source library called triage . The library allows developers to analyze an agent's execution steps and identify the specific cause of a failure. For example, it can help determine whether the agent called the wrong tool, got stuck in a loop, or encountered a problem with an external API. What I found particularly valuable was its approach to recovery. Based on the type of error that occurred, the system can automatically determine whether to roll back the agent's state, force it to re-plan, or escalate the issue to a human. I also strongly agreed with the idea of keeping this kind of routing logic explicit in Python code rather than delegating it to an increasingly bloated LLM prompt. It was a very practical approach to building robust AI systems, and I'd love to try incorporating some of these ideas into our own agents. 2026.pycon.jp Let's Have Fun Programming with Pyxel! This was the keynote on the second day, given by Takashi Kitao , the creator of Pyxel. github.com One point that particularly stood out to me was the idea of intentionally placing constraints on a library in order to pursue its core value. I felt that this is an important perspective not only when developing libraries, but also when building products. The talk also covered many other fascinating topics, including the vision for Pyxel v3 and its potential uses and significance in the age of AI. I had never used Pyxel before, but after listening to the talk, I'm definitely looking forward to giving it a try! Conclusion This was my first time attending PyCon JP, and the event was much larger and more energetic than I had imagined. There were more attendees and sessions than I expected, and I could really feel the enthusiasm throughout the event. Many of the sessions and keynotes covered fascinating topics, and overall, I found it to be a fantastic event. This was also RevComm's first time running a full-scale booth at a tech event, so we'd like to thank everyone who took the time to stop by and visit us! Announcement On Wednesday, September 9, from 7:00 PM to 8:30 PM JST, RevComm will be hosting an unofficial PyCon JP 2026 after-event together with Sansan, Inc. and Nealle Inc. The event will be held online, so please feel free to join us! sansan.connpass.com
はじめに 2026年8月21日から2026年8月22日にかけて開催された PyCon JP 2026 に参加しました。 2026.pycon.jp 弊社からはバックエンドエンジニアの陶山 嶺が登壇しました。また、今年は弊社 RevComm もゴールドスポンサーとして PyCon JP 2026 に協賛いたしました。 今回はイベントの振り返りを紹介します。 登壇振り返り 詳解ASGIミドルウェア シンプルながらも奥が深いASGIミドルウェアを使いこなす 概要: Pythonで本格的なWebアプリケーションを開発するためには、ASGIに対する理解は極めて重要です。本セッションではそのASGIのしくみの全てが詰まっていると言っても過言ではないASGIミドルウェアに焦点を当てて紹介します。アイディア次第で用途が広がるASGIミドルウェアのしくみを理解し、使いこなしていきましょう。 登壇者: 陶山 嶺 登壇の感想 PyCon JPでの登壇はこれまでにも何度かありますが、それでもやはり直前はスポンサーブースにいる同僚たちから「今日ずっとソワソワしていますね」と言われるくらい緊張していました笑 発表自体は直前まで何度も練習していたこともあり、全体的に上手く進めることができてよかったです。登壇後には「知らない話も聞けてすごく面白かったです」と言っていただけて発表して良かったなと改めて感じました。 今回の自分の発表時間帯は2日目午後だったので、次回があればパーティよりも前に発表できたら嬉しいです!笑 ブース運営について 今年はスポンサーとしてブースを出展させていただきました。 ブースまでお越しいただいた皆様、大変ありがとうございました。 PyCon JP 2026 RevComm ブース ブースには弊社で開発・提供している MiiTel の特徴を視覚的に理解いただけるよう、動画とパネルを展示しました。 また、ちょうど PyCon JP 2026 開催直前の2026年8月13日に、登壇者の陶山 嶺が執筆した「Python Web開発実践入門 ―― FastAPIによるWeb API開発と非同期処理」が発売されたため、ブースにも展示しました。FastAPI や Python による Web アプリケーション開発にご興味がありましたら、ぜひご覧ください! gihyo.jp 印象に残ったセッション Python and language learning: How did Python help me get the JLPT N1 英語を母語とする学生として来日し、やがて日本語のエンジニアリングチームを率いながら JLPT N1 に合格するまでの 10 年間の歩みについてのJose 氏のトークです。海外出身のエンジニアが日本で働くうえで日本語力の重要性が高まっている昨今のビザ要件もあり、このセッションは非常にタイムリーで意義深いものに感じました。 特に印象的だったのは、Jose 氏が自身の言語学習の経験を、ソフトウェアエンジニアとしての専門性と直接結びつけていた点です。一般的な教科書や単調な暗記に頼るのではなく、Python や最新の LLM を活用することで、自分に最適化された学習ツールを作れることを示してくれました。以前は学習素材を集めるために Web スクレイパーを書いて、ニュースサイトなどからデータを収集していたそうですが、現在では LLM を使うことで、そのようなコンテンツをその場で生成できるようになっています。 また、フラッシュカード作成のための genanki や、発音分析のための onsei といったオープンソースライブラリを活用し、自分の弱点に合わせたパーソナルな学習環境を、まさにプログラムによって構築していた点も印象的でした。この話を通じて、プログラミングのスキルを使って、新しい言語を学ぶという複雑なプロセスを「ハック」できるのだと視点が変わりました。紹介された具体的な方法にとどまらず、Python を活用すれば、さまざまな言語学習の目標に対して、もっと創造的なアプローチができるのではないかと感じました。  2026.pycon.jp Retry Is Not a Strategy: Classifying and Recovering from AI Agent Failures in Python LLM エージェントを開発していると、原因が分かりにくいサイレントな失敗や、意図しないループへの対処に悩まされることがあります。Cyrus Mante 氏のトークは、まさにそのような課題に正面から向き合う内容でした。単に最初からリトライして「今度はうまくいくはず」と期待するのではなく、Cyrus 氏は triage というオープンソースライブラリを紹介していました。 このライブラリを使うことで、開発者はエージェントの実行ステップを分析し、失敗の原因を具体的に特定できます。たとえば、誤ったツールを呼び出したのか、ループに陥っていたのか、外部 API で問題が発生したのか、といった切り分けが可能になります。 さらに優れていると感じたのは、復旧方法の扱いです。発生したエラーの種類に応じて、システムがエージェントの状態をロールバックするのか、再計画を強制するのか、あるいは人間にエスカレーションするのかを自動的に判断します。また、このようなルーティングロジックを肥大化した LLM プロンプトに任せるのではなく、Python のコードとして明確に保つという考え方にも強く共感しました。堅牢な AI システムを構築するうえで非常に実践的なアプローチであり、自分たちのエージェントにもぜひ取り入れて試してみたいと感じました。  2026.pycon.jp Pyxelで、プログラミングを遊ぼう! Pyxel の作者である北尾崇氏 ( https://github.com/kitao ) による 2 日目の基調講演です。 github.com 本質的な価値を追求するためにライブラリに意図的に制約を加えている、という点はライブラリの開発だけでなくプロダクト開発においても重要な視点なのではないかと感じました。その他にも、Pyxel v3 の構想や AI 時代における活用や意義など、興味深い内容がたくさんありました。 Pyxel は今まで使ったことがなかったのですが、今回の発表を聞いてぜひ遊んでみたいと思いました。 おわりに 筆者は PyCon JP に参加したのは今回が初めてのことでしたが、想像以上に参加者やセッション数が多く、非常に熱気を感じるイベントでした。セッションや基調講演にも興味深いテーマの発表が多く、素晴らしいイベントであったと感じています。 RevComm において Tech イベントでの本格的なブース運営は初めてという状況ではありましたが、ブースまで足を運んでいただいてありがとうございました。 お知らせ 2026年9月9日 (水) 19:00〜20:30 にSanSan株式会社様・株式会社ニーリー様・弊社の共同で PyCon JP 2026 の非公式アフターイベントを開催いたします!オンラインで開催いたしますため、ぜひお気軽にご参加ください! sansan.connpass.com
はじめに 2026年8月21日から2026年8月22日にかけて開催される PyCon JP 2026 に RevComm のエンジニア陶山 嶺が登壇します。 イベント概要 2026.pycon.jp 日程: 2026年8月21日 (金) 〜 2026年8月22日 (土) 会場: 広島国際会議場 主催: 一般社団法人 PyCon JP Association チケット申し込みはこちらから pyconjp.connpass.com 登壇情報 詳解ASGIミドルウェア シンプルながらも奥が深いASGIミドルウェアを使いこなす Pythonで本格的なWebアプリケーションを開発するためには、ASGIに対する理解は極めて重要です。 本セッションではそのASGIのしくみの全てが詰まっていると言っても過言ではないASGIミドルウェアに焦点を当てて紹介します。 アイディア次第で用途が広がるASGIミドルウェアのしくみを理解し、使いこなしていきましょう。 日時: 2026年8月22日(土) 14:30 - 15:00 登壇者: Rei Suyama リンク: https://2026.pycon.jp/ja/talks/7PAZYQ 協賛の背景 RevCommでは、音声解析AI「MiiTel」シリーズの開発のなかで、WebアプリケーションのバックエンドやAPI開発から、音声認識・自然言語処理の研究開発、LLMを活用した機能開発まで、さまざまな場面でPythonを使っています。日々の開発も研究も、Pythonコミュニティが積み上げてきたライブラリや知見に支えられており、PyCon JPは、2021年以降毎年RevCommのエンジニアが登壇してきた、私たちにとってとても縁の深いカンファレンスです。 今回、初めてスポンサーとして協賛するにあたって、大切にしたいことが2つあります。 1. コミュニティから受け取ってきたものを、発信で返していくこと 音声×AIというドメインには、RevCommならではの技術的なチャレンジや悩みどころがたくさんあります。そうした経験をブログや登壇を通じて共有し、コミュニティに少しでも貢献できたらうれしいです。今回の陶山の登壇も、その取り組みのひとつです。 2. エンジニアの登壇を、会社としても後押ししていくこと これまでは主に社内からPyCon JPに登壇、参加するエンジニアたちを支援してきましたが、スポンサーとしての協賛は今年が初めてです。エンジニアが登壇や発信に挑戦しやすい環境を整えながら、会社としても継続して関わっていけたらと思っています。 ブースでは、MiiTelを支える技術や開発の裏側についてエンジニアがお話しします! 気軽な質問も大歓迎ですので、ぜひお立ち寄りください。 おわりに ぜひ発表やブースにお越しいただけると幸いです! 会場でお会いできることを楽しみにしています!
はじめに こんにちは。RevComm でエンジニアをしている 林 です。 AI コーディングエージェントの普及でコードを書くコストは大きく下がり、ボトルネックはレビューと定型的な保守作業に移りました。 Addy Osmani 氏も「 Agentic Code Review 」で、レビューが PR の作成スピードに追いつかず、コードが読まれないままマージされ始めている現状を書いています。 MiiTel Phone(ブラウザ上で通話できる AI 搭載 IP 電話アプリ)の開発リポジトリでは月に 300〜600 件の PR がマージされます。その中には、振る舞いを変えないリファクタリング、依存パッケージの更新、ドキュメントの修正のような、判断の型が決まっているのにレビューの待ち時間だけは通常の PR と変わらないものが相当数含まれます。 こうした定型的な判断を GitHub Actions 上の AI に任せ、人間のレビューを本質的な変更に集中させる取り組みから、4 つの事例を紹介します。 いずれも Claude Code を GitHub Actions 上で動かす構成で、事例 1〜3 は公式の claude-code-action 、事例 4 は GitHub Agentic Workflows (gh-aw) を利用しています。 ※ 記事中のコードや設定は説明のために抜粋・簡略化しています。 設計原則を先に 4 つの事例は使うツールも対象も違いますが、設計は同じ型に収まっています。作業の大半はプロンプトを書くことではなく、「AI に何をさせないか」を決めることでした。 先に原則を挙げます。以降の事例は、これがどう具体化されているかとして読んでもらえればと思います。 判定は AI、実行は決定的なステップとして 。AI の出力は構造化された判定 or 成果物にとどめ、承認・マージ・クローズ・チケット起票といった副作用は、スキーマ検証を通った出力に対して後続のステップが実行します 自動化の終点を、人間の判断が残る場所に置く 。AI が進めるのは承認、コミットの push、draft PR の起票までで、マージするかどうかは人間が判断します いつでも止められるようにする 。設定をひとつ変えるだけで自動承認を止められるキルスイッチ、作成者がラベルを付けた PR だけを対象にするオプトイン方式、AI の実行回数・実行時間・同時実行数の上限。まず判定だけを動かして精度を確認し、納得してから副作用を有効化します AI への入力を信頼しない 。PR 本文やリリースノートはサードパーティ由来のテキストとして扱い、渡さないか信頼境界の外に置きます 運用で精度を上げる前提を組み込む 。週次のスポットチェック、誤判定を反例としてプロンプトに追記していく運用、リポジトリ固有の観点を自然言語で注入できる入り口、そして「処理しなかったもの」のログを確認可能にしておきます 事例 1: PR のレビュー要否を AI が判定する AI に任せるのはレビューではなく分類 きっかけはチームの振り返りで出た「テストコードやドキュメントだけの変更なら、レビューなしでマージしてよいのでは」という話でした。 前提として、PR を open すると GitHub Copilot や Devin が自動でレビューする設定はすでに入っています。ただし、これらは人間のレビューに観点を足す補助であって、承認までの待ち時間はそこまで変わりません。 今回のボトルネックはまさにこの待ち時間なので、AI レビューを強化して人間のレビューを置き換える方向は取りませんでした。 方針の参考にしたのは Findy Tech Blog の「 不要なレビューをAIにまかせてAIコーディングの環境改善を加速した 」です。要点は、AI を「レビュアー」ではなく「人間のレビューが不要な PR かどうかの分類器」として使うこと、そして Kent Beck 氏の『 Tidy First? 』でいう振る舞いを変えない整頓(tidying)だけを自動承認の対象にすることです。 レビューの代行が品質保証の責任ごと AI に移してしまうのに対し、分類は「人間がレビューすべき PR を人間に確実に届ける」ための振り分けです。 3 レイヤーのレビューポリシー 「AI で自動化」と言いつつ、最初のレイヤーに AI は登場しません。 レイヤー 内容 仕組み レイヤー 0 ドキュメントのみの変更の自動承認 ファイルパスによる機械的な判定(AI 不要) レイヤー 1 人間レビュー必須領域の定義 ポリシードキュメント レイヤー 2 振る舞いを変えない整頓の自動承認 AI 分類器 + 抜き取りの事後レビューと監査 レイヤー 0 は、変更ファイルがすべて docs/ 配下または *.md の PR を機械的に承認します。AI で判定できることでも、決定的に判定できるならそちらを選びます。誤判定リスクがなくコストもゼロだからです。 ただし、同じ Markdown でも .github/ 配下と CLAUDE.md / AGENTS.md は対象外です。これらの無人承認を許すと、自動承認システム自体の改変が無人で通ってしまいます。 レイヤー 1 は、AI の判定にかかわらず必ず人間がレビューする領域の明文化です。通話のような中核機能、feature flag、外部システムとの連携、個人情報や認証、テストの削除、依存パッケージ、CI 設定などが該当します。間違えると影響が大きい領域と、自動化自体の安全性を支える領域(テスト・CI)を並べています。 分類器の実装 レイヤー 2 が本題の AI 分類器です。PR の作成者が ai-classify ラベルを付けたときだけ動きます。分類器は『Tidy First?』の構造的リファクタリングを調整した分類表を持っています。 T1: ファイル・ディレクトリの移動、リネーム(import パスの機械的追随を含む) T5: 変数・関数・コンポーネントのリネーム T7: 未使用コード・ファイルの削除 A1: 既存 feature flag の targeting(どの環境・ユーザーに配布するかの設定)のみの変更(振る舞いは変わるが、チームが明示的にレビュー不要と合意した低リスク変更) NG2: 通話系コードに触れる変更(即 HUMAN_REVIEW) ポイントは、除外条件(NG)を「変更の性質」ではなく「触れた領域」で判定させていることです。通話系ファイルの変更が import パスの機械的な追随だけだったとしても NG2 に倒します。「機械的な変更だから実質は該当しない」という楽観的な判断を AI にさせないためです。 また、迷ったら UNKNOWN と出力させ、 誤って APPROVE するコストは誤って HUMAN_REVIEW とするコストよりはるかに大きい という原則もプロンプトに明記しています。 出力は自然言語のコメントではなく、構造化された判定ファイルです。 { " verdict ": " APPROVE ", " t_categories ": [ " T1 ", " T5 " ] , " reasons ": [ " 判定理由を簡潔に列挙する " ] , " summary ": " 1〜2 文の判定サマリ ", " sampling_used ": false } 後続のステップが jq でこのファイルのスキーマを検証し、検証を通った APPROVE 判定に対してのみ承認を実行します。 - name : Approve if : steps.verdict.outputs.verdict == 'APPROVE' && vars.AI_CLASSIFY_APPROVE_ENABLED == 'true' run : | gh pr review "$PR_NUMBER" --approve --body "$body" gh pr edit "$PR_NUMBER" --add-label ai-approved AI_CLASSIFY_APPROVE_ENABLED は GitHub の repository variable を使ったキルスイッチで、 true でないときは判定コメントの投稿だけを行います。導入初期は切ったまま判定精度を確認し、問題ないことを確認してから有効化しました。 分類器に渡すのは差分とファイル内容だけで、PR 本文とコメントは渡しません。ただし、diff 中のコードコメントには同じ誘導を書けるため、この経路まで消えるわけではありません。 そこは後段のスキーマ検証と、マージ判断が人間に残っていることでブロックします。承認後に push があれば自身の承認を取り消して再分類し、自動マージはどのレイヤーでも行いません。 運用と、PR の作り方への波及 自動承認された PR は週次で数件を人間が事後レビューしています。Addy Osmani 氏の整理を借りれば、すべての差分を人間が見る形から、抜き取り確認と監査で仕組み全体を見張る形への移行です(彼の記事の言葉では human in the loop から human on the loop へ )。誤判定が見つかれば、反例として分類器プロンプトの除外条件に追記します。 効果はレビューが速くなることにとどまりませんでした。分類器が APPROVE を出せるのは PR 全体が整頓で完結しているときだけなので、リファクタリングと機能変更が混ざった PR は HUMAN_REVIEW に倒れます。 つまり、作成者から見ると、混ぜれば全体がレビュー待ちになり、分ければリファクタリング部分を先に流せる。リネームやファイル移動を先行 PR に切り出して ai-classify を付け、その上に振る舞いを変える PR を積み重ねれば(stacked PR として)、人間に届くのは本質的な変更だけの小さな差分になります。 『Tidy First?』の「整頓と振る舞いの変更を分けよ」という規律は、分けること自体のコスト(PR が増え、レビュー待ちも複数回になる)のせいで努力目標になりがちでした。分類器が整頓側の待ち時間をゼロにしたことで、規律を守るほうが速いという構造に変わっています。 自動化が規律を強制するのではなく、規律に従うことが合理的な選択になるというのは、レビュー待ち時間の削減と並ぶ、もうひとつの効果だと思っています。 事例 2: Dependabot PR を毎朝トリアージする 依存パッケージの更新 PR への対応は、毎回ほぼ同じ判断の繰り返しでした。CI の成否を確認し、リリースノートを読んで破壊的変更の有無を確認し、低リスクなら承認してマージ、高リスクなら Notion にチケットを起票して PR はクローズし、動作確認を含めた計画的な対応に回す。判断の型は決まっているのに手作業では追いつかず、PR は溜まる一方でした。 最初の自動化は、この手順を Claude Code の Skill に書き起こすことでした。手元で「Dependabot の PR を対応して」と頼めば、オープン中の PR を順に解析して一括処理してくれます。 ただし、人が起動する半自動なので、誰かが思い出さない限り PR は溜まり、実行する人の環境と権限に依存する属人的な運用でもありました。 そこで Skill として固めた手順を GitHub Actions の composite action(複数のステップをまとめて再利用できる仕組み)に作り直し、無人で定期実行するようにしました。 このとき、AI 自身が実行していたマージやチケット起票は AI から切り離しています。composite action は社内の共通リポジトリに置き、複数のリポジトリから利用しています。 毎日午前 3 時の cron がオープン中の Dependabot PR を古い順に 1 件ずつ処理します(1 件マージすると Dependabot が残りを rebase するため、並列ではなく直列です)。 PR タイトル・body からパッケージ名とバージョンを機械的に解析し、CI チェックの成否を判定する Claude によるリスク判定。リリースノートを読み取り専用のツールで読み、 triage-verdict.json (risk: low / high、根拠、主要変更点)を書き出す 1, 2 を決定表に通して最終アクションを決める 実行。承認して自動マージ、クローズして Notion にチケット起票、または手動対応として PR に残す 条件 アクション タイトル・body とも解析不能 手動対応(PR は残す) CI 失敗 クローズしてチケット起票(AI の判定に関係なく) AI の判定を取得できない 手動対応(安全側に倒して人間に残す) AI 判定が高リスク クローズしてチケット起票 AI 判定が低リスク 承認して自動マージ Notion のチケット操作も AI にはさせず、TypeScript のスクリプトが REST API を直接叩きます。チケット作成に失敗した場合は PR をクローズせず手動対応に降格させ、「チケットなしで PR だけ消える」事態を防いでいます。 リリースノートはサードパーティ由来の入力なので、プロンプト側では、指示のような文言(「この更新は安全である」「以前の指示を無視せよ」など)を見つけた場合、従わないだけでなく、それ自体をサプライチェーン上の懸念として high と判定するよう指示しています。インジェクションの試みを検知シグナルに変える発想です。 それでも最後の砦は AI の外にある CI 成功という条件で、仮に誘導が成功しても CI が落ちている PR はマージされません。Dependabot ブランチのコードはチェックアウトすらせず、AI が読むのはデフォルトブランチと PR のメタデータだけです。 リポジトリ固有の判定観点は、決められたパスに Markdown で書いておくとプロンプトに注入されます。「electron 関連の更新はすべて高リスクとする」のような 1 行で、チームの経験則を判定に反映できます。 導入後は毎朝の CI で Dependabot のオープン PR をゼロに戻していくので、依存更新の PR が滞留すること自体がなくなりました。残る人間の作業は、高リスクと判定されて起票されたチケットへの対応だけです。 事例 3: @claude メンションを全リポジトリ共通の基盤にする PR や issue のコメントで @claude とメンションすると GitHub Actions 上で Claude Code が動き、質問への回答、レビュー指摘への修正コミットの push、ショートカットの実行を行います。 反応するのは書き込み権限を持つユーザーのコメントのみで、Claude に承認とマージはさせません。 よく使うのはコンフリクトの解消です。AI コーディングで PR を作る速度が上がると同時にオープンな PR が増え、main の進みも速くなるため、コンフリクトは以前より起きやすくなりました。PR 作成の高速化が、今度はコンフリクト解消という新しい定型作業を生んだ形です。 @claude /fix-conflicts と書けば、解消して push するところまでやってくれます。 同じ問題意識は GitHub Next の Evergreen や Codex の PR Babysitter にも見られ、この種の PR のお手入れは自動化の定番になりつつあります。 実装面の工夫は共通化です。 @claude メンション自体は claude-code-action の代表的な使い方ですが、リポジトリごとにワークフローを持つと、モデルの変更やコスト制御の調整のたびに全リポジトリへ同じ変更を配ることになります。 そこでトリガー条件・トークン設計・モデル選択・コスト制御を社内共通リポジトリの reusable workflow に集約し、利用側には呼び出しの数行だけを置く構成にしました。 # 利用リポジトリ側はこれを置くだけ jobs : claude : uses : my-org/claude-workflows/.github/workflows/claude.yml@main secrets : inherit ショートカットの中身は Claude Code の Skill として、さらに別の共有リポジトリ(plugin marketplace)に置いています。手元の Claude Code と GitHub Actions で同じ Skill 資産を共有するためで、ローカルで育てたワークフローがそのまま @claude メンションでも使えます。 事例 4: 定期メンテナンスを自然言語で書く (GitHub Agentic Workflows) gh-aw は、ワークフローを「YAML frontmatter + 自然言語プロンプト」の Markdown ファイルとして書き、 gh aw compile で実行可能なワークフロー( .lock.yml )にコンパイルする gh CLI の拡張機能です。 --- on : schedule : weekly on sunday around 20:00 # fuzzy schedule workflow_dispatch : permissions : contents : read engine : id : claude max-turns : 50 timeout-minutes : 30 safe-outputs : create-pull-request : title-prefix : '[main] ' draft : true --- # Docs Drift Audit You are auditing this repository's developer documentation for drift against the actual code, and fixing what has drifted. ... 事例 1〜3 が AI の判定や応答を自前のワークフローに組み込む構成だったのに対し、gh-aw はスケジュールで自律的に動き、リポジトリを調べて成果物(PR など)まで作るエージェントです。 その分、安全装置も枠組み側に強く組み込まれていて、エージェント本体は読み取り専用のトークンで実行され、書き込みは frontmatter の safe-outputs で宣言したものしかできません。 外部通信もファイアウォールコンテナで制限され、 weekly on sunday around 20:00 のようなあいまいな時刻指定はコンパイラが具体的な時刻に解決して、多数のリポジトリで cron が同時刻に集中するのを避けます。現在 2 本が週次で動いています。 ドキュメントと実コードの乖離監査 (docs-drift-audit) 1 本目は、開発ドキュメント( docs/ 配下、 AGENTS.md 、 CLAUDE.md など)と実コードの乖離を監査するワークフローです。参照先のパスが存在するか、記載コマンドが package.json の scripts と一致するか、feature flag 名やワークフロー名が実在するか、規約の記述が設定ファイルと矛盾していないか。確認できた乖離を修正して 1 本の draft PR にまとめ、乖離がなければ何もせず終了します。 これをわざわざ AI にやらせるのは、ドキュメントが人間向けであると同時に AI 自動化の入力でもあるからです。事例 1 の分類器プロンプトも、事例 3 の @claude が参照する開発ガイドも、鮮度が落ちればそのまま自動化全体の精度劣化につながります。 一方でコード側の変化は速く、手動での追従は現実的に漏れます。週次の監査で、月曜の始業前に修正 PR が用意されている状態を作りました。 プロンプトには「何を直さないか」も同じ分量で書いています。意図的に将来の姿を書いているドキュメントは直さない。リポジトリ外への参照は検証できないので触らない。文体の好みは直さない。そして確信の持てない修正は PR から外すのではなく、PR 本文の「要判断」セクションで曖昧さとレビュー時の確認点を説明させます。 期限切れ feature flag の掃除 (feature-flag-cleanup) MiiTel Phone では、新機能や通話まわりのような壊れたときの影響が大きい変更を feature flag で囲い、OFF のまま main にマージしたうえで、QA や社内テナントでの先行利用を経てから全環境で有効化することがあります。問題が起きたときも、コードを巻き戻す代わりにフラグを OFF に戻すだけで引き返せます。 flag は使い捨てで、安定したら flag と不要になった旧コードを削除する決まりで、放置すると誰も触れない謎のフラグが負債として残るため、各フラグに削除期限も設定しています。 期限切れフラグを Slack に通知して CI を fail させるワークフローはあったのですが、通知されても掃除の PR を作る作業が残るため対応が滞りがちで、一時は 15 件が同日に失効を迎える状況になっていました。 そこで通知の一歩先、削除 PR の作成までを自動化しました。対象は「配布設定が全環境で有効化済みで、かつ期限切れ」のフラグだけです。条件付き配布中のフラグの削除はリリース判断そのものなので、AI には触らせず人間に残します。 フラグの削除は定義を 1 行消して終わりではありません。ガードで分岐していたコードの無条件化に加え、それによって死んだコードの連鎖的な削除までがセットです。この作業パターンをそのままプロンプトに落とし込み、最後に grep でフラグ名の残存ゼロを検証させています。 処理しなかった期限切れフラグも、理由つきで実行ログに残させています。自動化が「何をやらなかったか」を隠すと、カバーされているように見えて漏れる領域ができるためです。 claude-code-action と gh-aw の使い分け どちらも GitHub Actions 上で Claude を動かす仕組みで、どちらでも実装できる仕事は多くあります。ただし対等な選択肢ではありません。 gh-aw は安全装置が組み込まれた専用ツールです。Markdown を書くだけで動く代わりに枠が決まっていて、副作用は safe-outputs で宣言できるものに限られます。外部サービスへの起票のような語彙にない副作用や、AI の出力を自分のコードで検証・分岐してから実行する構成は組めず、イベントに反応する対話的な用途も不得意です。 claude-code-action は自前のワークフローに AI ステップを埋め込む部品なので、トリガーも後段の処理も副作用も自由に設計できる代わりに、権限の最小化・出力の検証・キルスイッチはすべて自分で用意することになります。 タスクが gh-aw の枠に収まるなら gh-aw のほうが少ない記述で安全に済み、はみ出すなら claude-code-action で自分で設計する、という使い分けです。 さいごに 4 つの事例は別々のツールに見えますが、やっていることは 1 つです。判断の型が決まっている作業を AI に寄せ、人間のレビューを本質的な変更に再配分する。そしてそれを安全に成立させているのは、モデルの賢さよりも、判定と実行の分離・人間の最終判断・キルスイッチといった CI 側の設計でした。 この記事が、CI と AI の連携を設計する際の参考になれば幸いです。 参考文献 Agentic Code Review - Addy Osmani 不要なレビューをAIにまかせてAIコーディングの環境改善を加速した - Findy Tech Blog Kent Beck『Tidy First?: A Personal Exercise in Empirical Software Design』 anthropics/claude-code-action GitHub Agentic Workflows (gh-aw) Evergreen - GitHub Next
はじめに 昨今、仕様駆動開発 (Spec-Driven Development) の名前を耳にする機会が増えてきたと感じています。MiiTel Phone チームにおいても数ヶ月ほど OpenSpec というツールを使用して仕様駆動開発を試みていました。結論として、OpenSpec の採用は取りやめることにしました。今回は OpenSpec をベースに仕様駆動開発を試してみて感じた課題や所感について共有いたします。 前提 フロントエンド開発における採用です 新規開発ではなく、既存プロダクトの開発における導入です ⚠️ 注意書き 本記事は仕様駆動開発の手法や OpenSpec などのツールそのものを非難する意図はありません。 あくまで「MiiTel Phone チームでは合わなかった」というだけであり、 仕様駆動開発が有益であるかどうかは、チームの文化や開発フローなどによっても異なってくると思います。 そのため、最終的には実際にツールや手法などを試してから判断いただくことを推奨いたします。 📚 仕様駆動開発 (Spec-Driven Development) について 概ね「まず Spec を書き、それをベースに AI エージェントにコードを書いてもらう手法」のことを指すことが多いのではないかと思います。 Spec は AI エージェントと人の双方に対して信頼できる情報源として振る舞うことが想定されます。 martinfowler.com OpenSpec について OpenSpec は Node.js 製の仕様駆動開発のためのフレームワークです。 github.com OpenSpec を採用したのは以下の理由からです。 まずは既存のツールを使用して進めた方が自分達で仕組みを整備するよりも手間が少ないと考えたこと Node.js 製であるため、フロントエンド開発においては普段使用しているパッケージマネージャーを使用して導入・管理ができること シンプルであり、導入にあたっての心理的障壁が比較的低いこと 様々な AI エージェント (Claude Code, Gemini, Codex など) をサポートしていること 使い方 # インストール $ mise use npm:@fission-ai/openspec@latest $ openspec init # Claude Code での利用例 $ claude > /opsx:new 仕様駆動開発を試みた背景 仕様駆動開発を試みたのは、下記が目的でした。 AI コーディングエージェントは実装や仕様の背景などに関する知識を保持していないことに加え、セッションごとに記憶がリセットされてしまう。必要なコードが削除・改変されてしまうなど、意図せぬコード生成が行なわれてしまうケースをできる限り防止したい。 新規メンバーでもプロジェクトのキャッチアップを行いやすくするために、暗黙知をきちんとドキュメント化する仕組みがあると便利ではないかと考えていました。 OpenSpec により仕様駆動開発を実践し、Git リポジトリ内に要求や過去の設計における意思決定の背景などが自然と蓄積されることにより、これらの課題が自然に解消されることを目論んでいました。そして、仕様駆動開発を支援してくれる特定のツールを採用することによって実践のハードルを低下させつつ、チーム内で方法を統一できるようにすることを期待して、まずは導入コストが低いと思われる OpenSpec を導入することにしました。 OpenSpec ってどうなの? まず OpenSpec においては openspec/specs/<spec>/spec.md に信頼できる情報源としての仕様が蓄積されます。具体的には以下のようなフォーマットの Markdown によって仕様が記述されます。 # Example Specification ## Purpose This app supports a simple, reliable TODO management experience for individuals. ## Requirements ### Requirement: Creating and managing TODO items The TODO app MUST allow a user to create, view, and complete TODO items. #### Scenario: Creating a new TODO - **GIVEN** a user is signed in to the TODO app - **WHEN** the user enters a title and submits the form - **THEN** a new TODO item is created with status "Incomplete" - **AND** the new TODO item appears in the TODO list #### Scenario: Completing a TODO - **GIVEN** a TODO item exists with status "Incomplete" - **WHEN** the user marks the TODO item as completed - **THEN** the TODO item's status becomes "Completed" - **AND** the TODO item is visually distinguished as completed in the list 実装の開始時に /opsx:new コマンドを実行すると、OpenSpec は対話的にやり取りしたを内容ベースに openspec/changes/<change> ディレクトリへ以下のようなファイルを作成してくれます。 design.md - 実装予定の機能に関する設計をまとめたドキュメント proposal.md - 実装予定の機能に関するハイレベルな要約をまとめたドキュメント tasks.md - 実装時に行うべきタスクをチェックリスト形式でまとめたドキュメント <spec>/spec.md - 本実装を進めることによって openspec/specs/<spec>/spec.md に対して生じる仕様への差分をまとめたドキュメント 開発が完了したら、 /opsx:archive コマンドを実行することで、 openspec/changes/<change>/<spec>/spec.md が openspec/specs/<spec>/spec.md へマージされた後、 openspec/changes/<change> の各ドキュメントが openspec/changes/archive/<change> へ移動されます。 これにより、 openspec/specs/<spec>/spec.md が常に最新化されつつ、過去の実装における意思決定の背景や仕様に関する変化の過程などが openspec/changes/archive/<change> へ蓄積されてゆきます。 まず実装に取り掛かる前に design.md を作成してくれる点は良い点に感じました。事前にチーム内で design.md をレビューすることで、実装に取り掛かる前に考慮漏れがないか検討することができます。 また、OpenSpec は tasks.md のチェックリストに順次チェックをつけながら開発を進めてくれるため、途中まで開発を進めてもらった状態で一度セッションを中断し、後から新しくセッションを作り直して開発を再開してもらうような進め方などもやりやすかったです。 ただし、後述する課題により、少なくとも MiiTel Phone チームの開発においては「わざわざ OpenSpec などのツールを使ってもあまり期待した効果は得られなさそう」というのが率直な結論でした。 OpenSpec がマッチしないと感じた理由について なぜいまいちマッチしないのか考えたところ、4 つほど要因があるのではないかと思いました。 フロントエンド開発との相性がそこまでよろしくない 手間に見合うほどの効果を実感できなかった 他の手法によって代替可能であること 仕組みとしてはやや貧弱であること 1. フロントエンド開発との相性がそこまでよろしくない フロントエンド開発における関心ごとの大半はプレゼンテーションレイヤーに偏りがちです。ドメインレイヤーの関心ごとなどと比較して、自然言語によって「何が正しいか」を形式的に定義・記述することが難しいケースが多いと感じました。 また、プレゼンテーションレイヤーはドメインレイヤーにおける制約や振る舞いなどと比べて変化しやすく、頻繁に Spec の見直しが必要となってしまうケースが多いです。結果として手間がかかると感じてしまうケースがありました。 逆にドメインに関する関心ごとの比率が高いバックエンド開発においての方が仕様駆動開発との相性は良いのではないかと感じました。 2. 手間に見合うほどの効果を実感できなかった ※ そもそも MiiTel Phone チームにおいては、OpenSpec の導入以前から、要件定義の段階で要求や制約などをあらかじめ Notion ドキュメントにまとめてから開発に取り掛かるケースが多かったです。 OpenSpec における openspec/specs/<spec>/spec.md はあらかじめフォーマットが規定されているため、一貫したフォーマットを維持できるなどのメリットもあります。しかし、使い慣れている Notion ドキュメントと比較するとどうしても表現力に制限があると感じるケースが多々ありました。また、全体的にコーディングエージェントに生成してもらった文章はやや冗長になりがちで、人が仕様や要求を理解するためのドキュメントとしてはあまり効果を実感しづらかったという事情もあります。 OpenSpec がドキュメントを生成してくれることにより事前にレビューができるなどのメリットはあるものの、逆に更なるレビューや編集の必要性が発生することにより、却って手間がかかってしまうケースがあるとも感じました。 ※ ただし、この課題は OpenSpec の問題というよりは、MiiTel Phone チームにおける開発プロセスとの相性の問題であると思います。 3. 他の手法で代替可能であること OpenSpec をベースに仕様駆動開発を進めることで、以下のようなメリットが得られます。 過去の設計や実装における意思決定の履歴をリポジトリに記録として残すことができる。 開発に取り掛かる前に OpenSpec が生成した design.md をチーム内でレビューすることで、考慮漏れを探すことができる。 ただし、過去の意思決定の履歴をリポジトリへ記録することは、Git のコミットメッセージをきちんと記述することによって代替可能です。そしてコミットメッセージをきちんと記述することによるコストはコーディングエージェントによって大幅に緩和されています。 また、過去の意思決定の記録は、リポジトリ内に残さずとも、ADR を Notion データベースなどで管理しておき、Notion MCP などによって検索できるように整備しておくことによって代替可能です。 そして、過去の設計や実装における意思決定の履歴がリポジトリ内にファイルとして蓄積されていく ( openspec/changes/archive ) ことにより、コーディングエージェントが意図せぬタイミングで古いドキュメントを参照してしまい、却ってコード生成の精度が低下してしまうというデメリットが発生する懸念もあることに気づきました。 4. 仕組みとしてはやや貧弱であること ドキュメントはとても重要なものであるものの、仕様駆動開発における Spec はあくまで静的なドキュメントでしかありません。 そのため、AI エージェントが生成したコードが正しく仕様に則っている状況を維持し続けるためには、結局、信頼性の高いテストコードやコードレビューなどが必要です。 コストを掛けて投資をしていくのであれば、テストコードの信頼施の改善や拡充などにより力を入れ、テストコードを仕様として扱ってゆく方針で進めた方が効果が高いのではないか、という結論に至りました。 tech.revcomm.co.jp ただし、過去の意思決定の記録を残すために ADR を記述することは依然として価値があるとは感じており、このようなプラクティスは今後も実践し続ける想定です。 今後について 最終的に OpenSpec はいまいちチームにおける開発の進め方とマッチせず、メリットを活かし切ることもうまくできなかったこともあり、廃止をすることに決定しました。逆に OpenSpec が想定するやり方にチームの開発プロセスを合わせるという方法もありそうですが、そこまでして導入するメリットも薄い、との判断に至りました。 Notion で ADR や要求などをまとめたドキュメントを用意し、必要に応じて Notion MCP を活用して必要なドキュメントを参照できるようにすれば十分ではないかというのが結論です。 OpenSpec を試していく中で良かったと感じたこととしては、まず、要件や要求などをまとめたドキュメントを用意し、それに基づいて開発を進めるプロセスそのものはそこまで悪くはないと感じました。技術的な詳細はコーディングエージェントに委ねつつ、人はよりハイレベルなポイントに対して注力しやすくなります。 今後は ADR のより積極的な活用や、Notion との連携強化、テストコードの拡充や信頼性の改善などを進めていくことで、より効果的にコーディングエージェントを活用していきたいと考えています。
Introduction Hello, Jose here. This time, I joined Anthropic on the second day of its two-day event focused on developers and founders using Claude Code. The conference was organized into a fixed schedule following three tracks: Founder stage (founders using Claude to solve industry problems) Builders stage (people with little or no programming background shipping software using Claude) Workshops (Hands-on sessions about the Anthropic ecosystem). There are other excellent reports of the event ( [1] [2] [3] ), so this time I’ll write a collection of lessons from the talks I joined. Most AI projects by large corporations end in failure According to a 2025 MIT study, 95% of AI projects by major corporations yield zero return. RAND reports an 80% failure rate, while Gartner reports that more than 60% of AI projects were abandoned by 2026. The presenters at Tsukumo Labs’ talk “The last mile is the spec” argue that part of the problem lies in the lack of specifications. And as a tool to solve that problem, they are developing Tsukumo Log, an app that can use computer logs to reproduce and automate tasks (sending emails, writing and running scripts, etc). Some of their findings during the implementation were: With modern AI (LLMs), you can usually use computer operation logs to reproduce and automate operations, but high quality test data is critical: what are you feeding Claude and how can you interpret the results it gives? In agentic development, we should focus on the inverse process: define the expected answers given by Claude and backtrack them with a myriad of hypothesis.. Should Claude manage everything? In the old days of software engineering (five years ago), the business side waited for code to be written (Can engineering get to this by Friday?). Yet now, code can be written at neck-breaking speed. The problem has shifted to decision-making: what do we ship and who should be responsible for that shipment? Myrealtrip’s CTO, Wonjin Hur, experienced this first-hand when her team shifted to LLMs.They were working on a voice agent to handle customer calls asking for airline refund policies. Originally, they left Claude handle the whole workflow: parsing the data, computing the refund, and answering customers in natural language. Yet, this opened their system to a new class of hard-to-debug bugs. So they changed their focus. They separated the deterministic Python code for parsing and calculating the refund amount and let Claude handle the calls using that information. The results are revealing: they reduced expensive (and unnecessary) reasoning, with the added benefit of predictability for their business logic. Sometimes Claude isn’t wrong; the client is. In their book Pragmatic Programmer, Andrew Hunt and David Thomas argue we should actively use assertions to check for the impossible. This is exactly what happened to Gahee Seo at Federation. She described an unexpected challenge of building an HS Code classifier: customers could write in terms that only they understand! Misclassifications occurred because the input was ambiguous. The lesson? Build an input quality gate or put a human in the loop for ambiguous input. Final thoughts The event was great. We had a chance to talk to Anthropic engineers coming from San Francisco, and the banquet was delicious. Anthropic had a good sense for the omiyages, too! Claude plushie
はじめに こんにちは。Research Engineerの髙瀬です。 最近のAI開発では、モデルが生成したテキストの品質を人間が手動でチェックする代わりに、別のLLM(大規模言語モデル)に採点させる「LLM-as-a-Judge」という手法が広く使われるようになっています。 以前の記事でも、LLM-as-a-JudgeをGPT-4とChatGPTで実際のタスクに適用し、「実務採用前に精度検証をすること」の重要性を紹介しました。 tech.revcomm.co.jp 今回はその一歩先の問いを扱います。 「評価者のLLMが、自分自身の回答を不当に高く評価してしまうのではないか?」 従来の人手評価と比べて、LLM-as-a-Judgeには以下のメリットがあります。 圧倒的なスピード:数千件のサンプルをすぐに採点できる コスト効率:専門家による人手評価より大幅に安価 しかし「LLMが別のLLMを採点する」という設定には、この根本的な問いが付きまといます。この問題を「セルフバイアス(自己優遇バイアス)」と呼びます。 本記事で紹介する論文は、このセルフバイアスを統計的に厳密に測定するフレームワークを提案したものです(昨年8月発表)。 元論文: Play Favorites: A Statistical Method to Measure Self-Bias in LLM-as-a-Judge 著者: Evangelia Spiliopoulou, Riccardo Fogliato ほか(Amazon Web Services) 公開日: 2025年8月12日 LLM-as-a-Judge とは何か まず前提知識として「LLM-as-a-Judge」を整理しましょう。LLM評価のシナリオを具体的にイメージすると、こうなります。 あるプロンプト(質問や指示)に対して、複数のLLM(例:GPT-4oとClaude)がそれぞれ回答を生成する その回答の品質を、別の評価者LLMが「5段階で何点か」「役に立つか否か」などの軸で採点する 採点結果をもとに、どのモデルが優れているかを判定する この仕組みが研究・開発現場で広まっているのは、数千件・数万件のテキストを迅速に評価できるためです。しかし、採点者自身が「被採点者でもある」状況が生まれると、採点の公正性が問われます。 「えこひいき」問題:2種類のバイアス この論文が注目するのは、以下の2種類のバイアスです。 ① セルフバイアス(自己優遇バイアス) 自分が生成した回答に、不当に高いスコアをつける傾向です。 たとえばGPT-4oが評価を担当するとき、GPT-4oが生成した回答とClaude 3.5 Sonnetが生成した回答が同じ品質であっても、GPT-4oは自分の回答をより高く採点してしまうかもしれません。これがセルフバイアスです。 ② ファミリーバイアス(同族優遇バイアス) 自分と同じ「ファミリー(開発元・系統)」のモデルが生成した回答に高いスコアをつける傾向です。 たとえばClaude 3.5 Sonnetが評価者のとき、同じAnthropicが開発したClaude v2やClaude 3 Sonnetの回答を、他社モデルより高く評価してしまう現象です。 これらのバイアスは、モデルの比較評価や性能ランキングを歪め、正しい開発判断を妨げるリスクがあります。 既存手法の問題点 「セルフバイアスがある」と言いたいなら、シンプルに「自分の回答への点数が高ければバイアスあり」と判断できそうに思えます。しかし、これには2つの落とし穴があります。 落とし穴①:品質の差を見落とす もしGPT-4oが本当に高品質な回答を生成しているなら、評価者のGPT-4oが自分の回答に高い点をつけるのは正当な評価であり、バイアスではありません。「自分に高い点 = バイアス」とは言い切れないのです。 落とし穴②:採点スタイルの違いを無視する もう一つの比較方法は「人間の採点とLLMの採点を比べて、自分の回答だけ点数が高ければバイアスあり」というものです。しかしこれも単純ではありません。ある評価者LLMが全体的に人間より高めに採点するクセ(採点スタイルの違い)があった場合、その差分をバイアスと誤認してしまいます。 既存の多くの研究は、これらの問題を適切に切り分けられていませんでした。 提案手法:回帰モデルで「本当のバイアス」を測る 本論文における提案手法は、「評価者LLMのスコアを線形回帰でモデル化し、その係数(重み)を最小二乗法で推定することでバイアスを定量化する」というアイデアです。 これを数式で表すと以下のようになります。 Equation(1): Modeling approach 左辺の ​は「評価者LLM が、プロンプト 、評価軸 、モデル の回答につけたスコア」です。右辺はアンダーブレースの通り3つのグループに分かれています。 Human alignment は、評価モデル固有の採点スタイルを補正する項です。これには、採点基準が全体的に「甘いか辛いか」を示す項 と、人間が判定した品質の差をどの程度スコアに反映させるかという感度 が含まれます。 Self-bias は、評価者が自分自身の生成した回答を採点する際にのみ適用されるバイアス項であり、本解析において最も重要な指標となります。 Family-bias は、採点者と同一の開発元(ファミリー)に属するモデルによる回答に対して加算される偏りを示します。 このモデルにおいて鍵となるのが、セルフバイアス係数である の値です。この係数の正負によって、以下の3通りの解釈が可能になります。 :本当の品質が同じでも、自分の回答を高く評価する :セルフバイアスなし :逆に自分の回答に厳しい(ネガティブセルフバイアス) なぜ人間の参照スコアが必要なのか モデルに登場する「Human alignment」(第三者による品質評価)が重要な役割を果たします。例えば次の3つのシナリオを考えましょう。 シナリオ 評価者LLMの 回答品質 他モデルの 回答品質 参照スコアなしで見た差 A 低品質 高品質 差が小さい → バイアスが過小評価される B 同程度 同程度 差が明確 → バイアスが正確に見える C 高品質 低品質 差が大きい → バイアスが過大評価される シナリオAの補足として、 評価者LLMの回答は低品質でも、セルフバイアスによってスコアが底上げされます。一方、他モデルの高品質な回答には底上げがないため、この2つの力が打ち消し合い、スコアの差が実際のバイアスより小さく見えます。シナリオCはこの逆です また、論文の Figure 1 はこの3つのシナリオを可視化したものです。青い点が評価者LLM自身の回答、オレンジが他モデルの回答で、縦軸のズレ がセルフバイアスを表します。 Figure 1: セルフバイアスの3シナリオ(論文 Figure 1 より) 人間の参照スコアを「品質の基準」として組み込むことで、品質差の影響を取り除き、純粋なバイアスだけを測定できるようになります。 統計的有意性の確認 推定した が「本当にゼロではない(バイアスが実在する)」かどうかは、90%信頼区間(有意水準10%)を使って検証しています。信頼区間がゼロを含まない場合に「統計的に有意なバイアスあり」と結論づけます。通常は95%信頼区間が使われることも多いですが、本論文では計量経済学の実証研究で慣例的に用いられる10%水準を採用しています。また、不均一分散に対して頑健標準誤差(White標準誤差)を使用しており、モデルの仮定が一部崩れても推論が有効になるよう工夫されています。 実験設定 データセットの規模 596件のプロンプト(質問応答・要約タスク) 9つのLLMが各プロンプトに回答を生成(同一モデルが生成者にも評価者にもなる) → 計 5,364件の回答 各回答を9つの評価者LLMすべてが採点 使用したモデル ファミリー モデル Claude(Anthropic) Claude v2, Claude 3 Sonnet, Claude 3.5 Sonnet GPT(OpenAI) GPT-3.5 Turbo, GPT-4o Llama(Meta) Llama 3 8B, Llama 3 70B Mistral Mistral 7B, Mistral Large 評価軸(6次元) 各回答は以下の6つの観点でスコアリングされます。 評価軸 内容 完全性 必要な情報がすべて含まれているか 簡潔性 無関係な内容を含まず焦点が絞れているか 論理的堅牢性 論旨の流れが明確か 論理的正確性 事実的に正確か 有用性 ユーザーにとって役立つか 忠実性 入力内容を正確に反映しているか(主に要約タスク) 人間によるアノテーション 本実験では、LLM-as-a-Judgeのスコアからセルフバイアスを切り分けるために「回答の本当の品質」を示す参照スコアが必要です。その参照スコアとして人間アノテーターの採点結果を使用しています。 各回答は、インハウスの一般アノテーター3名が全評価軸についてLikertスケール(5段階・3段階・7段階)に基づいて採点しました。 さらにアノテーターの品質検証として、別の専門家チームが210件のサンプルに対して正解スコアの範囲(gold range)を設定し、一般アノテーターのスコアがその範囲内に収まるかを確認しました。バイナリの正誤ではなく専門家のスコアレンジへの収まり具合で品質を評価しており、結果は評価軸ごとに84〜95%、平均91%が範囲内に収まり、アノテーターの品質は高水準であることが確認されました。 実験結果 Figure 3: セルフバイアスとファミリーバイアスの推定値(論文 Figure 3 より) セルフバイアスの測定結果 Figure 3(左)はモデルごとのセルフバイアス推定値(90%信頼区間つき)を示しています。GPT系とClaude 3.5 Sonnet(青紫)の推定値が正方向に、Llama 3 8B(黄)が負方向に偏っており、本論文ではこれらのモデルでセルフバイアスの存在が明確に論じられています。 GPT-4oとClaude 3.5 Sonnetは、より高性能なモデルであるにもかかわらず、顕著なセルフバイアスを示しました。逆にLlama 3 8Bは「ネガティブセルフバイアス」、つまり自分の回答に厳しすぎる傾向が確認されました。評価軸「忠実性」において特に顕著で、人間評価では他モデルとの差が小さいにもかかわらず、Llama 3 8Bは自分の回答の忠実性を著しく低く評価していると解釈されています。 ファミリーバイアスの測定結果 Figure 3(右)はファミリーごとのバイアス推定値(90%信頼区間つき)を示しています。 ClaudeファミリーとGPTファミリーには明確なファミリーバイアスが確認されました。Claude 3.5 Sonnetは、Claude v2やClaude 3 Sonnetの回答を、他社モデルより高く評価します。GPTモデルも同様に、GPT系モデルの回答を優遇する傾向があります。本論文では、LlamaとMistralにはファミリーバイアスが見られないと解釈されてます。 小さな差でも大きな影響がある 実験では各モデルのスコアが0.90〜1.00の狭いレンジに集中していました。そのような状況では、セルフバイアスの大きさ(約0.02ポイント)がモデルランキングの順位を変えてしまうほど影響力を持ちます。 たとえば「Claude 3.5 SonnetとGPT-4oのどちらが優れているか」を比較するとき、スコア差がわずか0.02程度であれば、えこひいきによる底上げだけで結論が逆転するリスクがあります。 実践的な示唆 この研究から、LLM-as-a-Judgeを実務で使う際の重要な指針が得られます。 ① 独立した参照スコアを確保する 人間アノテーションや信頼できる第三者モデルのスコアが入手可能な場合は、論文の手法を使ってセルフバイアスとファミリーバイアスを推定し、スコアから差し引くことができます。これにより、補正された公正な評価スコアが得られます。 ② 複数ファミリーの評価者LLMを使う 人間によるアノテーションが一切利用できない場合は、異なるファミリーの評価者LLM(例:GPT、Claude、Llamaを組み合わせる)を使うことで、バイアスを相互に打ち消し合う効果が期待できます。人間によるアノテーションはコストがかかるため、現実的な選択肢として、コストパフォーマンスに優れています。 ③ 評価軸・タスクごとにバイアスが異なることを意識する セルフバイアスは質問応答タスクで大きく、要約タスクでは小さい傾向がある 「論理的正確性」や「忠実性」などの評価軸では、モデルによってバイアスの大きさが変わる 単一の評価軸に依存せず、複数軸での分析を行うことが望ましい Figure 4 は評価軸別(左)・タスク種別(右)のセルフバイアスの分布を示しています。Llama 3 8B の忠実性(×)が特に左に外れていることや、質問応答タスク(●)の方が要約タスク(▲)より全体的にバイアスが大きい傾向が読み取れます。 Figure 4: 評価軸・タスク別のセルフバイアス(論文 Figure 4 より) まとめ 本論文のポイントを振り返ります。 LLM-as-a-Judgeには「セルフバイアス」と「ファミリーバイアス」が存在する 既存手法は品質差や採点スタイルの違いを適切に制御できていなかった 線形回帰モデルによる統計的フレームワークを使えば、バイアスを正確に分離・定量化できる GPT-4oとClaude 3.5 Sonnetには顕著なセルフバイアスとファミリーバイアスがある一方、Llama 3 8Bは逆に自分に厳しすぎる「ネガティブセルフバイアス」を示す バイアスの大きさは小さく見えても、スコアが密集しているモデル比較では大きな影響を持つ LLMを使った自動評価は今後さらに広まっていくと考えられます。LLM-as-a-Judgeを利用する際には、単純に「LLMに採点させればOK」と思うのではなく、どのモデルがどんなえこひいきをするのかを理解し、適切に補正しながら使うことが重要になります。 参考資料 [1] E. Spiliopoulou et al., "Play favorites: A statistical method to measure self-bias in LLM-as-a-judge," arXiv preprint arXiv:2508.06709, 2025.
はじめに こんにちは、RevComm AI Div. の id:tmotegi です。 RevComm では Copilot ・要約・コーチングなど、LLM を活用した機能が急速に増えています。当初は1〜2つのサービスが LLM を直接呼び出すだけでしたが、新機能のリリースと並行して利用するプロバイダーも広がり、気づけば各サービスが個別に Claude(Bedrock)、OpenAI(Azure)を呼び出す構成になっていました。 しばらくはそれでも回っていましたが、LLM を利用するチームが増え、利用するモデルやプロバイダーも広がるにつれ、いくつかの問題が表面化してきました。 クォータ・レートリミット対応がサービスごとに分散 ─ 以前はクォータの上限が低く、サービスごとに複数アカウントを作成してレートリミットを回避していました。レートリミットに引っかからないよう、各チームが個別にリトライや切り替え処理を書いており、同じエラーハンドリングコードが各所に散在していました。 新モデル対応のコストが高い ─ 新しいモデルが出るたびに、複数サービスでクライアントコードやモデル名を個別に更新する必要がありました。 コストの内訳が見えない ─ 「どの機能が、どのテナントで、どのモデルをどれくらい使っているか」がプロバイダーをまたいで把握できず、プロバイダーの請求書を見ても機能別・テナント別の内訳がわかりませんでした。コスト増の原因を特定するだけで時間がかかっていました。 国内処理の適用漏れリスク ─ 顧客要件でデータを国外に出せない機能では、利用できるモデルが制限されます。その制約を各サービスで個別に管理していたため、適用漏れが起きる潜在的なリスクがありました。 これらを解決するために、社内の LLM 呼び出しを一本化する LLM Gateway を開発しました。 目的は大きく2つです。 マルチプロバイダーの統一 ─ 単一インターフェイスで Bedrock / Azure OpenAI / Vertex AI を透過的に扱い、クライアント側のコードを変えずにモデルを切り替えられるようにすること。 コストの可視化 ─ リクエストごとの利用状況を記録し、テナント・機能・モデル別にコストを集計できるようにすること。 最初から自分たちで実装すると決めていたわけではありません。まずは既製の LiteLLM Proxy Server をそのまま使うことを検討し、最終的に litellm をライブラリとして使って自分たちで実装する形に落ち着きました。 なぜ LiteLLM Proxy Server を最初の選択肢にしたのか? LLM Gateway の構築を決めたとき、まず検討したのは LiteLLM Proxy Server です。100 以上のプロバイダーを OpenAI API 互換の単一エンドポイントで束ねる OSS で、フォールバック制御・コスト追跡・Observability 連携が標準で揃っています。 はてな や 弁護士ドットコム など、国内企業の技術ブログでも導入事例が多く公開されており、LLM Gateway が必要になった企業が最初に検討する選択肢です。 LiteLLM Proxy Server が標準で持つ機能は非常に充実しています。 OpenAI API 互換エンドポイント(既存の OpenAI クライアントを向き先だけ変えてそのまま使える) 複数プロバイダーへのルーティングとロードバランシング リクエストボディで fallbacks フィールドを指定するフォールバック制御 コスト追跡・予算管理(モデルごとのトークン単価を設定しコストを自動計算) Langfuse、Datadog などの Observability 連携 特に「リクエストにフォールバック順序を書ける」機能は、私たちが必要としていた要件と重なっていました。 { " model ": " claude-sonnet-4 ", " messages ": [ ... ] , " fallbacks ": [ " gpt-4o ", " gemini-2.0-flash " ] } クライアントがフォールバック順序をリクエストに含めて送るだけで、LiteLLM Proxy Server 側がエラー時に次のモデルへ切り替えてくれます。導入するだけで多くの機能が手に入り、運用面の作り込みも不要になる魅力的な選択肢でした。 なぜ自分たちで実装したのか 「国内処理の制約」と「フォールバック戦略」を連動させる複雑なルーティングロジックを、LiteLLM Proxy Server の async_pre_call_hook などのフック処理内に無理に詰め込みたくなかった、というのが大きな理由です。 LiteLLM Proxy Server には async_pre_call_hook でリクエストを受け取り、 data["model"] を書き換えることで動的なルーティングを実現するカスタムハンドラーがあります。私たちの要件もこれで実装できます。ただし私たちのルーティングには、単純なモデル切り替えにとどまらない要件が重なっていました。 リクエスト単位で国内処理を強制できる ─ 特定のリクエストは日本国内リージョンで動作するモデルのみ使用する フォールバック先も国内リージョンのモデルに限定する ─ 国内処理が必要なリクエストがフォールバックしても、海外リージョンには流れない クライアントがフォールバック順序を指定できる ─ 「まず Claude、だめなら OpenAI」という優先順位をリクエストに書ける 要件1だけなら async_pre_call_hook でモデルを国内リージョンのものに差し替えれば済みます。要件3だけなら LiteLLM Proxy Server 標準の fallbacks フィールドをそのまま使えます。問題は1・2・3の 組み合わせ です。「国内処理を要求しつつ、かつクライアント指定の優先順位でフォールバックし、かつフォールバック先も国内に限定する」という連動した制御は、カスタムハンドラーとフォールバック機構をまたいだロジックになります。 async_pre_call_hook はシンプルなモデル切り替えやロギングには向いていますが、複数の制約を連動させるルーティングには向きません。そこで、この部分は専用のロジックとして自前で持つことにしました。 なお、LiteLLM Proxy Server は litellm という Python ライブラリをベースに作られており、litellm は単体でもライブラリとして利用できます。そこで Proxy Server は使わず、 litellm をプロバイダー抽象化のライブラリとして使い、ビジネスロジックは自分たちで設計・実装する 方が長期的な見通しが良いと判断し、自分たちで実装することを選びました。 litellm をどのようにライブラリとして組み込んだか? EKS 上の LLM Gateway アーキテクチャ図 FastAPI ベースの Python サービスとして実装し、DDD のレイヤー構成を採用しています。 litellm はインフラストラクチャ層の LLMClient クラス内だけで使い、ドメイン層はどのプロバイダーを使っているかを知りません。Bedrock / Azure OpenAI / Vertex AI の 3 プロバイダーに対応しながらも、上位レイヤーのビジネスロジックはプロバイダーの違いを意識せずに済みます。 class LLMClient: async def create_completion( self, deployment: ModelDeployment, request: ChatCompletionRequest ) -> ChatCompletion: response = await litellm.acompletion(model=model_string, ...) return ChatCompletion.model_validate(response.model_dump()) litellm が担うのは「Bedrock / Azure / Vertex AI の差異を吸収する HTTP クライアント」という役割だけです。各プロバイダーは認証方式と API の形式が異なりますが、litellm がその差異を吸収します。 フォールバックの判断やモデル選択といったルーティングロジックは、すべて自分たちのドメイン層に置いています。レイヤーを分けることでプロバイダーへの依存がコードベース全体に広がるのを防ぎ、ルーティングロジックの単体テストも書きやすくなりました。 設計で工夫したこと ① フォールバック戦略をクライアントが選べるようにする フォールバック戦略は auto / specified / none の 3 種類から、リクエストごとに revcomm_options.fallback_strategy で指定します。処理の特性に合わせて可用性・品質・エラー伝播の挙動を選べるようにした設計です。 戦略 動作 使いどころ auto 同一モデルの別リージョン・別アカウントに自動でフォールバック とにかく止めたくない処理 specified クライアントが指定した順番でフォールバック モデルの品質差が許容できない処理 none フォールバックしない(エラーをそのまま返す) ─ auto は可用性を優先するモードです。あるアカウントの Bedrock がレートリミットに引っかかった場合、同一モデルを提供する別アカウント・別リージョンのデプロイメントへ自動で切り替えます。国内処理を要求しているリクエストでは、切り替え先も国内処理に対応したデプロイメントに限定されます。 specified はクライアントが「まず Claude、だめなら OpenAI」という形で優先順位を明示するモードです。出力品質に敏感な処理では、意図しない品質の異なるモデルへの切り替えを避けたいためこちらを使います。 none はエラーをそのまま返します。 なお、フォールバックが発動するのは、レートリミットやタイムアウト・サーバーエラーなどの一時的なエラーに限られています。リクエスト内容の不備(不正なパラメータ、コンテキストウィンドウ超過など)ではフォールバックせず、そのままエラーを返します。意図しないモデルへの切り替えが起きるリスクを抑えるためです。 国内処理を要求するリクエストでは、フォールバック先も国内リージョンのモデルに絞られます。国内制約とフォールバック順序が連動して動く部分です。 ② データが国内で処理されることを保証する 顧客との契約でデータの国内処理が義務付けられているケースでは、一つでも適用漏れがあるとビジネスリスクになります。LLM Gateway ではモデル設定に国内・グローバルのフラグを持たせ、ゲートウェイがリクエスト単位で国内処理を強制します。この判断をアプリケーション側に委ねず、ゲートウェイで一元管理する設計です。 モデルごとのリージョン・プロバイダー設定は YAML で管理しています。 gateway_region: ap-northeast-1 deployments: - model_name: claude-sonnet-4 provider: bedrock region_scope: regional # 国内リージョン region: ap-northeast-1 - model_name: gpt-4o provider: azure region_scope: global # グローバルリージョン region: japaneast region_scope: regional のモデルが「国内処理」として扱われます。リクエストで国内処理を要求した場合、ゲートウェイが該当するモデルを選択します。 この設計の利点は、国内処理の判定ロジックがゲートウェイ内に閉じていることです。新しいモデルを追加するときも、YAML に region_scope を適切に設定するだけで国内処理の制約が適用されます。環境ごとの設定差分は別ファイルで管理しているため、呼び出し元のサービスは環境を意識せず同じリクエストを送るだけで済みます。 ③ モデルを切り替えても呼び出し元のコードを変えずに済むようにする ゲートウェイは OpenAI Chat Completions API 互換のインターフェースを提供しています。既存の OpenAI クライアントはエンドポイントの向き先を変えるだけで移行でき、モデルを切り替えるときも model フィールドの値を変えるだけです。RevComm 固有の制御は revcomm_options フィールドに集約しています。OpenAI クライアントからは extra_body={"revcomm_options": {...}} として渡すため、標準 API との後方互換性を維持しています。 { " model ": " claude-sonnet-4 ", " messages ": [ ... ] , " revcomm_options ": { " require_domestic_processing ": true , " prefer_prompt_caching ": true , " fallback_strategy ": " specified ", " fallback_models ": [ " gpt-4o ", " gemini-2.0-flash " ] , " labels ": { " tenant_id ": " tenant-123 ", " service ": " transcription ", " team ": " asr " } } } オプションを省略すれば通常の OpenAI API リクエストとほぼ同じ形式です。 レスポンスには revcomm_metadata を付与し、実際に使ったプロバイダーやフォールバックの発生有無を呼び出し元に返します。 { " revcomm_metadata ": { " actual_provider ": " bedrock ", " actual_model ": " claude-sonnet-4 ", " fallback_used ": true , " fallback_reason ": " RateLimitError: ... ", " gateway_latency_ms ": 42 , " domestic_processing ": true } } フォールバックが透過的に起きると、呼び出し元からは「なぜこのモデルが使われたのか」が見えなくなります。メタデータをレスポンスに含めることで、デバッグ時にフォールバックの発生をすぐに確認でき、コスト分析にも活用できます。フォールバックが頻発している場合は、特定のプロバイダーや時間帯に問題がある兆候として検知できます。 ④ 誰がどのモデルをどれだけ使っているかを把握できるようにする LLM Gateway のもうひとつの柱がコストの可視化です。LiteLLM Proxy Server には Cost Tracking 機能が標準で備わっていますが、litellm をライブラリとして使う場合はリクエストごとのコスト計算値は得られるものの、保存・集計・可視化は自前で用意する必要があります。リクエストごとのトークン数・プロバイダー・モデル・レイテンシを PostgreSQL に記録し、Redash でテナント別・機能別に可視化しています。ロギングはレスポンスを返した後にバックグラウンドで行うため、ゲートウェイのレイテンシには影響しません。 labels は JSONB カラムで、リクエスト時にオプションとして渡したキーバリューをそのまま保存します。JSONB にしているのは、ラベルのキーをあらかじめ固定せず、各サービスが必要なディメンションを自由に付与できるようにするためです。 SELECT labels->> ' tenant_id ' AS tenant_id, actual_model, SUM (total_tokens) AS total_tokens FROM llm_usage_logs WHERE created_at >= now() - INTERVAL ' 30 days ' GROUP BY labels->> ' tenant_id ' , actual_model ORDER BY total_tokens DESC ; テナント別・サービス別・チーム別など、集計軸をスキーマ変更なしに追加できます。「新しいチームが使い始めたのでチーム別集計を出したい」という要望があれば、そのチームが team ラベルをリクエストに含めるだけで対応できます。テーブル定義の変更もマイグレーションも不要です。 各サービスは推論リクエスト時に tenant_id や service をラベルとして渡すだけで、コスト集計の基盤が整います。プロバイダーの請求書を見ていてもわからなかった「どのテナントの、どの機能が、どのモデルをどれくらい使っているか」が一本のクエリで出せるようになりました。 テナント別・機能別コストを可視化するアナリティクスダッシュボードのイメージ 振り返って LiteLLM Proxy Server のカスタムハンドラーでも、技術的には実現できたはずです。ルーティングロジックとラベル集計を自社のドメインロジックとして持ったことで、要件の変化にコードで素直に対応できています。 フォールバックロジックとモデル選択ロジックを外部依存のない純粋な Python クラスで実装したため、単体テストも書きやすくなりました。「国内処理を要求した場合に region_scope: global のモデルは候補から除外される」「フォールバック戦略が none のときはエラーがそのまま伝播する」といったロジックを、LiteLLM Proxy Server を起動せずにテストできます。 一方で、コスト管理 UI や API キーのセルフサービス発行など、LiteLLM Proxy Server が標準で持つ運用機能は自前で用意する必要がありました。現時点では、コスト集計は Redash のダッシュボードで対応していますが、API キーのセルフサービス発行など長期的には整備が必要な領域もあります。 カスタム性が単純なうちは LiteLLM Proxy Server で十分です。 まとめ 各サービスに分散していた LLM 呼び出しを一本化するため、マルチプロバイダー統一とコスト可視化を目的に社内 LLM Gateway を開発しました LiteLLM Proxy Server を検討しましたが、国内処理制約 × フォールバック戦略の連動が設計想定外になるため、自分たちで実装することにしました litellm はプロバイダー抽象化のライブラリとして使い、ルーティングロジックは自分たちのドメインとして設計しました リクエストに付与する labels(テナント ID・サービス名など)を PostgreSQL の JSONB カラムに記録することで、スキーマを変えずに集計軸を追加できるようにしました
Background The release of DeepSeekMath[1] and DeepSeek-R1[2] brought Group Relative Policy Optimization (GRPO) into the spotlight, and it quickly became one of the most widely adopted post-training algorithms in the open-source LLM community. GRPO's significance lies in making Reinforcement Learning with Verifiable Rewards (RLVR) practical at scale. In domains like math reasoning and code generation, correctness can be checked directly: a solution is either right or wrong, and a unit test either passes or fails. GRPO turns these discrete, verifiable signals into an effective training signal for large language models, and the strong reasoning results from DeepSeek-R1 made it one of the de facto recipes for RL-based post-training. How does GRPO work How does GRPO work As shown in the figure, GRPO training can be split into three steps: Sampling. For each question q, the policy from the previous update samples a group of G candidate answers {o₁, …, o_G}. Sampling within a group is what gives GRPO its name. All subsequent computation is relative to this group. Reward / Advantage. Each answer is scored independently by a reward model or a rule-based reward function, producing per-sample rewards {r₁, …, r_G}. These rewards are then normalized within the group (subtracting the group mean and dividing by the group standard deviation) to produce the advantages {A₁, …, A_G}. The group itself serves as the baseline, so no separate value network is required. Update. The advantages drive the policy gradient term, while a KL divergence between the current policy and a frozen reference model keeps the update from drifting too far. These two terms are combined into the GRPO loss, which is then used to update the policy for the next iteration. Different variants Original GRPO[1,2] Published by DeepSeek as part of DeepSeekMath and later adopted in DeepSeek-R1, GRPO is the foundation that all later variants build on. For each prompt q, the old policy samples a group of G candidate outputs. Each output is scored by a reward function, and the advantage is computed by normalizing the reward against the group's mean and standard deviation. The group itself serves as the baseline, so no separate value network is required. The training objective applies token-level importance-sampling ratios with PPO-style clipping, averaged first within each response (by 1/|o_i|) and then across the group (by 1/G). GRPO Dr.GRPO[3] Published by Sea AI Lab, the National University of Singapore, and Singapore Management University, Dr. GRPO ("GRPO Done Right") observes that the original GRPO objective introduces two systematic biases: a response-level length bias from the 1/|o_i| normalization, which inflates short-response gradients and dilutes long ones, and a question-level difficulty bias from the std denominator in the advantage, which over-weights easy questions. Dr. GRPO removes both terms, dropping 1/|o_i| from the loss and std from the advantage; the rest of the algorithm is left unchanged. Dr.GRPO DAPO[4] Published by ByteDance, DAPO (Decoupled Clip and Dynamic Sampling Policy Optimization) refines GRPO with a set of practical fixes discovered while scaling RL training to long chain-of-thought reasoning. It introduces four key techniques: Clip-Higher. The upper and lower clipping bounds are decoupled into ε_low and ε_high, with a larger upper bound. This gives low-probability tokens more room to grow, promoting exploration and avoiding the entropy collapse commonly seen in long-CoT training. Dynamic Sampling. Groups in which all responses are either fully correct or fully incorrect produce zero advantage and waste compute. DAPO filters out such degenerate groups and resamples until the batch contains enough informative groups, improving both training efficiency and stability. Token-Level Policy Gradient Loss. Instead of averaging the loss per response and then across the group (as in GRPO), DAPO sums over all tokens in the group and divides by the total token count. This prevents long responses from being unfairly down-weighted. This is a critical fix in long-CoT scenarios where response lengths vary by orders of magnitude. Overlong Reward Shaping. Responses that exceed the maximum length are no longer assigned an arbitrary penalty. DAPO instead applies a soft, length-aware reward shaping that reduces reward noise and stabilizes training near the length limit. Together, these changes make DAPO substantially more stable than vanilla GRPO at scale, and it has become a common starting point for open-source long-CoT RL pipelines. DAPO GSPO[5] Published by Alibaba, GSPO (Group Sequence Policy Optimization) argues that GRPO's token-level importance ratio is ill-justified: applying off-policy correction independently at every token accumulates high-variance noise across long sequences, and PPO-style clipping makes the instability worse. GSPO replaces it with a sequence-level importance ratio s_i(θ), defined as the geometric mean of the per-token ratios over the full response. Clipping is then applied once per sequence rather than per token, which lowers gradient variance and proves especially valuable for MoE models where routing changes amplify token-level noise. GSPO Conclusion With the success of GRPO and RLVR, RL post-training has become a core part of the LLM training pipeline. Given a well-designed reward, an LLM can be continually improved through a simple sample–reward–update loop. In practice, however, the high variance inherent to sampling makes GRPO training prone to instability, and a wave of variants (Dr. GRPO, DAPO, GSPO, and others) has emerged to address different facets of this problem. As reward design and training algorithms continue to co-evolve, RL post-training is likely to remain one of the most active and impactful directions in LLM development. Reference [1] Shao, Zhihong, et al. "Deepseekmath: Pushing the limits of mathematical reasoning in open language models." arXiv preprint arXiv:2402.03300 (2024). [2] Guo, Daya, et al. "Deepseek-r1: Incentivizing reasoning capability in llms via reinforcement learning." arXiv preprint arXiv:2501.12948 (2025). [3] Liu, Zichen, et al. "Understanding r1-zero-like training: A critical perspective." arXiv preprint arXiv:2503.20783 (2025). [4] Yu, Qiying, et al. "DAPO: An Open-Source LLM Reinforcement Learning System at Scale." arXiv preprint arXiv:2503.14476 (2025). [5] Zheng, Chujie, et al. "Group sequence policy optimization." arXiv preprint arXiv:2507.18071 (2025).
Research Engineerの石塚です。スペインのバルセロナで開催されたIEEE International Conference on Acoustics, Speech, and Signal Processing 2026 (ICASSP 2026)という国際会議に参加し、「AUTOMATIC ESTIMATION OF SPEAKER DIARIZATION ERROR RATE BASED ON FEATURES OF AUDIO QUALITY AND SPEAKER DISCRIMINABILITY」というタイトルで研究発表を行ってきました。本レポートでは、ICASSP 2026の概要と発表した研究について紹介します。 ICASSP 2026とは ICASSP 2026とは、音声・音響・信号処理の分野において最大規模を誇る国際会議IEEE International Conference on Acoustics, Speech, and Signal Processingの年次大会であり、2026年5月4日から8日にかけて、スペインのバルセロナにあるバルセロナ国際会議場で開催されました。今回の大会が掲げるメインテーマは「Where Signals Meet Intelligence」であり、これは長年培われてきた伝統的な信号処理技術と、近年の飛躍的な進化を遂げている人工知能(AI)やデータサイエンスとの融合を表しています。 技術的な議論の対象は非常に多岐にわたり、音声認識や言語処理、画像・ビデオ解析といった馴染み深い領域から、次世代通信、さらには脳・コンピュータ・インターフェースといった最先端の分野までを幅広く網羅しています。また、大学や研究機関といったアカデミアのみならず、世界をリードする企業が数多く参加する、産学連携の巨大なプラットフォームとしての側面も持っています。 ICASSP 2026 RevComm Researchでは、業務で行っていた研究開発の成果の一部を論文としてまとめ、本国際会議に投稿して受理されたため、ポスター発表を行ってきました。以降は、その研究内容について説明します。 発表論文: AUTOMATIC ESTIMATION OF SPEAKER DIARIZATION ERROR RATE BASED ON FEATURES OF AUDIO QUALITY AND SPEAKER DISCRIMINABILITY Speaker Diarization技術とその課題 会議の自動議事録作成などにおいては、「誰がいつ話したか」を特定するSpeaker Diarization(SD)の技術が欠かせません。しかし、この技術は、下記のような環境に応じて、その難易度が大きく変わるという特徴があります。 高レベルの環境ノイズや反響 マイクの品質の低さ 声の特徴が似ている複数の話者の存在  そして、単一の静的なSDシステムで、あらゆる現実の音響環境において、高い精度と、計算コストのバランスを維持するのは簡単ではありません。常時、大規模で高精度なSDモデルを使うこともできますが、それではクリーンで簡単な環境においては計算コストの無駄になる可能性があります。そこでより実用的な解決策となるのが、事前に対象の音声をSDした時のエラー率を推定する適応戦略です。つまり、難易度の高い音声のときだけ、より堅牢なSDアルゴリズムに動的に切り替えるシステムとなります。この実行時の戦略によって、クリアな音声に対する不必要な計算コストを最小限に抑えつつ、ダイアライゼーションの精度を確保することができます。また、システムの運用時においても、事前にSDした時のエラー率を推定することができれば、対象の環境での推定エラー率が高いときにユーザにマイクなどの録音環境の改善を促したりといったこともできるでしょう。 論文の提案:Diarization Error Rateを自動で予測する! 音声認識システムにおける単語エラー率(WER)の自動推定は広く研究されていますが、話者ダイアライゼーションのエラー率であるDiarization Error Rate(DER)の自動推定はまだあまり開拓されていない分野です。Diarization Error Rateは 下記の式 で計算されます。 ここでの は、非音声を音声と誤って認識した部分の長さ、 は音声を非音声と誤って認識した部分の長さ、 は話者を誤って推定した部分の長さ、 は正解の音声の総時間です。 本記事で紹介する論文では、正解データを必要とせず、音声信号から直接SDアルゴリズムのDERを自動的に推定する手法を提案しています。その推定の鍵となるのが、Diarizationのパフォーマンスに影響を与える「2つの要因」を捉える特徴量を抽出するアプローチです。 音声品質の特徴量: バックグラウンドノイズなどの環境要因によって引き起こされる音声信号の劣化を捉えるように設計されています。 話者の音響的な識別可能性の特徴量: 対象となるSDモデルが生成する話者クラスターの分離可能性を評価するもので、潜在的な話者の混同エラーに関連付けられます。つまり、対話に参加している話者らの声質が似ていて聞き分けるのが難しい状態になっていないかを確認します。 提案手法:DER(Diarization Error Rate)の自動推定アーキテクチャ 本論文が提案するシステムは、正解データを用いることなく、入力された対話の音声信号から直接DERを推定する手法です。このシステムは、ダイアライゼーションの精度低下を招く要因を定量化するため、「音声品質(Audio Quality)」と「話者の識別可能性(Speaker Discriminability)」の2つの特徴量抽出モジュールを実行します。そして、抽出された特徴量をもとに回帰モデルで最終的なDERの予測値を出力するという構成になっています。 DER の自動推定アーキテクチャ 音声品質特徴量(Audio Quality Features) 背景ノイズなどの環境要因による音声信号の劣化を捉えるため、以下の2つの指標を抽出します 。 VAD Difference Rate(VAD検出差分率): 軽量なVoice Activity Detection(Weak VAD)と、ノイズに対して堅牢なDNNベースのVAD(Robust VAD)による音声区間検出結果の差分を利用した特徴量です。ノイズの多い環境下では、軽量な信号処理によるVADなどはノイズを音声として誤検出しやすいため、検出される総音声長が堅牢なVADよりも長くなる傾向があります 。この差分率が高いほど音声品質が低く、結果としてDERが高くなると仮定しています。本特徴量の概念図を下記に示します。 概念図 DNSMOS: 音声の知覚的品質(Mean Opinion Score)を予測するモデルであるDNSMOSを利用します。対象のSDシステムから得られた各音声セグメントのスコアを算出し、セグメントの長さに基づいた加重平均を取ることで、録音全体の品質スコアを求めています。 話者の識別可能性特徴量(Speaker Discriminability Features) SDの話者クラスタリングの難易度を測るため、対象のSDアルゴリズムが生成する話者埋め込み(Speaker Embeddings)から、以下のクラスタリング評価指標を算出します。つまり、声質が似ているなどで、聞き分けるのが難しい状態になっていないかを確認します。 Davies-Bouldin Index (DBI): クラスターの凝集度(クラスター内の分散)と分離度(クラスター間の距離)を考慮して、クラスタリングの品質を定量化する指標です 。マクロな視点から全体のクラスター構造を評価し、DBIが低い(クラスターが密集し、かつ他と十分に離れている)ほど話者の音響的特徴が明確であり、DERが低くなると仮定します。 Silhouette Score: 各データポイント(埋め込み表現)が、自身が属するクラスターにどれだけ適合しているか(凝集度)と、最も近い隣接クラスターのすべてのデータポイントからどれだけ離れているか(分離度)を比較する指標です 。DBIがマクロな指標であるのに対し、こちらは各データポイントの妥当性をミクロな視点から評価するものであり、両者は相補的に機能します。 回帰モデルによるDER推定 上記2つのモジュールから得られた計4つの特徴量(VAD Difference Rate、DNSMOS、DBI、Silhouette Score)を特徴量として結合し、回帰モデル(本研究では主にSupport Vector Regressionを使用)に入力します 。これにより、事前の正解アノテーションなしで最終的な予測DERを出力することが可能となります 。 評価実験 今回の提案手法でどの程度の精度でDERを予測できるのかを確かめるため、以下のような条件で検証が行われました。 検証に使ったデータセット 実験には、話者ダイアライゼーションの評価でよく使われる2つの公開データセットを組み合わせて使用しました 。 VoxConverse: テレビ番組の動画から構成されるデータセットです 。 MSDWild: 日常会話の動画から構成されるデータセットです 。VoxConverseと比較すると、声の重なりやノイズが多く、ダイアライゼーションを高精度で行うのがより難しいデータになっています 。 対象となるSDモデル エラー率予測のターゲットとして、仕組みの異なる2つの事前学習済みモデルを使用しました 。 pyannote.audio 3.1 (Pyan): End-to-End Neural DiarizationとVector Clusteringを組み合わせたEEND-VC手法を採用したSpeaker Diarizationツールキットです。 Wespeaker (Wesp): 従来型のアルゴリズムを採用したSpeaker Diarizationツールキットです。 実験結果 まず、VoxConverseとMSD Wildの合計899件のテスト音声データについて、音声の各特徴量と、音声をSDした時のDERの構成要素とのピアソンの積率相関係数を確認しました。相関係数は絶対値が1に近いほど「予測と実際の結果がピッタリ連動している」ことを意味します。結果を見ると、特徴量によって、DERのどの構成要素と関係が強いのかが異なっていることがわかります。 結果 さらに、VoxConverseとMSD Wildの学習音声データでSupport Vector Regressionの回帰モデルを構築します。そして、テスト音声データについて、回帰モデルが算出した「推定エラー率」と「実際のエラー率」のピアソンの積率相関係数を算出しました 。その結果、相関係数は以下のようになりました。 pyannote.audio 3.1 (Pyan)の場合:0.806 Wespeaker (Wesp)の場合:0.800 0.8以上という数値は一般的に「非常に強い相関がある」と評価されるため、かなり的確にエラー率を予測できることがわかりました。また、各特徴量とDERの間の相関の値より、SVRで構築したモデルによる推定値との相関の値の方が大きくなっていることからも、4つの特徴量を組み合わせることで、より高い推定精度が得られていることがわかります。下記に推定エラー率と実際のエラー率の散布図を示します。 推定エラー率と実際のエラー率の散布図 まとめ 本記事では、国際会議ICASSP 2026にてRevComm Researchが発表した、Diarization Error Rate自動推定技術について紹介しました。本研究では、「音声品質」と「話者の識別可能性」という2種類の特徴量を抽出し、回帰モデルを用いて事前のエラー率を予測する手法を提案しました。評価実験の結果、推定値と実際のエラー率の間で約0.8という高い相関が確認され、高精度な予測が可能であることが実証されました。この技術を活用することで、音声の難易度に応じたモデルの動的な切り替えによる計算コストの削減や、ユーザーへの録音環境の改善提案など、より効率的で実用的なシステム運用への応用が期待できます。 今後もRevComm Researchでは、実社会の課題解決に繋がる研究開発を推進し、機会があればこのような国際的な場でも成果を発信していきたいと考えています。
Hi, I'm Jose. I work as a Full Stack Software Engineer at Revcomm and also help with Developer Relations. We organize events, write blog posts, and give talks across Japan and overseas. Right before the New Year, I spoke with an R&D manager. He said R&D would submit a paper to ICASSP 2026 and asked if I could join if it was accepted. It sounded like a great opportunity to learn and catch up on the latest in Digital Signal Processing and Speech-Language Processing. Yet, hearing ICASSP made me pause. The name sounded familiar. Back in 2016, when I was working on my thesis, my professor suggested I look for conferences to submit to. ICASSP in Shanghai was one option, but since my thesis was about automatic ontological generation, it didn't seem like the right fit for a signal processing conference. I submitted it somewhere else, but I don't remember much after that. Fast forward—the paper was accepted, and by May 1st, I was on a flight to Barcelona. Preparations Barcelona Poster and talk recommendations Lessons (TLDR;) Preparations At Revcomm, I get to work with AI in lots of ways. We use Claude Code for vibe coding, rely on GitHub Copilot for code reviews, and experiment with new ways to improve our development pipelines. For example, we recently implemented a pipeline to analyze audit logs using BigQuery MCP, so customer support members can request specific logs via prompting. This change reduced the time to retrieve audit logs from hours to just a few minutes and has already helped the support team resolve customer issues more quickly, along with reducing the burden on the tech side. We also hold internal tech meetings where teams share what they know. I've heard R&D talks about Speaker Diarization, generative error correction, and fake detection. The topics themselves weren't new to me, but I wondered how much value I could really get from the conference. So, what did I do? I decided to focus on what I wanted to learn. With AI tools today, it's easier than ever to find the information you need quickly. First, I used Google Deep Research to look up the latest trends in Speaker Diarization and other relevant topics at the company. Starting in April, I read one or two interesting papers each day until the conference proceedings were released. The ICASSP proceedings came out on May 4, and they were huge, more than 2GB and over 4,000 papers. To avoid missing any relevant sessions, I set up a workflow using Claude Cowork: I uploaded the proceedings index and my own reading list from the past month. Then, I asked Claude to filter out the presentations for a three-hour period from the tasks I was most interested in. As context, I also included information from the papers I’d read. I iterated over step 2 with different parameter values. For instance, I wanted to connect with Japanese researchers, so I searched for presentations by Japanese universities. I also searched for researchers whom I have interacted with before. With this approach, to my surprise, I discovered that my former Computer Science professor was a co-author on one of the papers. With the information provided, I manually created a schedule for each day and checked with my colleagues to avoid unnecessary overlap and cover more ground. Barcelona ICASSP 2026 ran from May 4 to May 8, in the middle of Japan’s Golden Week. The first day was all about tutorials. We used that day to acclimatize and print the poster. Our first real day was May 5. Barcelona is truly one of Europe’s major tech hubs—friendly, warm, safe, and full of energy. Our Airbnb was about 15 minutes on foot or 13 minutes by bus, which is pretty typical for Barcelona. The conference venue was impressive and felt more like a Hilton hotel. Barcelona International Convention Center We registered, picked up badges, and received a Barcelona Hello Card for free metro transport. The conference offered two main experiences: walking the poster area or attending track presentations. Each day, I spent an hour on posters and used the remaining time on presentations. The schedule was packed, lunch ran an hour to an hour and a half, and coffee breaks were around 3:30 pm, with work zones also available for those who needed them (a lot actually). The next day, on May 6, we had our poster presentation first thing at 9:00 a.m. It went really well. We got questions from university students and professors from all over the world. It helped that the poster’s position was easier to see. Poster print. Beautiful That evening, I attended the Young Professionals Networking Event at La Mar, a seaside restaurant with a fantastic view of yachts docked on the port. Although the website said it was a ten-minute walk from the main venue, I got lost along the way. Fortunately, I wasn’t alone, and several of us ended up arriving together, starting conversations before we even entered. The remaining days settled into a routine of an early breakfast at 7:00 and a walk to the conference by 9:00 a.m. Lar mar's view Poster and talk recommendations Here were a few standout posters and talks. Planning ahead helped maximize relevant takeaways, but some spontaneous choices were also impactful. What Statistics and AI Offer Each Other? by Emmanuele Candès By integrating statistical insights into AI processes—specifically for data collection, quality control, and uncertainty quantification—we can build more reliable and efficient AI systems that adhere to the high standards of scientific research. Fully unsupervised score ensembling (FUSE) only slightly outperforms supervised learning. Throw more compute, throw more data, and things will go well. A related lecture on this is live on YouTube: https://www.youtube.com/watch?v=Nj9hcoHhglw KAME: A tandem architecture for enhancing knowledge in real-time speech-to-speech conversational AI. The engineers at Sanaka AI were very generous in explaining their work. They also wrote a blog that summarizes it:  https://pub.sakana.ai/kame/ Entangling  classical- and quantum-domain information/signal processing by Lajos Hanzo I attended this plenary talk because I was curious about quantum computing and expected to be overwhelmed by technical jargon. I was pleasantly surprised. Professor Hanzo was very expressive and connected well with the audience. As people were leaving for lunch, he asked, “I know you are hungry, but just before you go, how many of you were interested in Quantum after this talk?” I raised my hand, and I wasn't the only one. Signal Quality Assessment in the Era of Foundation Models Video quality, playback smoothness, and network performance are the main challenges in signal quality assessment. This topic really resonated with me as an engineer. https://ieeexplore.ieee.org/document/11460679 Scaling an Audio-Visual Quality Assessment Dataset via Crowdsourcing People often focus on the weakest aspect of a system but judge it based on its strongest feature. https://ieeexplore.ieee.org/document/11463693 And many more. See this year’s proceedings here: https://ieeexplore.ieee.org/xpl/conhome/11460365/proceeding Lessons (TLDR;) When I joined  a local conference  in 2016, Machine learning and Data Science were driving AI. In NLP, I saw papers on vectorization, autoencoders, LDA, and more. A decade later, LLMs, agents, and quantum computing are popular. Still, the vibe is the same. People are excited, talking passionately. Methods have evolved, but the people remain. I won’t wait another decade to go again (ICL? Maybe next year in Toronto). Here’s what I learned: Go to networking events—they are the best way to meet people and learn from peers. My strategy was to look for paper authors or poster presenters, as it was easier to start a conversation around them. Overall, everyone was friendly, so that made things easier. If you are unsure how to start a conversation, asking someone what brought them to the event is a good opener. If you're an engineer, tutorials are great. You'll feel most at home. Pick your track ahead and know what to see. It can get overwhelming, so use AI tools to prioritize. Big conferences also have industry tracks. Don't hesitate to ask questions. The people presenting posters are always happy to answer. It's the people who make these trips worthwhile. You meet researchers, engineers, and entrepreneurs, make connections, start collaborations, and maybe make friends. Write notes on each session you attend. Even a brief summary is enough. If possible, categorize them by tags. Without this, I wouldn’t have written this article. Thanks to Revcomm for the opportunity. You can read our R&D paper at the link below. It proposes a novel method to estimate the speaker diarization error rate. https://ieeexplore.ieee.org/document/11463892 Thank you for reading. You can find me on  LinkedIn .
RevCommの熊谷です。 AIエージェントを使った開発が当たり前になり、コーディングの速度は確かに上がりました。一方で、フロントエンドは自由度が高いぶん、油断するとすぐに 設計がブレる という次の問題も見えてきます。今回は、それをどうやって抑えているかを書いてみます。私たちのチームではルールを文章で書き下すよりも、「参考実装を指す」ことを軸にしています。 困っていたこと 紹介するのは、MiiTel Admin という管理画面での話です。自社サービス「MiiTel(ミーテル)」を支える、Vue 3 + Nuxt 4 + TypeScript で作られた Web アプリケーション。ミーテルの成長とともに機能が増え続け、現在は 92 ページの大規模な管理画面になっています(ここ半年で 8 ページ増えてる...!)。 このプロダクトはフロントエンド専任ではない開発者(バックエンド・モバイル・音声 AI エンジニア)も気軽に機能追加に入ってくれます。それ自体はありがたいのですが、そのぶん設計のブレが起きやすい環境でもあります。 エージェント導入前は、既存のページをコピペして少し直すような書き方が多かったんですよね。コピペにはもちろん問題もあるのですが、結果として構成が大きく外れにくく、書く速度もそこまで速くなかったので、設計のずれはレビューで拾えていました。 ところが、エージェントが入ってから一気に状況が変わりました。 みんな書く速度が一気に上がった 設計のブレがレビューで追いつかない量で発生するようになった 開発者ごとに使うエージェント・モデルが違う(Copilot / Claude / Cursor / Devin、モデルもいろいろ) このまま放っておくとカオスになる、というのが課題感でした。 基本方針 打ち手はいくつかあるのですが、根っこの方針はシンプルです。 エージェント・モデルは開発者の好きなものを使ってOK(強制しない) ただし「何を見て書くか」だけは全エージェント共通にする 人間がレビューで都度直すのではなく、書かれる前に方向を揃える エージェントを「設計のブレ防止装置」として使うイメージです。本業で使い慣れたエージェントをそのまま使える方が、開発者にとっても嬉しいですしね。 やっていること エージェント向けのドキュメント整備は ルールを文章で書き下す アプローチが多いと思いますが、私たちはむしろ 参考実装そのものを指す アプローチを軸にしています。文章のルールはどうしても実装とズレていきますが、コードは常に最新だからです。以下の 3 つは、その方針から派生した工夫です。 1. リファレンス実装と「参考の優先順位」を明示する 1ページを徹底的にリファクタしてお手本にする、というやり方です。 前回のブログ でも書いたパターンですね。 現在は SMS テンプレート機能 src/pages/sms-template/ を参照実装にしていて、エージェントが見る .github/copilot-instructions.md にも明記しています。 ## 作業の進め方(最優先) - 変更行数が合計 20 行を超える場合は必ず守ること。 1. コードを書く前に対応するスキルを呼び出す 2. 使用するスキルが 3 つを超える場合は、作業分割をユーザーに提案する — ユーザーが「そのまま進めて」と回答すれば継続してよい 3. 既存のコンポーネント構造・設計に従う 4. 明示的な指示がない場合は新しい設計パターンの導入はしない 5. 判断に迷った場合は、一般的なベストプラクティスに従うよりも、既存コードの設計・パターンに合わせることを優先する ## 参考実装の優先順位 1. `src/pages/sms-template/`(composable 分離・Pinia Setup Store・vee-validate・i18n が揃ったパターン) 2. `src/pages/softphone-setting/`(Pinia 移行済みの最新実装) 3. 同カテゴリの既存画面(同じユースケース・UI 構成) 4. `src/composables/`、`src/components/organisms/` の既存実装 参考実装の場所さえ明示しておけば、エージェントは常に最新のコードを見にいってくれます。ドキュメントの更新が遅れても、自然と最新パターンに揃っていくというのが、このやり方のいちばん効いている点です。 「既存パターンを優先する」と明文化しておくと、エージェントがプロジェクト側に寄せてくれやすくなります。バックエンドエンジニアもフロントを触る体制なので明示的に書いていますが、フロント専任メンバーで固まっているチームでは、ここまで強く書く必要はないかもしれません。 2. コンテキストに乗せる情報を絞る ドキュメントは育てると肥大化しがちです。とくに先ほどの参考実装アプローチでは、エージェントが skill / instructions に加えて実コードまで読みにいくので、コンテキストはなおさら膨らみやすい。コンテキストが膨らむと、 エージェントの注意がそれて、長いルールほど読み飛ばされやすくなる 。動作そのものも遅くなります。なので、「何を読ませないか」もセットで設計しています。 トピックごとに skill に分割: state-management / i18n-rules / vee-validate-form など。関連する作業のときだけ読み込ませる 小規模変更ではスキップ可能に: 「変更行数が 20 行を超える場合は必ず skill を呼ぶ」のように、軽微な編集では読み込みを省略できる逃げ道を書いておく 人間向けドキュメントと分離: 設計ドキュメント ( docs/architecture.md など) は人間向けに保ち、エージェント向けは簡潔な技術用語の羅列に近い形に 最後の点は試行錯誤がありました。最初は copilot-instructions.md から docs/architecture.md を参照させていたのですが、結局コンテキストが膨らむだけで効果が薄かった。エージェント向けには「読みやすさ」より「短さ」を優先して、技術用語の羅列に近い形に分離したら、必要な情報だけ拾ってもらえるようになりました。 3. ライブラリの標準パターンに素直に乗る 2 はドキュメントを絞る工夫でしたが、もう一歩進めて、 そもそもドキュメントを書かなくて済むようにする こともできます。エージェントがすでに知っている公式パターンに乗せれば、プロジェクト固有のルールを書く必要がありません。 これは、考え方が大きく変わったところです。 以前は「Nuxt/Vue 固有機能をバックエンドエンジニアにまで覚えてもらうのは忍びない」と思っていて、Vanilla 寄りの実装で吸収していました。たとえばページの表示条件は、各ページで router.push を使って書いていました。 // src/pages/sms-templates.vue (抜粋) const router = useRouter(); const { isSmsAvailable } = usePermission(); onMounted(() => { if (!isSmsAvailable.value) router.push('/'); }); 今は Nuxt の definePageMeta + middleware に置き換えました。各ページは宣言を 1 行書くだけです。 // src/pages/sms-templates.vue (抜粋) definePageMeta({ permission: (ctx: TenantContext) => ctx.isSmsAvailable, }); 判定の本体は middleware に集約。全ページぶんが、ざっくりこの数行で完結します。 // middleware/permission.global.ts (抜粋) export default defineNuxtRouteMiddleware(to => { const ctx = usePermission(); if (!to.meta.permission?.(ctx)) return navigateTo('/'); }); この設計は Vue Router 公式の route.meta + navigation guard パターン をほぼそのまま踏襲したもので、プロジェクト固有の語彙も permission / TenantContext の 2 つに絞れているので、エージェントは公式パターンの知識をほぼそのまま使って書けます。 振り返ると、MiiTel Admin は SSR を採用していないこともあり、これまで Nuxt らしい機能を十分に活かせていませんでした。それがエージェントの登場で逆転しました。 エージェントが補完してくれるなら、標準パターンに乗る方がブレない 。学習コストの高い固有機能も気軽に取り入れられるようになり、Nuxt を選んだメリットをようやく実感できているところです。 ルールを長く運用する工夫 一次ソースを 1 箇所に集約する ドキュメントは長く運用すると、コピーが増えてあちこちでずれていく、というのが起きがちです。一次ソースを 1 ヶ所に集約して、各エージェントからは同じファイルを参照させるようにしています。 .github/copilot-instructions.md と .github/instructions/*.instructions.md を起点に、Claude 用 .claude/skills/ や Cursor からも同じファイルを参照。コピーを作らないので更新がずれません。一番制約がきつい Copilot(2026 年 5 月時点では外部ファイルを参照できず、 .instructions.md 自体に内容を直接書く必要があります)に合わせて書いておけば、他のエージェントでも破綻しにくい、というのも分かってきました。 たとえば Claude 用の .claude/skills/i18n-rules/SKILL.md は、 .github/instructions/ 配下の対応するファイルを cat するだけのシンプルな中継ファイルです。 --- name: i18n-rules description: Use this skill when creating or editing i18n files (...). --- !`cat ${CLAUDE_SKILL_DIR}/../../../.github/instructions/i18n-rules.instructions.md` これで Claude も Copilot も結局は同じ .github/instructions/i18n-rules.instructions.md を見ているので、更新は 1 箇所で済みます。 PR レビューでルールを育てる それでも、思ったような PR が出てこないことはあります。レビューで違和感を覚えるときって、実装した本人も「なんか変だな」と感じていることが結構あるんですよね。そんな時は、本人が使っているエージェント環境で、そのまま 「今回どの skill / instructions 読んだ?参考実装は何を見た?」と聞いてもらいます。自分の環境で再現するより速いし、本人もその場で原因を切り分けやすい。ルール自体が足りないのか、ルールへの導線が悪いのか、参考実装が古いのか。「あ、これ参照されてなかったね、書き足しておくね」みたいに、雑談ベースでドキュメント改善を回しています。 原因の切り分けが終わったら、その流れでエージェントに .github/instructions/ の修正 PR まで作ってもらうこともあります。違和感を覚えた人が、その場でルールを直す側に回れるのは、エージェント時代ならではの良さだなと感じています。 おわりに エージェント時代のフロントエンド開発で効いたのは、 「何を見て書くか」を揃えること だなと感じています。エージェントを厳しく縛るのではなく、既存設計に自然に合流できる道を整える。それができれば、速く作りながら長く保守できるプロダクトに近づけるはずです。 「スピードは出るようになったけど、設計がブレ始めた」というフェーズに入ったチームの参考になれば嬉しいです。
はじめに こんにちは。RevComm でエンジニアをしている 林 です。 MiiTel Phone では長らく ESLint + Prettier をフロントエンドの lint / formatter として使ってきましたが、約 1,300 ファイル規模の React + TypeScript プロジェクトで CI と AI コーディングループのフィードバックがどうしても重くなってきていました。 今回、Rust 製ツールチェインである Oxc ベースの Oxlint / Oxfmt へ完全移行したので、その経緯と方法、得られたメリット・デメリットについて紹介します。 Oxc とは Oxc (The JavaScript Oxidation Compiler) は、パーサー・リンター (Oxlint)・フォーマッター (Oxfmt)・トランスパイラーといった JS/TS ツールチェインを Rust で書き直す OSS プロジェクトです。今回扱うのは次の 2 つ。 Oxlint: ESLint 互換のルールセットを持つ Rust 製リンター。 公式ドキュメント では ESLint 比 50〜100 倍速いとしている。JS Plugins (Alpha) と Type-Aware Linting (Alpha) も順次拡張中 Oxfmt: 2026-02 に Beta。Prettier v3.8 の JS/TS conformance 100% を達成し、出力互換を保ったまま高速化できる 開発主体は VoidZero Inc. — Vue.js / Vite の作者 Evan You 氏が 2024 年に設立したスタートアップです。Vite / Vitest / Rolldown / tsdown / Oxc を開発しています。 Biome ではなく Oxc を選んだ理由 似た立ち位置のツールに Biome がありますが、決め手は速度や Prettier 互換ではなく、周辺ツール全体を束ねる存在 ( Vite+ ) でした。 観点 Oxc (Oxlint / Oxfmt) Biome 開発主体 VoidZero (Vite 作者の会社) Biome コミュニティ 周辺ツールとの統合 Vite+ で build / test / lint / format が同じロードマップに乗る 独立型 規則互換性 ESLint ルール名 / Prettier 出力と互換重視 独自ルールセット中心 Vite+ は Vite / Vitest / Oxlint / Oxfmt / Rolldown / tsdown を vp という単一 CLI に束ねた統合ツールチェインです。 今回のプロジェクトも将来的に Vite+ への合流を視野に入れており、その時点で Oxlint / Oxfmt に乗っておいて良かったと言える状態を作りたかったのが Oxc 採用の主な動機です。 Biome に乗ると、将来 Vite+ に揃えたいときに lint / format をもう一度乗り換えるコストが再発します。 移行理由 1. CI / pre-commit / editor の速度がボトルネックになっていた 全件 lint で 13 秒、format チェックで 5 秒というのは、単体で見ればまだ我慢できる範囲です。ただし PR ごとに何度も走り、ローカルの保存時 lint や pre-commit hook でも体感に効いてくるため、合計すると無視できない時間になっていました。 2. AI コーディング時代のフィードバックループ Claude Code や Codex などの AI エージェントがほとんどのコードを書くようになると、lint は人間による最終レビューの代替手段から、 AI がコードを書く途中でその場でミスを弾いて修正する仕組み に役割を変えます。 具体的には、エージェントの編集ごとに lint と formatter を回し、エラーを次のターンに即フィードバックして自己修正させるという使い方です。 ESLint の 1 ファイルあたり 1.72 秒という所要時間ではこのループに乗せづらく、Oxlint の 0.43 秒で初めて現実的になりました。 AGENTS.md / CLAUDE.md の自然言語ガイドは読んだ後に指示が守られるかどうかはお祈りレベルの保証しかなく、絶対に止めたい違反はツール側で機械的に弾いた方が確実です。 lint にその役割を担わせる場合、速度がそのままループの成立可否になります。加えて、一度入れたルールはその後の全セッションに効くので、AI が書くコード量が増えるほど 1 ルールあたりの効果が積み重なっていきます。 3. 設定の簡素化と依存削減 ESLint 設定は eslint.config.js が 220 行、プラグインが 11 個ぶら下がっていました。Oxlint の native plugin で同等のカバレッジが取れるなら、設定もぐっとシンプルになります。 @eslint/compat のような互換レイヤーや、フォーマッター衝突回避のための周辺パッケージも整理できます。 4. Prettier 互換性が確保された Oxfmt が Prettier v3.8 互換 100% を達成したことで、フォーマッターを乗り換えると全ファイルに差分が出るという移行最大の壁が消えました。これがなければ移行コストが見合わなかったので、Beta リリースを待ってから着手する予定でした。 移行方法 Oxc は単一ツールではなく個別のツール群なので、Prettier → Oxfmt と ESLint → Oxlint をそれぞれ独立した PR で進めました。先に Oxfmt 側を済ませてから Oxlint に取りかかる順番です。 Prettier から Oxfmt 1. 差分ゼロ確認 まずローカルで src 配下の全ファイル (約 1,300 ファイル) について、 prettier --write の結果と oxfmt の結果が一致するか git diff で確認しました。 Beta とはいえ Prettier conformance テストが通っているので、現行コードに対しては実用上問題なしと判断しています。 2. 設定ファイル変換 Oxfmt は Prettier 設定からの自動変換コマンドを持っているので、これを使います。 npx oxfmt --migrate=prettier 生成された .oxfmtrc.json の例 ↓ { " $schema ": " ./node_modules/oxfmt/configuration_schema.json ", " semi ": true , " trailingComma ": " all ", " singleQuote ": true , " printWidth ": 120 , " tabWidth ": 2 } .prettierrc / .prettierignore は削除します。 3. scripts / hooks / codegen の置き換え // package.json (抜粋) { " scripts ": { " lint:oxfmt ": " oxfmt --check 'src/**/*.{ts,tsx,mdx}' ", " fix:oxfmt ": " oxfmt 'src/**/*.{ts,tsx,mdx}' " } } pre-commit hook の Prettier エントリも oxfmt に置き換え、 graphql-codegen の afterAllFileWrite や独自 codegen スクリプトで prettier --write を呼び出していた箇所も npx oxfmt に差し替えます。CI ワークフローのジョブ名もここで変更しました。 4. eslint-config-prettier はこの段階では据え置き 公式ドキュメント の推奨に従い、この段階では eslint-config-prettier をそのまま残しました。フォーマッタと衝突する ESLint ルール ( indent など) を無効化する役割は、Prettier でも Oxfmt でも変わらないためです。 なお ESLint 本体を撤去する後続の Oxlint 移行 PR で、同パッケージも併せて削除しています。 ESLint から Oxlint Prettier 移行と違ってこちらは挙動の互換性を見るだけではなく、ルールセットの取捨選択が必要です。方針として「native plugin 主軸、必要なものだけ JS Plugins (Alpha) で補う」を取りました。 1. 完全移行か併用か Oxlint 公式ドキュメントは「小〜中規模は完全置換、大規模は ESLint との併用を推奨」としています。併用したい場合は eslint-plugin-oxlint で Oxlint が拾えるルールを ESLint 側で重複排除し、両者を直列で回すのが定石です。 { " scripts ": { " lint ": " oxlint && eslint . " } } ちなみに今回は 1,300 ファイル規模ながら完全移行を選びました。主な理由は以下です。 速度メリットが消える: 全件 lint で oxlint 0.35s + eslint 12.96s ≈ 13.31s となり、現状とほぼ変わらず、AI ループ用 hook に乗せる速度を取り戻せない 設定の二重管理: eslint-plugin-oxlint で重複排除すると、独自設定 ( no-restricted-globals のカスタム設定など) が無効化されたり、既存の inline // eslint-disable-next-line ... が "Unused eslint-disable directive" として警告化されたりした ロードマップとの整合: 将来 Vite+ に揃えたい以上、併用は足がかりにもならない 「完全移行で破綻するほど大規模か」「速度を hook ループに取り戻したいか」が判断の分岐点で、以降のステップは完全移行ルートの内容です。 2. プラグインの仕分け 既存の ESLint プラグインを、native で代替可能 / JS Plugins で残す / 捨てる の 3 つに分類します。 既存の ESLint plugin 対応方針 typescript-eslint native typescript plugin react / react-hooks native react plugin (react-hooks 同梱) jest native jest plugin unused-imports typescript/no-unused-vars で代替 react-compiler eslint-plugin-react-hooks v6+ に統合されたため native でカバー testing-library / jest-dom oxlint jsPlugins (Alpha) 経由で src/__tests__/** 限定で残す storybook 撤退。 storybook build と Chromatic 視覚回帰で代替 import ( import/order ) oxfmt の sortImports に寄せる (後述) JS Plugins は ESLint プラグインをそのまま読み込めて便利ですが、Alpha かつ Oxlint の Rust native で速いという売りを部分的に削るレイヤなので、メインでは使いませんでした。 テスト系の 2 プラグインだけは「テスト信頼性に直接効くので確実に止めたい」「スコープが __tests__ 配下に閉じている」という条件が揃ったので、限定的に採用しています。 3. 自動変換 + 手調整 flat config からの自動変換は oxlint-migrate が使えます。 npx @oxlint/migrate <optional-eslint-flat-config-path> 生成された JSON に対して、native plugin で対応するルール名 ( @typescript-eslint/... → typescript/... など) の最終調整と、JS Plugins / overrides の追加を手で行います。最終的に出来上がった構成の骨子は次のような形です。 { " $schema ": " ./node_modules/oxlint/configuration_schema.json ", " plugins ": [ " typescript ", " oxc ", " react ", " jest " ] , " jsPlugins ": [ " eslint-plugin-testing-library ", " eslint-plugin-jest-dom " ] , " categories ": { " correctness ": " error ", " suspicious ": " error ", " pedantic ": " off ", " perf ": " off ", " restriction ": " off ", " style ": " off ", " nursery ": " off " } , " rules ": { // ... プロジェクト固有の制約ルール ... } , " overrides ": [ { " files ": [ " src/__tests__/**/*.{ts,tsx} " ] , " rules ": { " testing-library/no-node-access ": " error ", " jest-dom/prefer-in-document ": " error " // ... 他のテスト系ルール ... } } ] } plugins キーを書くと Oxlint のデフォルト plugin セットを上書きします。 typescript / oxc はデフォルトで有効ですが、明示的に書いておかないと意図せず外れる事故が起きやすいので注意が必要です。 もう一つ押さえておきたいのが categories と rules の関係です。 categories は Oxlint のルールを 7 種類 (correctness / suspicious / pedantic / perf / restriction / style / nursery) で一括 ON/OFF する仕組みで、今回は correctness (明らかなバグ) と suspicious (疑わしいコード) のみ error 、それ以外は off にしています。 優先順位は rules > categories なので、 categories.correctness: "error" で一括有効化したうえで、違反が多くて即修正できないルールだけを rules: { "no-extra-boolean-cast": "off" } で個別に逃がす、という使い方ができます。 次の「違反ルールは一時 off」はまさにこの優先順位を使った運用です。 4. 違反ルールは一時 off → follow-up で再有効化 Oxlint デフォルトの correctness カテゴリには、ESLint で off にしていたルールも一部入っています。本 PR で既存コードに手を入れない方針を取ったため、違反が出るルールは一度 off にしておき、別 PR で違反修正 + 再有効化する形にしました。 具体的には no-extra-boolean-cast , no-unneeded-ternary , no-useless-rename , no-useless-catch , jest/require-to-throw-message , react/jsx-key などです。1 PR にまとめると差分が膨大になって reviewer の負担になるので、ルールごとに小さく切り出すと進めやすかったです。 再有効化漏れを防ぐため、一時 off にしたルールはトラッキングチケットに「ルール名 / 違反件数 / 担当 / follow-up PR」のチェックリストで一覧化し、各 follow-up PR から逆リンクを張る運用にしています。 一方で oxlint-migrate の出力には Oxlint で未対応のルールも並びます。今回は @typescript-eslint/no-floating-promises のような type-aware 系 (本移行のスコープ外として別途段階導入する想定) と、 storybook/* のように代替手段ありで撤退と判断したものが中心でした。 未対応ルールは .oxlintrc.json に書いても効かないため、設定からは削除しています。 5. CI / pre-commit の切り替え // package.json (抜粋) { " scripts ": { " lint:oxlint ": " oxlint --deny-warnings src ", " fix:oxlint ": " oxlint --fix --deny-warnings src " } } Prettier から Oxfmt への移行段階で残していた eslint-config-prettier も含め、ESLint 関連 devDependencies はすべて削除しています。 6. import order は Oxfmt の sortImports に寄せる eslint-plugin-import の import/order ルールに相当する機能は Oxlint native にはありません。Oxfmt の sortImports で customGroups / groups を指定すれば、 pathGroups 相当のグループ分けが再現できるので、こちらに寄せました。 // .oxfmtrc.json (sortImports 部のみ抜粋) { " sortImports ": { " customGroups ": [ { " groupName ": " internal-modules ", " elementNamePattern ": [ " <your-dir-1>/** ", " <your-dir-2>/** " ] } ] , " groups ": [ " builtin ", " external ", " internal-modules ", [ " parent ", " sibling ", " index " ] , " style ", " unknown " ] , " newlinesBetween ": true , " order ": " asc " } } これにより lint と format の責務がきれいに分かれ、import の並び替えは Oxfmt の --fix で完結するようになりました。なお sortImports を有効化したコミットは全ファイルに差分が出るので、 .git-blame-ignore-revs に登録して git blame の汚染を防いでいます。 移行したことによるメリット 効果を数字でまとめると次の通りです。 項目 移行前 (ESLint / Prettier) 移行後 (Oxlint / Oxfmt) 倍率 全件 lint (約 1,300 ファイル) 12.96s 0.35s 約 37 倍 全件 format チェック 5.2s 0.5s 約 10 倍 単一ファイル lint 1.72s 0.43s 約 4 倍 設定行数 (lint) eslint.config.js 約 220 行 + 11 プラグイン .oxlintrc.json 約 80 行 — 数字には表れない次のような効果もありました。 単一ファイル lint が hook に乗る速度に収まり、AI エージェントの編集ループ内で常時走らせられるようになった Claude Code の PostToolUse hook で確実に止められる品質チェックをかけられるようになり、エージェントが見逃すエラーを一段減らせた ESLint 10 への移行で必要になる @eslint/compat のような互換レイヤが不要になった devDependencies が ESLint 本体 + 11 プラグイン分減り、 npm install 時間と node_modules サイズが改善 import order を Oxfmt 側に寄せたことで、lint と format の責務分離がクリアになった 移行したことによるデメリット JS Plugins が Alpha なので、 testing-library / jest-dom を残している部分は互換性が壊れるリスクを抱えている type-aware ルール ( no-floating-promises など) は速度トレードオフで今回意識的に lint 層から外している。Alpha 卒業待ちというより、現状の type-aware は 1 ファイルあたり数秒オーダーで hook ループのミリ秒単位の速さに載らないという判断。該当する欠陥は tsc と統合テストでカバーし、lint 層の速度を死守する方針 ESLint / Prettier に比べてコミュニティが小さく、トラブル時の情報量が少ない さいごに 今回の移行は単なる lint / formatter の乗り換えではなく、AI のフィードバックループそのものを速くするための改善でした。lint を hook ループに載せられるか否かが、AI が書くコードの品質に直接効いてくる時代になっています。 次に視野に入れているのは Type-Aware Linting です。たとえば no-floating-promises は AI が書いたコードで起きがちな await 漏れを検出できるルールで、 tsc だけでは取りこぼしやすく本来 lint で止めたい類の問題です。ただし現状の type-aware lint は速度的にまだ重く、hook の高速ループには乗せづらいため、当面は tsc とテストでカバーしつつ常時 hook には組み込まない方針でいく予定です。 AI 時代のフロントエンド開発環境として、これからもフィードバックループ全体の最適化を続けていきたいと思います。 参考文献 Oxc - The JavaScript Oxidation Compiler VoidZero - The Future of JavaScript Tooling Vite+ - The Unified Toolchain for the Web Oxlint Linter Guide Oxlint Configuration Oxlint JS Plugins (Alpha) Oxlint Type-Aware Linting Oxfmt Beta announcement (2026-02-24) Migrate from Prettier - Oxc oxlint-migrate (GitHub) eslint-plugin-oxlint (GitHub)
はじめに 初めまして、RevComm の 楽桑 と申します。 MiiTel Call Center (CC) フロントエンドで React 18 → 19 のアップグレードを実施した。単なるバージョンアップではなく、 Semantic UI の完全削除 と Recoil から Jotai への移行 もまとめて片付けた。 この記事では、AI Agent(Claude Code)を 効率化ツール として活用し、このプロセスをどう加速させたかを紹介する。 背景 プロジェクト構成 Next.js 14 (Pages Router) + Static Export → S3 配信 UI: Semantic UI (semantic-ui-react) + MiiTel Design System (MDS) + Emotion CSS-in-JS 状態管理: Recoil リアルタイム: Apollo Client + GraphQL Subscriptions (WebSocket) React 19 アップグレードの動機 React 19 の新機能が欲しい、というより 周辺事情が同じ方向を指していた : 依存ライブラリが次々と React 19 を前提にし始めた Semantic UI が React 19 未対応かつメンテ停滞。他のアップデートの足枷になっていた Recoil は公式アーカイブ済み ついでに React 19 本体の改善(型、Actions など)も取り込める 1. React 19 本体より、周辺作業のほうが重かった 1-1. 実際に時間を使ったのは実装以外の仕事だった React 19 本体の breaking changes 対応は、実はそこまで重くなかった。 useRef の引数必須化、 RefObject<T | null> の型変更、 Symbol → string の明示変換 — いずれも機械的な修正で、数時間で片付く。 実際に時間を使ったのは 実装以外の仕事 だった: 30 以上ある依存ライブラリの React 19 対応状況を一つずつ調べる Semantic UI の撤去後に全画面で発生する CSS リグレッションを特定・修正する Recoil から Jotai への移行で、60 個以上の atom と多数の hooks 呼び出しを漏れなく置換する セッションをまたいで「前回どこまで進んだか」を記録・引き継ぐ React のメジャーアップグレードは「バージョンを上げる作業」より「上げるために必要な調査と確認」のほうが圧倒的にコストが大きい。 1-2. AI Agent を入れて変わったこと Claude Code を導入して変わったのは、コードを書く速度もだが、それ以上に 判断に入るまでの時間 が短くなった。 「このライブラリは React 19 に対応しているか?」「この CSS の差分は直すべきか受け入れるべきか?」— こういった判断の前提となる情報収集・整理・比較を AI に任せることで、人間は「何を採用するか」「その差分を受け入れるか」の意思決定に集中できた。 2. Claude Code に任せたのは「判断以外」 「判断以外」という境界にたどり着いたのは、最初からではない。まず丸投げして失敗し、そこから「何を任せて、何を任せないか」を学んだ。 2-1. 最初の失敗 ― ボタン移行を “丸投げ” したら崩壊した 最初は役割分担を決めず、AI に全部任せてみるところから始めた。 「Semantic UI の Button を全部 MDS の Button に置き換えて。」 Claude Code は長時間かけて大量のファイルを書き換えた。diff はそれっぽく見えたが、ブラウザで開くとボタンのサイズがページごとにバラバラ、アイコンが消えている箇所があり、Primary と Secondary が入れ替わっている箇所まであった。 実際のコミット履歴がその過程を物語っている: 3/5 refactor: Migrate button components to MDS ← 初回の一括移行 3/5 fix: Match reset and edit button styles ← 即座にスタイル崩れ発覚 3/5 fix: correct edit button text color 3/5 fix: match edit button layout and height 3/10 fix: resolve interactive element nesting 3/11 feat: introduce SecondaryButton component ← ラッパーコンポーネントが必要と判明 3/13 feat: enhance button styling (8コミット連続) 3/14 feat: replace SecondaryButton with StyledButton 3/17 feat: replace LinkIconButton with SupportLinkButton 3/17 fix: unify LinkButton primary colors ← 12日後、やっと安定 合計: 42 コミット / 36 ファイル / +968 -677 行 Semantic UI の Button は props が多彩で、MDS とは一対一で対応しない。つまりこの変換は機械的な一括置換ではなく、各使用箇所で “MDS ではどう表現するか” を決める判断の集合だった: Props マッピング(実際の PR より): label → children ← 機械的 icon → startIcon ← 機械的 isLoading → loading ← 機械的 isFullWidth → fullWidth ← 機械的 color = "alert" → variant = "negative" + CSS 上書き ← 判断が必要 color = "plain" → variant = "default" + 白背景上書き ← 判断が必要 color = "secondary" → SecondaryButton ラッパー新規作成 ← 判断が必要 isLinkButton → LinkButton / LinkIconButton 新規作成 ← 判断が必要 判断が必要な箇所を丸投げしたので、AI はそれっぽく見える変換を量産しただけになった。 2-2. 学び ― “丸投げ” ではなく “分解して委譲” する この失敗から、移行作業は 3 ステップに組み替えた: 対応が必要なファイルを AI に洗い出させる(この段階では置換しない) ファイルごとに対応コストを分類する(機械的置換 / props 読み替え判断 / 代替なし・自前実装) コスト大きい方から着手し、人間のレビューを必ず入れる この流れに変えた瞬間、型崩れはほぼ消えた。AI Agent は “網羅” と “分類” には強いが、判断が混ざった実装を丸ごと投げると表面的な正しさで走ってしまう。判断は人間が先に固めて、AI にはその方針に沿った実装だけを任せる。これが一番効いた基本動作だった。 2-3. 依存ライブラリ調査を AI に任せる package.json を起点に、全依存ライブラリの React 19 対応状況を横断調査させた。 Claude Code に投げたプロンプト(要約): package.json を読んで、全依存ライブラリの最新バージョンと React 19 対応状況を調査。対応済み / 未対応 / 要調査 で分類した表を作って。 未対応のものは issue やリリースノートへのリンクも付けて。 返ってきた整理表: ライブラリ 状態 備考 react-hook-form ✅ 対応済み v7.52.0 emotion ✅ 対応済み - react-konva ✅ 対応済み v19.0.1 Semantic UI ❌ 未対応 メンテ停滞 Recoil ❌ アーカイブ済み Jotai へ置き換え react-draggable ⚠️ findDOMNode 依存 自前実装必要 この表があるだけで 意思決定のコストが激減する 。人間は「何から片付けるか」を決めるだけでよくなった。 2-4. Recoil → Jotai の置き換えを AI と分担する Recoil は Meta が公式にアーカイブ済みで、React 19 の concurrent features との相性も怪しくなっていた。このタイミングで Jotai に乗り換えた。 両者とも「atom 単位で状態を管理する」思想は同じなので、API のマッピングは素直。ただし atom が 60 個以上、 useRecoilState / useRecoilValue / useSetRecoilState の呼び出しはそれ以上ある。機械的に置換できる部分を最大化しつつ、微妙に違う箇所だけ個別判断、という進め方にした。 // Before: Recoil import { atom, useRecoilState } from 'recoil' ; const selectedAgentAtom = atom< Agent | null >( { key : 'selectedAgent' , // 一意な key が必須 default : null , } ); const [ agent , setAgent ] = useRecoilState(selectedAgentAtom); // After: Jotai import { atom, useAtom } from 'jotai' ; const selectedAgentAtom = atom< Agent | null >( null ); // key 不要 const [ agent , setAgent ] = useAtom(selectedAgentAtom); 移行の進め方: grep で atom({ / selector({ / useRecoilState / useRecoilValue / useSetRecoilState を全検出 ファイル単位で Claude Code に変換を指示、diff をレビュー 型チェック & テスト → 次のファイルへ この規模の置換はまさに「網羅性が必要な機械的作業」で、AI との分担が効く典型例。 人間がやると「このファイルは変えたっけ、こっちはまだだ」となりがちな作業を、AI が一切こぼさずに進めてくれる。 2-5. Storybook MCP でコンポーネントのバリエーションを網羅する Semantic UI → MDS の移行では、「Semantic UI の Dropdown を MDS では何に置き換えるのか」「どんな props があるのか」「どの Story で確認できるのか」を調べる段階で時間を取られる。 Storybook MCP( @storybook/mcp / @storybook/addon-mcp )は、この「コンポーネントを知る」段階を AI Agent に任せるツール。Storybook 上のコンポーネント一覧、各コンポーネントの API・props・Story 情報を AI に提供する。 人間 : 「Dropdown を MDS に移行して」 AI Agent : 1 . Storybook MCP で Dropdown のドキュメント・props・Story 一覧を取得 → MDS Select / Menu が候補、Single / Multi / Searchable のバリエーションがあると把握 2 . Storybook MCP で各 Story のプレビュー URL を取得 3 . chrome - devtools MCP でその URL を開き、getComputedStyle でベースライン取得 4 . コードを置き換え 5 . 再度 chrome - devtools MCP で計測し、差分を比較 Storybook MCP が「何があるか、どこで見られるか」を提供し、chrome-devtools MCP が「実際に開いて計測する」。この 2 つの MCP の組み合わせで、AI Agent がコンポーネントのドキュメント読みからビジュアル検証までを一貫して行える。 2-6. 進捗記録を AI に任せる 作業セッションが終わるたびに、Claude Code に 進捗サマリを Notion に書かせる 運用にしている。 以下は実際に Notion に蓄積された記録の抜粋。Semantic UI 移行状況は、セッションごとに自動更新される: Semantic UI → MDS 移行状況(4/10 更新) ✅ 完了: MDS 代替あり(16ファイル) Popup系(6ファイル)→ MDS Tooltip / Popup Tab系(4ファイル)→ MDS Tabs Dropdown系(6ファイル)→ MDS Select / Menu Input系(2ファイル)→ MDS Input ✅ 完了: MDS 代替なし(4ファイル) List(2ファイル)→ HTML ul/li Statistic(1ファイル)→ HTML div + Emotion CSS Transition(1ファイル)→ CSS animation + useFadeAnimation hook ✅ semantic-ui-react / fomantic-ui-css パッケージ削除済み MDS canary テストの結果もイテレーションごとに記録される: イテレーション 変更内容 結果 1 回目 peer deps + testing-library 更新 ✅ 型チェックパス / ✖ 1 test suite 失敗(React 内部 API に依存したビルド構成が React 19 でエラー) 2 回目 styled-components 6.4.0 ✖ 同じエラー継続 3 回目 vite.config external に react/jsx-runtime 追加 ✅ 117/117 test suites 全パス こういう表が 作業の副産物として Notion に残る 。後から見返す・チームに共有する・引き継ぐ、のどれも低コストでできる。手動で議事録を書く必要がなく、作業しているだけでドキュメントが蓄積される。 この蓄積は人間が確認するときの根拠になるだけでなく、 AI Agent にとっても重要なコンテキスト になる。次のセッションで Claude Code が前回の記録を読み込むことで、「前回どこまで進んだか」「どのアプローチがうまくいった / いかなかったか」を踏まえて作業を再開できる。人間が「これどうだったっけ」と振り返る根拠であり、AI が次の一手を打つための判断材料でもある。 3. 視覚リグレッションを AI と一緒に潰す 3-1. 一番つらかったのは、テストでは拾えない崩れだった main (修正前) PR (移行後) Semantic UI を剥がすと、 コード上は何も壊れていないのに全画面の見た目が微妙に崩れる 。 fomantic-ui-css (Fomantic UI は Semantic UI のコミュニティフォークで、CSS テーマ部分を提供するパッケージ) がグローバルに撒いていた reset CSS、Lato フォント、line-height などが一斉に消えるため。 pnpm ts → PASS、 pnpm build → PASS、 pnpm e2e:run → 40/40 passed。しかしブラウザで開くと、チェックボックスのサイズが違う、日付入力の幅がずれている、行間が微妙に変わっている。 ビルド成功 ≠ UI 正常 。 3-2. 目視の根性論ではなく、差分の観測に寄せた Chrome-devtools MCP(MCP = Model Context Protocol。AI Agent が外部ツールを操作するための標準プロトコル)を使い、main と PR で同じ要素の getComputedStyle を自動取得して比較する方式にした。「何が違うか」を先に機械的に洗い出してから、人間が判断する流れ。 3-3. reset CSS 欠落で input margin が復活した Semantic UI はコンポーネントライブラリであると同時に、 fomantic-ui-css (Fomantic UI は Semantic UI のコミュニティフォークで、CSS テーマ部分を提供するパッケージ)というグローバル CSS もバンドルしていた。この CSS には normalize / reset ルール( input { margin: 0 } 、 body { overflow-x: hidden } など)が含まれており、アプリ全体が暗黙的に依存していた。Semantic UI のコンポーネントを全て MDS に置き換えた後、 fomantic-ui-css ごと削除したことで、これらのリセットルールが消失した。 上記二つの画像の場合, chrome-devtools MCP 経由で getComputedStyle を main / PR で比較。 実際のアウトプット: // main (/callcenter/report/) - label 内部構造 { " tag ":" LABEL ", " rect ": { " w ": 30 ," h ": 30 } , " margin ":" 0px " } { " tag ":" INPUT ", " rect ": { " w ": 30 ," h ": 30 } , " margin ":" 0px ", " border ":" 1px solid rgb(23, 100, 233) " } { " tag ":" SPAN ", " rect ": { " w ": 14 ," h ": 21 } , " text ":" 日 " } // PR (/callcenter_preview_4868/report/) - 同じ label { " tag ":" LABEL ", " rect ": { " w ": 37 ," h ": 36 } , " margin ":" 0px " } { " tag ":" INPUT ", " rect ": { " w ": 30 ," h ": 30 } , " margin ":" 3px 3px 3px 4px ", " border ":" 1px solid rgb(23, 100, 233) " } { " tag ":" SPAN ", " rect ": { " w ": 14 ," h ": 21 } , " text ":" 日 " } INPUT の margin が 0px vs 3px 3px 3px 4px 。fomantic が提供していた input { margin: 0 } が消え、ブラウザデフォルトの margin が復活していた。label のサイズが 30×30 → 37×36 に膨張。 reset.css に追加して解決。 3-4. 大事だったのは「直す差分」と「受け入れる差分」を分けること Semantic UI と MDS は別物なので、MDS に寄せた時点で見た目が変わる箇所は必ずある。検出された差分のうち、 直さない差分のほうが件数は多い 。 直すべき 受け入れるべき 機能の破壊(クリック領域、要素の重なり) デザイントークン由来の変化(色・余白・書体・ラディウス) 意図しない結果(リセット CSS 欠落、フォールバック) MDS 側の改善(focus ring、ARIA 属性) ユーザーが混乱するレイアウト崩れ UX の改善 ピクセル一致ではなく、 「意図しない破壊がないこと」 を目的にした。 3-5. ここでの AI と人間の境界 AI は差分検出と原因特定の補助が得意。一方、その差分が仕様か不具合かを判断するのは人間の仕事。この分担が、視覚リグレッション対応では特に効いた。AI が差分をピクセル単位で絞り込み、人間がその箇所を重点的に目視確認する。 4. 手順と判断基準を Skill にする アップグレード作業の佳境に入った頃、社内の別チームが Claude Code の Skill を使って移行作業を標準化していることを知った。そのアプローチを参考に、今回の Semantic UI → MDS 移行にも Skill を導入した。 4-1. 単発で使うだけでは、再現性が出ない コンポーネント移行は似た作業の繰り返しになる。そのたびにプロンプトを考えるのは非効率だし、個人技のままだと品質もぶれやすい。 4-2. Semantic UI → MDS 移行の流れを Skill 化した Claude Code の Skill(再利用可能なカスタムコマンド) として、移行ワークフローを標準化した。 実行すると AI Agent が以下を自動で進める: 対象コンポーネントの使用箇所と Storybook を網羅的に検索 chrome-devtools MCP でベースラインの getComputedStyle とスクリーンショットを取得 MDS の対応コンポーネントに置き換え 移行後の getComputedStyle とスクリーンショットを取得し、ベースラインと比較 差分を分類して対応 4-3. Skill に埋め込んだのは、手順だけではない 差分を 3 層に分類する判断基準まで含めて標準化した: 自動修正層: 意図しない破壊(リセット CSS 欠落など)→ AI が修正 人間判断層: デザイントークン由来かもしれない差分 → レポートして人間に判断を戻す 記録のみ層: MDS 仕様として正しい差分 → ログに残す 手順だけでなく 判断基準を埋め込む ことで、別メンバーが別コンポーネントを移行するときも同じ品質で作業できる。この Skill は社内マーケットプレイスに登録しており、チーム内の誰でも同じコマンドで移行作業が回せる。 4-4. AI 活用を「うまく使える人」依存にしない AI を使うこと自体ではなく、 AI が機能する作業設計のほうが重要 だった。再利用可能な形にしておくことで、プロンプトの書き方や AI への指示の仕方を個人に依存させない。 5. まとめ 5-1. AI Agent が速くしたのは、実装そのものより「判断に入るまで」 調査、整理、比較、記録のコストを大きく下げられた。React 19 本体の breaking changes 対応よりも、依存ライブラリ調査・CSS リグレッション検証・レビュー対応の効率化のほうが AI Agent の貢献が大きかった。 5-2. 実務で AI Agent を使うなら、まず任せる範囲を決める AI に任せる: 網羅・比較・記録のような、正確さと量が求められる作業 人間が担う: 意図と責任が伴う判断 この境界を先に決めると、使い方がぶれにくい 5-3. 今回の学び AI は万能ではないが、 作業の前後にある重い仕事 にはかなり効く 特に、大規模移行のような「調査と確認が多い仕事」と相性がよかった 単発活用より、 手順と判断基準を仕組みに落とす ほうが持続的に効く ※ 今回の Skill はアップグレード作業の佳境に入った頃に導入したため、恩恵は限定的だった。最初からあれば、繰り返し作業の品質と速度をもっと引き上げられたはずだ。 使用ツール: Claude Code / chrome-devtools MCP / GitHub CLI / Notion MCP
はじめに MiiTel Phone の開発チームでは Claude Code などの AI コーディングエージェントを活用して、生産性やコード品質の改善などに取り組んでいます。 本記事では実際に取り組んでいる内容や課題などについて紹介します。 実施している取り組み UI コンポーネントの実装の効率化 Figma MCP などを活用して、デザインデータから React コンポーネントを生成しています。Storybook 向けのストーリーの生成も任せることで、コーディングエージェントが実装してくれた UI コンポーネントをすぐに確認することができて便利です。 help.figma.com また、React Grab を活用することで、ちょっとした UI の手直しなどが行いやすくなりました。 github.com コードレビューの効率化 GitHub Copilot code review や Devin Review などをコードレビューに活用しています。工夫している点として、過去に発生したバグや実装上注意すべき点などを Markdown ファイルにチェックリスト形式でまとめておき、それらを .github/copilot-instructions.md や REVIEW.md から参照させることで、実装時の考慮漏れを指摘してもらえるようにしています。 <!-- 例) .github/copilot-instructions.md のイメージ (実際のコードとは異なります) --> ## Code Review - [ チェックリスト ]( ../docs/checklist.md )で指示されたチェックを実施してください。 - 日本語でコメントしてください。 - [ ` openspec/specs/*/spec.md ` ]( ../openspec/specs/ )で定義された要求に対する違反がないことをチェックください。 今のところ、GitHub Copilot code review はきちんとチェックリストを参照した上でレビューを実施してくれており、意図せず問題が再発してしまうことの防止に役立っています。 弊社における GitHub Copilot code review の活用例についてはぜひ下記記事も参照ください! tech.revcomm.co.jp 問い合わせやバグなどの調査 Pup CLI という Datadog と連携するための CLI ツールが公開されています。 github.com Pup CLI を使うことで、 Datadog のログの検索などをコマンドラインから行うことができます。 様々な用途に活用できそうですが、Pup CLI の大きな特徴として Agent Mode というコーディングエージェントとの連携を想定した機能が提供されています。 github.com 具体的には、Claude Code などのコーディングエージェント経由での Pup CLI の実行が検出された場合、Pup CLI は自動的にコーディングエージェント向けに最適化された出力を返却してくれます。 注意点として、Pup CLI を使用する際は意図せずコーディングエージェントにセンシティブな情報が渡されてしまわぬよう注意すると良いと思います。Datadog に送信するログなどはきちんと精査し、必要に応じてマスキングを実施するなどの対応を行なっておくと、より安心して連携できます。 仕様駆動開発 コーディングエージェントを使うようになってから、実装スピードや問い合わせ・バグなどの調査速度はかなり改善されたと感じるものの、既存コードの背景や意図、仕様などの暗黙的な知識をどうやってコーディングエージェントに伝えるかという点を課題に感じていました。 対策としては信頼性の高いテストコードの割合を増やし、コーディングエージェントが意図せぬ変更を実施した際にきちんと捕捉できるようにする仕組みを整備する方法などが考えられます。しかし、コーディングエージェントが意図せずテストコードの方を修正して強引にテストを通そうとするケースも多々見受けられ、この手法にも限界がありそうです。 これらの課題の解決策として仕様駆動開発が有効なのではないかと思い、試しています。 過去の変更の背景をきちんとリポジトリ内で文書化・蓄積できるようにし、そして変更を実際にコーディングエージェントに依頼する前にあらかじめ要求や背景などの情報を伝えられるようにすることで、意図せぬ変更を防止しつつ、よりスムーズに機能を実装できるようになることを期待しています。 仕様駆動開発を補助する OSS としては、下記のツールが人気であると思います。 Spec Kit OpenSpec MiiTel Phone チームでは OpenSpec を採用しています。理由としては以下です。 OpenSpec は TypeScript 製であり、普段使用しているフロントエンド関連のツールのみでの導入および管理が可能で、より手軽に試しやすかったこと openspec/specs/*/spec.md に Source of truth として機能する仕様を蓄積し、 openspec/changes/archive/ に過去の時点における実装の背景などがスナップショットとして蓄積されてゆく作りであるため、導入の目的により近いこと また、OpenSpec は既存のプロダクトの開発に後から導入したケースにおいてもうまく機能してくれるのが良い点です。 openspec/specs/*/spec.md に、段階的に既存機能の仕様や要求などを追加していくことができます。 例えば、以下は spec.md の例です。 spec.md を記述する際は、所定のフォーマットに準拠していることを検証できるよう、 husky や lefthook などのツールで openspec validate コマンドの実行を自動化しておくと便利です。 # Example Specification ## Purpose This app supports a simple, reliable TODO management experience for individuals. ## Requirements ### Requirement: Creating and managing TODO items The TODO app MUST allow a user to create, view, update, complete, and delete TODO items. #### Scenario: Creating a new TODO - **GIVEN** a user is signed in to the TODO app - **WHEN** the user enters a title and submits the form - **THEN** a new TODO item is created with status "Incomplete" - **AND** the new TODO item appears in the TODO list #### Scenario: Editing an existing TODO - **GIVEN** a TODO item exists - **WHEN** the user updates the TODO title and saves - **THEN** the TODO item shows the updated title - **AND** the TODO item's completion status remains unchanged #### Scenario: Completing a TODO - **GIVEN** a TODO item exists with status "Incomplete" - **WHEN** the user marks the TODO item as completed - **THEN** the TODO item's status becomes "Completed" - **AND** the TODO item is visually distinguished as completed in the list #### Scenario: Deleting a TODO - **GIVEN** a TODO item exists - **WHEN** the user deletes the TODO item - **THEN** the TODO item is removed from the TODO list - **AND** the TODO item MUST NOT appear in future list results ### Requirement: Filtering and searching TODOs The TODO app SHALL allow a user to filter and search TODO items. #### Scenario: Filtering by completion status - **GIVEN** the user has both completed and incomplete TODO items - **WHEN** the user filters the list to "Incomplete" - **THEN** only incomplete TODO items are displayed #### Scenario: Searching by keyword - **GIVEN** the user has multiple TODO items with different titles - **WHEN** the user searches for a keyword that matches some TODO titles - **THEN** only TODO items with titles matching the keyword are displayed まだあまり OpenSpec の導入による効果は実感できていないというのが正直なところではあるのですが、以下の点を期待して、少しずつ使用を進めています。 OpenSpec は特定のエージェントに依存せず、Markdown ベースのシンプル かつ 一貫した形式で要求を記載してゆくため、人にとってもコーディングエージェントにとっても内容を理解しやすい 仮にうまく仕様駆動開発がワークしてくれなかったとしても、導入・検証の過程でリポジトリ内に追加したドキュメントはそのまま残るため、全く無駄になってしまうことはないであろうと判断しました 定型的タスクの効率化 RevComm では社内向けの Claude Code プラグインマーケットプレイスが整備されており、定型的タスクを効率化するための様々なコマンドやスキルが蓄積されています。問い合わせの調査に特化したスキルやその他日頃の雑多なタスクを効率化するためのスキルやコマンド群が蓄積されており、日頃の業務の効率化に活用しています。 社内向けプラグインマーケットプレイスがあると、仕組みを手軽に活用・共有できるようになり、とても便利に感じています。 ガードレールの整備 コーディングエージェントが意図せぬ振る舞いをしてしまうことがどうしても出てくるため、ガードレールを整備して、より安心して利用できるようにすることが重要と感じています。具体的には以下のような取り組みが効果的であると思います。 信頼性の高いテストコードの比率を増やす tech.revcomm.co.jp ESLint の設定の厳格化やプラグインの追加により、コーディングエージェントのミスを早期タイミングで捕捉できるようにする 意図せぬ振る舞いをした際は適宜、 AGENTS.md を見直す 課題 コードレビュー コーディングエージェントにより開発速度が大幅に向上しても、人によるコードレビューやテストはどうしても時間がかかってしまいがちです。 GitHub Copilot code review や Devin Review などは、コードレビュー時の見落とし防止などに活用できて非常に便利ではあるものの、どうしてもコードレビューやテストには時間がかかってしまいがちです。 対策としては、一つ一つの PR をできる限り小さくすることで、レビューの手間を軽減したり手戻りを減らすことなどが期待できますが、この方法にも限界はあります。 取り急ぎできることとしては信頼性の高いテストコードの割合を徹底的に高め、CI などで迅速にリグレッションの発生を検知できるようにしつつ、レビュープロセスを簡素化できるようにすることが良いのではないかと感じています。 また、レビューにどうしても時間がかかってしまう問題については、レビューに関する方針を見直す必要性が生じてくるのではないかと考えています。逐一、細かなレビューは実施せず、どこかのタイミングで一括してレビューを実施することで効率を改善するなどの方法が必要ではないかと思います。そのためには、コード変更に関するトレーサビリティーを改善することも重要ではないかと思っています。例えば、下記のようなツールがこの点に貢献してくれるのではないかと期待しており、ぜひ今後、試してみたいです。 github.com github.com 意図せぬ UI の生成 Figma MCP などを活用することで、UI コンポーネントの生成は大幅に効率化されますが、時折、コーディングエージェントが意図せぬレイアウトでコンポーネントを出力を生成するケースが見受けられます。 この課題は今後のモデルの進化に伴い改善されてゆく可能性は高いと思いますが、現時点では Storybook の活用などによりスムーズに UI を確認できるようにしておくと、コーディングエージェントに素早く修正依頼ができるため、効率的ではないかと感じています。 おわりに Claude Code などのコーディングエージェントの活用を始めてから、コードの記述は大幅に効率化されました。しかし、膨大な変更を一度に任せてしまうと、巨大な PR ができてしまいレビューの負担が増したり、精度が低下して却って手戻りが増えてしまうなどの問題も発生してしまいます。できる限り PR のサイズを小さくしつつ、コーディングエージェントに並列で作業を任せるのが現時点では効率的ではないかと感じています。 また、コーディングエージェントはとにかく変化が激しいため、継続的な振り返りなどに基づいて、適宜、使い方や運用を見直したり、チーム内外で情報を共有することが重要と感じました。 本記事の内容が参考になれば幸いです。
はじめに こんにちは、Research Engineerの春日です。宇都宮で開催された言語処理学会第32回年次大会(NLP2026)に参加し、「生成的ASR誤り訂正におけるWeb検索の活用と文脈処理の最適化」というタイトルで発表しました。 本学会ではポスター発表形式で研究成果を報告し、幸いにも多くの参加者の方々と活発な議論を交わすことができました。本レポートでは、私たちが発表した研究の概要に加え、会場の様子や、他の参加者の興味深い研究をご紹介します。 学会の概要 https://www.anlp.jp/nlp2026/ 言語処理学会(ANLP)は、自然言語処理(NLP)に関する研究・技術の発展と普及を目的とした、国内最大級の学術コミュニティです。年次大会では、大学・研究機関・企業の研究者や実務者が一堂に会し、最新の研究成果や応用事例について、口頭発表・ポスター発表・デモ展示など多様な形式で議論します。 本大会で扱われるテーマは、言語モデル、音声認識・音声合成、機械翻訳、情報検索、対話システム、要約、評価手法、データセット構築、社会実装や倫理・安全性など幅広く、基礎研究からプロダクトに直結する取り組みまでを俯瞰できる点が特徴です。参加者同士の議論を通じて、研究の課題設定の妥当性や評価設計の改善点が見つかるほか、新たな共同研究やプロジェクトのきっかけが生まれやすい場にもなっています。 また、後述の通り年次大会の規模は年々拡大しており、本大会は発表件数が歴代1位の797件となりました。各セッションにはひっきりなしに聴講者が訪れ、発表時間の90分間はノンストップで議論が続く状況でした。 開催場所 期間:2026年3月9日 ~ 3月13日 会場:ライトキューブ宇都宮 (栃木県宇都宮市) https://light-cube.jp/ 参加者数・発表件数・スポンサー数 事前+直前参加登録数: 2,236人 (歴代2位) 発表件数:797件 (歴代1位) スポンサー数:100団体 (歴代2位) 報告内容のご紹介 題目 生成的ASR誤り訂正におけるWeb検索の活用と文脈処理の最適化 背景・モチベーション 自動音声認識(ASR)技術は近年大きく発展していますが、希少語彙や最新語彙など「未知語」の誤認識は依然として解決が難しい課題です。大規模言語モデル (LLM)を用いた生成的誤り訂正(GEC)はその対処法として注目されていますが、LLMの学習データに含まれない語彙は修正が困難で、ハルシネーションのリスクもあります。 本研究では、Web検索を組み合わせた後処理アプローチでこの問題に対処しました。以下に提案手法の詳細を説明します。 提案手法 Web検索を用いたGECパイプライン 提案手法は、以下の3ステップからなるパイプラインです。 クエリ生成 :LLMがASR出力と周辺の文脈を読み込み、誤りを含む可能性のある語彙の情報を得るための検索クエリを生成します。 Web検索 :生成されたクエリを外部検索エンジンに投入し、最新情報を取得します。これにより、LLMの学習データに含まれない未知語の修正候補を外部知識として補完できます。 訂正実行 :LLMが検索結果と文脈を照合し、誤り箇所を適切な表記へと修正します。 周辺文脈の拡張 訂正の精度は、LLMに与える「文脈の範囲」に大きく依存します。本研究では以下の3段階の文脈設定を比較しました。 局所的文脈 :誤りを含む発話1文のみを入力。最小限の情報で訂正を試みます。 大域的文脈 :対話全体の書き起こしを入力。会話の流れや話題を参照することで、より適切な修正候補を導きます。 超大域的文脈 :対象の対話に加え、データセット全体から「同一の誤り文字列」を含む他の対話を検索・抽出して統合したものを入力。複数の対話に分散しているヒントを集約し、訂正の手がかりを最大化することを狙っています。 要約アプローチ 長大な文脈はLLMのコンテキスト長や推論コストの観点で課題が生じます。そこで、長大な文脈をそのまま入力する代わりに、LLMに文脈から意味的・音響的特徴を推測した要約を生成させてから入力する「要約アプローチ」も検討しました。 データセット 本研究では、既存のベンチマークでは評価が難しい「最新語彙」に特化したデータセットを独自に構築しました。構築手順は以下の通りです。 最新語彙の選定 :実験当時のモデルに含まれない最新語彙(固有名詞・新語など)を30語選定しました。 対話テキストの生成 :LLMにペルソナを付与し、選定語彙を含む2者間の自然な対話テキスト(20ターン以上)を生成しました。その後、TTS (Text-to-Speech)で音声合成しました。 ASR出力の取得とアノテーション :Whisper-large-v3でASRを実行し、誤り箇所を人手でアノテーションしました。最終的に全309箇所の誤りを収集し、ユニークな誤りは204種類となりました。 主な実験結果 Web検索の効果 :全条件において「検索あり」が「検索なし」を上回り、未知語の訂正精度が向上しました。 文脈範囲の影響 :Geminiは文脈が広がるほど精度が向上し、超大域的文脈+Web検索で正答率最高の0.797を達成しました。一方、ClaudeやLlamaは超大域的文脈になると性能が低下、または伸び悩む傾向が見られました。 要約の有効性 :モデル間で結果が二分し、Claude・Llamaは要約によりノイズが減って性能が向上した一方、Gemini・Qwen3は情報欠落により性能が低下しました。 考察・まとめ Web検索の導入は未知語訂正に対して一定の効果があります。ただし、文脈範囲の拡張や要約の活用はモデルの長文脈処理能力に依存するため、一律に有効とは言えません。実運用においては、推論コストと精度のバランスを考慮しながら、文脈範囲や要約の有無をモデルに合わせて切り替えるシステム設計が有効と考えられます。 いただいた主なご質問 Q1. 対話データの多様性はどの程度確保できているのか?実際の対話とどの程度近いのか? 対話テキストの生成プロセスは、Taoらの手法 *1 を参考にしています。まず、職種、立場(メンバー・リーダー・マネージャー)、態度(ポジティブ・ネガティブ・ニュートラル)がそれぞれ異なる多様なペルソナをLLMで生成します。次に、それらのうち異なる2者間を、それぞれ顧客と営業として対話データを生成しています。そのため、各データは異なる対話の展開になるよう設計されています。 一方で、合成対話データが実データとどの程度近いかという点については評価できていません。実データにおける本提案手法の有効性の検証は今後の課題となっています。 Q2. Web会議など複数者間の対話でも応用できるか? 可能です。ただし、話者が増えるほど参照すべき「誰が何を言ったか」の追跡が難しくなり、誤り訂正時の根拠となる文脈が散逸しやすくなると考えられます。 Q3. 入力の周辺文脈は実際何トークンぐらいの入力になっているのか? 実験条件やデータによって大きく変わりますが、概算としては、 局所的文脈 (近傍数発話):数十〜数百トークン程度 大域的文脈 (対話全体):数千トークン程度 超大域的文脈 (特定の対話に加えてを追加):1万トークン以上 となり得ます。本研究では入力トークン数と正答率間の相関関係は捉えず、あくまで入力の方法によって文脈の長さを定義づけていますが、今後はトークン数そのもの(推論コスト)と精度の関係も含めて定量的に評価し、実運用での設計指針として整理する必要があります。 Q4. 対象の未知語以外の無関係な箇所まで修正されることはないのか? 本研究では、未知語の誤認識箇所に対する訂正精度のみを評価しており、誤り検出の性能や、それに伴う不適切な修正については評価対象としていません。一方、実運用に向けては、誤り検出を含めたパイプライン全体の性能を文字誤り率(CER)などで評価する必要があり、多角的な検証が今後の課題です。 気になる発表 学会の規模が大きいだけに、興味深い研究はいくつもありましたがここでは3件だけご紹介させていただきます。 音声・テキストペアが存在しない状況におけるTTSおよびSTTによる合成データ混合を用いたASR学習 *2 ASRモデルの精度向上には、大量の音声・テキストのペアデータが必要ですが、収録や文字起こしにはコストがかかります。本研究では、音声・テキストのペアが一切存在しない状況を想定し、新聞記事テキストから作成したTTSデータと、自然音声からSTT (Speech-to-Text)で生成したテキストを組み合わせ、合成データのみでWhisperをファインチューニングする手法を提案・検証しています。 実験では、TTSデータのみでは性能が悪化する場合がある一方で、TTS・STTを1:1で混合した場合は、音声・テキストのペアデータ(Original)でのファインチューニングと同等以上のCER改善が得られることが示されました。また、STTの比率が高いほどCERが小さくなる傾向があり、自然音声由来のSTTデータがTTSの音響的な弱みを補完していると考察されています。さらに、ポスター発表時には、TTSデータが4,990時間に対してSTTデータが10時間というごく少量の混合でもCERが改善したという、非常に興味深い結果が示されていました。 実運用では「ペアデータを用意できない」場面が多く、異なる由来のTTS・STT合成データを混合するという実践的なアプローチが示された点が印象的でした。弊社でも実音声へのアノテーションにコストがかかるという課題があるため、合成データを活用したASRモデル更新の一つの指針として、特に関心を持ちました。 大規模言語モデルによる日本語診療テキストからの人名抽出 *3 診療テキストの二次利用には個人識別情報の非識別化が必要ですが、人手作業はコストが高く、自動化が求められています。本研究では、東北大学病院の経過記録を対象に、ファインチューニング済みBERT(ModernBERT-Japanese)とzero-shot LLM(Qwen3-32B)による人名抽出性能を比較しました。疑似人名を埋め込んだデータセットを用い、学習データに含まれる「既知」人名と含まれない「未知」人名に分けて評価した点が特徴です。 全体ではBERT(F1=0.971)がLLM(F1=0.932)を上回りましたが、未知人名のみを対象とした場合はLLM(F1=0.899)がBERT(F1=0.876)を上回りました。LLMの主な誤りは施設名の誤抽出(55.6%)で、Precisionが低い一方、Recallは高い傾向がありました。 弊社でも対話データにおける個人情報マスキングの需要が高まっているため、学習データで人名を十分に網羅できる場合はBERT、未知人名が多い別施設・別期間のデータに適用する場合はLLMが有効という、実運用上の使い分け指針を示した点が実践的で印象に残りました。 LLMベース文法誤り訂正における編集の多数決による過剰訂正の抑制 *4 LLMによるGECでは、文法的に正しい箇所まで書き換えてしまう「過剰訂正」が頻発します。本研究では、モデルの追加学習なしでこの問題に対処するため、単一LLMから複数の訂正候補を生成し、編集レベルの多数決により、投票数が閾値以上の候補にのみ現れた訂正を採用するアンサンブル推論手法を提案しています。投票数の閾値を調整することで、訂正の抑制度合いを柔軟に変えられる点が特徴です。 gemma-2-9b-itやLlama-3.1-8B-Instructを用いた4-shot実験では、過剰訂正が問題となりやすい低誤り密度ドメイン(CWEB-G)でF0.5が大きく向上しました。追加学習済みモデル(EPO)には効果がなかった点から、提案手法は「過剰訂正を抑制するための推論時アンサンブル」として位置づけられます。私たちの研究と同様にGEC分野の取り組みであり、LLMを実用する際の手軽な改善手法として参考になる研究でした。 最後に 私の発表の日、宇都宮は21年ぶりに10センチを超える大雪に見舞われましたが、そのような状況でも会場は熱気に包まれ、参加者で溢れかえるほどでした。大会のSlackでは参加者同士で活発な議論や暖かなふれあいがあり、近隣の飲食店やお土産の情報交換もあるなど、大規模ながらもアットホームな雰囲気に包まれた素敵な大会でした。宇都宮餃子も美味しかったです。 最後に、当日ブースにお立ち寄りいただき、ご質問やコメントをくださった皆さまに感謝いたします。議論を通じて得られた示唆を今後の研究・開発に反映していきます。 参考文献 *1 : Tao Ge, Xin Chan, Xiaoyang Wang, Dian Yu, Haitao Mi, and Dong Yu. Scaling synthetic data creation with 1,000,000,000 personas, 2025. https://arxiv.org/abs/2406.20094 *2 : 野田 陽, 酒井 眞, 杉野 かおり, 田森 秀明, 岡崎 直観, 乾 健太郎. 音声・テキストペアが存在しない状況におけるTTS および STT による合成データ混合を用いた ASR 学習, 2026. https://www.anlp.jp/proceedings/annual_meeting/2026/pdf_dir/C2-20.pdf *3 : 岩瀬 裕哉, 柴田 大作, 大西 颯真, 辻川 剛範, 渡辺 純子, 石井 亮, 中川 敦寛, 香取 幸夫, 久保 雅洋. 大規模言語モデルによる日本語診療テキストからの人名抽出, 2026. https://www.anlp.jp/proceedings/annual_meeting/2026/pdf_dir/Q3-8.pdf *4 : 五藤巧, 坂井優介, 渡辺太郎. LLM ベース文法誤り訂正における編集の多数決による過剰訂正の抑制, 2026. https://www.anlp.jp/proceedings/annual_meeting/2026/pdf_dir/P7-14.pdf
チームで GitHub Copilot code review を全PRに自動で入れるようにし、去年12月ごろから数か月運用してきました。 狙いはシンプルで、レビュアーが見る前にPRの質を上げて、人によるレビューの時間を減らすことです。 実際にやったこと自体は、全PRで自動レビューを入れる設定と、 copilot-instructions の整備が中心です。この記事では、その設定そのものよりも、今どんな流れで運用しているのかと、実際にどうよかったのかを書きます。 チームとしての課題 自分たちのチームで大きな課題だと感じていたのは、 レビュー負荷の高さ でした。 AI を使って PR を作ることが増え、以前よりもレビュー対象となる PR の数や変更量が増えていました。加えて、バックエンドとフロントエンドを兼務しているメンバーや新しく入ったメンバーもいて、レビューの前提知識や得意領域にも差がありました。 そのため、変更内容によっては既存仕様や周辺実装を理解したうえでレビューする必要があり、レビューに時間がかかりやすい状況でした。さらに、チームとして扱う PR の量やコンテキストもさまざまで、レビュー開始からアプルーブまでの時間も長くなりやすい、という課題がありました。 そのため、人によるレビューに入る前に、ある程度 PR の質を上げておき、レビュー負荷を下げたいと考えました。 最初にやったこと 最初にやったことは、主に2つです。 1つ目は、GitHub Copilot code review を全PRで自動実行するようにしたことです。 GitHub の Rulesets で自動レビューを設定し、PR を open したタイミングで Copilot のレビューが入るようにしました。 設定方法はこちら 2つ目は、 copilot-instructions を最初に整備したことです。 自動レビューを入れるだけでも効果はありますが、そのままだとコメントはやや汎用的になりがちです。そこで、自分たちのリポジトリでは、レビューで見てほしい観点を copilot-instructions に書いています。 たとえば、次のような内容です。 日本語でコメントする プロジェクトの概要 レビューで見てほしい観点 バックエンド / フロントエンドそれぞれで気をつけたいこと これを入れる前よりも、入れた後のほうが「その観点を見てほしかった」と思う指摘が増えました。 なお、運用を始める前に、PR 作成者側でも Copilot を使える状態にしておきました。 下記の制限もあるため、確認しておく必要があります。 copilot-instructions に関する制限事項(2026年3月時点) Copilot code review では最初の4000文字だけ読み込まれるとのことです。 Copilot code review only reads the first 4,000 characters of any custom instruction file. Any instructions beyond this limit will not affect the reviews generated by Copilot code review. This limit does not apply to Copilot Chat or Copilot coding agent Using custom instructions Copilot code review では CLAUDE.md を読み込んでくれないみたいです。 Support for different types of custom instructions 今のレビュー運用 今の運用は、だいたい次の流れです。 PR を open すると Copilot code review が自動で入る PR 作成者が Copilot の指摘を先に確認する 直せるものは先に修正する 対応済みのものは resolve し、残すものは理由を書く そのあとで人によるレビューに進む 振り返りで出た観点を copilot-instructions に反映する 1. PR を open すると Copilot code review が自動で入る PR を open すると、Copilot code review が自動で入るようにしています。 手動で必要なときだけ呼ぶ運用もできますが、自分たちのチームでは人によって使ったり使わなかったりして、効果にばらつきが出やすいと感じました。そのため、全PRで自動レビューを入れる方針にしました。 最初は push のたびにレビューさせることも試しましたが、コメントが増えすぎて読むコストが上がりやすかったため、今は PR を open したタイミングでまずレビューし、その後は必要なときだけ再レビューする形にしています。 2. PR 作成者が Copilot の指摘を先に確認する Copilot のレビューが入ったら、まず PR 作成者が指摘を確認します。 作成者が先に指摘を確認できるようにしておくと、このあとの対応を進めやすくなります。 3. 直せるものは先に修正する Copilot の指摘のうち、すぐ直せるものは人によるレビューに入る前に先に修正します。 作成者側で先に直せるものを減らしておくことで、そのあとのレビューを進めやすくしています。 4. 対応済みのものは resolve し、残すものは理由を書く 対応済みのものは resolve し、すぐ閉じないものは対応中か、今回は対応しない理由をコメントで残すようにしています。 これをしないと、レビュアーから見てその指摘が放置なのか判断しづらく、レビューのノイズになりやすいためです。 5. そのあとで人によるレビューに進む Copilot の指摘をある程度捌いたあとで、人によるレビューに進みます。 Copilot の指摘を先に確認してから渡すことで、人によるレビューではより重要な論点を見やすくなります。 6. 振り返りで出た観点を copilot-instructions に反映する スプリントの振り返りや障害の振り返りで「今後はこの観点もレビューで見たい」となった内容は、必要に応じて copilot-instructions に反映しています。 そうすることで、次のレビューで同じような見落としを未然に防ぎやすくしています。 運用でよかったこと フロントエンドでは、細かい指摘を前段で減らせた フロントエンドで特によかったのは、細かい指摘を人によるレビューの前に減らしやすくなったことです。 たとえば、 翻訳漏れ 文言の不整合 UI ライブラリ移行時のスタイルミス のようなものです。 どれも致命的ではないですが、人が毎回見ると地味に時間を使います。こうした指摘を Copilot が先に拾ってくれるようになってから、人によるレビューではより重要な確認に入りやすくなりました。 バックエンドでは、見落としやすい観点を指摘してくれた バックエンドでよかったのは、知らないと抜けやすい観点を先に出しやすくなったことです。 たとえば自分たちの環境では、 ECS をデプロイ後に新旧バージョンが並行稼働する前提があります。 そのため、コードとしては正しく見えても、並行稼働時に問題が起きないかは別で見ないといけません。 そのため、次のような観点も見ていたりします。 並行稼働時の安全性 レースコンディション マイグレーション時の注意点 このあたりは、バックエンドに慣れている人なら自然に見ますが、フロントエンド出身の自分含め兼務メンバーや経験が浅い人だと抜けることがあります。Copilot が先にそこをコメントしてくれるだけでも、論点が最初からレビューに出てくるので助かっています。 レビューからアプルーブまでの時間も改善した Copilot の指摘を PR 作成者が先に確認し、直せるものを人によるレビューの前に修正する運用にしたことで、自分たちのチームではレビューからアプルーブまでの時間も改善していました。 もちろん、時期による開発状況や他の改善の影響やメンバーの努力も大いにあるとは思いますが、少なくとも PR 作成者が先に Copilot の指摘を捌く運用は、レビューを進めやすくするうえで効果があったと感じています。 その結果、人によるレビューが重要な論点に集中しやすくなった フロントエンドでは細かい指摘を前段で減らしやすくなり、バックエンドでは見落としやすい観点を先に出しやすくなりました。 その結果として、人によるレビューでは、仕様や UX、設計の意図のような、より重要な論点に集中しやすくなったと感じています。 もちろん、最終的に判断するのは人です。 ただ、最初から論点がある程度整理された状態になっているだけでも、レビュー全体はかなり進めやすくなります。 まとめ GitHub Copilot code review を全PRに自動で入れてよかったのは、レビュアーが見る前に PR の質を上げやすくなったことでした。 自分たちのチームでやったこと自体はかなりシンプルで、 全PRで Copilot code review を自動実行する copilot-instructions を最初に整備する の2つが中心です。 そのうえで、 PR を open すると Copilot のレビューが入る PR 作成者が先に指摘を確認して、直せるものは直す resolve やコメントで状態を残してから人によるレビューに進む 振り返りで出た観点は copilot-instructions に戻す という流れにすることで、レビュー全体がかなり回しやすくなりました。 GitHub Copilot code review を使うなら、手動でたまに呼ぶより、まずは全PRに自動で入れてみるのがおすすめです。そのうえで、 copilot-instructions や運用を少しずつ育てていくのが、自分たちのチームではうまく回っています。
はじめに 昨年の 2025年12月に RevComm では Hack Day 2025 という社内イベントを開催しました。今日はその内容について振り返ります。 何をやったの? お題 private-isu というリポジトリを題材に、Webアプリケーションのパフォーマンスチューニングに取り組みました。 github.com 開催地は RevComm オフィスが入っているビルのレンタルスペースを使用しつつ、遠方のメンバーなども参加できるようオフラインとオンラインを併用した形式で開催しました。 様々なチームや担当領域のメンバーが参加できるよう、下記の3つの言語の中から各々のメンバーが使いたい言語を選択した上で、一定人数ごとにグループを分けて取り組みました。 Python Go TypeScript (Node.js) 準備 まずは private-isu の公式マニュアルや 達人が教えるWebパフォーマンスチューニング の書籍を参考に作成されたハンズオン資料を確認しつつ、ログの確認方法やベンチマークの実行方法などを全体で確認しました。 github.com 最初の例としてテーブルへのインデックスの設定を行いました。 ab コマンドなどを活用してベンチマークを実施し、パフォーマンスが改善されていること確認していく一連の手順をハンズオン形式で学びました。 実践 ハンズオン形式で方法や手順を確認した後は、各自でグループに分かれてパフォーマンスチューニングに取り掛かりました。 筆者は普段はフロントエンド開発を行なっているため、 TypeScript で取り組みました。private-isu ではTypeScriptにおいては Hono とMySQLをベースにWebアプリケーションが実装されています。 まずは N+1 問題の解消やその他、細かな改善などを取り組んでみることにしました。都度、ベンチマークを実行して意図した通りにパフォーマンスが改善されていることを確認しながら作業を進めました。 成果発表 最後に各グループごとに成果の発表を行いました。グループごとに採用している言語や実践したチューニング内容などが異なっており、参考になりました。 まとめ 筆者は日頃、フロントエンド開発を中心に行なっており、サーバーサイドにおけるパフォーマンスチューニングに携わる機会が少ないため、こういった実践を通して安全にパフォーマンスチューニングを体験することができて、とても有意義に感じました。 また、今回題材として活用した private-isu はとてもよくできていると感じました。private-isu を題材にパフォーマンスチューニングに取り組むことで、ベンチマークの計測方法やパフォーマンスチューニング、ログの閲覧方法など様々なことを学ぶことができました。もし社内イベントの企画などを検討されている場合は、題材として検討されてはいかがでしょうか。
※この記事はリサーチエンジニアのJennifer Santosoによる記事『 IPSJ 266th Natural Language Processing & 158th Speech Language Information Processing Joint Research Presentation Meeting - Presentation and Participation Report 』を翻訳したものです。 はじめに RevComm Researchのサントソです。12月中旬に開催された研究会に参加し、日頃の研究成果を登壇してきました。今回は「Generative Error Correction for Product Names with Phonemic and Lexical Constraints」というテーマで、最新の成果を報告しました。多くの専門家と議論を交わし、非常に有意義な時間を過ごすことができました 。 学会の概要 https://www.ipsj.or.jp/kenkyukai/event/nl266slp158.html 本学会は、国内最大級のIT団体である情報処理学会(IPSJ)が主催しています。今回は、言葉をコンピュータで扱う「自然言語処理(NL)」と、音声の解析・生成を担う「音声言語処理(SLP)」の2つの研究会が合同で開催されました。これらは年数回、分野を横断した議論の場として共催されており、口頭発表を中心に最新の研究報告が行われます。 開催期間 2025年12月15日〜17日 開催地 京都テルサ(京都市)( https://www.kyoto-terrsa.or.jp/ ) 対象分野 主に自然言語処理(NL)および音声言語処理(SLP)を対象としています。特に、大規模言語モデル(LLM)を用いた音声認識の高度化や、ドメイン特化型の言語処理が大きなテーマとなっていました 。 発表件数 口頭発表: 28件 招待講演: 2件 国際学会参加報告: 2件(INTERSPEECH 2025とACL2025) 計32件の非常に濃密なセッションが組まれました。 学会の概要と発表総数 参加人数 およそ200人(オンライン参加者が含む) 会場の様子 学会の看板 招待講演 登壇報告 ビジネス会話における音声認識(ASR)の精度向上、特に「商品名」の誤認識をLLMでいかに修正するかという研究を発表しました 。研究会にある発表はほとんど日本語で行われましたが、今回の発表・質疑を英語で行いました。 題目 Generative Error Correction for Product Names with Phonemic and Lexical Constraints(音韻的・語彙的制約を用いた商品名の生成的な誤り訂正) 背景・モチベーション 本研究では、ASRシステムがしばしば苦手とする製品名や型番といった用語を、LLMベースのフレームワークを用いて後処理・修正する手法を提案しました。具体的には、音声から抽出した音声情報を製品辞書を用いてLLMプロンプトに統合します。これにより、コアASRモデルを再学習することなく、語彙外(Out-of-vocabulary; OOV)用語を正確に修正することが可能になります。 英語と日本語の両方のデータセットを用いた実験結果では、製品名認識精度(Accuracy)が大幅に向上しました。この開発により、専門用語が頻繁に使用されるビジネス環境における自動議事録の品質が大幅に向上すると期待されます。 本研究の主な貢献 本研究の主なポイントは以下の2点です: データセット構築プロトコルの提供: 商品名や型番に特化した、会話音声データの構築手法を提案しました 。 図1.データセット構築プロトコル 新たな誤り訂正フレームワークの提案: ASRを再学習することなく、音素情報(Phoneme)と商品辞書(Lexical)、そして会話の文脈を融合させて、ゼロショットで誤りを訂正する手法(Generative Error Correction; GEC)を開発しました 。 実験の結果、英語・日本語の両データセットにおいて、商品名の認識精度が大幅に向上することを確認しました 。 図2.提案した誤り訂正フレームワーク(GEC)手法の流れ いただいた質問 発表後には、20分間の発表に対して5分間の活発な質疑応答が行われました。 Q1. ファジーマッチングの具体的な閾値とその理由は? 回答: 0.6から0.9の範囲を0.05刻みで検証し、最適な値を決定しました 。これは、検索の成功率と、LLMに渡す情報のノイズを抑える精度のバランスを最適化するためです 。 Q2. データセットの話者数が少ないようだが、テスト設定として一般的か? 回答: 本研究では、英語・日本語それぞれ男女2名ずつのリファレンスボイスを使用して合成データを作成しました 。この分野では話者数よりも商品名の多様性が重視されます。認識結果のばらつきが十分に見られるよう設定しているため、評価設定として一般的だと考えています。 Q3. 会話シーケンスの代わりに、要約を文脈として利用できるか? 回答: 本研究の目的は、商品名の認識精度を高めることでビジネスにおけるインサイトを明確にすることにあります。正確な要約を作成するためには、その前提となる商品名の正しさが不可欠です。もし誤認識が含まれたまま要約を行ってしまうと、重要なキーワードが抜け落ちたり、内容の質が低下したりする恐れがあります。その結果、LLMが本来注目すべき商品名を認識できず、修正能力が十分に発揮されない可能性があるため、要約ではなく生の会話シーケンスを直接文脈として活用する手法が適していると考えています 。 気になる発表 招待講演1:言語モデルのマルチモーダル言語理解能力 LLMが言語や視覚情報をどの程度「理解」しているのかを4つの側面から検証した刺激的な講演でした。逐次通訳への応用では、プロンプト制御によりデータ不足を克服し、従来手法を上回る精度を実現しています。一方、視覚情報を用いた調音推測などの実験を通じ、絶対的な視覚理解には依然として課題があるという知見が示されました。 招待講演 2: 音声コーパスの過去・現在・未来 本講演では、AI時代における音声データに関する提言が提示されました。日本語およびアジア言語のコーパス拡充に向けた国際協力の重要性、そしてLLM開発のためのデータの「量」と厳密な実験のためのデータの「質」のバランスを取る必要性が強調されました。また、会話音声、特に顔データを含む会話音声は、厳格な倫理的管理と同意取得を必要とするセンシティブな個人情報であることも強調されました。 国際学会参加報告 INTERSPEECH 2025: ASRとLLM、そして自己教師学習(Self-supervised learning; SSL)の融合が主流のトレンドであると報告されました。音声言語モデル(Speech Language Models)の台頭により、この分野はパラ言語理解、全二重対話、ゼロショットTTSへと拡大しています。 ACL 2025: ARR(ACLローリングレビュー)システムに関する知見が共有されました。議論は、改善のための再提出の重要性と、堅牢な反論の必要性に焦点が当てられました。また、混雑した会場で注目を集めるための、視覚的にミニマルでありながらインパクトのあるポスターを作成するためのヒントも提供されました。 まとめ 12月の研究会にて、私たちの最新の研究成果を共有できたことを光栄に思います。約200人の参加者が集まった会場は熱気に包まれ、招待講演や国際学会の報告を含め、LLMを用いた音声処理の可能性について活発な議論が交わされていました。 専門家の方々と直接対話することで、非常に有意義なフィードバックをいただくことができました。チーム内で議論を尽くして準備した発表でしたが、こうした対面での議論を通じて、新たな課題や今後の研究の方向性がより明確になったと感じています。 RevComm Researchでは、今後も継続的な研究と発表を通じて、コミュニケーションを科学する挑戦を続けていきます。現在、私たちと共にこれらの刺激的な技術課題に取り組んでいただける仲間を募集しています。