Exploring Code With Databricks Apps - Databricks Community - 117634の翻訳です。
本書は著者が手動で翻訳したものであり内容の正確性を保証するものではありません。正確な内容に関しては原文を参照ください。
イントロダクション
本書では、複雑なコードベースを理解するためにDatabricksアプリの開発、設定、デプロイの方法をウォークスルーします。オープンソースでは探索すべき世界クラスのソフトウェアが多数利用できますが、手元にあるプログラミング言語になじみがない場合、コードベースを把握することは困難となります。これが課題となる業界は多数存在します。例えば、多くの製造業ではCコードの複雑な結びつきに依存しており、データベースソフトウェアはRustやCのようにクイックな学習が困難な言語で記述される傾向があります。本書では、Cコードや1兆以上のデバイスにデプロイされているデータベースであるSQLiteを探索するために、Databricks Appsを活用します。エンドツーエンドのサンプルに関しては、こちらのリポジトリを試してみてください。
セットアップ手順
このソリューションをウォークスルーする前に、こちらがこのDatabricksアプリのセットアッププロセスとなります:
- ボリュームの作成: これはSQLiteソースコードファイルを格納するために使用されます。
- お使いのLLMのサービングエンドポイントを作成します。シンプルさのために、最初はDatabricks基盤モデルAPIにトークン課金のエンドポイントを使うことにします。
- リポジトリをクローンします(ローカルで実行している場合にはステップ4を行います):
- ローカルディレクトリにリポジトリをクローンします(ワークスペースで実行している場合はステップ3を実行します)
- 設定ファイルを変更します:
- パスがお使いのボリュームに一致するように、必要に応じて
app.yaml
を編集します。 - ローカルで開発している場合、ガイドとして
.env.sample
を用いてご自身の.env
ファイルを作成します。
- パスがお使いのボリュームに一致するように、必要に応じて
- アプリをデプロイします:
- Databricksでアプリを起動します
- アプリのワークスペースディレクトリにgitフォルダからリソースをコピーします。あるいは、ローカルで開発している場合には、あなたのアプリの
Edit in Your IDE
セクションのコマンドを実行してデプロイします。 -
app.yaml
ファイルのSERVING_ENDPOINT
変数で参照されるLLMサービングエンドポイントへのアクセスを追加します。
- ボリュームにアクセスできるようにLLMサービングへのアクセスを追加します。アプリの詳細ページにあるアプリのサービスプリンシパルの情報を参照し、他のユーザーと同じようにアクセスを許可します。このアプリでは、
READ VOLUME
とWRITE VOLUME
権限が必要となります。 - アプリを起動し、ボリュームにファイルを登録するために
Download Example Files
をクリックします。以下のようにしてアプリを操作することができるようになります:- コードの変数をブラウズするために検索バーを用いたり、有向リレーションシップグラフを参照します
- アプリケーションのチャットインタフェースで自然言語のプロンプトを入力します。あなたのファイルのコードに関してAIを活用した洞察を得るために質問したり、指示を与えることができます
ソリューションの概要
我々のソリューションはいくつかの主要コンポーネントから構成されています:
- カタログピッカーのためのPython SDK
- 変数の依存関係グラフを構築するためのパーサー
- グラフ探索機能
- 検索のためのローカルベクトルデータベース
- LLMサービングのためのLlama(Databricksサービングエンドポイント)
それぞれのコンポーネントの詳細を見ていきましょう。
カタログピッカーのためのPython SDK
カタログピッカーを作成するためにDatabricks SDKを用いており、ユーザーは分析するCファイルを選択するために、カタログ、スキーマ、ボリュームをナビゲートすることができます。catalog_picker.py
のCatalogPicker
クラスはこの機能に対応します。このクラスは、ファイル選択のためのユーザーインタフェースを作成し、Databricksワークスペースとのやり取りを管理します。WorkspaceClient
を通じてSDKにアクセスします:
from databricks.sdk import WorkspaceClient
w = WorkspaceClient()
catalog_options = [
{'label': catalog.name, 'value': catalog.name}
for catalog in w.catalogs.list()
]
このスニペットはカタログを一覧し、類似したスニペットはスキーマ、ボリューム、選択されたボリュームのファイルを一覧します。ファイルを選択すると、更なる分析のためにアプリにファイルの中身をダウンロードするためにSDKを使うこともできます:
w.files.download(file_path=file_path)
file_contents = response.contents.read()
return file_contents.decode('utf-8')
コードの解析
code_analyzer.py
のCodeAnalyzer
クラスは、Cファイルの解析と変数の依存関係のグラフの構築を担当します。このクラスは、変数の宣言、割り当て、利用を特定するために正規表現を使用し、変数化のリレーションシップを表現する有向グラフを構成します。完全に抽象化された構文ツリーを取得するためにより洗練されたライブラリを使うこともできますが、正規表現は依存関係の欠如のような変更に対する影響が少ないです。
def extract_variables(self, body: str, func: Function):
"""Extract variable declarations from function body"""
# Updated pattern to catch more variable declarations
var_pattern = r'\b(?:static\s+)?(?:const\s+)?([a-zA-Z_][a-zA-Z0-9_]*(?:\s*\*)*)\s+([a-zA-Z_][a-zA-Z0-9_]*)\s*(?:[=;]|[\[{])'
# Also look for function parameters
param_pattern = r'\b([a-zA-Z_][a-zA-Z0-9_]*(?:\s*\*)*)\s+([a-zA-Z_][a-zA-Z0-9_]*)\s*(?:,|\))'
for match in re.finditer(var_pattern, body):
# process the matches
次のセクションで使用するために、この依存関係を有向グラフの頂点とエッジとして追跡します。
グラフの探索
CodeAnalyzer
クラスは、依存関係グラフの探索のためのメソッドも提供します。get_variable_dependencies()
、get_variable_info()
、visualize_dependencies()
メソッドによって、ユーザーは変数間のリレーションシップを探索することができます。コードすべての依存関係の有向グラフが手元にあるので、ユーザーが選択した変数のみに感っ系するサブグラフを抽出することができます。デフォルトでは、中心性に基づくグラフアルゴリズムによって特定された25の非常に関係する変数のみを含めています。
upstream = list(self.dependency_graph.predecessors(node))
upstream_subgraph = self.dependency_graph.subgraph(upstream)
centrality = nx.eigenvector_centrality(upstream_subgraph)
upstream = sorted(upstream, key=lambda x: centrality.get(x, 0), reverse=True)
ローカルベクトルデータベース
コードに対する効率的で意味に基づく検索を可能にするために、ローカルのベクトルデータベースを使用します。ファイル全体のテキストを渡すことができますが、時には多すぎるコンテキストとなってしまいます。ベクトルデータベースから取得されるコードの重要なパーツのみを引き渡すことで、コストとレイテンシーを改善することができます。非常に広範なコードベースではDatabricksのVector Searchwと使うことができますが、おそらくあなたのデータは非常にスケーラブルなソリューションを必要とするほど大きなものではないでしょう。vector_store.py
のCodeVectorStore
クラスは、セマンティック検索に対応し、chat_interface.py
から呼び出されます:
# vector_store.py
for i, chunk in enumerate(chunks):
chunk_id = self._generate_id(f"{file_path}:{i}:{chunk}")
ids.append(chunk_id)
documents.append(chunk)
metadatas.append({
"file_path": file_path,
"chunk_index": i,
"total_chunks": len(chunks)
})
# Add to collection
self.collection.add(
ids=ids,
documents=documents,
metadatas=metadatas
)
# chat_interface.py
search_results = self.code_analyzer.search_code(last_message + code_context)
コードは解析済みなので、ユーザーの質問に加えて選択された変数の依存関係ツリー全体に関係するコードを取得していることに注意してください。
LLMを活用した説明
AIを活用したアシスタントを提供するために、Databricksのエンドポイントを通じてLlamaモデルをインテグレーションしています。chat_interface.py
のChatInterface
クラスは、LLMとのやり取りを管理し、コードを説明するためにユーザーの質問と取得したコードをLLMに引き渡します。endpoint_name=os.getenv("SERVING_ENDPOINT")
のようにapp.py
で参照されるDatabricksアプリのリソースを用いて、別のサービングエンドポイントに簡単に切り替えることができます。シンプルにするために、初めはトークン課金のエンドポイントを使っても構いません。サービングエンドポイントにアクセスするためのシンプルな手段としてDatabricks Python SDKを使っています:
response = self.w.serving_endpoints.query(
name=self.endpoint_name,
messages=api_messages,
)
まとめ
これらのコンポーネントを組み合わせることで、開発者がSQLiteのような複雑なCのコードベースを探索、理解できるようにするパワフルなDatabricksアプリを作成しました。このアプリはコードのナビゲーション、変数の依存関係の可視化、コード理解のためにAIを活用したアシスタントの活用のための直感的なインタフェースを提供します。このソリューションは、非構造データ(この場合はCファイル)に特化された、詳細な分析のためのツールの作成におけるDatabricks Appsのパワーを示しています。解析のためのAIが生成したいくつかのPythonコードとDatabricksのリソースとのやり取りのためにDatabricks SDKを活用することで、他の複雑なコードベースやプログラミング言語に拡張可能なハイレベルのコード探索のためのアプリを構築しました。エンドツーエンドのサンプルに関しては、こちらのリポジトリをご覧ください。