🤼

一律修正・横展開の調査をLLMで行うツールを作り、LLMを比較した

に公開

概要

この記事では、一律修正・横展開の調査をLLMで行うツール yokoten の紹介と、それを使ったgemini/claude/gptの一部モデルの比較、抽象的な指示と具体的な指示の比較、Vue3を使ったアプリに関するレビューを行わせた場合の具体的なハルシネーションの内容などについて述べます。
yokotenはClaude Code等よりは安いぐらいの費用で、手軽に横展開のための調査ができ、簡易レポートを生成できます。興味のある方はぜひ使ってみてください。
また、yokotenの実装はだいたいLLMで行っており、特別な工夫をしない場合に、今のLLMが出力するコードの品質がどれぐらいのものかも見ていただけます。

モチベーション

障害やライブラリのバージョンアップの対応など、「同じような処理」を行っているプログラムに対して一律修正(横展開)が必要となる場面があります。このとき、

  • 修正の可能性のある対象を単純に抽出するとたくさんの候補が上がる
  • 実際に修正する必要があるかを単純な条件式で書き下すことができず、人間が直接調べて判定しないといけない

といったことがあります。そのようなケースについて、LLMで1ファイルずつ修正点を確認させると効率よく判定ができるのではないか?と仮説を立て、それを行うためのコマンドラインツールを作りました。LLMは全体を俯瞰するような作業をさせようとするとトークン数の増加による品質劣化がありますが、1ファイルずつ処理する方式であれば品質劣化が少なく、ある程度は指示を全うできるのではないかと考えたからです。修正自体も行える可能性が十分にあると思いますが、指摘としての精度の検証もしたかったので、一旦は修正の要否と、修正すべき内容について出力するように調整しました。

ツールの場所

https://github.com/sasanquaneuf/yokoten

使い方

インストール(pip install yokoten)して、config.yamlおよび使用するLLMのAPIキー類の設定を行ったあと、調査したい場所でyokotenを実行すると、対話的に対象を指定できます。
(チェック対象のファイルを確定するためにgrepするパターンと、それぞれのファイルを送信するときの具体的な指示(チェック観点など)を入力します。)
その後、HTMLでレポートを出力します。
以下がサンプル出力です(実際の出力を多少調整しています)

% yokoten                               
Amazon Bedrock SDK を使用します。
------------------------------
git grepするパターンを入力してください: <template
------------------------------
今回の横展開タスクに関する追加の指示を入力してください。
例: CVE-2024-XXXX の修正が他の箇所にも必要か確認してください。特にXXX関数呼び出し箇所を注視してください。
> vue3で推奨されない記述方法を記述している箇所がある場合は、それを列挙してください。
------------------------------
grepしています...
パス '/Users/user/Repositories/repo'47 件のファイルがヒットしました。
------------------------------
合計 47 件のファイルが見つかりました。これらのファイルを分析します。
------------------------------
[1/47] ファイル 'src/App.vue' を処理中...
  Attempt 1/5...
  分析成功 (JSON形式OK)
  -> 対応は不要と判断されました。
...
[47/47] ファイル 'src/components/additional_field/form/PaymentDataForm.vue' を処理中...
  Attempt 1/5...
  分析成功 (JSON形式OK)
  -> ★対応が必要と判断されました。
------------------------------
分析完了 (処理時間: 0:06:28.447644)
詳細な分析結果を 'result_yokoten20250506141344.html' に保存しました。
------------------------------
【横展開対応が必要な可能性のあるファイル一覧】
- src/components/additional_field/field/NormalField.vue
...
- src/components/additional_field/form/PaymentDataForm.vue
------------------------------
簡易的な結果閲覧: 'result_yokoten20250506141344.html' を確認してください。
スクリプトを終了します。

なお、本来はより具体的な横展開、例えば

  • データベース接続が正しく閉じられていない箇所の調査
  • メモリリークの調査
  • 特定のモデルを使用する箇所でのN+1問題の調査

などといったことを想定して作っていますが、具体的な事例では秘密の情報が多くサンプルを作りにくかったので、抽象度の高い指示をサンプルとしています。

処理が終了すると、以下のような見た目のHTMLが生成されます。ブラウザで開いて確認してください。


考察

実際にプロダクションコードでDBの接続断がうまく行えていない箇所の検出を行い、そのような箇所を検出できたので、少なくとも全く何もしないよりは効果がありました。明示的に観点を指定したうえで一律チェックを行えるので、てきとうに突っ込んでいるBedrock(Claude)によるGitHub Actionsのレビューと比較して効果があるように感じます。[1][2]

その上で、上記サンプルでも示した抽象的な指示を出した場合に、モデルによってどれぐらいの差が出るかということを見てみます。
このサンプルは、Vue3で書かれている47コンポーネントぐらいのアプリ(一部はVue2その他で作成していた機能などを移植したもの)について、Vue3で推奨されない記述を指摘するように依頼したものです。

プロンプト: vue3で推奨されない記述方法を記述している箇所がある場合は、それを列挙してください。

