有名テック企業の技術ブログを、ひとつのフィードで。
フィード
32件
【デジカルチーム ブログリレー1日目】エムスリーエンジニアリンググループ、デジカルチームの安江です。Scalaとマミさんが好きです。 双子が生まれ、1年間の育休を取りました。育休中に新しいチャレンジについて打診があり、自分でも希望してデジカルチームへ異動。この春に復帰したのですが、想像以上のギャップが待っていました。 この記事では「1年間のブランクからどう復帰したか」にテーマを絞って書きます。これから育休を考えている方、長期の離脱からの復帰に不安を感じている方の参考になれば幸いです。 育休中の技術との距離感 復帰したら何もかもが違った Claudeのおかげで未経験領域を突破した チームの力 変わるものと変わらないもの おわりに We are hiring !! エンジニア採用ページはこちら カジュアル面談もお気軽にどうぞ インターンも常時募集しています 育休中の技術との距離感 1年間、技術から完全に離れていたかというと、そうでもありません。 育休が長期になると会社PCは返却する必要があり、会社とのつながりは個人メールだけになります。エンジニアとしてのアイデンティティが揺らぐ感覚がありました。 その揺らぎを埋めるように、育児の空いた時間でScalaを触っていました。その中でコンパイラのバグを見つけて Scala 3 に貢献 できたのは、育休中のちょっとした誇りです。自宅のネットワーク整備にハマっていたのもこの時期です。 一方、育休期間中に世の中ではAIコーディングが爆発的に流行していましたが、自分はYouTube眺めてるだけでした。これが復帰後に効いてくるとは、この時はまだ知りません。 復帰したら何もかもが違った 自分で希望した異動とはいえ、実際に復帰してみると「昔話の浦島太郎どころじゃない」というのが正直な感想でした。エムスリーでは各チームが最適な技術を選定しているため、異動すると技術スタックや開発プロセスがガラッと変わります。 育休前 復帰後 プロダクト 医師向けメディアサービス 電子カルテ リポジトリ構成 複数リポジトリ モノレポ ワークフロー GitHub Flow Git Flow IaC Terraform AWS CDK これだけ環境が変わると不安もありましたが、育休中にAIコーディングが急速に進化していたことに救われました。育休前は「Copilotすげー」と叫んでいたのに、復帰したらClaudeがコードを書いている。このAIの進化が、キャッチアップを大きく助けてくれました。 Claudeのおかげで未経験領域を突破した 復帰前の得意領域はScalaでした。育休中もScalaを触っていたくらいで、React + TypeScriptは未経験です。 ところが復帰後、そのReact + TypeScriptのコードベースにすぐ機能追加ができてしまいました。Claudeのおかげです。 実際の時系列を振り返ると、こうなっています。 復帰初日〜: flakyテストの修正やログ設定の整理など、足元を固めるタスクから着手 復帰3日後: フロントエンドのUI改善タスクに着手。React + TypeScriptのコードに初めて手を入れた 復帰3週間後: 電子カルテの医療ドメインに踏み込んだ機能をReact + TypeScriptで実装した 以前なら React って何? TypeScript の型ってどう書くの? という勉強期間が必要だったのに、コーディングの重心が「自分で全部書く」から「AIと一緒に書く」に移ったことで、未経験の言語でも実戦投入までの時間が劇的に短くなりました。 その後もELT/Dataformによるデータパイプライン構築、バッチのメモリ最適化、Safariのポップアップブロック回避など、フロントエンドからインフラまで幅広く手を動かしています。「Scalaしか書かない人」はもういません。 最近は「AIに仕事を奪われる」という話をよく耳にしますが、1年間のブランクを経験した身からすると、むしろ逆でした。AIがブランクを埋めてくれました。育休のようなキャリアの中断がある人にとっても、復帰のハードルは確実に低くなっていると思います。 チームの力 もちろんAIだけで何とかなったわけではありません。 「Claudeと一緒にMR作ってみました」と復帰3日目に投稿した時も、特に驚かれることなく普通にレビューしてもらえました。AIコーディングがチームの中で当たり前になっていたことも、復帰のハードルを下げてくれた要因だと思います。 チームの雰囲気も大きかったです。復帰初日、環境構築でハマった時、Slackで質問したらチームメンバーがすぐに反応してくれて、その日のうちに開発環境が立ち上がりました。チームリーダーがとても優秀でドメイン知識の面でも迷いなく仕事を進められたし、復帰から2ヶ月後にはCIパイプラインの問題を自分が調査してチーム全体に共有・修正する側に回れました。助けてもらう側から、助ける側へ。この速度で立ち上がれたのは、チームの受け入れ体制があってこそでした。 変わるものと変わらないもの 復帰して驚いたことがもう1つ。 育休前に自分が作った「Googleカレンダーからチームメンバーの休暇予定を収集してスプレッドシートに一覧表示するツール」が、自分がいない間もずっと動き続けていました。しかも元のチームだけでなく、他のチームでも使われていました。 1年離れても残るものがある。一方で、AIコーディングのように業界のトレンドは完全に変わっている。変わるものと変わらないものがある、というのが復帰後の率直な感想です。 おわりに 今は毎朝6時に起きて子供たちの朝食を済ませ、車で保育園を回って帰宅する生活をしています。育休は終わりましたが、育児は終わっていません。 1年間の育休で学んだのは、「変わるものと変わらないもの」は技術だけでなく、自分自身にも当てはまるということです。コードの書き方は変わった。使うツールも変わった。でも、子供の寝顔を見て「今日も生きてるな」と思う感覚は、第一子の時から何も変わっていません。 これから育休を考えている方、特に「1年は長すぎるかな」と迷っている方へ。1年取ったからこそ見えた景色がありました。子供の成長を毎日隣で見られる時間は、キャリアの中のたった1年ですが、家族の中では二度と戻ってこない1年です。 We are hiring !! エムスリーでは一緒に医療の未来を変えるエンジニアを募集しています。育休も取れます。 エンジニア採用ページはこちら jobs.m3.com カジュアル面談もお気軽にどうぞ jobs.m3.com インターンも常時募集しています open.talentio.com
【マルチデバイスチーム ブログリレー5日目】 マルチデバイスチームの小林(@bakobox)です。 2026年4月に Android CLI が公開されました。プロジェクトの作成から実機デバイス / エミュレータの操作までを行える CLI で、AI エージェントによるアプリ開発の操作を容易にすることを目的に開発されています。 この Android CLI と、昔からある ADB コマンドの使い方を AI エージェントに教えるだけで、公式ツールのみで Android 実機 / エミュレータを操作できます。 本記事では、Android CLI と ADB を使って Android 実機 / エミュレータを操作するエージェントスキルを、実際のコマンドとあわせて紹介します。 AI エージェントによる Android デバイス操作で何が変わるか 使用する Android CLI コマンド layout screen capture / screen resolve 使用する ADB コマンド スキル まとめ We are hiring! エンジニア採用ページはこちら カジュアル面談もお気軽にどうぞ インターンも常時募集しています AI エージェントによる Android デバイス操作で何が変わるか AI を使ったアプリ開発では、次のようなループが起こります。 AI が実装 -> 人間が動作確認 -> AI が修正 -> 人間が動作確認 -> AI が修正 -> ... このループの中で、動作確認は人間が担う必要があり、そこがボトルネックになっていました。 AI エージェントが Android デバイスを操作できるようになると、このループは次のように変わります。 AI が実装 -> AI が動作確認 -> AI が修正 -> AI が動作確認 -> AI が修正 -> ... 動作確認も含めたループを AI だけで回せるようになります。 次の動画は、ガジェット EC アプリのサンプルで Claude Code に動作確認をしてもらっている様子です。 商品一覧の表示・検索・カルーセルのスクロール・商品のカートへの追加といった操作を、人間が介入することなく自律的に行っています。 使用する Android CLI コマンド デバイスの UI 情報を取得するために使う Android CLI コマンドを紹介します。 layout android layout [--pretty] [--output] [--diff] このコマンドは、現在接続されている実機またはエミュレータのアクティブなアプリの UI レイアウトを JSON 形式で返します。 例えば、このサンプルアプリで layout コマンドを実行すると、次のようなデータが返されます。 [ { "interactions": [ "scrollable" ], "center": "[540,1335]", "bounds": "[0,310][1080,2361]", "resource-id": "product_list", "key": 3506402 }, { "interactions": [ "clickable", "focusable", "long-clickable" ], "center": "[540,404]", "resource-id": "search_field", "key": 3506402 }, { "text": "Featured", "center": "[151,710]", "resource-id": "header_Featured", "key": 3506402 }, { "interactions": [ "scrollable" ], "center": "[540,1095]", "bounds": "[42,774][1038,1417]", "resource-id": "featured_carousel", "key": 3506402 }, { "text": "All Products", "center": "[193,1491]", "resource-id": "header_All_Products", "key": 3506402 }, { "content-desc": "Search gadgets", "center": "[540,404]", "key": 3506402 }, ... ] 返される JSON は UI 要素の配列で、配列の各要素が画面上の1つの UI 要素に対応します。各要素には次のようなフィールドが含まれます。 text - 要素が持つテキスト resource-id - 要素を参照するための Android リソース ID content-desc - アクセシビリティツール向けの UI 要素の説明 interactions - 要素がサポートするユーザー操作の種類。checkable、clickable、focusable、scrollable、long-clickable、password のいずれか、または複数 state - 要素の現在の状態。checked、focused、selected のいずれか、または複数 bounds - 要素のバウンディングボックスの画面座標。[最小X,最小Y][最大X,最大Y] 形式 center - 要素の中心の画面座標。[x,y] 形式 off-screen - true の場合、要素は UI 階層に存在するが表示されていない。スクロールすることで表示される可能性がある これらのフィールドを手がかりに、目的の UI 要素の座標を特定して操作します。例えば次のように活用できます。 resource-id が search_field の要素は center が [540,404] なので、検索フィールドをタップしたい場合はこの座標を操作すればよいとわかります。このように、 UI 要素の座標さえ取得できれば、あとはその座標に対してタップなどを実行するだけです。 このコマンドは、 Jetpack Compose で構築した UI はもちろん、 Flutter で描画した UI でも使えます。 ただし、 WebView 内の要素は取得できない点に注意が必要です。アプリ内 WebView の要素の座標を特定したい場合は、次のコマンドを使います。 screen capture / screen resolve android screen capture [--output] [--annotate] このコマンドを使って、接続されたデバイスのスクリーンショットをキャプチャできます。この出力を AI が読むようにすれば、見た目の確認作業を AI に任せられます。 さらに --annotate オプションを付けて実行すると、画像内の UI 要素の周りに数字付きのバウンディングボックスを描画します。 この画像とバウンディングボックスの数値を次のコマンドで指定することで、対象要素の座標を特定できます。 android screen resolve --screenshot=<path> --string=<string> 例えば、先ほどのキャプチャの9番の要素の座標を特定する場合、 android screen resolve --screenshot=screenshot.png --string="input tap #9" を実行すると、 input tap 539 404 と出力されます。 これで、 WebView 内の目的の要素の座標も得られます。 まずは layout コマンドで UI 要素の座標の特定を試み、取得できなかったら screen capture / screen resolve ベースの手法にフォールバックすることで、ほぼすべての要素の座標を特定できます。 使用する ADB コマンド 入力操作に使う ADB コマンドを紹介します。 いずれも adb -s <serial> shell input から始まるコマンドです。-s <serial> で操作対象のデバイスを指定します(adb devices で確認できるシリアル番号)。複数のデバイスやエミュレータが接続されている場合に、どれを操作するかを明示するために付けています。 adb
【マルチデバイスチーム ブログリレー2日目】 エンジニアリンググループ マルチデバイスチームの田根です。 日本最南端の波照間島のニシ浜にはウミガメがいっぱい 私は主に IntelliJ IDEA を使って開発しているので、AIエージェントは JetBrains Junie をメインで使っています。 Claude Code も IntelliJ IDEA のプラグイン Claude Code [Beta] - IntelliJ IDEs Plugin | Marketplace があり、IntelliJ IDEA 上で動かせます。 今回は、この両者を実際の開発プロジェクトでガッツリ使い倒して分かった「リアルな違い」を、率直に比較レビューします。 Junieの自律思考とClaude Codeの馬力、どちらがどんな場面で活きるのか? 使い分けのヒントをお届けします。 ※本記事では、Junieは最も安価な Gemini 3 Flash Previewモデル、Claude Codeは最新モデルを使用して比較しています。 JetBrains Junie とは 1分でわかる最大の違い 機能・挙動の徹底比較 【思考の深さ】気を利かせるJunie vs 実直なClaude Code Junie:文脈を読んで「関連クラス」まで自動修正 Junie:「テストファースト」へのこだわり 【パワーと規模】大局に強いClaude Code vs 迷子になりやすいJunie Claude Code:大規模なリファクタリングも力強く完遂 Junie:大きすぎる指示は迷子に 【開発UX】コード変更の「承認プロセス」の違い まとめ:私たちはどう使い分けるべきか? JetBrains Junie を使うべきシチュエーション Claude Code を使うべきシチュエーション We are hiring! JetBrains Junie とは JetBrains Junie は、JetBrains が提供する AI コーディングエージェントです。IntelliJ IDEA などの JetBrains IDE にネイティブ統合されており、コードの生成・修正だけでなく、テストの作成・実行やプロジェクト全体の文脈を踏まえた自律的なコード変更が特徴です。 Junie のモデルは複数から選べますが、モデルによって消費されるクレジットが異なります。 Junie で選択できる Model 1分でわかる最大の違い ひと言で表すなら、両者のキャラクターは次のように綺麗に分かれます。 Junie: プロジェクト全体を俯瞰し、テストを回しながら自律駆動する「自走型シニアエンジニア」 Claude Code: 指示された内容を圧倒的なパワーで超高速に完遂する「圧倒的馬力の実行特化型エージェント」 機能・挙動の徹底比較 比較項目 JetBrains Junie Claude Code 指示へのアプローチ 自動で気を利かせて関連箇所まで修正。ファイル指定しているのに他のファイルまで修正してしまうことも 指示されたこと(そのファイル)のみを修正 テストの扱い 実装前にテストを書く(テストファースト)、挙動確認もテストを実行して検証 指示しないとテストコードの修正・作成はしないことが多い 大規模な変更 苦手(Spring Bootのバージョンアップなどは頓挫しがち) 得意(大きな指示でもパワーで押し切れる) コード変更の安全性 修正後に確認(事後確認)。コンパイルエラーにはなりにくい IntelliJ上で事前にプレビューされ「承認」する形。指示次第ではコンパイルエラーのままになることも 修正方針の確認 選択肢を提示せず、自己判断で修正を進める 修正方針の選択肢を提案し、人間に判断を委ねる 他のプロジェクトの参照 Project Settings の Modules に追加しないと参照できない 可能 価格 AI Pro プラン 月額 2,800 JPY〜(法人) API従量課金(Vertex AI等)または Pro プラン 月額 $20〜 / Max プラン 月額 $100〜 【思考の深さ】気を利かせるJunie vs 実直なClaude Code この2つの決定的な差は、「プロンプトの読解力と、それに伴う周辺への気配り」にあります。 Junie:文脈を読んで「関連クラス」まで自動修正 例えば「このエンティティに項目を1つ追加して」と頼んだとします。 Junieの場合: 「項目を追加するなら、当然DTOやリポジトリ、マッパー、さらには関連するクラスも修正が必要だな」と自走し、関連ファイルを芋づる式に自動で修正してくれます。 Claude Codeの場合: 指示されたそのクラスに愚直に項目を追加して終了します。関連クラスの修正や、テストコードの追従も「指示されないとやらない」というスタンスです。ただし、修正方針に複数のアプローチがある場合は選択肢を提案してくれるため、人間が意思決定しやすいという利点があります。 Junie:「テストファースト」へのこだわり Junieの最も面白い特性が、「この場合はどうなりますか?」と質問したときの挙動です。 Junieは言葉で回答するだけでなく、実際に検証用のテストコードを書いて動作確認を行い、コンパイルエラーにならないことを確認した上で結果をレポートしてくれます。 この「テストを書いて実際に動かして確かめる」というプロセスを自動で行うため、最終的なコードや回答の信頼性はJunieが一歩リードしています。 Claude Code に比べ間違った回答をすることはかなり少ない印象です。 【パワーと規模】大局に強いClaude Code vs 迷子になりやすいJunie 一方で、タスクの規模が大きくなると評価は真逆になります。 Claude Code:大規模なリファクタリングも力強く完遂 「Spring Bootのバージョンを4.xに上げて、非推奨になったAPIを全部置き換えて」といった、プロジェクト全体に影響が及ぶ大きな指示はClaude Codeの独壇場です。コンテキストの広さと処理能力が高いため、大きな一歩をガツンと踏み出すのが得意です。ただし、大味な指示を出すとコンパイルエラーのまま作業を終えてしまうこともあるため、人間側の手綱引きが必要です。 Junie:大きすぎる指示は迷子に 逆にJunieは、大きすぎる指示を出すと試行錯誤を繰り返した結果、意図しない方向にコードが修正されてしまう(迷子になる)ケースが散見されます。Junieに依頼するときは、タスクをある程度細分化してあげるのがコツです。 【開発UX】コード変更の「承認プロセス」の違い 勝手にコードが変わるのが怖いエージェントツールにおいて、レビューのしやすさは重要です。 Claude Code (IntelliJ プラグイン):コードを変更する前に、IDE上にプレビューが表示され、人間が「承認(Approve)」ボタンを押して初めてコードが適用されます。勝手に書き換えられない安心感があります。 Junie:基本的には「修正後に人間が確認する」スタイルです。ただし、前述の通りテストを回してコンパイルエラーにならない状態まで仕上げてから出してくるため、事後確認でも破綻しにくいという自信の表れでもあります。 まとめ:私たちはどう使い分けるべきか? JetBrains Junie を使うべきシチュエーション 新機能の実装で、関連する複数のファイルをスマートに一括生成・修正してほしいとき。 「テストコードもセットで、絶対に動く状態」の綺麗なコードを担保したいとき。 定額の価格体系の中で、じっくりエージェントに自律思考させたいとき。 Claude Code を使うべきシチュエーション フレームワークのメジャーアップデートや、大規模なライブラリ刷新など、馬力が必要なとき。 「ここをこうして」という明確な指示があり、余計な忖度なしで爆速でコードを書き換えてほしいとき。 変更前に必ず手動でプレビューと承認を入れたい安全第一の開発。 どちらが優れているかではなく、「足元の泥臭い実装やテストはJunieに任せ、大きな構造変更はClaude Codeでゴリ押す」という、適材適所の併用スタイルが、現時点で最適な開発環境のひとつと言えそうです。 さらに、Junieで修正したコードはClaude Codeにレビューしてもらい、Claude Codeで修正したコードはJunieにレビューしてもらうという「クロスレビュー」も実践しています。それぞれ視点が異なるため、片方だけでは見落としがちな問題を補完し合えるのもこの併用スタイルの大きなメリットです。 JetBrains Junie は一番安い AI Pro プラン(月額 2,800 JPY)で利用していますが、Claude Code と使い分ければ余裕で足りています。 We are hiring! エムスリーでは AI を「道具」として使い倒し、爆速でプロダクトを育てる仲間を探しています! 「最新のAIツールを実務でガンガン試したい」 「退屈な定型業務や泥臭いリファクタリングはAIに丸投げして、自分はコアドメインの設計に集中したい」 そんな、AIエージェントと共に進化していきたいAIネイティブなエンジニア(または、これからそうなりたい方)を大募集しています! jobs.m3.com
【マルチデバイスチーム ブログリレー1日目】 エンジニアリンググループ・マルチデバイスチーム(以下「マルデバ」)の星野です。 私は普段マルチデバイスチームに所属し iOS/Android アプリの開発をしていますが、同時にエムスリーテクノロジーズにも出向という形で在籍し、グループ会社においてもモバイルアプリの開発・支援をしています。 今回は、現在私が進めているグループ会社のモバイルアプリのリファクタリング事例をご紹介し、エムスリーテクノロジーズにおける業務の様子をお伝えできればと思います。 リファクタリングイメージ図 サービスの紹介 リファクタリング 1. CocoaPods から SPM への切り替え 2. 画面毎のリファクタリング 2-1. 継承の解消 2-2. DI の導入 2-3. MVVM の整理 2-4. async / await への書き換え 3. AWS Code Commit から GitHub への移行 今後の予定 まとめ We are hiring! サービスの紹介 今、私が開発に携わっているサービスは介護領域で toB 向けに展開しているサービスです。導入いただいている企業は数千を超え、今なお拡大中です。サービスは主に管理サイト・Android / iOS アプリが存在し、管理サイトは主に介護事業者向けのサービス、Android / iOS アプリは介護業務に従事する方が利用するサービスとなっています。 グループ会社でリリースしているこのサービスは、スマホに慣れていないご高齢な介護従事者の方にも安心・安全・簡単に使っていただけるという大きな特徴があります。この唯一無二の利便性をより多くの企業・介護従事者の方々にお届けし、介護現場が抱える慢性的な人手不足や業務負荷を軽減し、誰もが本来の「介護ケア」に専念できる環境を支えていきたい——。エムスリーテクノロジーズではそのような更なる飛躍を目指すべく、開発スピードの引き上げ・新しい価値をよりスピーディに届ける方法の1つとして、土台・構造の整理 = リファクタリングを進めることなりました。 こちらについてはエムスリーテクノロジーズ取締役 / VPoE 藤原のインタビューにも記載されていますので、あわせてご参照ください。 www.m3t.co.jp リファクタリング サービスのリファクタリングでは管理サイト・インフラ・Android / iOS アプリなどさまざまな面のリファクタリングを並行していますが、今回のブログでは iOS アプリのリファクタリングについて紹介いたします。 昨年末頃に私が参画した当時の技術スタックは次のような状況でした。 - アーキテクチャ: MVVM - レイアウト: Storyboard / Xib ファイル - ライブラリ管理: CocoaPods - DI: なし - テスト: なし - その他: - MVVM ではあるが、V(View, ViewController)もロジックを持つ - すべての画面(ViewController) が共通したカスタム親クラスを継承していて、さらに多段階継承している場合もある - ゴッドオブジェクト、責務が不明瞭なマネージャクラス多数存在する - 通信の完了時の処理がハンドラ形式で書かれている 7-8年ほど前であれば、どの会社でも見かけるような一般的な技術スタック・状況ですが、今後もサービスを飛躍させたいと考えた場合に、未来に向けてのリファクタリングは必要だと判断し、リファクタリングを進めています。 1. CocoaPods から SPM への切り替え まず最初に行ったのが、ライブラリ管理の CocoaPods から SPM への切り替えです。現在はどのライブラリも SPM 版が用意されている為、大きな問題もなく2-3時間程度で切り替えられました。 2. 画面毎のリファクタリング その次に実施したのが、カスタム親クラスの排除・DI の導入・MVVM アーキテクチャの整理・モダンなコードへの書き換えを画面毎に進めることでした。アプリのマニュアルや開発関連ドキュメントはありますが、エムスリーテクノロジーズは途中からの参画のため、仕様を頭できちんと理解するにはやはり実際にコードを読んで動かすことが一番です。幸いにも画面毎にできること(機能・仕様)がしっかりと定まっていたため、画面単位でリファクタリングし、リファクタリングが完了したタイミングで適宜リリースする形が取れました。 2-1. 継承の解消 画面毎のリファクタリングの際に共通して最初に行うのが、カスタム親クラス(ここでは仮に BaseViewController と呼びます)の引き剥がしです。すべての画面が共通利用する親クラスは、実装時のお手軽さはありますが、「子クラスによっては不要な機能まで継承することになる」「本来関係ないはずの画面同士が親クラスを通じて結びついてしまう」など、保守運用の観点からはコードの見通しが悪くなるというデメリットの方が大きいため、本当に必要な共通処理は protocol + デフォルト実装に切り出す or 各画面毎に必要な機能を実装していく形で BaseViewController の継承を剥がしていきます。 子画面で必要な機能の解析・移行(protocol / 個別クラスへの実装)については Claude Code に任せるのが手っ取り早いので、ここは Claude Code に簡単な指示だけ出して、あとは出来上がったコードを確認するだけで簡単に進められます。 import UIKit // 共通の基底クラスによる NG なサンプル class BaseViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() // 良かれと思って、アプリがフォアグラウンドに戻った時の通知を設定 NotificationCenter.default.addObserver( self, selector: #selector(didReceiveAppWillEnterForegroundNotification), name: UIApplication.willEnterForegroundNotification, object: nil ) } @objc private func didReceiveAppWillEnterForegroundNotification() { // 画面データの最新化など、共通で行いたい「重い処理」 print("【Base】アプリ復帰通知を受信:データをリロードします。") loadData() } func loadData() { // 各画面でオーバーライドされる想定の空メソッド } } // 1. 通知を受け取ってリロードしたいメイン画面(これは意図通り) class HomeViewController: BaseViewController { override func loadData() { print("ホーム画面の最新データをAPIから再取得します。") } } // 2. ❌ 通知処理が「不要」なはずの、固定文言を出すだけのヘルプ画面 class HelpViewController: BaseViewController { // 静的な画面なので、loadDataは必要ないためオーバーライドもしない // ⚠️ しかし、BaseViewControllerを継承しているせいで… // アプリが復帰するたびに、この画面も裏で通知を勝手に受け取ってしまう。 // 結果として、不要な処理が走ったり、思わぬバグやメモリリークの原因になる。 } また、一部のクラスで「UIViewController ← 親クラス ← 子クラス ← 孫クラス」という多段階継承された状況で、コード上では孫クラスを使うのに、レイアウトファイル(Storyboard)は子クラスしか存在しない」という特殊な状況の画面が複数ありました。これに関しては、仕方なく先に孫クラス用の Storyboard を新たに用意(複製)し、継承を解消した後、どこからも参照されなくなったタイミングで子クラスの Storyboard とコードを削除する形で継承を引き剥がしました。 2-2. DI の導入 クラスの継承をすっきりさせたところで、次に行うのは DI の導入です。DI については最近マルデバでよく採用している swift-dependencies を利用しました。軽量なコードで DI がかけますので、もしまだ使ったことのない人がいればぜひ試してみてください。エムスリーテックブログでも、一年前に取り上げられてますので、こちらもご参照ください。 www.m3tech.blog 2-3. MVVM の整理 次に MVVM アーキテクチャの整理です。画面毎に機能が異なるため、簡単な画面であれば Claude Code であっさり片付きますが、機能が複雑すぎる画面の場合は「副作用のあるメソッド」が他の「副作用のあるメソッド」を呼んでいたり、さらにそれらがいろいろな箇所から呼ばれていて、どのように整理するのが適切かすぐにわからない場合があります。 この場合は Claude Code に指示を出す前に、まず「何をやっているメソッドなのか」「依存関係はどうなっているのか」を解析してもらって、人間がある程度把握しておく必要があります。そうでないと、いくら指示を出したところで絡まったままのなんとなくの整理にしかならないため、意味のないリファクタリングになってしまいます。解析した内容や問題点を issue としてまとめ、それをもとに Claude Code に指示を出して修正を進めていきます。時間はかかりますが、今後の保守・運用を考えた場合にこの整理はとても重要なポイントなので、地道でも確実に進めていきます。 2-4. async / await への書き換え 通信の完了時の処理については GCD を使ったハンドラ形式の書き方になっていたのですが、このサービスでは通信を直列で順番に処理するシーンが多数あり、「ハンドラによるネスト」がコードリーディングの妨げとなっていました。そのため、あたらに通信クラスを作って「async に切り替える」+「ViewModel を MainActor 化することで、GCD によるスレッド操作を排除する」ことで、コードの可読性を向上させました。 // GCD のハンドラによるネストのサンプル class ViewModel { let networkManager = LegacyNetworkManager() var displayRecord: String = "" func loadDashboardData() { // ❌ GCDによる非同期ネスト地獄の始まり networkManager.fetchStaffInfo { [weak self] result in switch result { case .success(let staffId): // ネスト2階層目:入居者一覧の取得 self?.networkManager.fetchPatients(for: st
基盤開発チームブログリレー2日目の記事です。 1日目は田尻さんがOCamlについて書いてくれました。そのOCaml熱にあおられ私も仕事で使い始めたので、私も便乗してOCamlについて書いてみます。 www.m3tech.blog 覚醒したラクダ はじめに OCaml、書いてますか。私も書いています。仕事の補助ツールとして。 こんにちは、基盤開発チームの林です。 以前まで、ちょっとしたCLIツールを作る言語としては、簡潔に書ける Ruby を使うことが多かったのですが、最近はAIエージェントをよく使うようになったこともあり、自分用のCLIツール開発には OCaml を使うようになりました。 もともと私はローカル環境や設定の作り込みをするタイプではなく、むしろクリーンな環境・ゼロ設定で仕事をするのが好みだったのですが、近頃はCLIツール開発も含めてローカル環境の作り込みをするようになってきたので、そのあたりで考えたことなどを記事にしてみます。 なお、最初にお断りをしておくと、関数型言語を仕事で使った経験はほとんどなく*1、OCaml 自体にも詳しくはないので、OCaml 初心者による初心者向けの記事です。 はじめに 自分用のCLIツールを開発したくなってきた経緯 CLIツール開発言語に求めたい性質を列挙したら OCaml が残った 実際に OCaml をCLIツール開発に使ってみて AIへの指示 型駆動設計 let* によるネスト改善 使っているライブラリ おわりに We're hiring! エンジニア採用ページはこちら カジュアル面談もお気軽にどうぞ インターンも常時募集しています 自分用のCLIツールを開発したくなってきた経緯 昨年から Claude Code を使い始めてからは、あらゆる手元の作業を Claude Code にお願いするようになりました。 また、Claude Code に作業を円滑にしてもらえるように、目的ごとに Skill ファイルもせっせと作るようになりました。 たとえばコードレビューの時は GitLab の公式CLIの glab コマンドを使って、コメントを取得する時はこのコマンドで、、と自然言語とコマンド例で書いていたりしたのですが、Skill を読ませても思った通りコマンド実行してくれず、何度もやり直しになることが多々ありました。 そもそも自然言語で書くとどうしても曖昧さが残りますし、Skill のメンテをするのにも疲れてきました。 そこで、定型の処理については、分かりやすいコマンド体系で決定的な動作をするCLIツールを自分用に作るのがよいと気付きました。 また、現在エムスリーでは Claude Team プランが導入されているので一定の上限枠までは定額利用でき、私はフルタイムで Claude Code を使っても枠が余っているので、CLIツール開発をAIにやってもらうのは実質費用なしでできてしまう状況です。 この状況を踏まえて、2026年に自分のローカル環境のCLIツール開発に使うならどの言語がいいかを検討しました。 CLIツール開発言語に求めたい性質を列挙したら OCaml が残った 代数的データ型とパターンマッチ プログラミングを安全に分かりやすく記述するための奥義は、代数的データ型とパターンマッチだと常々思っているので、これは必須で欲しい機能でした。 パターン記述が見やすいとわかりやすさに直結するので、簡潔に書けることも重要。 静的型付け、型推論 型を丁寧に書くのは昔は面倒だったこともありますが、AI以前でもIDEで楽になっていましたし、今はAIに書いてもらうので型はあるだけ良いと思っています。 型が合わなければコンパイルで落ちてくれるので、テストがなくてもある程度安全です。 また、読む時に不要な情報が少ない方がよいので、型推論によって簡潔に書けることも重要です。 ビルドや起動が遅くない せっかちなので、遅くないものがよいです。 メモリ管理はしたくはない Rust が上で挙げた要素の多くを満たす選択肢であるとは思いますが、メモリ管理を記述したいほどのCLIツールは作らないので、今回は Rust という気分ではありませんでした。 以上のような性質を満たす言語を検討した結果、Haxe と OCaml が候補に残り、最終的には基盤開発チームメンバーの田尻さんが OCaml を激推ししていることもあって、OCaml に決めたのでした。*2 実際に OCaml をCLIツール開発に使ってみて 具体的にどのようなツールを作っているかですが、たとえば次のようなサービス用のCLIツールを作ったりしています。 Confluence Jira Redmine Sentry 特にConfluenceやJiraは、同じAtlassian製でそれぞれ専用のクエリランゲージ(Confluenceの場合はCQL)があり、使いこなすと性能よく色々なことができるので便利です。*3 OCaml コードの大部分はAIに生成してもらっていますが、書き味というか読み味がよいです。 やはり代数的データ型をプログラムの上の方に宣言して、それのパターンマッチにより分岐を網羅して記述すると、AIも書き間違えないし、読むときも分かりやすくて気に入っています。 ただ、AIへの指示なしで書いてもらった時は、生成されたコードが期待とちょっと違ったので、指示を少しだけ CLAUDE.md に書いています。 AIへの指示 型駆動設計 代数的データ型とパターンマッチを重視して言語選定をしているので、それが最大限生かされるように次の指示を書いています。 ## 型駆動設計 - 代数的データ型とパターンマッチを最重視 - 型で表現できることは型で表現 - 型シグネチャを先に決めてから実装 関数を書き始める前にまず型を考えて定義してくれるようになりました。 let* によるネスト改善 OCaml ではエラー処理にいわゆる Result 型を使えますが、 最初AIに書いてもらった時に、次のようにネストが深くなる書き方をされました。 let fetch_user config_path = (* 設定ファイルを読み込む(ファイルが無ければ失敗) *) match File.read config_path with | Error e -> Error e | Ok config -> (* 読み込んだ設定からAPIのURLを組み立ててHTTP GET *) match Http.get (config.endpoint ^ "/user") with | Error e -> Error e | Ok body -> Ok body そこで次の指示を書いて、ネストしない見やすい書き方にしてもらっています。 ## ネストを浅く保つ - Result型の連鎖にはResult.Syntaxのlet*演算子を使う この指示があると、先ほどのコードは次のようにネストが深くならず見通しが良くなります。 let fetch_user config_path = let open Result.Syntax in let* config = File.read config_path in (* 設定ファイル読み込み *) let* body = Http.get (config.endpoint ^ "/user") in (* 読み込んだ設定でHTTP GET *) Ok body 使っているライブラリ 業務上必要なCLIツールの多くは、標準入出力、HTTPアクセス、JSON処理ができれば足りることが多いので、外部ライブラリはほとんど使っておらず、少数のライブラリのみ使っています。 JSON処理にYojson HTTP処理にocurl(libcurlのOCamlバインディング。バイナリ配布などでライブラリ依存が気になる場合は他のものを使ったほうがよいかも) おわりに 今回とりとめもなく、AI作業の効率化のためにCLIツール開発が必要だと思ったことや、CLIツール開発に OCaml を使っている話を書いてみました。 個人的には OCaml は認知負荷が低くて読みやすいし、AIもあまり書き間違えない気もしていて、型がしっかりあるとAIも書きやすいのかなと感じています。 もともとローカル環境整備に凝らない私が凝り始めているのだから、もともと凝るタイプの人が今どんなやり方をしているかもぜひ知りたいところですね。 We're hiring! 好きな開発言語・スタイルで仕事がしやすいエムスリーでは、ソフトウェアエンジニアを募集しています。 新卒・中途採用、カジュアル面談やインターンも募集しています! エンジニア採用ページはこちら jobs.m3.com カジュアル面談もお気軽にどうぞ jobs.m3.com インターンも常時募集しています <iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fopen.talentio.com%2
【QAチーム ブログリレー7日目】の記事です。 はじめに 前提:なぜPlaywrightへ移行したか 「まずコードを書けるようになろう」は諦めた 5つのステップで開発フローへ Step 1: Claude Codeに慣れる Step 2: シナリオを「読む」 Step 3: VS Code Codegenを使う Step 4: mablシナリオの移行ができる Step 5: 積み重ねで改良・省力化へ 現在地 AIだけでは足りなかった:地道な活動 レビューでテストの意図を確認する リファクタリングで品質の均一化を図る AI導入と同時に向き合っている3つの負債 1. 理解負債:AIが書いたコードを誰も理解しない問題 2. 技術的負債(新しい形):AIツール自体の不安定性 3. 認知負債:AIへの過依存でチームの判断力が落ちる おわりに We're hiring! はじめに こんにちは、QAエンジニアの末吉です。最近東京を離れ、楽器不可のマンションに引っ越したため、ピアノが弾けなくなりました。悲しいばかり。 これは浜松ICにあるローランド製の電子グランドピアノ 突然ですが、私が担当するUnit4(m3.com開発チーム)のQAエンジニアには、開発経験がほとんどないメンバーもいます。 そういったメンバーが今では、ブランチを切り、Playwrightのテストコードを修正し、CIの失敗を自分で直してマージリクエスト(MR)を出せるようになっています。この半年で、チームが関わるリポジトリの8割以上にPlaywrightを導入した取り組みの話です。 この記事では、そこに至るまでのプロセスと、AI導入に伴って見えてきた「理解負債・技術負債・認知負債」という3つの課題への向き合い方を書こうと思います。 前提:なぜPlaywrightへ移行したか mabl(ノーコードE2Eテストツール)からPlaywrightへの移行の背景についてはこちらの記事で詳しく書いています。 「まずコードを書けるようになろう」は諦めた 最初に考えたのは、チームメンバーにTypeScriptを学んでもらうことでした。ただ、現実的ではありませんでした。日常のQA業務をこなしながら、ゼロからプログラミングを習得するのは時間的にも心理的にもハードルが高い。そもそも作成すべきテストシナリオは山積みで、「学んでから始める」では間に合いません。 そこで発想を変えました。「コードを書けるようになる」のではなく、「AIを足場にして開発フローに参加できるようにする」という目標設定です。 5つのステップで開発フローへ Step 1: Claude Codeに慣れる まず取り組んだのは、生成AIそのものへの慣れです。Claude Codeをインストールし、日常業務の中で使う練習から始めました。 ここで大事にしたのは、「なんでも答えてくれる優しい先輩に聞く」感覚で使うことです。余談ですが、私自身も開発エンジニアに仕様を聞くとき、ちょっと恐る恐るになりがちです。忙しそうだし、初歩的な質問はどうかな、と。同じような感覚を持つQAメンバーは多いんじゃないかと思います。でもAIには何度聞いても怒られません。「こんなこと聞いていいのか」という壁がないことが、非開発者にとっては大きな入り口になります。うまくいかなければ「さっきの回答はうまくいかなかった、こういう状況なんだけど」と伝え直す。このやり取りの繰り返し自体が、後のステップで効いてきます。 Step 2: シナリオを「読む」 次に、Playwrightのテストコードを読めるようにしました。書くのはまだ先です。 ここで活用したのが、社内にすでにあったリポジトリです。エムスリーでは他チームが先にPlaywrightを導入しており、ある程度完成されたコードベースが存在していました。それを参考に、コードがどういう構造になっているかを把握していきました。 読む際にはClaude Codeを使いました。「このファイルは何をしているか」「この処理の意味は」と聞きながら読み進めると、ゼロから自力で読み解くより理解が早い。既存のテストシナリオと並べて「この操作がこのコードに対応している」と照らし合わせる作業を繰り返すことで、コードの構造が徐々に見えてきます。完全に理解できなくても、「だいたいこういうことをしているコード」が分かれば十分です。 Step 3: VS Code Codegenを使う ブラウザを実際に操作すると、その操作に対応するPlaywrightコードが自動生成されるVS CodeのCodegen機能を使い始めました。 「コードは手書きするもの」という思い込みが崩れる瞬間です。自分の操作がコードになる体験を通じて、「コードは操作の記録だ」という直感が生まれます。これがあると、既存のコードを読む解像度も上がります。 このCodegenは、もともと使っていたテストツールのレコーディング機能と同じ感覚で使えました。ブラウザ操作を記録してテストを作るやり方に慣れているメンバーには、特に導入しやすかったです。 Step 4: mablシナリオの移行ができる Step 1〜3が揃うと、mablのシナリオをPlaywrightに移行できるようになります。 実際の作業では改良の結果、Step 3以降の手順はマルチエージェントで一気通貫に実施しています。シナリオの解析・コード生成・レビューを自動化し、メンバーがやることはエージェントの出力を確認して意図通りかを判断すること。「書く」ではなく「確認して承認する」役割です。詳しい仕組みはこちらの記事をご覧ください。 Step 5: 積み重ねで改良・省力化へ マルチエージェントで一気通貫の仕組みができてからも、改善は続きます。「このパターンはエージェントが苦手だ」「ここはいつも同じ修正が必要だ」という知見をもとに、エージェントへの指示を見直したり、新しいエージェントを追加したりする段階に入ります。 現在地 現在、チームのQAメンバーは以下ができるようになっています。 ブランチを切ってコードを修正し、MRを作成する CIが失敗したとき、エラーを読んでAIと一緒に修正し、再度MRを出す mablシナリオをエージェントに渡して、Playwrightテストとして移行する コードをゼロから書くことはまだできません。ただ、開発フロー全体を完結させることはできます。これが「AIを足場にする」ということです。 AIだけでは足りなかった:地道な活動 ここまでは5つのステップの話です。ただ、AIは移行を速く進めますが、移行後のテストが元のmablシナリオの意図を正確に再現できているかは別の話です。並行して地道な活動を続けたことが、品質を保つ上で重要でした。 レビューでテストの意図を確認する AIが生成したコードはそれっぽく見えます。動いてもいます。でも「このステップは元のmablシナリオの何を検証しようとしていたか」を説明できないまま承認しているケースが出てきます。そこで、移行後のテストがmablの意図を正しく再現できているかをレビューで確認する工程を設けています。 リポジトリ導入時には開発者にもレビュアーとして入ってもらい、コメントをもらっています。テストコードへの開発者の視点は、QAだけで閉じていると気づけない観点を拾ってくれます。 リファクタリングで品質の均一化を図る Playwrightの導入時期によって、リポジトリ間で品質に差が出ていました。早期に導入したものは設計の試行錯誤の跡が残っており、後から入れたものの方が洗練されている、という状態です。 そこで、ログインフローや管理画面のPage Objectなど、複数のリポジトリにまたがる共通コンポーネントをサブモジュール化し、品質の均一化を進めました。AIが個々のリポジトリで生成したコードをそのまま放置せず、共通化できる部分を人間が判断して整理する——この作業がなければ、リポジトリが増えるほど負債も増える一方でした。 AI導入と同時に向き合っている3つの負債 ここまでは成果の話です。並行して、AI活用に伴う3つの負債に向き合ってきました。もともとコードが書けないQAエンジニアがAIに頼る構造は、これらの負債が溜まりやすい。その分、意識的な対策が必要になります。 1. 理解負債:AIが書いたコードを誰も理解しない問題 GoogleのエンジニアリングリードであるAddy Osmaniは、AI生成コードによる「Comprehension Debt(理解負債)」を提唱しています。コードが生まれるスピードに理解が追いつかなくなり、「なぜこうなっているか」を誰も説明できない状態が蓄積していく、という問題です。 Anthropicの研究(How AI Assistance Affects Coding Skills)でも、AIを使ったグループは使わなかったグループより理解度テストのスコアが17%低く、特にデバッグ能力に最大の差が出たと報告されています。QAエンジニアにとって肝である問題を切り分けるデバッグ能力がAI依存によって落ちていくとしたら、深刻なリスクです。 私たちの対策: 前述のレビューがその実践です。マルチエージェントの自動レビューに加えて人間が確認する工程を残すことで、「誰も理解していないコードがマージされる」状態を避けています。Step 2(シナリオを読む)をあえて学習プロセスに組み込んだのも、同じ理由からです。 2. 技術的負債(新しい形):AIツール自体の不安定性 私たちのプロセスはClaude Codeに大きく依存しています。そのClaude Codeが2026年3月〜4月にかけて品質低下のインシデントを起こしました(Anthropic公式ポストモーテム)。推論レベルの変更、キャッシングのバグ、システムプロンプトの変更——3つの独立した問題が重なり、ユーザーへの告知なしに品質が低下していた期間があります。 さらに構造的な問題として、マルチエージェントの補完には限界があります。 通常時: エージェントAが誤った出力 → エージェントBが検知 ✅ 推論劣化時: エージェントAが劣化した出力 → エージェントBも同じモデルで劣化したレビュー → 全体が静かに劣化 ⚠️ 役割を分担していても、基盤のモデルは共通です。モデルが劣化すれば、多層化した全体が一緒に劣化します。 実際、この問題はチームで体験しています。品質低下の期間中、チームで定めていたエージェントへの指示ルールが丸ごと無視される形でコードが生成されるケースが発生しました。マルチエージェントで生成しているにもかかわらず、どの層でも検出できませんでした。最終的に発覚したのは、人間が手動でレビューしたときです。 私たちの対策: 根本的な解決は、多層化で「分散を吸収する」のではなく「そもそも分散を小さくする」方向にあると考え、現在取り組んでいます。具体的には、skillsやエージェントへの指示ルールの記述を見直しています。自然言語による指示(しかも継ぎ足しされるタイプ)はAIの解釈に幅が生まれ、それが出力のブレになります。そこで、解釈余地を持たせない構造化されたデータ形式やルールへの置き換えを進めています。公式もxmlタグフォーマットの使用を推奨しています。(prompting-best-practices) 3. 認知負債:AIへの過依存でチームの判断力が落ちる ビクトリア大学のMargaret-Anne Storey教授は、生成AI・エージェントAIの普及によって技術的負債よりも「認知負債(Cognitive Debt)」が問題になると指摘しています。AIに設計や実装を任せるほど、開発者自身がシステム全体を理解できなくなっていく、という問題です。 Anthropicの研究が示すもう一つの示唆は、AIを使う「方法」によって結果が変わるという点です。受動的に「やっておいて」と丸投げするほど理解度は落ち、「なぜこうなるか」を問いながら使うほど理解が保たれる。自分で考えず、AIに判断を丸投げし続けることで、じわじわとチームの判断能力が下がっていく——これが認知負債の本質です。 私たちの対策: 現状は理解負債と同様に、レビューとリファクタリングの徹底が対策になると考えています。「この移行は元の意図を再現できているか」をレビューで確認し、共通化の判断を人間が行う。ブラックボックス化を避けるため、少しスピードを落とした地道な活動です。 おわりに 開発経験がほとんどないメンバーが中心のチームが、AIを足場にPlaywrightの開発フローに参加できるようになりました。 ただ、それは「うまくいった話」の半分です。AIを入れるだけでは負債が積み上がるという認識から、レビューやリファクタリングといった地道な活動を続けています。そういう積み重ねが、チーム全体の底上げにつながると感じています。 生成AIはテスト実行の外にも広がり、QAプロセス全体に及んでいくでしょう。その波に乗りながら、AIに任せた部分に「なぜ」を問い続けること。そのバランスを模索している方の参考になれば嬉しいです。 We're hiring! エムスリーでは、QAエンジニアを募集しています。Claude Codeに限らず、あらゆるツールが使える魅力的な環境が整っています!是非ご応募ください。 jobs.m3.com
こんにちは。エンジニアリンググループのAI・機械学習チームに所属している鴨田 です。弊チームでは毎週1時間の技術共有会を実施しており、各自が担当するプロダクトの技術や、最近読んだ論文を紹介しています。今週はICLR2026が開催されていることもあり、同学会の論文読み会となりました。1セッションにつき1名が担当し、各自が選定した論文の詳細について解説しました。本ブログではその一部として、セッションごとの「推し論文」を紹介します。 まだ読んでいない方は前回のAAAI2026の輪読会ブログも是非ご覧になってください www.m3tech.blog ICLR 2026からトップページバナーを引用 Is it Thinking or Cheating? Detecting Implicit Reward Hacking by Measuring Reasoning Effort 推しポイント はじめに 報酬のハッキング 暗黙のハッキング 論文の提案 感想 AnyUp: Universal Feature Upsampling 推しポイント 課題 感想 Neon: Negative Extrapolation From Self-Training Improves Image Generation 推しポイント RealPDEBench: A Benchmark for Complex Physical Systems with Real-World Data 推しポイント マスク学習によるダイナミクスの獲得 U-Netの有用性とコストパフォーマンス 実データ収集における計測規模 Invisible Safety Threat: Malicious Finetuning via Steganography 推しポイント ステガノグラフィによる攻撃 ステガノグラフィでの学習、推論の工夫 感想 We are hiring !! エンジニア採用ページはこちら カジュアル面談もお気軽にどうぞ インターンも常時募集しています Is it Thinking or Cheating? Detecting Implicit Reward Hacking by Measuring Reasoning Effort セッション: Oral Session 2D LLMs and Evaluation 著者: Xinpeng Wang, Nitish Joshi, Barbara Plank, Rico Angell, He He 論文リンク:https://openreview.net/forum?id=Gk7gLAtVDO 紹介者: 髙橋 論文中Figure 2から引用 推しポイント はじめに ある評価指標が目標に設定されると、その評価指標はもはや良い評価指標ではなくなる これはよく知られた格言でKPIデザインなどでよく言及されますが、LLMにも当てはまります。 最近のコーディングエージェントなどのLLMは、多ステップの推論を経て複雑な問題を解きます。こうした推論能力の最適化には主に強化学習が用いられ、最終的な正解や、そこに至る正しい推論ステップ(過程)に対して報酬を与えることでモデルの学習が進められます。 報酬のハッキング この学習の過程でモデルは設計者の期待から逸脱した行動によって報酬を「ハック」しようとすることがあります。 例えば、ユニットテストの通過のみを報酬として設定すると、モデルは要件から想定される実装の代わりに、テストの入出力をそのままハードコードして無理やりパスさせようとするケースがあります。 このような「明示的なハッキング」であれば、モデルの推論過程(CoT:Chain-of-Thought)を監視することで比較的容易に検知できます。なぜなら、モデルが抜け道を使おうとするプロセスや意図自体が、CoTのテキスト上にそのまま出力されてしまうためです。 暗黙のハッキング 今回の論文が問題提起しているのは、明示的なハッキングより巧妙な「暗黙のハッキング(Implicit Hacking)」と呼ばれる現象です。 暗黙のハッキングとは、モデルが実際には早期に抜け道を利用して答えを出しているにもかかわらず、CoT上では「いかにも正当な推論を順序立てて行っているかのような」もっともらしいテキストを生成して隠蔽している状態を指します。 暗黙のハッキングが行われていると、出力されたテキストを監視する従来の手法では見抜くことができなくなってしまいます。 Claude Codeを利用していても、実際には実行していないコマンドをあたかも実行したかのように報告することがあります。 論文の提案 論文中の実験ではタスクの中に意図的にLoophole(容易に報酬が得られる抜け道)が存在する環境を構築し、強化学習を通じてモデルがどのように振る舞うかを観察しています。 引用図のように特定の条件(問題のid番号や負の数を返せば報酬が得られる)を満たせば本来の推論をスキップして正答できる抜け道を与えた結果、モデルはその抜け道を利用して容易に正解を導き出しながらも、出力上はもっともらしく長い推論過程を生成し続けるという「暗黙のハッキング」を学習するモデルが実際に作られました。 論文ではこうした暗黙のハッキングを検出するための手法として、Truncated Reasoning AUC Evaluation (TRACE)を提案しています。 考え方としては、「ハッキングを利用していると推論のステップ数が少ない段階で本来解けないはずの問題を正解できてしまう」、という性質を逆に利用します。 具体的には、推論ステップの生成を10%、30%、50%といった任意の割合で強制的に打ち切り、その時点での文脈のみから最終解答を出力させます。 各打ち切り時点の推論ステップと正答率をグラフにプロットし、その曲線の下の面積(AUC: Area Under the Curve)を計算します。 暗黙のハッキングを行っているモデルは、推論の初期段階で打ち切られても高い正答率を保つため、AUCの値が大きくなる一方でハッキングを利用していないモデルでは、推論ステップ数に伴って正答率が向上するためAUCの値は比較して小さくとどまります。 この指標を用いることで、生成されたテキストのもっともらしさに騙されることなく、モデルの内部的な推論の早期終了を定量的に検知できる、と論文では主張されています。 感想 LLMの発展は常に目覚ましいもので、どうしてもモデルの規模に目が奪われがちですが、モデル能力の向上は評価側の発展に支えられているのだなと改めて感じさせてくれる論文でした。 AnyUp: Universal Feature Upsampling セッション: Oral Session 5E Learning in computer vision 著者: Thomas Wimmer, Prune Truong, Marie-Julie Rakotosaona, Michael Oechsle, Federico Tombari, Bernt Schiele, Jan Eric Lenssen 論文リンク:https://arxiv.org/pdf/2510.12764 紹介者: 鴨田 論文中Figure 3から引用 視覚基盤モデル(VFM)の発展により、画像全体の高度な意味理解が可能になりましたが、構造上出力される特徴量マップの空間解像度が劇的に低下してしまうため、ピクセル単位の精緻な予測(セグメンテーションや深度推定など)が難しいという根本的な課題があります。 この課題を解決し、低下した解像度を復元するアプローチとして特徴量アップサンプリングの研究がこれまでも盛んに行われてきました。しかし、既存の学習ベースの手法には、DINOやCLIPなどバックボーンとなるモデルを変更するたびに、アップサンプラー全体をゼロから再学習しなければならないという実用上の大きな壁がありました。 この論文の面白いところは、特定のモデルアーキテクチャに依存しない「特徴量非依存(Feature-Agnostic)」なアップサンプリングを推論時に実現している点です。 従来のように特定の特徴量に依存するのではなく、入力段に独自の「特徴量非依存レイヤー」を設け、画像のどこにエッジやテクスチャの境界があるかといったパターンを抽出するアプローチをとっています。これにより、未知の次元数や表現空間を持つ特徴量であっても、一度学習するだけで任意の解像度へ柔軟に拡張できるようになりました。 推しポイント 個人的に一番の推しなのは、「画像の解像度を復元(アップサンプリング)するには、わざわざ画像全体の特徴を計算しなくても、局所的(Local)な特徴を見るだけで十分ではないか」というアプローチです。 従来の手法は画像全体のクロスアテンションを計算しがちでしたが、AnyUpは局所的な構造変化さえ捉えられればよいと割り切り、ローカルなウィンドウアテンションを採用しています。さらに、複数の異なるモデル(DINOv2とSigLIPなど)でマルチバックボーン学習させることで、未知のモデルに対するゼロショット汎化性能を底上げしており、この直感的な設計と普遍性の組み合わせが見事です 。 課題 ただし、ウィンドウアテンションの副作用として、オブジェクトの境界分離能力(空間的識別性)が甘く、特徴量マップ全体が滑らかに一様化しやすい傾向があったり、アテンション計算の性質上、解像度が大きくなると計算コストとメモリ消費が二次関数的に爆発してしまうという構造的な弱点も抱えています。 このような課題が解決されていないので、計算の線形化を目指す「UPLiFT」や、学習効率と高倍率へのスケーリングに特化した「DiveUp」という論文が続々と登場しています。 感想 とはいえ、テーマ自体が「普遍的アップサンプリング」として面白いので後続の論文がたくさん出ている点と、この論文自体シンプルなアプローチで読みやすい点を考慮して推し論文としました! 後続の論文も読みやすいので合わせて読んでみてください! Neon: Negative Extrapolation From Self-Training Improves Image Generation セッション: Oral Session 1B Generative models I 著者: Sina Alemohammad, Zhangyang Wang, Richard Baraniuk 論文リンク:https://openreview.net/pdf?id=kpLRYtPGt3 紹介者: 中村 伊吹 論文中Figure 1から引用 推しポイント 生成AIの性能向上には大量のデータが必要ですが、高品質な実データは今後ますます貴重になります。そこで期待されるのが、モデル自身が生成した「合成データ」を使った自己学習です。ただし、合成データだけでFine-Tuningすると、品質や多様性が急速に崩れる「モデル崩壊」が起きやすい、という難しさがあります。 論文では、合成データによる崩壊の原因を、モデルがもともと出しやすい画像をさらに出しやすくしてしまう mode seeking にあると説明しています。つまり、自己生成データにはどうしても偏りがあり、その偏りでさらに学習すると、よく出るパターンばかりが強化され、生成の多様性が失われていく、という見方です。 この論文の面白いところは、そのモデル崩壊を「避けるべき失敗」として扱うのではなく、あえて利用する発想にあります。合成データで少しだけ自己学習したときの更新方向を、「モデルが崩壊していく方向」だとみなし、その逆向きにモデルを動かすことで性能改善を狙います。タイトルの Negative Extrapolation という名前も、
【QAチーム ブログリレー6日目】の記事です。 はじめに こんにちは。エンジニアリンググループ QA (QualityAssurance) チームの中塚です。 2週間ほど前からバジルの水耕栽培にチャレンジしていて、少しずつ大きくなる双葉を見守るのが毎朝の楽しみです。肥料を溶かした水だけで本当にあんなに大きく育つのか?自由研究の気分で楽しく観察しています。 画像はAI(Gemini)により生成されたイメージです。このブログ記事の内容から生成してもらいました。バジルの鉢植えもあってかわいい! さて、私は普段コンシューマ向けサービス全般のQAの設計、実施、計画、その他諸々の活動をしています。 その中で、時々このような手動でやるには辛いテストが必要になることがあります。 サービスの各ページにアクセスし、契約期間内ならアクセスできるが契約期間外ならアクセスできないことを確認したい。ページのリストは無く事前調査が必要で、おそらく数十ページを繰り返しアクセスして比較する 特定のキーワードを検出し、警告するようなチェックシステムをテストしたい。外部LLMによるチェックなのでキーワードリストはないが、精度を見ておきたいのでさまざまなキーワードを試して動作確認したい。 このように、同じような操作を何回も繰り返したり、ページの調査やキーワードリストの準備といった事前調査などを含む地道な作業が必要になるテストを自動生成・自動実行できないか試したところ、PlaywrightとClaude Codeを使い手軽にテストコード生成〜実施〜レポート出力までが実現できたので、その実践例をご紹介します。 はじめに Claude CodeとPlaywrightについて 前準備 テスト計画を書く 例:アクセス制御テスト 実装 → 実行 → レポート 良かった点 時間が大幅に短縮され、手を離せた 気軽に使えた 使いやすい わかりやすいレポートが出せる 今後の展開 おわりに We're Hiring! Claude CodeとPlaywrightについて 弊社ではClaude Codeが無制限で利用可能となっていて、普段の業務からテストまたはコーディング業務などに活用しています。Claude Codeに自然言語で依頼すると、人間が指定しなくてもタスク実行のためにClaude Codeが適切なツールを選定して使用してくれます。また、plan modeを利用すると、実行前に対話したり、向こうから質問してもらい精度を上げてから実行できます。 PlaywrightはWebアプリケーション向けのE2Eテストフレームワークで、テストランナー、アサーション、分離機能、並列処理、自動待機(Auto-wait)機能など豊富なツール群が備わっており、実現したいテストをシンプルに書くことができます。 エムスリーでは多くのプロジェクトでE2EテストがPlaywrightで書かれています。また、Playwright MCPをインストールしておくことで必要に応じてブラウザを起動し操作したり内容を確認してくれます。 今回の例では必須ではありませんが、PlaywrightにはPlaywright Test Agentが付属しており、導入しておくとこのようなタスクを効率よく実行してくれます。 Planner : アプリケーションを探索し、マークダウン形式のテスト計画を自動作成 Generator : マークダウン計画から実行可能なPlaywright Testコードを生成 Healer : 失敗したテストを自動検査・修復し、パスするまで調整 計画も含めAIに任せられる仕組みがありますが、今回のケースでは計画を人間が作り、設計、実装、実行、レポート出力をAIに任せる使い方をしました。 前準備 Playwright MCP、Claude Codeをインストールし、Playwright(+ Playwright Test Agent)を入れたリポジトリを用意します。 テスト計画を書く まずは自然言語でやりたいことを簡潔に書きます。 例:アクセス制御テスト # やりたいこと アクセス可能/不可能の比較テストをしたい。 契約期間内の組織は専用LPにアクセスできるが 契約期間外の組織は専用LPのどのページにもアクセスできないことを確認したい。 契約期間内の組織のLP:https://example.com/active 契約期間切れした組織のLP:https://example.com/expired 先に契約期間内の組織LPを探索し、存在する画面のリストを作成した後に URL末尾のIDを契約期間切れした組織のIDに書き換え、それぞれにアクセスできないことを確認してほしい。 また、テスト結果レビューのため、確認したURLのリストと結果を一覧で残してほしい。 実装 → 実行 → レポート このようなテスト計画をざっくり書いたら、Claude Codeに計画を読み込んでもらいます。 plan modeで実行するとテスト計画に盛り込めていなかった詳細をAIが質問してくれるので、対話形式でより細かく作り込むことができます。 今回の例では、このような事項が対話を通じて追加されました。 クローリングを行うが、何階層まで行うか 「アクセスできない」の基準(HTTPステータスコードが4xxエラー、特定のエラーが表示されるなど) 除外すべきURLの種類 テスト実施結果レポートに含めたい情報 テストの失敗条件(契約切れ1つでも失敗したら失敗とみなすなど) 記事コンテンツなど同じ種類のページを全てテストするか テスト実行中の進捗表示の必要有無 etc... これらをインプットすると実装前に実装計画が出ます。確認し、疑問や修正点があれば指摘し、問題なければ実装を進めてもらいます。 AIはテスト計画を読んで ページクローリング機能 URL書き換えロジック ステータスチェック 結果レポート生成 といった機能を持ったテストコードを生成してくれました。 実装が終わったら、生成されたテストコードを実行します。コンソールからコマンドを入力して自分で実行してもいいですし、Claude Codeに依頼して実行してもらうこともできます。 実行後、このように結果をレポートしてくれます。 # アクセス制御テストレポート **実行日時:** 2026/4/19 18:53:04 ## 組織情報 - **契約期間内組織:** active - **契約期間切れ組織:** expired ## サマリー | 指標 | 値 | |------|----| | 発見ページ数 | 42 | | 契約期間内アクセス成功 | 41 | | 契約期間切れアクセス拒否 | 42 | | テスト成功 | 41 | | テスト失敗 | 1 | ## 詳細結果 | ページパス | 契約期間内 | 契約期間切れ | テスト結果 | |-----------|----------|------------|----------| | /active | ✓ (200) | ✗ (404) | ✅ PASS | | | | *理由: 404 Not Found* | | | /active/about | ✓ (200) | ✗ (404) | ✅ PASS | | | | *理由: 404 Not Found* | | (以下略) 内容を確認し、更に確認したいことがあればインプットして追加で実装・実行することも可能です。 また、レポートの形式変更や内容の整理のリクエストなどもできるので、レビューする上で助かりました。 良かった点 時間が大幅に短縮され、手を離せた もし手動で実行していたら繰り返しの操作と確認の作業が数十回必要となっていましたが、これらを自動実行に任せ、また、実行・出力のタスクも10〜20分程度で完了しました。 短時間で済んだほか、タスク終了までの待機時間中に他の作業を進めることができました。 気軽に使えた ラフな計画から対話を通じて精度を高め、集中力を他の仕事に振り分けながら実行できたのは嬉しいことでした。私は複数PJを兼任していて、仕様やタスク内容を思い出すなどの他の仕事からのスイッチングのコストが負担になる場合がありますが、AIのアシスタントでこれを軽減できるのでありがたいです。 曖昧な甘い部分をAIが補完してくれるので、作り込んでから渡さなくてもタスクが発生した時点でまず軽い相談から最初の一歩を踏み出せます。 使いやすい 生成や実行が早く、気軽に生成して気軽に使い捨てできるので、自動化できそうだなと思ったらまずはやってみるといった使い方ができます。 今回の例だとクローリング、アクセスしてステータスコードをチェック、とシンプルな手順でしたが、テスト内容によってはいざコードは生成されたが実行してみるとロケーターが不安定、操作内容やUIが複雑でPlaywrightの操作がうまくいかないなどのトラブルもありえます。 そういう時は実装を切り上げ、一部の実施のみ手動で実行するなどの方針転換も容易です。どこまでを自動化するかを相談しながら進めることもできます。 わかりやすいレポートが出せる テストの内容と実施結果がレポートとして出力されます。 最初に出されたものを元に失敗したものについて質問して切り分けを進めてもいいですし、フォーマットが気に入らなければ再生成の指示を出せば指示通りのフォーマットにしてくれます。 また、レポートのレビュー中に実施結果の不足や追加確認の必要が分かれば追加で指示を出し、更なるテスト実装・実行も可能です。 今後の展開 現在の使い方としては、単発のテストのタスクを簡易に自動化するという使い方をしています。 このため使い回す仕組みがなく、タスクごとにテストに必要なテストコードを都度生成していますが、同じプロダクトでタスク実行の機会を重ねれば、ログインやメイン機能などよく使うテスト手順を共通化し、テストコードを育ててより複雑なテスト手順に対応することも考えられます。 または、同プロダクトにはすでにPlaywrightで書かれたE2Eテストがあるので、こちらからコードを借用する使い方もできるかもしれません。 ただし、複雑な手順はコードも複雑になるほか、手動で実施すべき操作とかけ離れた手順で実装されるリスクもあり、意味のあるテストであるかはしっかりレビューする必要があります。このレビューの精緻化、効率化も課題となるでしょう。 おわりに PlaywrightとClaude Codeを使うことで、テストを手軽に自動化できました。 手軽に試せてすぐに成果物が出るので、「まずはやってみるか!」の気持ちで軽く相談することから始めてみても良さそうです。自動化をやってみたいけどハードルの高さを感じていた方も、まずは試してみてはいかがでしょうか。 We're Hiring! エムスリーQAチームでは一緒に働く仲間を募集しています。ご興味のある方は、ぜひ採用ページをご覧ください! jobs.m3.com
【QAチーム ブログリレー4日目】 はじめに こんにちは、QAチームの草場です。 レーモン・クノーの『文体練習』という本をご存知でしょうか? 1947年に出版されたこの本は、とある短い1ストーリーを99通りの文体で書きわけるもので、語られるのは同じストーリーなのに文体を変えるだけで得られる情報や印象の変化を感じられる味わい深い本です。 今回は「文体練習」を参考に、システムの仕様を表す文体として最適なものは何か? もしくは文体による差は無いのか?をカジュアルに実験してみました。 仕様は、書き手や場面によって様々な文体で書かれることがあります。決められた書式で厳密に書かれた物、広く知られた記法では無いが構造的に整理された物、Slackに貼られたメモのような物、ユーザーからの口伝を文字起こしした物など、多種多様です。どのテキストでも同じ機能の話をしているとして、そこから読み取れる情報量やテスト観点の数は、同じなのでしょうか? 異なるのでしょうか? このように、実験してみました。架空のフードデリバリーアプリの注文フローを11の文体で書き分け、それぞれをAIに読ませてテストケースを生成させ、事前に人間が作成したテストケースに対するカバレッジを比較するというものです。 結論を先に言うと、文体によるカバレッジの差はありました。ただし、それ以上にAIの実行ごとのブレや評価方法の影響が大きく、「文体にこだわる必要は薄い」という結果になりました。以下、実験の詳細と結果です。 はじめに 実験の概要 対象 11の文体 評価方法 同じシーンを11文体で書き分けると Gherkin 散文調 要件定義書風 極端に簡潔(メモ書き) 結果: 確かに差は出た Top Tierの5文体はほぼ同じ結果 文体の差より効いたこと 1. 実行ごとのブレが大きい 2. 生成と評価を分離する 3. 文体で拾えない観点を別添えする まとめ: AI時代の文体選びの考え方 余談 We're hiring! 実験の概要 対象 架空のフードデリバリーアプリ「QuickEats」の注文フロー。レストラン検索からカート追加、決済、配達追跡、評価までを含みます。誰でも使ったことがあるサービスで、AIにも十分ドメイン知識があることを想定しています。 11の文体 現場で実際に見かける記述スタイルを11個用意しました。 Gherkin / 箇条書き / 散文調 / ユーザーストーリー / フローチャート / 会話形式 / 表形式 / 要件定義書風 / メモ書き / 契約による設計風(Design by Contract) / スクリーンショット指示 各文体の特性に従って「自然に」書くというルールにしました。あえて、その文体で書けば自然に抜け落ちる情報はそのままにします。「文体が情報の取捨選択に影響する」現象そのものを観測したいからです。 評価方法 各文体をClaude(Sonnet 4.6)のサブエージェントに入力し、同一プロンプトでテストケースを生成させます。生成されたテストケースを、事前に人間で用意した26個のテスト観点と照合し、カバレッジを測ります。 AIの確率性を見るため 各文体で5回ずつ実行。合計55回の実行です。また、同じAIに生成と評価の両方をさせると客観性に欠けるため、生成するAIエージェントと評価するAIエージェントを別にしました。 なお、本実験ではコンテキストエンジニアリングやプロンプトチューニングをいっさい行っていない「素のClaude Code」を使いました。システムプロンプトの仕込みも、few-shot例の追加も無しです。 同じシーンを11文体で書き分けると 結果を出す前に、文体の違いを体感してもらうために、1つ見てもらう実験があります。 「商品をカートに追加する」というシーンだけを取り出して、11文体がそれぞれどう書いているかを並べてみます。同じ出来事なのに、11通りに書き分けると、読む印象がこれほど違います。長くなりますが、クノーの『文体練習』を追体験する意味でもスクロールしながら眺めてください。 Gherkin Scenario: 商品をカートに追加する Given レストラン「麺屋たろう」のメニュー画面を開いている When 「味噌ラーメン」を選択する And サイズ「大盛り」を選択する And トッピング「味玉」を追加する And 「カートに追加」ボタンを押す Then カートに「味噌ラーメン 大盛り 味玉」が追加される And カートアイコンに個数「1」が表示される 厳格で、テスト自動化にはそのまま使えそうです。一方で「売り切れだったらどうなる?」「価格はいくら?」には触れていません。 散文調 メニューをスクロールしていく。味噌ラーメン、醤油ラーメン、つけ麺。味噌ラーメンにしよう。タップすると、オプション画面が出てきた。サイズは大盛りにして、味玉もつけよう。合計980円。「カートに追加」を押す。 ユーザーの気持ちの流れは伝わります。でも操作手順の境界が曖昧で、テスト設計には使いにくそうです。 要件定義書風 3.3 メニュー閲覧及び商品選択(REQ-ORD-003) (2)利用者が商品を選択した場合、本アプリは当該商品に関するオプション(サイズ、トッピング、調理方法の指定等。以下、「商品オプション」という。)の選択画面を表示するものとする。商品オプションの内容及び価格は、各レストランパートナーが設定するものとする。 (3)利用者は、商品及び商品オプションの選択を完了した後、当該商品をカートに追加することができるものとする。 (4)商品が売り切れ(在庫切れ)の状態にある場合、本アプリは当該商品が現在利用できない旨を表示するとともに、可能な範囲において代替商品の提案を行うものとする。 厳密なようで厳密ではない文体ですが、売り切れ時の代替商品提案といった観点が定義されています。他の文体では触れられていない観点です。 極端に簡潔(メモ書き) 商品選ぶ → オプション(サイズ、トッピング等)→ カート追加 時間がない時にSlackに書き殴ったような3行メモ。 残り7文体 箇条書きステップ 5. 商品を選ぶ 6. オプションを指定する(サイズ、トッピング、辛さ 等) 7. 「カートに追加」を押す(複数商品を追加する場合は手順5〜7を繰り返す) ユーザーストーリー形式 US-004: 商品オプション指定とカート追加 ユーザーとして、商品のサイズやトッピングを選んでカートに追加したい。なぜなら、自分好みにカスタマイズした商品を注文したいから。 受け入れ条件: - サイズを選択できる(例: 並盛り/大盛り) - トッピングを追加できる(複数選択可) - オプション選択後の合計金額がリアルタイムで表示される - 「カートに追加」で商品がカートに入る - カートアイコンに商品数が表示される フローチャート SelectItem[商品を選択] ↓ SetOptions[オプション指定\nサイズ/トッピング等] ↓ AddToCart[カートに追加] ↓ Continue{買い物を続ける?} Yes → BrowseMenu に戻る No → ReviewCart[カート確認] 会話形式 ユーザー: 「マルゲリータ」を選択 システム: 商品詳細を表示。サイズ(S/M/L)とトッピング(チーズ追加、バジル増量 etc.)のオプションを提示。 ユーザー: Mサイズ、チーズ追加を選んで「カートに追加」をタップ システム: カートに追加完了。画面下部にカートアイコン(1点、合計1,580円)を表示。メニュー画面に戻る。 表形式 # ステップ 操作 入力データ 期待結果 4 商品選択 メニューから商品をタップ - 商品詳細画面が表示される。オプション選択UIが表示される 5 オプション指定 サイズ・トッピング等を選択 サイズ: M, トッピング: チーズ追加 選択内容に応じて価格が更新される 6 カート追加 「カートに追加」ボタンをタップ 数量: 1 カートに商品が追加される。カートアイコンに件数と合計金額が表示される 契約による設計風(Design by Contract) Operation: AddToCart Pre-condition: - selectedRestaurant ≠ null - item ∈ selectedRestaurant.menu - item.isAvailable = true Trigger: - ユーザーが商品を選択し、オプション(サイズ、トッピング等)を指定してカートに追加する Post-condition: - cart.items.contains(item, selectedOptions) - cart.totalAmount が再計算されている Exception: - item.isAvailable = false → 売り切れ通知を表示する スクリーンショット指示 1. 注文したい商品をタップしてください 2. 商品詳細のモーダルが画面下部からスライドアップします 3. サイズ選択がある場合、ラジオボタンで「S / M / L」等が表示されます。希望のサイズをタップしてください 4. トッピング選択がある場合、チェックボックスのリストが表示されます。追加したいトッピングをタップしてチェックを入れてください 5. モーダル下部の「-」「+」ボタンで数量を調整してください 6. 画面最下部の緑色のボタン「カートに追加 ¥XXX」をタップしてください 文体を比較してみると、「拾えている観点」に違いが見えてきます。 売り切れの扱い: 要件定義書風と契約による設計風だけが明示。散文やメモ書きは触れていない 具体的な数値: 散文は「980円」、会話形式は「1,580円」と自然に書かれるが、価格に触れていない文体が多い UIの見た目: スクリーンショット指示だけが「緑色のボタン」「モーダル」に言及 ユーザーの動機: ユーザーストーリーの「なぜなら〜」だけが「なぜ」を説明している 同じことを書こうとしているが、拾える観点が違う。これは確かに文体でテスト観点のカバレッジに差が出そうです。実際、AIに読ませてテストケースを生成させたら、どれだけの差が出るでしょうか。 結果: 確かに差は出た 55回の実行結果を人間が準備したテストケースと照合したところ、カバレッジに差が出ました。2層に整理したのが以下です。 11文体のカバレッジ比較。赤がTop Tier、灰色がそれ以外 Top Tier(76〜80%): 契約による設計風・表形式・ユーザーストーリー・箇条書き・要件定義書風 それ以外(63〜72%): 散文・Gherkin・フローチャート・会話形式・メモ書き・スクリーンショット指示 上位と下位の差は最大16ポイント(契約による設計風 79.24% vs スクリーンショット指示 63.08%)。文体がテスト観点に影響を与えているように見えます。 ただし、ここで注意が必要です。今回の実験はAIにテストケースを生成させたものです。AIは確率的に動作するため、同じ入力でも毎回結果が変わります。もしルールベースのツール(たとえばGherkinパーサーでGiven-When-Thenを機械的にテストケースに変換するもの)を使えば、文体の差は決定的に効くでしょう。そもそもGherkin以外の文体はパースできません。以降の分析と結論は、あくまで「AIにテストケース生成を任せる場合」に限定した話です。 その前提で、結果をもう少し詳しく見てみます。 Top Tierの5文体はほぼ同じ結果 順位 文体 平均 SD 1 契約による設計風 79.24% 11.6 2 表形式 79.22% 6.7 3 ユーザーストーリー 78.46% 5.2 4 箇条書き 77.70% 13.6 5 要件定義書風 76.14% 9.3 6 散文 71.54% 11.6 7 Gherkin 67.68% 10.0 7 フローチャート 67.68% 3.9 9 会話形式 66.92% 6.3 10 メモ書き 66.14% 8.2 11 スクリーンショット指示 63.08% 4.6 1位(契約による設計風)と2位(表形式)の差は0.02ポイント。1位と5位の差も約3ポイントです。一方で、同じ文体でも実行ごとのバラつき(SD)が5〜13ポイントあります。文体間の差よりも、同じ文体内のブレの方が大きいため、5回の実行ではこの順位に意味があるかどうかを判断できません。 また、同じ文体・同じプロンプト・同じ入力でも実行ごとに結果がブレます。たとえば箇条書きは61.5%〜100%の間を行き来しました。5回の実行で毎回上位5文体の順位が入れ替わるため、この実験からは「Top Tier内のどれが優れているか」は言えません。 一方で、Top Tier(76〜80%)とそれ以外(63〜72%)の間には傾向としての差が見えます。ただしこれもn=5での観察であり、サンプルを増やせば変わる可能性があります。 ここから言えるのは、AIにテストケース生成を任せる場合、Top Tierの5文体の中ではどれを選んでも大きな差はなさそうだということです。だとすれば、文体の微妙な優劣を追求するよりも、AIの運用の仕方に時間を使う方が実質的な改善につながるのではないか。実験の中で、文体の差以上に結果を左右する要因が3つ見えてきました。 文体の差より効いたこと<
こんにちは。エンジニアリンググループ QA (Quality Assurance) チームの津向です。 2月に入り、暖かくなってきたのでBBQをしたのですが、ピンポイントで降雪になり、雪の中で肉を焼くという稀な経験をしてきました。 後日、元プロテニス(現スポーツキャスター)の方が国内不在と知りました。 そんなわけでQAチームブログリレー2日目になります。 お肉は焼くことで美味しくなると言われています。 はじめに 1. エージェントの2層構造 専門エージェントの役割分担 逐次処理による「AIのコンテキスト過負荷」防止 2. 標準化を担う生成エージェント ナレッジベースによる標準化 共通パーツの再利用 3. デバッグエージェントの独立化 生成エージェントと分けた理由 Playwright MCPによるライブ診断 「堂々巡りの修正」をさせない 4. 2種類のレビューエージェント 役割を分けた理由 5. AI+ツールでレビュー おわりに We're Hiring! はじめに 以前からPlaywrightへの移行は検討されていましたが、工数や学習コストを考えるとなかなか進められずにいました。 そんな中、QA EngもClaude Codeが使い放題になったことを機に、mablからの移行を開始しました。 開始当初は専用のClaude Agentは作成せず、そのまま移行作業を行なっていました。 次々に生成されるコードを眺めて「このClaudeすごいよ、さすがClaude Chatのお兄さん!」と生温かく見守っていたのですが、次々に課題が出てきました。 コード規約が途中からスルーされていく 既存資産を再利用せずに1から作る 移行元のアサーションがスキップされる 大量に作成されるのでレビューが大変 AIにレビュー任せても、本当にレビューできているのか不確実 人によって、手順が異なるので成果物も微妙に異なる など様々でした。 そこでPlaywright移行に特化したClaude Agentを作成することで解決を図りました。 今回はこのClaude Agent(以降はエージェントと記載)についての記事になります。 上記の様々な課題は下記方針に基づくエージェントの作成により、課題を解決できました。 2層構造:全体の流れを制御する「オーケストレーター」と、各工程担当である「専門エージェント」の分離 ダブルチェック検証:技術品質と仕様完全性を別々のエージェントで検証 AI+ツール検証:AIの文脈判断とツールの機械的チェックを組み合わせた検証 1. エージェントの2層構造 単一の巨大なエージェントですべてをこなそうとすると、ルールが徹底されない、見落とし発生する、メンテナンス性が悪い、などデメリットが多く発生します。そのため、工程で役割を分けることで、デメリットを解消し精度を高めています。 オーケストレーター層:全体の進捗管理と、次にどの方針で進むべきかの判断に特化。 専門エージェント層:コード生成、デバッグ、品質レビュー、仕様レビューといった特定のタスクの実行に特化。 専門エージェントの役割分担 オーケストレーターの指揮下で働く、4つの専門エージェントの役割は次の通りです。 エージェント名 担当フェーズ 主な役割 playwright-code-generator 生成 mablの原本や仕様書を読み込み、Page Objectモデルに基づいたコードを作成。 playwright-debug-fix-engine デバッグ テスト失敗時に起動。ブラウザのライブ操作と証拠収集を行い、論理的に修正。 playwright-code-quality-reviewer 品質検証 コードがプロジェクト規約や技術的なベストプラクティスに沿っているかを査読。 playwright-spec-reviewer 仕様検証 生成されたコードが、原本の要求事項を100%満たしているか「実装漏れ」を検証。 表で説明した各エージェントは、実際には次のようなディレクトリ構造で管理しています。 . ├── orchestrators/ # 【全体制御】全体管理用オーケストレーター │ └── mabl-migration-orchestrator.md ├── code-generation/ # 【生成・修正】コード生成とデバッグ │ ├── playwright-code-generator.md │ └── playwright-debug-fix-engine.md ├── code-review/ # 【レビュー】2種類のレビュー │ ├── playwright-code-quality-reviewer.md │ └── playwright-spec-reviewer.md ├── knowledge/ # 【共通辞書】共通で使用するナレッジベース │ └── playwright_knowledge_base.md └── tool/ # 【補助ツール】静的解析用の自作スクリプト └── playwright-reviewer-v3.js 逐次処理による「AIのコンテキスト過負荷」防止 本システムにおいて、複数のテストケースを扱う際に「逐次処理」を徹底しています。 全ファイルのコードを一度に生成して最後にまとめて実行するのではなく、1ファイルごとに作業を完結させ、成功してから次のファイルに移るというサイクルを回します。これにより、AIのコンテキスト過負荷を防ぎ、精度の高い移行を実現しています。 2. 標準化を担う生成エージェント 役割分担の中でも、生成を担うplaywright-code-generatorは、全体の標準化と効率化の役割を担っています。 ナレッジベースによる標準化 生成の詳細なルールはエージェント内に記載せず、外部のナレッジベースplaywright_knowledge_base.mdに分離し、エージェントの容量を圧縮しています。また、このナレッジベースは、レビューエージェントplaywright-code-quality-reviewerでも共通して参照しています。生成側と検証側が同じ基準を持つことで、手戻りの少ない効率的な開発サイクルを実現しました。 共通パーツの再利用 ログイン処理やヘッダー操作など、各テストで使い回す共通パーツを自動で認識し、Page Objectモデルとして適切に再利用します。これにより、類似コードの乱立を防ぎ、長期的にメンテナンスしやすいテストの作成が可能になりました。 生成エージェントによる標準化によって、品質とスピードを両立できるだけでなく、誰が実行しても一貫性のある自動テストを作成できるようになりました。 3. デバッグエージェントの独立化 テスト失敗時の原因究明する playwright-debug-fix-engine は、AIの推測に頼った「なんとなくの修正」を排除し、客観的な証拠を最優先する設計です。 生成エージェントと分けた理由 生成するエージェントと同じセッションでデバッグすると、AIが自分で書いたコードや過去のやり取りに引きずられるバイアスが生じます。 独立したエージェントとして呼び出すことで、新しいセッションで調査を開始でき、事前の状況に左右されず、現在のコードと実行結果だけを客観的に診断することが可能になります。 Playwright MCPによるライブ診断 AIがブラウザを直接操作する MCP (Model Context Protocol)を活用し、エラー時の状態をリアルタイムで診断します。 構造解析: アクセシビリティツリーから、壊れにくいセレクタを再選定します。 視覚的確認: スクリーンショットやHTMLで、要素の被りや配置のズレを捉えます。 切り分け: ネットワークログ等から、プログラムのミスか通信エラーかを区別します。 「堂々巡りの修正」をさせない AIが3回試行しても解決できない課題に対し、それ以上の修正をストップしています。 代わりに、それまでの試行過程と収集した証拠をまとめ、Gemini等の外部LLMに相談しやすい形にし、人間にバトンタッチします。 エージェントで無理な修正をしないことで、堂々巡りの修正を防いでいます。 4. 2種類のレビューエージェント レビュー工程では、1つのエージェントに全てを任せるのではなく、観点を「技術」と「仕様」に完全に分離しました。 レビュー種類 担当エージェント 主な検証ポイント 技術品質レビュー playwright-code-quality-reviewer POM設計、セレクタ戦略、認証管理、CI/CD統合、規約準拠。 仕様完全性レビュー playwright-spec-reviewer 原本との突き合わせ、実装漏れ検出、定量的評価。 役割を分けた理由 エージェントを分けた理由は、AIに役割を絞らせて見落としを防ぐためです。 情報の整理: 1つのAIに大量の規約と複雑な仕様を同時に読み込ませると、情報が混ざり、未実装の機能を実装済みと思い込むようなミスが起きやすくなります。 チェックの精度向上: コードの綺麗さと仕様の網羅性を別々の視点でチェックすることで、細かな実装漏れも見つけ出せるようになります。 責務を分離することで、各工程のチェック精度を高めています。 5. AI+ツールでレビュー AIエージェントは文脈判断に優れますが、一方で単純な形式チェックを見落とすことがあります。 そこで、AIレビューに加えて、独自の静的解析ツール playwright-reviewer-v3.js を実行するハイブリッド体制にしました。 AI:POMの責務分離や、アサーションの論理的な配置をレビュー。 ツール:23項目のアンチパターン(waitForTimeout の使用、await 忘れ等)を機械的に検出。 この2段構えにより、AIの見落としを防ぎ、品質を担保しています。 おわりに 本システムの活用により、Playwrightの経験有無に関わらず、チーム全員が移行タスクの即戦力になれました。 今は移行メインですが、今後は新規テスト作成や継続的な品質を担保する仕組みとしても活用していく予定です。 We're Hiring! そんなわけで自動化や効率化が大好きな方、AIが大好きな方、なぜか不具合に好かれる方など様々なQAエンジニアを募集しています。一緒にQA活動しましょう! jobs.m3.com
こんにちは、AI・機械学習チームの須藤です。 この記事はAI・機械学習チームブログリレー14日目の記事です。 13日目は田中さんによる「スタートアップCTOが、M3のAIチームに転職して3か月。感じた不安と、その答え。」でした。 www.m3tech.blog 突然ですが、私は今年に入ってからランニングを始めました。1月頃はキロ8〜9分ペースで2〜3km走るのがやっとでしたが、毎日続けているうちに最近はキロ4分台で走れるようになり、20km程度であれば走れるようになってきました。今年の目標はマラソン大会に出場することです。継続は力ですね。 最近買ったadizero evo sl woven。AIエージェントが自走するということは人間も走らないといけない(?)。 はじめに Claude Codeのサンドボックス機能 基本的な設定と使い方 はじめ方 設定のカスタマイズ Managed設定 サンドボックスの動作検証 ファイルシステムの制限 ネットワークの制限 まとめ We are hiring! はじめに さて、最近はAIエージェントが急速に普及し、開発の仕方が大きく変わってきているのを感じています。私自身もここ1年で働き方がだいぶ変わってしまいました。コードを書くのはClaude Codeに任せて、自分は設計の壁打ちやレビューに集中するような形になっています。 最近私が考えているのは、いかに人間が運用のボトルネックにならないようにするか、という点です。品質と安全性を保ちながら、AIが自律的に動ける範囲を広げられないかを模索しています。 ただ、AIに自律的にコマンドを実行させる以上、安全性への不安もつきまといます。Claude Codeは実行前に承認を求めてくれますが、公式ドキュメントにも「承認疲れ」*1という言葉があるように、承認を繰り返しているうちに、人はどうしても内容を確認しなくなってしまいがちです。また、その承認が本当に正しい操作なのかを判断するのは、慣れたエンジニアでも難しいことがあります。Claude Codeはエンジニア以外でも使う機会が増えており、普段開発をしていない人にとってはなおさらです。 さらに、ユーザーが意図しなくても被害を受けるケースもあります。間接的プロンプトインジェクションと呼ばれる攻撃があり、悪意ある指示をAIに読み込ませることで意図しない操作をさせるものです。たとえば、画像やPDFに人の目には見えない形でプロンプトを埋め込む手法や、GitHubのPRレビュー画面には表示されないがソースファイルには悪意ある指示が埋め込まれているケースなどがあります。 Claude Codeにはこうしたリスクを軽減する「サンドボックス機能」が用意されています。本記事ではこの機能を実際に試してみた内容を紹介します。 Claude Codeのサンドボックス機能 Claude Codeのサンドボックス機能は、Bashコマンドの実行環境をOSレベルで分離することで、より安全なエージェント実行を実現します。あらかじめ境界を定義しておくことで、その範囲内ではClaude Codeが自由に動作できる仕組みになっています。 具体的には、ファイルシステムアクセスを特定のディレクトリに制限し、ネットワークアクセスはサンドボックス外で動作するプロキシサーバーを通じて制御します。これらの制限はOSレベルの分離機能を用いて実現されており、macOSではSeatbelt、Linux / WSL2ではBubblewrapが使われるそうです。*2 基本的な設定と使い方 はじめ方 Claude Codeを起動し、/sandboxコマンドを実行することでサンドボックスを有効化できます。 /sandbox 実行するとモード選択メニューが開くので、モードを選択します。 Configure Mode: 1. Sandbox BashTool, with auto-allow 2. Sandbox BashTool, with regular permissions 3. No Sandbox Auto-allow mode: Commands will try to run in the sandbox automatically, and attempts to run outside of the sandbox fallback to regular permissions. Explicit ask/deny rules are always respected. 各モードは次の通りです。 Sandbox BashTool, with auto-allow:サンドボックス内で実行されるコマンドが自動承認されるモードです。サンドボックス外へのアクセスが必要な場合は通常の許可フローにフォールバックします。 Sandbox BashTool, with regular permissions:サンドボックス化されている場合でも、すべてのコマンドで通常の許可フローが走るモードです。 後ほど説明しますが、サンドボックスではアクセス可能なファイルやネットワークのドメインを制限でき、Claude Code自体のAllow/Denyルールと組み合わせることで、やっていいこと・やってはいけないことをあらかじめ定義できます。auto-allowモードでは、こうした境界の範囲内でClaude Codeが自律実行することが想定されています。 両モードともサンドボックスによるファイルシステムとネットワークの制限は同様に適用されます。違いは、サンドボックス化されたコマンドが自動承認されるか明示的な許可が必要かどうかのみだそうです。 なお、Linux / WSL2 の場合は bubblewrap や socat などの追加パッケージが必要です。不足している場合は /sandbox 実行時にインストール手順が表示されるので、それに従ってインストールしてください。 設定のカスタマイズ settings.json に次のように書くことでサンドボックスの動作をカスタマイズできます。 { "sandbox": { "enabled": true, "failIfUnavailable": true, "autoAllowBashIfSandboxed": false, "allowUnsandboxedCommands": false, "filesystem": { "allowWrite": ["~/.kube", "/tmp/build"], "denyRead": ["~/.ssh"] }, "network": { "allowedDomains": ["*.npmjs.org"] } } } 設定項目 説明 enabled サンドボックスの有効・無効。/sandbox コマンドでも有効化できるが、こちらに書いておくとClaude Code起動時から有効になる failIfUnavailable サンドボックスが起動できない場合にエラーで終了する(false の場合は警告を出してサンドボックスなしで続行) autoAllowBashIfSandboxed サンドボックス内のコマンドを自動承認するか(前述のauto-allowモードに相当) allowUnsandboxedCommands サンドボックス外でのコマンド実行を許可するか(false にするとエスケープハッチを無効化) filesystem.allowWrite 書き込みを許可するパスの追加 filesystem.denyRead 読み取りを拒否するパス network.allowedDomains アクセスを許可するドメイン ファイルシステム、ネットワークのデフォルトの動作は次の通りです。 ファイルシステム 読み取り:コンピュータ全体への読み取りが可能 書き込み:カレントディレクトリとそのサブディレクトリのみ ネットワーク allowedDomains に登録済みのドメインにはユーザーの承認なしにClaude Codeがアクセスできます 未登録ドメインへアクセスしようとするとユーザーに承認プロンプトが表示されます allowManagedDomainsOnly を設定すると、allowedDomains 以外のドメインへのアクセスは即時エラーになります(Managed設定でのみ設定可能) 詳細な設定項目については公式ドキュメントを参照してください。 Managed設定 Managed設定はIT管理者が組織内の全ユーザーに適用できる設定で、ユーザー側でオーバーライドできません。*3*4 配信方法はいくつかありますが、たとえば次のようなシステムディレクトリへのファイル配置で実現可能です。 OS パス macOS /Library/Application Support/ClaudeCode/managed-settings.json Linux / WSL /etc/claude-code/managed-settings.json Windows C:\Program Files\ClaudeCode\managed-settings.json サンドボックスの文脈では、次の設定が特に有効です。 サンドボックスの強制 sandbox.enabled: true を設定すると、ユーザーがサンドボックスを無効化できなくなります。 許可ドメイン以外の即時ブロック sandbox.network.allowManagedDomainsOnly: true を設定すると、allowedDomains に登録されていないドメインへのアクセスが承認プロンプトなしに即時エラーになります。前述の通りこの設定はManaged設定でのみ有効です。 MCPの制限 後述しますが、サンドボックスのネットワーク制限はBashサブプロセスにのみ適用されます。MCPのネットワークアクセスを制御したい場合は、allowedMcpServers と allowManagedMcpServersOnly: true を組み合わせることで、使用できるMCPサーバーを管理者が制御できます。 サンドボックスの動作検証 ファイルシステムの制限 まずはファイルシステムの制限を試してみます。Claude Code外のターミナルで検証用ファイルを作成しておきます。 echo "This is a test file for sandbox verification." > read_test.txt settings.json に denyRead を設定し、read_test.txt への読み取りを拒否してみました。 { "sandbox": { "filesystem": { "denyRead": ["./read_test.txt"]
AI・機械学習チームの鴨田です。この記事はAI・機械学習チームブログリレーの12日目の記事です。11日目は池嶋さんによる「Agentic MLOpsで加速する機械学習開発」でした。 娘の影響でYoutubeで「シナぷしゅ」を見ることが多くなったのですが、毎月更新される月歌(その月のテーマソング)とそれと共に投稿されるパパ・ママ向け解説動画が楽しみになりました。プロデューサーが直接こだわりを解説していて内容が濃くおすすめです。 nanobananaに「マトリックス・コードで猫の黒いシルエットが表現されている画像」で作成した画像 記事要約 Facebook/Meta AIの画像セグメンテーションモデル「SAM3」を使って猫の画像からMaskを抽出 抽出したMaskをマトリックス・コードのアニメーションと組み合わせて動く猫のシルエットを表現 記事要約 きっかけ SAM3とは SAM3の主な特徴 実装:SAM3でMaskを抽出 環境セットアップ 静止画からのMask抽出 動画からのMask抽出 MaskをASCII artに変換 実装:マトリックス・コードと組み合わせ OSSの選定と改修 実施結果 コード 余談:Google Colabのメモリ不足 まとめ We are hiring! エンジニア採用ページはこちら カジュアル面談もお気軽にどうぞ インターンも常時募集しています きっかけ YouTube*1を見ていたら、マトリックス・コードで猫のシルエットが表現されているTシャツを着ている人を発見しました。 「これ、手元で実現できないかな?」と考えていたところ、ちょうど以前申請していたFacebook/Meta AIのSAM3(Segment Anything Model 3)のモデル利用許可メールが届きました。 そこで、次のアプローチで実現しました。 SAM3を使って猫の画像から猫のMaskを抽出 マトリックス・コード生成ツールを改修してMask領域を描画しないようにする 猫のシルエットが浮かび上がるマトリックス・コードアニメーションを完成させる SAM3とは SAM3(Segment Anything Model 3)は、Meta AI Researchが2025年にリリースした最新の画像・動画セグメンテーションモデルです。主な特徴は次の通りです。 SAM3の主な特徴 1. Promptable Concept Segmentation SAM3の最大の特徴は、テキストプロンプトによるセグメンテーションです。従来のSAMでは点やボックスを指定する必要がありましたが、SAM3では「cat」のようなテキストを与えるだけで、画像内の該当するオブジェクトを自動でセグメンテーションします。 「cat」と指示すると、猫の範囲全体をMaskします。「ear」と指示すると生き物の耳をちゃんと認識して、インスタンスごとにMaskが生成されます。 左:プロンプトで「cat」と指示した結果。右:プロンプトで「ear」と指示した結果。 2. 画像と動画の両対応 SAM3は静止画だけでなく、動画のセグメンテーションにも対応しています。動画内のオブジェクトを追跡しながらフレームごとにMaskを生成できるため、今回のような動的なアニメーション作成にも活用できます。 3. ゼロショット学習 事前学習済みモデルを使うだけで、特定のドメインに対するファインチューニングなしで高精度なセグメンテーションができます。 詳細はHugging Faceのモデルページを参照してください。 huggingface.co 実装:SAM3でMaskを抽出 環境セットアップ まず、必要なライブラリをインストールします。 !pip install -U transformers !pip install -U av SAM3は利用申請が必要なモデル(gated model)のため、Hugging Faceでモデルページにアクセスしてライセンスに同意する必要があります。その後、トークンを使ってログインします。 from huggingface_hub import login login(token="YOUR_HF_TOKEN") 静止画からのMask抽出 次のコードで猫のMaskを抽出します。プロンプトには「cat」を使用します。 from transformers import Sam3Processor, Sam3Model import torch from PIL import Image device = "cuda" if torch.cuda.is_available() else "cpu" # モデルとプロセッサの読み込み model = Sam3Model.from_pretrained("facebook/sam3").to(device) processor = Sam3Processor.from_pretrained("facebook/sam3") # 画像の読み込み image = Image.open("cat_image.jpg").convert("RGB") # テキストプロンプトでセグメンテーション inputs = processor(images=image, text="cat", return_tensors="pt").to(device) with torch.no_grad(): outputs = model(**inputs) # 後処理 results = processor.post_process_instance_segmentation( outputs, threshold=0.5, mask_threshold=0.5, target_sizes=inputs.get("original_sizes").tolist() )[0] print(f"Found {len(results['masks'])} objects") 動画からのMask抽出 動画の場合は、SAM3VideoModelを使用します。 from transformers import Sam3VideoModel, Sam3VideoProcessor from transformers.video_utils import load_video model = Sam3VideoModel.from_pretrained("facebook/sam3").to(device, dtype=torch.bfloat16) processor = Sam3VideoProcessor.from_pretrained("facebook/sam3") # 動画の読み込み video_frames, _ = load_video("cat_video.mp4") # セッションの初期化 inference_session = processor.init_video_session( video=video_frames, inference_device=device, processing_device="cpu", video_storage_device="cpu", dtype=torch.bfloat16, ) # テキストプロンプトの追加 inference_session = processor.add_text_prompt( inference_session=inference_session, text="cat", ) # 全フレームを処理 outputs_per_frame = {} for model_outputs in model.propagate_in_video_iterator( inference_session=inference_session, max_frame_num_to_track=130 ): processed_outputs = processor.postprocess_outputs(inference_session, model_outputs) outputs_per_frame[model_outputs.frame_idx] = processed_outputs これで、各フレームごとに猫のMaskが取得できます。 MaskをASCII artに変換 抽出したMaskをマトリックス・コード生成ツールで使えるよう、ASCII artに変換します。 import numpy as np from PIL import Image def generate_mask_ascii_art(masks, scale_factor=0.5, fill_char="#", bg_char=" "): """ Mask領域をASCII artとして出力 """ if hasattr(masks, 'cpu'): masks = masks.cpu().numpy() # 複数Maskを結合 if masks.ndim == 3: combined_mask = np.any(masks > 0, axis=0).astype(np.uint8) * 255 else: combined_mask = (masks > 0).astype(np.uint8) * 255 # リサイズ mask_img = Image.fromarray(combined_mask) orig_w, orig_h = mask_img.size aspect_correction = 0.5 <s
こんにちは、エムスリー AI・機械学習チームの氏家(@mowmow1259)です。 この記事はAI・機械学習チームブログリレーの10日目の記事です。9日目は苅野さんによる「Claude Code と進める Ingress から Gateway への移行」でした。 移行をゴリゴリ進めていただいている苅野さんによる解説記事です! www.m3tech.blog 私は福岡に住んでいるんですが、最近同僚と大濠公園でお花見BBQをしてきました。 お花見と言いつつ桜はまだ咲いていなかったのでただBBQをしただけになりました。 お花見シーズンに限らず、大濠公園は落ち着いて過ごせるいいところなので福岡に来た際にはぜひ立ち寄ってみてください。 桜が咲き掛けの大濠公園 さて、生成AIの台頭により、画像・テキスト・コードとあらゆるコンテンツ生成が飛躍的に容易になりました。 エムスリーでも業務効率化だけでなく、生成AIの各種プロダクトへの組み込みを積極的に推進しています。 一方で、生成物を直接利用する場合には、低品質な生成物はユーザー体験の悪化や意図しない情報漏洩にも繋がるため、品質担保は避けて通れない課題です。 本記事では、LLMによる生成物をプロダクトに組み込む際の品質担保戦略として、エムスリーで実践しているLLM-as-a-Judgeを中心としたアプローチを紹介します。 なお、本記事は先月福岡で行われたPythonのコミュニティイベント Python Meetup Fukuoka #6 *1での登壇内容です。 ご興味がある方はぜひ登壇資料もご覧ください。 LLM as a Judgeとは 基本的な考え方 LLM-as-a-Judgeに潜むバイアス バイアスを考慮した評価 ① 評価観点の明確化 ② 複数モデルでのバリデーション ③ 人間による生成との対決(ペアワイズ法) ④ 評価理由やリファレンスを生成させる ⑤ 人手評価によりプロンプトをチューニング ⑥ プロンプトのチューニングもLLMにやらせる LLMの評価をプロダクトの一部としてとらえる まとめ We are Hiring! エンジニア採用ページはこちら カジュアル面談もお気軽にどうぞ エンジニア新卒採用サイト! LLM as a Judgeとは 基本的な考え方 LLM-as-a-Judge とは、生成AI(LLM)自身に評価をさせるアプローチです。 近年ではレビュー論文*2も公開されるなど活発に議論されている分野です。 例えば、このブログについて、5点満点で評価してくださいのような形でプロンプトを作り、LLMの出力を直接評価に使います。 出された評価でフィルタリングをしてもいいですし、評価を元に改善のサイクルを回すこともできます。 人手による評価と比較して圧倒的にスケールするのが最大の利点です。 LLM-as-a-Judgeに潜むバイアス ただし、先ほどの例のように素直に評価させても多くの場合うまくいきません。 LLMの生成にバイアスやハルシネーションがあるように、LLMを評価に用いる際にもさまざまなバイアスは避けて通れません。 例えば代表的なバイアスとして次のものがあります。 Position Bias: 複数選択肢のうち最初のものを高く評価しがち Length Bias: 長く冗長な文章ほど高く評価しがち Self Enhancement Bias: 自身が生成した出力を高く評価しがち これらのバイアスを踏まえた上で「どうやってLLMに公正・妥当な評価をさせるか」がLLM as a Judgeの研究分野であり実用上の肝になります。 バイアスを考慮した評価 では、どのようにしてバイアスを排除しながら評価させたら良いのでしょうか。 ここでは例として、プログラミングクイズを出題するプロダクトのために、LLMでクイズを生成したとします。 問題: 次のデータ型のうち、一度作成すると中身を変更できない(イミュータブルな)ものはどれ? A. リスト (list) B. 辞書 (dict) C. タプル (tuple) D. セット (set) 当然答えはCです。 ここからはこのクイズに対して適切に評価させる方法を紹介していきます。 ① 評価観点の明確化 「いい評価をしてください」という曖昧な指示では一貫性のある評価になりません。 まずは、何の観点で、どういう基準で評価するかを明示することが重要です。 次のPythonクイズを次の4つの観点から、5点満点(1〜5)で評価し、総合評価を同じく5点満点で評価してください。 評価項目: - 有用性: 実務や学習の初期段階で、知らなければ困る重要な知識か。 - 選択肢の罠: 誤答の選択肢に「他言語の癖」や「直感的な勘違い」を誘う説得力があるか。 - 意外性: 正解や解説を聞いたときに「なるほど!」という驚きや納得感を得られるか。 - 汎用性: その問題の答えが他の文法やライブラリの理解にも繋がる根本的なルールか。 評価観点を明示することで評価軸やスケールが揃い、妥当な評価になりやすくなります。 冒頭のクイズだと選択肢の罠の観点でGeminiに3をつけられてしまいました。 ここから、setの代わりにdataclassを選択肢に入れるなどの改善が考えられます(その場合Geminiの評価は5に上がります)。 ② 複数モデルでのバリデーション コストとの兼ね合いになりますが、単一モデルの評価はそのモデルのバイアスをそのまま反映してしまいます。 Gemini、GPT、Claude Sonnetなど異なるLLMに同じプロンプトで評価させ、複数の評価結果を統合することで、self enhancement biasを考慮したより頑健な評価が得られます。 モデルA: 5点 モデルB: 2点 → 平均: 2.7点 モデルC: 1点 また、スコアのばらつき自体も「評価が難しい問題かどうか」の指標になります。 ③ 人間による生成との対決(ペアワイズ法) ここまでは絶対評価を前提に紹介しましたが、絶対評価だけでなく相対評価も品質担保に有効です。 生成物と人間が作ったコンテンツを並べて比較させ、相対的に品質の高いクイズかを評価できます。 次の2つはPythonに関するクイズです。どちらがよりソフトウェアエンジニアが監修した クイズとして質が高いでしょうか。 クイズA: XXXXX クイズB: YYYYY Position Biasへの対策として、クイズAとBの順番を入れ替えたパターンを実施するのもよいでしょう。 ④ 評価理由やリファレンスを生成させる スコアだけでなく評価理由も生成させることで、CoTの文脈で評価精度の向上が期待できますし、人手で後から確認する際の効率も上がります。 ... ただし、評価は次の形式で理由も含めて出力してください。 修正や批判を行う場合はそれが正しいと主張できる箇所を原文から抽出して併記してください。 { "has_issues": true/false, "issues": [ { "category": "有用性" | "意外性" | "汎用性", "description": "具体的な問題点の詳細説明", "severity": "high" | "medium" | "low", "details": "カテゴリ分析結果や具体的な指摘内容" } ] } 根拠を原文から抽出させる指示を加えることで、ハルシネーションのチェックの効率も上がります。 ⑤ 人手評価によりプロンプトをチューニング ここまで評価のためのTipsを紹介してきましたが、一発で実用に足る評価プロンプトを作ることは困難です。 解いている問題のドメインや難易度によってプロンプトの調整は必須だと思っています。 そのため、従来の機械学習と同様に人手評価 → プロンプト改善のサイクルを回すことが重要です。 プロンプトv1で評価 → 人手評価と比較 → ずれを修正したプロンプトv2を作成 「LLMが3点と評価したが人間は5点と評価した」というようなずれを収集し、そのずれのパターンに基づいてプロンプトを改善していきます。 ⑥ プロンプトのチューニングもLLMにやらせる 人手評価を進めていく中で、プロンプトの修正案作成自体もLLMに任せるのもよく行っている方法です。 [正解例] 問題: XXXXX 人手評価: 5点、理由: 〇〇 LLM評価: 2点、理由: △△ → このずれを修正するプロンプトv2の改善案を生成してください 人手アノテーションのコストを抑えつつ、評価プロンプトの品質を継続的に向上させられます。 LLMの評価をプロダクトの一部としてとらえる LLMによりいかにバイアスを排除した評価を実施しても、ドメインやプロダクトの特徴によっては生成物をそのまま利用することが難しい場合ももちろん多くあります。 LLMが高評価したから大丈夫だ、というような使い方ではなく、プロダクトによって適切に道具の一つとして使っていくのが重要です。 例えば、コンテンツを大量生成・評価させた上で人手による評価ができる程度にフィルタリングしたり、LLMによる評価をプロダクトそのものにすることも考えられます。 まとめ LLM-as-a-Judgeを活用した生成物の品質担保戦略を紹介しました。 生成AIをビジネスに組み込む際、「生成できること」と「信頼できる品質で生成できること」の間には大きなギャップがあります。LLM-as-a-Judgeをそのギャップを埋める現実的な手段として、ぜひ活用してみてください。 We are Hiring! エムスリーではエンジニアを絶賛募集しています! 少しでもご興味をお持ちの方は、ぜひカジュアル面談等にご応募ください! エンジニア採用ページはこちら jobs.m3.com カジュアル面談もお気軽にどうぞ jobs.m3.com エンジニア新卒採用サイト! fresh.m3recruit.com *1:LINEヤフーさん、GMOペパボさん、エムスリーの共同開催 *2:Jiawei Gu, et al. (2025). A Survey on LLM-as-a-Judge. arXiv:2411.15594. https://arxiv.org/abs/2411.15594
皆さんこんにちは。去年10月からAIチームにジョインした、台湾出身の范(ハン)です。 そんな大好きな国ですが、そこからさらに自分の視野を広げたくて、実は大学生のころからずっと「海外で勉強したり働いたりしたいな」と思っていました。 でも、大学・大学院の時期にちょうどコロナが流行ってしまい、卒業後には台湾の男性の義務である「兵役」にも行く必要がありました。 2024年になって無事に兵役が終わり、コロナも落ち着いたので、これ以上待たずに海外での就職に挑戦することにしました!技術的な興味や、気候が合うかどうかも考えて、新しい就職先は「日本」に決めました。 この記事では、私が仕事探しや面接の準備をしてから、実際に入社するまでの体験をまとめました。海外で働いてみたいエンジニアの皆さんの参考になれば嬉しいです。 先に結論を言うと: 文化に馴染むのは大事!まずはネイティブを目標に、言語をしっかり勉強しましょう LLMを最強の相棒として使いこなしましょう 新しい環境に慣れるのは大変だけど、精一杯楽しもう 言語と「信頼」の壁を突破する 生活について:新竹から東京への日常の変化 入社後の洗礼:適応期と言語の壁を越えて LLMがもたらす技術と文化の変革 まとめ We are hiring! エンジニア採用ページはこちら カジュアル面談もお気軽にどうぞ インターンも常時募集しています 言語と「信頼」の壁を突破する エンジニアの就活で一番心配なのは「技術面接をクリアできるか」だと思いますが、当時の私が一番不安だったのは「自分の日本語レベルで、全部日本語の環境でうまくコミュニケーションが取れるか」ということでした。 大学から日本語を勉強していてJLPTのN1も持っていましたが、海外で生活したり働いたりした経験は「ゼロ」でした。事前に「日本企業は、日本に住んだことがない外国人に対して不安を感じやすい(日本の文化に馴染めるか、ホームシックで辞めてしまわないかなど)」とよく聞いていました。 面接官の不安をなくすために、何度も模擬面接を練習しました。目標は、面接でしっかり会話できることをアピールして、「この人なら日本の環境やうちのチームにもすぐ馴染めるな」と安心してもらうことでした。 普通の勉強に加えて、会話の練習のためにマンツーマンの家庭教師もつけました。そのおかげで、面接ではなんとかスムーズに話せるようになりました。でも正直に言うと、これだけ準備しても、実際に働き始めると日本人との差はまだまだ大きいと感じます。とっさに言葉が出なかったり、どう言えばいいか分からなかったりすることが多く、これからも努力が必要だなと思っています。 生活について:新竹から東京への日常の変化 東京に来る前、私は「台湾のシリコンバレー」と呼ばれる新竹(しんちく)サイエンスパークで働いていました。働くにはとても便利な場所ですが、遊ぶ場所は少なく、食べ物が美味しくないことでも台湾ではかなり有名で、長く住んでいると少し退屈で気分が落ち込むこともありました。 東京に来てから、生活は大きく変わりました!ライブや同人誌のイベントに行ったり、自然の中をハイキングしたりするのがすごく便利になりました。遊ぶ選択肢が一気に増えたので、今では「誘惑に負けないように」自分を抑えるのが大変です(笑)。 例えば先週末は、天気が良かったので高尾山へ登りに行きました。景色が良くて、山にはまだ桜がたくさん咲いていて本当に綺麗でした。 入社後の洗礼:適応期と言語の壁を越えて 生活の変化を楽しんだ後、いざ入社してみると職場でもたくさんの壁がありました。職場文化と働き方が前の会社とは全然違いました。特にエムスリーでは非常に仕事の進むスピードが早く、最初は大変ですが、優しい同僚たちがサポートしてくれたおかげで、意外と短い期間で慣れました。 特に会話では、会議で話し終わった後に「今の自分の説明、すごく下手だったな…」と反省することがよくあります(笑)。それでも、同僚たちはいつも広い心で受け入れてくれて、本当に感謝しています! ちなみに、初めてエムスリーのオフィスに行く時、絶対に『半沢直樹』や『逃げ恥』みたいなオフィスだと思っていたのですが、実際は全然違ってびっくりしました 一方、チャットなどのテキストのやり取りでは、LLM(大規模言語モデル)にすごく助けられています。今はメッセージを送る前にLLMにチェックしてもらい、変な表現や、特に苦手な敬語を直してもらっています。 Claude君、ありがとう!Gemini君、ありがとう! LLMがもたらす技術と文化の変革 LLMといえば、言葉のサポートだけでなく、実際の技術開発でLLMを使うのにも、最初は少し慣れる時間が必要でした。 前社でもLLMは使っていましたが、今のAIは昔よりずっと賢くなっています。エムスリーでは、LLMが毎日の仕事に欠かせないものになっているとすごく実感します。 会社の中でどれだけLLMが使われているかには驚きました。ただのお試しではなく、開発を含めて、毎日の仕事にしっかり組み込まれています。また、会社全体がAIツールにとてもオープンで、Slackではチーム関係なく、みんなが新しい使い方やコツをよくシェアしています。みんなで新しいツールを使って仕事を効率化しよう!という雰囲気は、エンジニアにとって本当に最高の環境です。 まとめ 今振り返ってみると、海外で働く決断も、今の会社に入ったことも、本当に正解だったと思います。この半年間の自分の成長や生活の変化は、期待通りのものでした。 でも、実際に日本で働いてみると、日本語の勉強に「終わりはない」と痛感しています。今思えば、「よくあの中途半端な日本語レベルで面接に挑んだな?」と当時の自分にツッコミを入れたいくらいです。 慣れ親しんだ環境から勇気を出して一歩を踏み出すのは難しいですし、その一歩を踏み出した後も大変かもしれません。でも、今は新しい生活も、仕事も楽しんでいます。 日本語をちゃんと勉強して、新しい環境を楽しむ気持ちがあれば、きっとなんとかなると思います。 We are hiring! AI・機械学習チームでは、国籍を問わず一緒に働く仲間を募集しています。この記事のように、海外から日本で挑戦したいエンジニアも、多様なバックグラウンドを持つメンバーと働きたいエンジニアも大歓迎です。 エンジニア採用ページはこちら jobs.m3.com カジュアル面談もお気軽にどうぞ jobs.m3.com インターンも常時募集しています open.talentio.com