大きくて乱雑なコードベースを理解するのが難しいと感じたことがありますか?または、コードを分析して探索するツールが実際にどのように機能するか疑問に思いましたか?この記事では、強力なコードベース探索ツールをゼロから構築することにより、これらの問題を解決します。静的コード分析とGeminiモデルを使用して、開発者がコードから有用な洞察をクエリ、理解、および獲得するのに役立つ使いやすいシステムを作成します。コードのナビゲート方法を変更する準備はできましたか?始めましょう!
データサイエンスブログの一部として公開されました。 目次 よりスマートなコード探索の必要性
アーキテクチャの概要プロジェクトフォルダーの構造は、これらの
に似ています次のステップでプロジェクト環境をセットアップします:
|--codebase_explorer/ |src/ ├──| __init__.py ├──| indexer/ │ ├── __init__.py │ └── code_parser.py ├──| query_engine/ │ ├── __init__.py │ ├── query_processor.py │ └── gemini_client.py | ├── main.py └── .env
コードの実装
#create a new conda env conda create -n cb_explorer python=3.11 conda activate cb_explorer
コードベース解析システムの理解と実装から始めます。 2つの重要な関数
がありますpip install google-generativeai google-ai-generativelanguage pip install python-dotenv typer llama-index
extract_definitions()
この関数は、Pythonファイルのディレクトリをスキャンし、コンテンツを読み取り、構造を抽出します。
import ast import os from typing import Dict, Any def extract_definitions(tree: ast.AST) -> Dict[str, list]: """Extract class and function definitions from AST.""" definitions = { "classes": [], "functions": [], "imports": [] } for node in ast.walk(tree): if isinstance(node, ast.ClassDef): definitions["classes"].append({ "name": node.name, "lineno": node.lineno }) elif isinstance(node, ast.FunctionDef): definitions["functions"].append({ "name": node.name, "lineno": node.lineno }) elif isinstance(node, ast.Import): for name in node.names: definitions["imports"].append(name.name) return definitions
指定されたディレクトリ内のすべてのサブディレクトリとファイルをループします。
os.walk()astモジュールを使用して、ファイルのコンテンツを抽象的構文ツリー(AST)に解析します。これは、ファイルの構造を表します。抽出されたツリーは、extract_definitions(tree)に渡されます。解析が失敗した場合、エラーメッセージを印刷しますが、他のファイルの処理を継続します。 クエリ処理エンジン
クエリエンジンディレクトリでgemini_client.py and query_processor.pyという名前の2つのファイルを作成しますこのファイルは、
< google_api_key>ここでは、GoogleのGemini AIモデルと対話するために、a geminiclient
クラスを定義します。 .envファイルから|--codebase_explorer/ |src/ ├──| __init__.py ├──| indexer/ │ ├── __init__.py │ └── code_parser.py ├──| query_engine/ │ ├── __init__.py │ ├── query_processor.py │ └── gemini_client.py | ├── main.py └── .env
を使用してモデルを認証します。モデルAPIを構成した後、特定のプロンプトで応答を生成するクエリメソッドを提供します。 クエリ処理システム このセクションでは、QueryProcessorクラスを実装してコードベースコンテキストを管理し、Geminiでクエリを有効にします。 必要なライブラリを読み込んだ後、load_dotenv
()gemini api
key。#create a new conda env conda create -n cb_explorer python=3.11 conda activate cb_explorer
geminiembeddingクラスは、Googleサーバーから埋め込み001モデルを初期化します。
QueryProcessorクラスは、コードベースコンテキストを処理し、GeminicLient.Loading_ContextMethodロードJSONファイルからコードベース情報をロードするように設計されています。
thesaving_contextmethod現在のコードベースコンテキストをjsonファイルに保存してpersistence.save_contextmethodはコードベースコンテキストを更新し、すぐにusingsave_contextとtheformat_contextmethodを保存します。
|--codebase_explorer/ |src/ ├──| __init__.py ├──| indexer/ │ ├── __init__.py │ └── code_parser.py ├──| query_engine/ │ ├── __init__.py │ ├── query_processor.py │ └── gemini_client.py | ├── main.py └── .env
最初にディレクトリが存在するかどうかを確認し、次にparse_codebase関数を使用して、ディレクトリ内のpythonファイルの構造を抽出します。
解析後、query_processorに解析されたコードベース構造が保存されます。すべてのプロセスは試行およびブロックを除いて、例外を解析中に注意して処理できるようにします。 Geminiモデルを使用して効率的なクエリのためにコードベースを準備します。 ステップ4:コードベースのクエリ
最初に、
query_processor#create a new conda env conda create -n cb_explorer python=3.11 conda activate cb_explorer
query_processorのqueryメソッドを使用して、クエリを処理します。 そして最後に、LLMから端子への応答を typer.echo()メソッドを使用して印刷します。
ステップ5:アプリケーションを実行アプリケーションをテスト
pip install google-generativeai google-ai-generativelanguage pip install python-dotenv typer llama-index
次に、テスト用のPythonファイルを保存するルートにproject_testフォルダーを作成します
import ast import os from typing import Dict, Any def extract_definitions(tree: ast.AST) -> Dict[str, list]: """Extract class and function definitions from AST.""" definitions = { "classes": [], "functions": [], "imports": [] } for node in ast.walk(tree): if isinstance(node, ast.ClassDef): definitions["classes"].append({ "name": node.name, "lineno": node.lineno }) elif isinstance(node, ast.FunctionDef): definitions["functions"].append({ "name": node.name, "lineno": node.lineno }) elif isinstance(node, ast.Import): for name in node.names: definitions["imports"].append(name.name) return definitions
1つのPythonファイルに正常にインデックスを表示することができます。そして、JSONデータは
import ast import os from typing import Dict, Any def parse_codebase(directory: str) -> Dict[str, Any]: """Parse Python files in the directory and extract code structure.""" code_structure = {} for root, _, files in os.walk(directory): for file in files: if file.endswith(".py"): file_path = os.path.join(root, file) with open(file_path, "r", encoding="utf-8") as f: try: content = f.read() tree = ast.parse(content) code_structure[file_path] = { "definitions": extract_definitions(tree), "content": content } except Exception as e: print(f"Error parsing {file_path}: {e}") return code_structure
プロジェクトのクエリ
import os from typing import Optional from google import generativeai as genai from dotenv import load_dotenv load_dotenv() class GeminiClient: def __init__(self): self.api_key = os.getenv("GOOGLE_API_KEY") if not self.api_key: raise ValueError("GOOGLE_API_KEY environment variable is not set") genai.configure(api_key=self.api_key) self.model = genai.GenerativeModel("gemini-1.5-flash") def query(self, prompt: str) -> Optional[str]: """Query Gemini with the given prompt.""" try: response = self.model.generate_content(prompt) return response.text except Exception as e: print(f"Error querying Gemini: {e}") return None
output:
import os import json from llama_index.embeddings.gemini import GeminiEmbedding from dotenv import load_dotenv from typing import Dict, Any, Optional from .gemini_client import GeminiClient load_dotenv() gemini_api_key = os.getenv("GOOGLE_API_KEY") model_name = "models/embeddings-001" embed_model = GeminiEmbedding(model_name=model_name, api_key=gemini_api_key) class QueryProcessor: def __init__(self): self.gemini_client = GeminiClient() self.codebase_context: Optional[Dict[str, Any]] = None self.index_file = "./indexes/codebase_index.json" def load_context(self): """Load the codebase context from disk if it exists.""" if os.path.exists(self.index_file): try: with open(self.index_file, "r", encoding="utf-8") as f: self.codebase_context = json.load(f) except Exception as e: print(f"Error loading index: {e}") self.codebase_context = None def save_context(self): """Save the codebase context to disk.""" if self.codebase_context: try: with open(self.index_file, "w", encoding="utf-8") as f: json.dump(self.codebase_context, f, indent=2) except Exception as e: print(f"Error saving index: {e}") def set_context(self, context: Dict[str, Any]): """Set the codebase context for queries.""" self.codebase_context = context self.save_context() def format_context(self) -> str: """Format the codebase context for Gemini.""" if not self.codebase_context: return "" context_parts = [] for file_path, details in self.codebase_context.items(): defs = details["definitions"] context_parts.append( f"File: {file_path}\n" f"Classes: {[c['name'] for c in defs['classes']]}\n" f"Functions: {[f['name'] for f in defs['functions']]}\n" f"Imports: {defs['imports']}\n" ) return "\n\n".join(context_parts) def query(self, query: str) -> Optional[str]: """Process a query about the codebase.""" if not self.codebase_context: return ( "Error: No codebase context available. Please index the codebase first." ) prompt = f""" Given the following codebase structure: {self.format_context()} Query: {query} Please provide a detailed and accurate answer based on the codebase structure above. """ return self.gemini_client.query(prompt)
import os import json import typer from pathlib import Path from typing import Optional from indexer.code_parser import parse_codebase from query_engine.query_processor import QueryProcessor
将来の開発 これは、
などの多くの興味深い機能で拡張できるファンデーションシステムのプロトタイプです。CodeBase Explorerはコードナビゲーションを簡素化し、開発者が複雑なコード構造を迅速に理解および管理できるようにします。
コードベースエクスプローラーはデバッグ効率を高め、依存関係を分析し、問題をより速く特定するためのツールを提供します。
geminiは、従来の静的分析と組み合わせると、コードの理解を大幅に向上させることができます。以上がGoogle'のGemini-2.0を使用してコードベースエクスプローラーを構築しますの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。