具体的な横展開の場合や指示を具体的にした場合にはもう少し有意義な結果が出るという前提で、差し引いて評価いただくのがよいかと思います。また、各種パラメータを調整していないので、チューニングによって大きく結果が変わるかもしれません。(抽象的な指示かつ初期実装のパラメータでもこれぐらいのことはできる、という見方もあるかもしれません。)

参考まで、上から4つのコンポーネントについての指摘事項をモデル別で比較・コメントします。ちなみに、いずれのコンポーネントも指摘をしようと思えば指摘できる内容があります。どのモデルも、100%十分な指摘を行えてはいません。
★以下、ハルシネーションをハルシネーションと明示していますが、誤解のないように注意ください。

gemini-1.5-pro-latest

レポート画像

すべてのコンポーネントに指摘をしているのですが、ハルシネーションがひどく、指摘として書いてあることの半分(以上?)が間違っています。具体的なハルシネーションの例としては、

Vue3では、import.meta.envへのアクセスはsetup関数内で行う必要があります。

vue3ではnextTickのimportはimport { nextTick } from 'vue' から import { nextTick } from '@vue/runtime-core'に変更する必要があります。

select要素(単一選択、prefecture、年/月/日、年/月)のv-modelを削除し、computedプロパティとwatchで値を管理するように変更します。

などです。これらはいずれも単純に誤っています。最後のものは、optionが動的に変わる場合にwatchで初期化するロジックがありえるという話が由来にあるようですが、これも特殊な状況の話で、今回指摘されたものについてはoptionが動的に変化しないです。

ただし、一方で4つめのコンポーネントについて唯一以下の指摘をすることができました。この指摘は正しいですが、今回試した他のどのモデルでも指摘がありませんでした。[3]

正しい指摘

v-forディレクティブ内でkeyとして'tel_field_' + indexを使用していますが、これは推奨されません。
配列のインデックスをkeyとして使用すると、要素の追加、削除、並び替え時に予期しない動作を引き起こす可能性があります。

※このindexはv-forのindexであって、まさに推奨されない使い方になっていました。(ただ、マウント後にデータが変化することがないので結果的に実害がありませんでした)

gemini-2.5-pro-preview-03-25

レポート画像

ハルシネーションが少なくなり、この4つに出てくる指摘は概ね妥当になりました。例えば、先程のv-modelに関するコメントは以下のようになっています。

正しい指摘

date / yearmonth における疑似 v-model: 年/月/日の select 要素で :value@change を使って手動で値のバインディングを行っています。これも v-modelcomputed を組み合わせることで、値の分割・結合ロジックを computed の getter/setter 内にカプセル化し、テンプレートをより宣言的に記述できます。

ただし、4つ目のコンポーネントのv-forへの指摘はできていません。(これは他のモデルも同様)
また、2つ目のコンポーネントにも問題があるのですが、それは指摘できていませんでした。

anthropic.claude-3-5-sonnet-20240620-v1:0

レポート画像

指摘が少なくなりました。ただし、指摘がある3番目のコンポーネントについては4つの指摘があり、うち1つはハルシネーションでした。

ハルシネーション

Vue3ではv-modelの代わりにv-model:modelValueを使用することが推奨されています。以下の箇所で修正が必要です:

  • <textarea v-model="fieldData">
  • <select v-model="fieldData">
  • <input v-model="fieldData">

正しくは...v-model:modelValue と明示的に書くのは、一つのコンポーネントに複数の v‑model を使いたい場合や modelValue 以外のprop名でバインドしたい場合の特殊ケースだけです。通常のフォーム入力では不要です。

us.anthropic.claude-3-7-sonnet-20250219-v1:0

レポート画像

ハルシネーションはなくなり、こちらも指摘されていることはすべて正しいのですが、だいぶあっさりとした内容になってしまいました。v-modelのattributeとしての位置が違うなど。ただ、メモリリークの指摘ができたのは流石という感じがします。既存の非Vue実装からの移植で発生した window.addEventListener についての正しい指摘:

window.addEventListenerをコンポーネントで直接使用しています。これはコンポーネントがアンマウントされた時にイベントリスナーが残ったままになり、メモリリークの原因となります。Vue3ではonMountedonUnmountedライフサイクルフックを使用して、適切にイベントリスナーを追加・削除するべきです。

gpt-4o

レポート画像

一番指摘の量が少ないですが、間違ってはいません。Claude 3.5 Sonnetのほうがいいかな、というぐらいです。

gpt-4.1-2025-04-14

レポート画像

4つのサンプルにおいてはハルシネーションがなく、指摘としては適切で、かつ一番本質的な指摘が多いように思いました。指摘の日本語が一番こなれています。
2つ目のコンポーネントについては、以下のような指摘内容になっています。

<a>タグに@clickでイベントハンドラを付与し、prevent修飾子でデフォルト動作を抑止していますが、Vue3ではボタン要素(<button>)の使用が推奨されます。アクセシビリティやフォーム送信の観点からも、ボタンタグへの修正が望ましいです。

これは、裏事情としてはcssをそのまま使うためにa要素で作られているといったケースもありました。
ちなみに1つ目のコンポーネントのメモリリークの件は、単純に非推奨という言い方になっていました。

