Pythonプログラムの関数の関係性をLLMに説明させてみる

この記事は約3分で読めます。

はじめに

 Pythonで大規模なプログラム(複数のpyに分けるようなもの)を書いていると、全体像が分かりにくくなることがあり、LLMを使ってうまく説明させられないかと思ったのが始まり。

 ちなみに、開発を始める前にOllamaに単純にpyファイルを投げて試したが、当然失敗したorz

仕組み(ざっくり)

下準備(静的解析)

 pyファイルをそのままLLMに投げてもいい解説が生成できないため、まずはじめにastモジュールを使ってソースを構造木に変換する。
 で、各pyファイルの中から関数、クラス、メソッドを抽出し、呼び出しを行っているコードをコレクトする。

 呼び出し先の解決をするが、単純に名前で一致させようとすると、別ファイルや関数内にネストされた同名のものがあった場合、誤ったマッチングが発生する。そのため、呼び出している名前がどのファイルのどの関数のものかをPythonの探し方に近い方法で探す必要があり、これがなかなか難しい。

呼び出しの形特定の方法
helper()自分の中で定義された関数 → 同じモジュールの定義 → import した名前 の順に探す
self.method() / cls.method()自分が属するクラスのメソッドを探す。なければ親クラスを遡る
super().method()親クラス側から探す
mod.func() / pkg.mod.Class.method()importの対応表で正式な名前に直し、プロジェクト内の定義と照らし合わせる
パッケージのinit.py 経由で公開された名前init.pyのimportをたどって本来の定義まで行く
プロジェクト内で見つからなかったものprint や pd.DataFrameなどは「外部の呼び出し」として分離する

関係性をグラフ化

 静的解析で取得した関数の一覧と、呼び出しの関係性を、そのままnetworkxの有向グラフに投入する。

g = nx.DiGraph()
g.add_node("code_atlas.llm:LLMClient.chat")              # 点を追加
g.add_edge("code_atlas.llm:LLMClient.test_connection",   # 矢印を追加
           "code_atlas.llm:LLMClient.chat")

 関数が数百個あると、全部を表示しても線が絡まって読めなくなるため、「指定した中心の関数から N 歩以内だけ」に絞り込む。
 幅優先探索という方法で、池に石を投げて波紋が広がるように、中心から近い関数等を順に調べていきます。

 できあがったネットワーク図をpyvisを使ってHTML化し、st.iframeに突っ込んで描画させる。

LLMに解説させる

 ソースコードと一緒に静的解析の結果を渡して、どの関数で何を呼び出しているかをLLMに分からせることで、推測で解説させない。
 また、チャットによる質問部分では、ソースコード全体をプロンプト内に埋め込むことは出来ないため、質問に関連するソースコードのみ(呼び出し元・先含む)を渡す。基本的にRAGは意味的類似度検索(ベクトル)を使うが、質問文に出てくる関数、ファイル名を正規表現で抽出することで、誤ったマッチングを減らしている。

スクリーンショット

 テストで使ったLLMはQwen3.5-9B

概要説明

関係図をネットワークマップで図示(pyvis)

各pyファイルの概要

各関数の概要

チャットでLLMに解説させる

 テストがまだ十分じゃないのと、自分が欲しかったので作ったものなので、ソースコードは気が向いたら公開する…かも?

コメント