Avez-vous déjà eu du mal à comprendre une grande base de code désordonnée? Ou vous vous êtes demandé comment les outils qui analysent et explorent le code fonctionnent réellement? Dans cet article, nous résoudrons ces problèmes en construisant un puissant outil d'exploration de base de code à partir de zéro. En utilisant l'analyse de code statique et le modèle Gemini, nous créerons un système facile à utiliser qui aide les développeurs à interroger, à comprendre et à obtenir des informations utiles à partir de leur code. Prêt à changer la façon dont vous naviguez par code? Commençons!
Cet article a été publié dans le cadre du Data Science Blogathon.
Deuxièmement, les projets logiciels d'aujourd'hui se composent de milliers de lignes de code écrites dans de nombreux fichiers et dossiers. Approches traditionnelles de l'exploration du code, telles que la fonction de recherche Grep ou IDE. Ce type de système échouera lorsque les développeurs doivent comprendre les concepts ou les relations de niveau supérieur dans la base de code. Nos outils alimentés par l'IA peuvent faire une foulée significative dans ce domaine. Notre application permet aux développeurs de poser des questions sur leur base de code en anglais simple et de recevoir des réponses contextuelles détaillées.
Aperçu de l'architecture
Cette section vous guidera à travers les étapes initiales pour construire et mettre en œuvre votre projet, assurant une expérience d'apprentissage en douceur et un apprentissage efficace.
La structure du dossier du projet sera similaire à celles
|--codebase_explorer/ |src/ ├──| __init__.py ├──| indexer/ │ ├── __init__.py │ └── code_parser.py ├──| query_engine/ │ ├── __init__.py │ ├── query_processor.py │ └── gemini_client.py | ├── main.py └── .env
Configurer l'environnement du projet à l'étape suivante:
#create a new conda env conda create -n cb_explorer python=3.11 conda activate cb_explorer
Installez toutes les bibliothèques nécessaires:
pip install google-generativeai google-ai-generativelanguage pip install python-dotenv typer llama-index
Nous allons commencer par comprendre et implémenter le système d'analyse de base de code. Il a deux fonctions importantes
Extraction des définitions de l'arbre de syntaxe abstrait:
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
Il s'agit d'une fonction d'assistance pour parse_codebase (). Il faudra un arbre de syntaxe abstrait (AST) d'un fichier python. La fonction initie un dictionnaire avec des listes vides pour les classes, les fonctions et les importations. Maintenant, Ast.Walk () itère à travers tous les nœuds de l'arborescence AST. Le module AST identifiera toutes les classes, fonctions, importations et numéros de ligne. Puis ajoutez toutes les définitions au dictionnaire des définitions.
Cette fonction scanne un répertoire pour les fichiers Python, lit leur contenu et extrait leur structure.
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
Les fonctions lancent avec le chemin du répertoire en tant que chaîne. Il publie un dictionnaire des structures du code. Le dictionnaire stocke les données extraites pour chaque fichier python.
Il traverse toutes les sous-répertoires et les fichiers du répertoire donné. os.walk () a fourni un moyen récursif d'explorer l'ensemble de l'arborescence du répertoire. Il traitera les fichiers mettant en fin des extensions .py.
en utilisant le module Python AST pour analyser le contenu du fichier dans une arborescence de syntaxe abstraite (AST), qui représente la structure du fichier. L'arbre extrait est ensuite transmis aux Extract_Definitions (arbre) . Si l'analyse échoue, il imprime un message d'erreur mais continue de traiter d'autres fichiers.
Dans le répertoire du moteur de requête, créez deux fichiers nommés gemini_client.py et query_processor.py
Ce fichier utilisera & lt; google_api_key & gt; pour authentifier l'API du modèle Gemini de Google. Dans la racine du projet, créez un fichier .env et y mettez votre clé API Gemini. Obtenez votre api_keyhere.
|--codebase_explorer/ |src/ ├──| __init__.py ├──| indexer/ │ ├── __init__.py │ └── code_parser.py ├──| query_engine/ │ ├── __init__.py │ ├── query_processor.py │ └── gemini_client.py | ├── main.py └── .env
Ici, nous définissons une classe geminiclient pour interagir avec le modèle Gemini AI de Google. Il authentifiera le modèle à l'aide de google_api_key à partir de votre fichier .env. Après avoir configuré l'API du modèle, il fournit une méthode de requête pour générer une réponse sur une invite donnée.
Dans cette section, nous implémenterons la classe QueryProcessor pour gérer le contexte de base de code et permettre la question avec les gemini.
#create a new conda env conda create -n cb_explorer python=3.11 conda activate cb_explorer
Après avoir chargé les bibliothèques nécessaires, load_dotenv () charge les variables d'environnement du fichier. Env qui contient notre google_api_key pour le gemini api clé.
Étape 1: Importer les bibliothèques
pip install google-generativeai google-ai-generativelanguage pip install python-dotenv typer llama-index
typer et de requête à partir des classes.
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
index sera utilisée comme commande dans le terminal, et la fonction indexera la base de code Python dans le répertoire spécifié pour la future requête et analyse.
|--codebase_explorer/ |src/ ├──| __init__.py ├──| indexer/ │ ├── __init__.py │ └── code_parser.py ├──| query_engine/ │ ├── __init__.py │ ├── query_processor.py │ └── gemini_client.py | ├── main.py └── .env
Il vérifiera d'abord si le répertoire existe, puis utilise la fonction parse_codebase pour extraire la structure des fichiers python dans le répertoire.
Après l'analyse, il enregistrera la structure de base de code analysée dans query_processor . Tous les processus sont en essai et à l'exception du bloc afin que les exceptions puissent être gérées avec soin pendant l'analyse. Il préparera la base de code pour une interrogation efficace en utilisant le modèle Gemini.
Après l'indexation, nous pouvons interroger la base de code pour comprendre ou obtenir des informations sur toutes les fonctions dans la base de code.
#create a new conda env conda create -n cb_explorer python=3.11 conda activate cb_explorer
Tout d'abord, vérifiez si le query_processor a chargé un contexte de base de code ou non et d'essayer de charger le contexte à partir du disque dur de l'ordinateur. puis utilise la méthode de requête query_processor pour traiter la requête.
Et le dernier, il imprimera la réponse du LLM au terminal en utilisant typer.echo () Méthode.
pip install google-generativeai google-ai-generativelanguage pip install python-dotenv typer llama-index
Pour tester votre travail acharné, suivez les étapes ci-dessous:
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
Ce fichier trouvera le palindrome à partir d'une chaîne donnée. Nous indexerons cette requête de fichier à partir du terminal à l'aide de l'application CLI.
Maintenant, ouvrez votre terminal, collez le code et voyez la magie.
Indexation du projet
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
Sortie:
Vous pouvez afficher avec succès 1 fichier Python indexé. Et les données JSON ressemblent à
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
Interroger le projet
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)
Sortie:
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
Sortie:
Si tout est fait correctement, vous obtiendrez ces sorties dans votre terminal. Vous pouvez l'essayer avec vos fichiers de code Python et me dire dans la section des commentaires quelle est votre sortie. Merci de rester avec moi.
Il s'agit d'un prototype du système de fondation qui peut être étendu avec de nombreuses fonctionnalités intéressantes, telles que
L'explorateur de base de code vous aide à comprendre l'application pratique de l'IA dans les outils de développement de logiciels. En combinant une analyse statique traditionnelle avec des capacités d'IA modernes, nous avons créé un outil qui rend l'exploration de base de code plus intuitive et efficace. Cette approche montre comment l'IA peut augmenter les flux de travail des développeurs sans remplacer les outils existants, fournissant une nouvelle couche de compréhension et d'accessibilité aux bases de code complexes.
Tout le code utilisé dans cet article est ici.
a. L'outil utilise un système d'indexation persistant qui analyse et stocke la structure de base de code, permettant des requêtes efficaces sans nedding pour réanalyser le code à chaque fois. L'index est mis à jour uniquement lorsque la base de code change.
q 2. L'outil peut-il fonctionner hors ligne?a. L'analyse du code et la gestion de l'index peuvent fonctionner hors ligne, mais la question de la base de code à l'aide de l'API Gemini a besoin d'une connexion Internet pour communiquer avec les serveurs externes. Nous pouvons intégrer Olllama avec les outils qui seront possibles d'utiliser le modèle LLM ou SLM sur les appareils tels que LLAMA3 ou PHI-3 pour interroger la base de code.
Q 3. Dans quelle mesure les réponses générées par LLM sont-elles précises?a. La précision dépend à la fois de la qualité du contexte du code analysé et des capacités du modèle Gemini. Les outils fournissent des informations de code structurées au modèle d'IA, avec une aide à améliorer la précision de la réponse, mais les utilisateurs doivent toujours vérifier les informations critiques par des moyens traditionnels.
Le média présenté dans cet article ne appartient pas à l'analyse vidhya et est utilisé à la discrétion de l'auteur.
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!