コードの見える化: 決定的な計算を活用する
「このシステム、外部のインターフェースが変わったら、どこを直せばいいの?」
見慣れないコードベースを前にして、こういう問いに答えられる状態を作りたい、という話がありました。
調査はエージェントにやらせる前提です。そのまま読ませるとコンテキストがあふれてしまいます(みなさんおそらく経験済みですよね?)。また、分析結果もなるべく再現性のある形で出したいです。
そこで、静的解析ツールの出力を根拠として引用させる方針で、使えるものを調べてみました。とりあえず静的解析のドメインやツールについて整理して、導入の容易性やカバー範囲の広さで絞っていきました。
最初の方針
少し調べたあとに、大きな方針として
- 自分が過去にやった調査を参考に、想定調査手順を決める
- 上記手順に使用できる決定的な計算をするツールを用意して、調査は可能な限りそれらを根拠として進める
としました。静的解析のツールとして選んだのは ripgrep・ast-grep・Serena・CodeQL の4つです。
想定した調査の流れと選んだツール
最初に、自分ならどう調べるかを考えました。全体像から細部へ調査を進めたい、一致文字列を全て探したい、参照しているコードを列挙したいなど、いくつかのケースがありそうでした。
いろいろと考えた結果、以下の組み合わせである程度広範囲の調査ができるのではと考えました。
- ripgrep: プログラマのお供である grep の便利かつ高速版です。名前や文字列の網羅と、他のツールの結果の検算が得意です。一方で、コメントや文字列リテラルを区別したいときには少しやっかいです。構文として絞りたいときは ast-grep、型まで必要なら Serena で補います。
- ast-grep: AST(抽象構文木)のノードに対して検索をするツールです。検索クエリも柔軟に構成できます。grep で十分な気もしますが、もしかしたら構文マッチで外部通信等が楽に列挙できることもあるかと思い、入れておきました。AST と聞くと変換時の情報落ち(コメントやマクロ、プリプロセッサ)が気になりますが、実はこのツールは内部で CST(具象構文木)を利用しているため、コメントは問題なく検索でき、マクロやプリプロセッサ用のコマンドも展開はされないものの取り出しはできます。対応していない言語も下で使っている Tree-sitter というパーサライブラリでパーサを作れば対応できそうです(未検証)。
- Serena: IDE の Go to Definition や Find Usages の裏で動いている言語サーバ(LSP)の機能を MCP で提供しているものです。シンボル単位の操作をエージェントに渡します。特定の小さいコードからの深掘り用、というイメージです。型を解決した、正確な参照を追えるのが強みですが、index を作るのに少し時間がかかります。
- CodeQL: コードをデータベースにして、多角的な分析機能を提供します。主に脆弱性とエラーの特定に使われるみたいですが、データフローの解析や、コードベースの主要指標の計算にも使用でき、全体感の把握用として、候補に入れました。一方で、4つの中で事前処理がいちばん重いです。
ただし、それぞれ対応言語が異なり、古い言語だと苦労します。
私のケースだと全ツール使えるものもあれば、ripgrep しか使えるものがないシステムもありました。後者は実験も含めて、ripgrep だけでどこまでできるのかを少し試してみました。
実際にやってみて分かったこと
ちょっとだけ試した段階(CodeQL はまだ使えてません)ですが、こんな学びがありました。
ツールが無くても、AI が自分でツールを作った。 古い言語の調査で、「なるべく決定的な計算をもとにレポートを構成すること」と指示を与えると、AI が自分で解析スクリプトを書いて実行し、その結果を根拠に使っていました。考えてみればそのくらいはできるのですが、驚きました。ただし、自作ツールはそれ自体がレビューの対象になるので注意が必要です。
ripgrep と ast-grep の差は、思っていた通りだった。 ripgrep と ast-grep で外部通信部を検出するタスクの結果に差異が出たため、見てみるとやはり ripgrep がコメントをカウントしたのが原因でした。ast-grep の優位性を把握するとともに、複数ツールで検証する重要性についても考えさせられました。
Serena は、照合の補完にとどまった。 今回の簡単な実験では、ast-grep との比較検証や、結果の照合の補完に使うところで終わりました。文字コードの変換が必要であったり、外部ライブラリの型情報がなかったりといくつか問題が出ましたが、まだ試せていないところで、効きそうな目処はあります。
- ast-grep で検知された「呼ばれていないであろうメソッド」などの、厳格な検査
- Java でいうインターフェースの実装の解決のような、型情報を頼りにした「本当に使われているのか、いないのか」の確認
- 冒頭の「ここのコードが変わったときに、どういう影響があるのか」という影響調査
こういった部分では、Serena がかなり効くのではないかと思っています。
LLM には読解もさせた方がよさそう。 最初は、決定的に出せる結果の解釈だけを LLM にやらせていました。しかし、それではレポートの有用性が下がってしまいます。コードに書かれている本来の意味や、暗黙のルール、プログラム全体のフローを解釈するところに LLM を使うのは有用で、出力のレビューは必要なものの、そのデメリットを差し引いても余りある価値があると判断しました。
ツールは固定せず、調査対象ごとに選び直した方がよさそう。 システムによって、使われているソースコードやデータは違います。Java を読むなら Java 向けのツール、JSON を読むなら JSON 向けのツール、というように、最適な組み合わせは変わります。固定のツールだけでは足りないので、もう少し広い範囲のツール調査が必要だと感じています。迷っているのは、ツールの調査まで含めて、どこまで動的にするかです。ただ、今まで LLM を使ってきた経験上、直観的に「ツールを調べる段階」と「ツールを使う段階」は、分けたほうがよいと思っています。どれだけ多くのツールをもたせるのかも、今後の調査になりそうです。
これらの発見も踏まえて、現在は次の方針で進めています。
- LLM にレポートを作成させるが、その際は、なるべく決定的な計算を行うツールの結果を引用する形で作成する。
- それに加えて、LLM 自身のエージェンティックなコード分析もレポートに含めるが、上とは明示的に区別して出す。
- 自作ツールについては、どのような処理を行うものかを、ソースコードも含めてレポートに載せる。
- ツールは調査対象ごとに選び直す。ツールの調査と使用は分けて、「どのツールをどの用途で使うか」を先に定義してから調査に入る。
- 自作ツールが使用されたら、同様のことをするツールが他にないかを探してツール定義の更新を検討する。
感想
RAG を作っていたころから、根拠や引用の重要性は意識していたつもりでした。ただ、決定的な計算をツールとして提供して、LLM の応答の曖昧さを絞る、という考え方を意識したのは、今回が初めてです。
これまでだと、こういった方向に話が行くと「LLM ステップがついているワークフローが最適なんじゃないか」と考えることが多かったのですが、今回のケースだと、エージェンティックにツールを選択して深掘り調査をしてほしいんですよね。しかし、絶対にスキップしてほしくないステップもある。そういうケースだと、事前に必須ステップを実行した上でエージェントにその結果を渡し、その上でエージェンティックに調査を進めてもらうみたいなプロセスが必要になってきたりするのかなと考えていました。これはエージェントが中にいるワークフローですね。
ただその一方で、これをオーバーエンジニアリングなのではと感じていることも確かです。そもそも最近のそこそこ賢いモデルは、私の実感としては、人間よりもよっぽど手順に従ってやってくれる印象があるわけです。今後、こうしたモデルの能力が広く実感されるにつれて、確率的な応答そのものへの懸念も、少しずつ変わっていくのではないでしょうか。一つ前の記事でも話しましたが、どのあたりまでを許容していくのかのラインというのは、今後2、3年で業界の標準ができるのではと考えています。まあでも、こういった過渡期においては、安全側に倒すのがベストプラクティスでしょう。
今後は、AI の出力をいかに説得力のあるものにできるのかが、有用性の肝になりそうです。これからどう進化していくのかは、コストとも相談しながら、実験しながら考えていきます。
そのうえで、やはり、信頼性の第一歩は、出典をきちんと明示することなのかなと思います。
[出典] ChatGPT
関連記事: Software is over: 終わるのは、純粋な機能を売るビジネスだ・AI開発は天才宇宙人のマネジメント
コメント (0)
まだコメントはありません。最初のコメントを投稿しましょう!