您是否曾经发现很难理解一个杂乱的代码库?还是想知道分析和探索代码的工具实际上是如何工作的?在本文中,我们将通过从头开始构建强大的代码库探索工具来解决这些问题。使用静态代码分析和Gemini模型,我们将创建一个易于使用的系统,可帮助开发人员从其代码中查询,理解和获得有用的见解。准备更改您导航代码的方式了吗?让我们开始!
>本文是> > data Science Blogathon的一部分。 目录的目录
项目文件夹结构将类似于这些
在以下步骤中
设置项目环境:|--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
我们将从理解和实施代码库解析系统开始。它具有两个重要的功能
pip install google-generativeai google-ai-generativelanguage pip install python-dotenv typer llama-index
extract_definitions()
>解析代码库
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
此功能扫描python文件的目录,读取其内容并提取其结构。
>
>它通过所有子目录和给定目录中的文件循环。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
提供了一种浏览整个目录树的递归方法。它将处理结束.py扩展的文件。
>使用Pythonast模块将文件的内容解析到代表文件结构的抽象语法树(AST)中。然后将提取的树传递到extract_definitions(tree)。如果解析失败,它将打印一条错误消息,但继续处理其他文件。
的文件
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
类以与Google的Gemini AI模型进行交互。它将使用.ENV文件使用 google_api_key 来验证模型。配置模型API后,它提供了一种查询方法,可以在给定的提示符上生成响应。 >查询处理系统
在本节中,我们将实现查询过程类以管理代码库上下文并使用Gemini启用查询。加载必要的库后,
load_dotenv#create a new conda env conda create -n cb_explorer python=3.11 conda activate cb_explorer
键。 > GEMINIEMBEDING类从Google服务器初始化嵌入式-001型号。> QueryProcessor类旨在处理代码库上下文并与geminiclient.loading_contextmethod进行交互。 > thesaving_contextmethod将当前的代码库上下文保存到JSON文件中以供persistence.save_contextmethod更新代码库上下文,并立即将其保存为usingsave_context和theefformat_contextmetext和theeformat_contextMethod,将代码库数据转换为可读取人类的字符串形式,以征求人类的字符串for for for for Human-munther-formaT for gromand formaties forman-fime >查询双子座是最重要的方法,它将使用代码库上下文和用户的查询构建提示。它通过GeminicLient将此提示发送到Gemini模型并恢复响应。 >命令行应用程序实现(CLI)
>在这里,
|--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的#create a new conda env conda create -n cb_explorer python=3.11 conda activate cb_explorer
和最后一个,它将使用typer.echo()方法。 步骤5:运行应用程序
测试应用程序
测试您的辛勤工作,请按照以下步骤进行操作:pip install google-generativeai google-ai-generativelanguage pip install python-dotenv typer llama-index
>在project_test文件夹中创建一个find_palidrome.py文件,然后将以下代码放在文件中。
>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
输出:
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
未来的发展 这是基础系统的原型,可以通过许多有趣的功能进行扩展,例如
结构代码解析是代码分析的最重要的技术。
以上是使用Google' s gemini-2.0构建代码库资源管理器的详细内容。更多信息请关注PHP中文网其他相关文章!