【指南】使用Ollama构建本地LLM代码分析助手(1)

2024年12月20日 由 alex 发表 45 0

是否曾经想要一个完全在本地机器上运行的、由AI驱动的代码审查工具?在本系列的两部分教程中,我们将使用ClientAI和Ollama来构建这样一个工具。


我们的助手将分析Python代码结构,识别潜在问题,并提出改进建议——同时确保你的代码保持私密和安全。


项目概述

我们的代码分析助手将能够:

  • 分析代码结构和复杂性
  • 识别风格问题和潜在问题
  • 生成文档建议
  • 提供可操作的改进建议


设置我们的环境

首先,为你的项目创建一个新目录:


mkdir local_task_planner
cd local_task_planner


安装支持Ollama的ClientAI:


pip install clientai[ollama]


确保你的系统上已安装Ollama。你可以从Ollama的官方网站获取它。


现在,让我们创建将要编写代码的文件:


touch code_analyzer.py


并且从我们的核心导入开始:


import ast
import json
import logging
import re
from dataclasses import dataclass
from typing import List
from clientai import ClientAI
from clientai.agent import (
  Agent,
  ToolConfig,
  act,
  observe,
  run,
  synthesize,
  think,
)
from clientai.ollama import OllamaManager, OllamaServerConfig


这些组件中的每一个都发挥着至关重要的作用:

  • ast:通过将Python代码解析为树结构来帮助我们理解代码
  • ClientAI:提供我们的AI框架
  • 各种用于数据处理和模式匹配的工具模块


构建我们的分析结果

在分析代码时,我们需要一种清晰的方式来组织我们的发现。以下是我们将如何结构化我们的结果:


@dataclass
class CodeAnalysisResult:
  """Results from code analysis."""
  complexity: int
  functions: List[str]
  classes: List[str]
  imports: List[str]
  issues: List[str]


可以将这视为我们的代码分析报告卡:

  • 复杂度评分显示了代码的复杂程度;
  • 函数和类的列表帮助我们理解代码结构;
  • 导入部分展示了外部依赖;
  • 问题部分追踪了我们发现的任何问题。


构建核心分析引擎

接下来是真正的核心部分——让我们来构建我们的代码分析引擎:


def analyze_python_code_original(code: str) -> CodeAnalysisResult:
    """Analyze Python code structure and complexity."""
    try:
        tree = ast.parse(code)
        functions = []
        classes = []
        imports = []
        complexity = 0
        for node in ast.walk(tree):
            if isinstance(node, ast.FunctionDef):
                functions.append(node.name)
                complexity += sum(
                    1
                    for _ in ast.walk(node)
                    if isinstance(_, (ast.If, ast.For, ast.While))
                )
            elif isinstance(node, ast.ClassDef):
                classes.append(node.name)
            elif isinstance(node, (ast.Import, ast.ImportFrom)):
                for name in node.names:
                    imports.append(name.name)
        return CodeAnalysisResult(
            complexity=complexity,
            functions=functions,
            classes=classes,
            imports=imports,
            issues=[],
        )
    except Exception as e:
        return CodeAnalysisResult(
            complexity=0,
            functions=[],
            classes=[],
            imports=[],
            issues=[str(e)]
        )


这个函数就像我们的代码侦探一样。它:

  1. 将代码解析为树结构;
  2. 遍历树以查找函数、类和导入;
  3. 通过计算控制结构来评估复杂度;
  4. 返回一个全面的分析结果。


实现风格检查

优秀的代码不仅仅是能够正确运行——它还应该是可读和可维护的。以下是我们的风格检查器:


def check_style_issues_original(code: str) -> List[str]:
    """Check for Python code style issues."""
    issues = []
    for i, line in enumerate(code.split("\n"), 1):
        if len(line.strip()) > 88:
            issues.append(f"Line {i} exceeds 88 characters")
    function_pattern = r"def\s+([a-zA-Z_][a-zA-Z0-9_]*)\s*\("
    for match in re.finditer(function_pattern, code):
        name = match.group(1)
        if not name.islower():
            issues.append(f"Function '{name}' should use snake_case")
    return issues


我们的风格检查器主要关注两个方面:

  1. 行长度——确保代码保持可读性;
  2. 函数命名规范——强制执行Python推荐使用的snake_case风格。


文档助手

文档对于可维护的代码至关重要。以下是我们的文档生成器:


def generate_docstring(code: str) -> str:
    """Generate docstring for Python code."""
    try:
        tree = ast.parse(code)
        for node in ast.walk(tree):
            if isinstance(node, (ast.FunctionDef, ast.ClassDef)):
                args = []
                if isinstance(node, ast.FunctionDef):
                    args = [a.arg for a in node.args.args]
                return f"""
                Suggested docstring for {node.name}:
                Args:
                {chr(4).join(f"{arg}: Description of {arg}" for arg in args)}
                Returns:
                    Description of return value
                Examples:
                    ```python
                    # Example usage of {node.name}
                    ```
                """
        return "No functions or classes found to document."
    except Exception as e:
        return f"Error generating docstring: {str(e)}"


这个助手:

  • 识别函数和类;
  • 提取参数信息;
  • 生成文档模板;
  • 包含示例的占位符。


使我们的工具具备AI集成能力

为了准备将我们的工具与AI系统集成,我们需要将它们包装成JSON友好的格式:


def analyze_python_code(code: str) -> str:
    """Wrap analyze_python_code_original to return JSON string."""
    if not code:
        return json.dumps({"error": "No code provided"})
    result = analyze_python_code_original(code)
    return json.dumps({
        "complexity": result.complexity,
        "functions": result.functions,
        "classes": result.classes,
        "imports": result.imports,
        "issues": result.issues,
    })
def check_style_issues(code: str) -> str:
    """Wrap check_style_issues_original to return JSON string."""
    if not code:
        return json.dumps({"error": "No code provided"})
    issues = check_style_issues_original(code)
    return json.dumps({"issues": issues})


这些包装器增加了输入验证、JSON序列化和错误处理,使我们的助手更加防错。


文章来源:https://medium.com/@igorbenav/building-a-code-analysis-assistant-with-ollama-a-step-by-step-guide-to-local-llms-3d855bc68443
欢迎关注ATYUN官方公众号
商务合作及内容投稿请联系邮箱:bd@atyun.com
评论 登录
写评论取消
回复取消