より具体的な指示をした場合

では、ここで4つ目のコンポーネントでgemini-1.5-proでしか見つけられなかったv-forの中の:keyにidentityに相当するものを使う(他の行と本質的に個体識別できる値を使う)という指摘について、直接検証させるとどうなるでしょうか。具体的には、同じ抽出条件でプロンプトを以下のものに変更します。

プロンプト: v-forのkeyに使っている項目が、配列の順序などではなくidentityになっているかを確認してください。もしidentityになっていなさそうな場合は警告してください。

結論としては、この4つのコンポーネントの場合については、どのモデルでも同じように検出ができました。具体的には、1つ目と2つ目は問題なし、3つ目と4つ目は問題ありです。なんと、じつは3つ目のコンポーネントにも同様のv-forの:keyにindexを使っている箇所があったことがわかったのでした。。。(どのモデルも指摘できていなかった)
この結果を踏まえると、抽象的な指示ではやはり正確性に限界があり、具体的な指示をすることでその事象の検出率は高まる ことになります。ただし、当然ですが指示していないことは検出できないので、例えばいくつかの観点を抑えたうえでそれぞれ別で検証させる、といったことも考えられます。

ちなみに、上記4コンポーネント以外についてハルシネーションや見逃しがないかというとそういうことではなく、ハルシネーションっぽい事象も見逃しもありました。この場合のざっくりとした感度が9割〜、特異度が8割〜という印象です。印象的な指摘(結果的に正しくないもの)は以下のようなものでした。

このファイル自体にv-forの使用はありませんが、子コンポーネントである<nantoka-list>にkantokaListという配列を渡しています。これらの配列要素がv-forでレンダリングされる可能性が高いですが、親コンポーネントからキーの指定についての情報が提供されていません。

v-forのkeyにentryDetail.idが使用されています。これは、各entryDetailを一意に識別できるIDのようです。entryDetails配列の要素が削除・追加など変更される場合、idが変わらないという前提であれば問題ありません。
しかし、entryDetailsが何らかの操作で一時的に空になり、再度データがフェッチされる場合、idが以前と同じとは限りません。その場合、keyが正しく機能せず、予期しないレンダリングが発生する可能性があります。

それを言い出すと...と思いましたが、一応idについて問題がなさそうか確認したりしました(結論としては問題ありませんでした)

モデル比較考察まとめ

抽象的な指示をする場合、基本的にはどのモデルも新しい方がハルシネーションが少なく、また見逃しも少なくなりますが、今回の例ではgemini-1.5-proでしか指摘できない内容もありました。この指摘箇所については簡単なケースで動作はしますが、割と初歩的で重要な指摘ではあります。したがってLLMの指摘がないからOKという言い方はできないですが、ただある程度は問題を拾ってくれるというような見方をするべきかなと思います。指摘は鵜呑みにせず、可能性のあることとしてきちんと確認をして検討するのがよいと思います。
また、抽象的な指示で具体的な観点が引き出せた場合には、その具体的な観点で改めて横展開をかけさせた方が明らかに精度が上がります。この場合、主要な部分での結果は概ねモデルによりませんが、細かい部分では差異があり、都度複数モデルを試すか一つのモデルでよしとするかは判断が分かれるなあと思いました。

費用について

yokotenを実行してレポートを作成するにあたって、各モデルでそれぞれ、〜数十円ぐらいかかりました。(諸々合計で数百円ぐらい)

時間について

モデルによりますが、47コンポーネントを一つずつAPIで投げて応答を得て、を直列で実行すると数分〜長いもので15分ほどかかりました。

まとめ

今の時点でのこのツールの最も有意義な使い方としては、チェック観点をかなり具体的に与えた横展開 ということだと思います。実際にそう思ってyokotenという名前にしており、抽象的なチェックが得意だとは思っていないですが、ただ抽象的なチェックについても良いモデルを使ってチェックさせることで品質改善に貢献できるかなと思いました。

なお、このコード自体も、概ねLLMから出力されたもの[4]を使っています。若干いけてない部分もありますが、ファイルを丸ごと突っ込んでLLMに直させるぐらいの考えで雑に作った状態としています。微妙な機能を手動で追加してはいますが、レポート部分も含めてほぼLLMで出来たのは便利だなと思いました。

脚注
  1. てきとうに突っ込んでいるレビューも、通常は2割ぐらいの精度で有意義な指摘をしてくれます。おそらく、今回見つかった不備も、当該箇所が差分になっていれば指摘できていた可能性があると思うのですが、差分に含まれない箇所への指摘が行われない仕組みのため、改めてファイル全体をチェックした場合の正当性の検証などには不向きでした。 ↩︎

  2. DBの接続断を気にしないといけない基盤実装がよくない、それはそう ↩︎

  3. 単純な運の問題かもしれませんが、ただ再実行で大きく傾向は変わらないように感じたので、運だけでない何かがあるのかなと思いました。 ↩︎

  4. Gemini 2.5 Pro / ChatGPT o3 ↩︎

Discussion