> ## Content Index
> Fetch the complete content index at: https://www.hubwiz.com/blog/llms.txt
> Use this file to discover other available public pages before exploring further.

# 打造自己的代码审查助手
- URL: https://www.hubwiz.com/blog/build-your-own-code-review-assistant/
- Published: 2024-12-17T23:34:27.000Z
- Updated: 2024-12-17T23:34:27.000Z
- Description: 你是否曾经想要拥有完全在本地机器上运行的 AI 代码审查器？在本教程中，我们将使用 ClientAI 和 Ollama 构建该工具。
- Author: admin
- Tags: APPLICATION, CLIENTAI, OLLAMA, CODE REVIEW, PYTHON, AST, #Import 2026-10-14 14:41

你是否曾经想要拥有完全在本地机器上运行的 AI 代码审查器？在本教程中，我们将使用[ ClientAI](https://igorbenav.github.io/clientai/?ref=hubwiz.com) 和 Ollama 构建该工具。

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

让我们开始吧！

## 1、项目概述

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

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

所有这些都将在你的机器上本地运行，让你能够使用 AI 辅助代码审查，同时保持代码的完全隐私。

## 2、设置环境

首先，为你的项目创建一个新目录：

```
mkdir local_task_planner
cd local_task_planner
```

安装支持 Ollama 的 ClientAI：

```
mkdir local_task_planner
cd local_task_planner
```

确保你的系统上安装了 Ollama。你可以从[ Ollama 的网站](https://ollama.ai/?ref=hubwiz.com)获取它。

现在让我们创建将代码写入其中的文件：

```
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 框架
- 用于数据处理和模式匹配的各种实用程序模块

## 3、构建分析结果

在分析代码时，我们需要一种干净的方法来组织我们的发现。以下是我们将如何构造结果：

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

将其视为代码分析的成绩单：

- 复杂度分数表示代码的复杂程度
- 函数和类的列表有助于我们理解代码结构
- `imports` 显示外部依赖关系
- `issues` 跟踪我们发现的任何问题

## 4、构建核心分析引擎

现在来看看实际的核心 — 让我们构建代码分析引擎：

```
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)]
        )
```

此函数就像我们的代码侦探。它：

- 将代码解析为树结构
- 遍历树以查找函数、类和导入
- 通过计算控制结构来计算复杂度
- 返回综合分析结果

## 5、实施样式检查

好的代码不仅仅是正常工作——它应该是可读且可维护的。这是我们的样式检查器：

```
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
```

我们的样式检查器重点关注两个关键方面：

- 行长度 — 确保代码保持可读性
- 函数命名约定 —强制执行 Python 首选的蛇形命名风格

## 6、文档助手

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

```
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)}"
```

此帮助程序：

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

## 7、使我们的工具适用于 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 序列化和错误处理，使我们的助手更能防错。

## 8、使用 ClientAI 注册我们的工具

首先，我们需要将我们的工具提供给 AI 系统。以下是我们注册它们的方式：

```
def create_review_tools() -> List[ToolConfig]:
    """Create the tool configurations for code review."""
    return [
        ToolConfig(
            tool=analyze_python_code,
            name="code_analyzer",
            description=(
                "Analyze Python code structure and complexity. "
                "Expects a 'code' parameter with the Python code as a string."
            ),
            scopes=["observe"],
        ),
        ToolConfig(
            tool=check_style_issues,
            name="style_checker",
            description=(
                "Check Python code style issues. "
                "Expects a 'code' parameter with the Python code as a string."
            ),
            scopes=["observe"],
        ),
        ToolConfig(
            tool=generate_docstring,
            name="docstring_generator",
            description=(
                "Generate docstring suggestions for Python code. "
                "Expects a 'code' parameter with the Python code as a string."
            ),
            scopes=["act"],
        ),
    ]
```

让我们分解一下这里发生的事情：

每个工具都包装在一个 `ToolConfig` 对象中，该对象告诉 ClientAI：

- `tool`：工具。要调用的实际函数
- `name` ：名称。工具的唯一标识符
- `description` ：描述。工具的功能以及它需要哪些参数
- `scopes` ：范围。何时可以使用该工具（“观察”用于分析，“行动”用于生成）

我们将工具分为两类：

- “观察”工具（`code_analyzer` 和 `style_checker`）收集信息
- “行动”工具（`docstring_generator`）生成新内容

## 9、构建 AI 助手类

现在让我们创建 AI 助手。我们将设计它分步工作，模仿人类代码审查员的思维方式：

```
class CodeReviewAssistant(Agent):
    """An agent that performs comprehensive Python code review."""

    @observe(
        name="analyze_structure",
        description="Analyze code structure and style",
        stream=True,
    )
    def analyze_structure(self, code: str) -> str:
        """Analyze the code structure, complexity, and style issues."""
        self.context.state["code_to_analyze"] = code
        return """
        Please analyze this Python code structure and style:

        The code to analyze has been provided in the context as 'code_to_analyze'.
        Use the code_analyzer and style_checker tools to evaluate:
        1. Code complexity and structure metrics
        2. Style compliance issues
        3. Function and class organization
        4. Import usage patterns
        """
```

第一个方法至关重要：

- `@observe`装饰器将此标记为观察步骤
- `stream=True` 启用实时输出
- 我们将代码存储在上下文中，以便在后续步骤中访问它
- 返回字符串是引导 AI 使用我们工具的提示

接下来，我们添加改进建议步骤：

```
    @think(
        name="suggest_improvements",
        description="Suggest code improvements based on analysis",
        stream=True,
    )
    def suggest_improvements(self, analysis_result: str) -> str:
        """Generate improvement suggestions based on the analysis results."""
        current_code = self.context.state.get("current_code", "")
        return f"""
        Based on the code analysis of:

        ```python
        {current_code}
        ```

        And the analysis results:
        {analysis_result}

        Please suggest specific improvements for:
        1. Reducing complexity where identified
        2. Fixing style issues
        3. Improving code organization
        4. Optimizing import usage
        5. Enhancing readability
        6. Enhancing explicitness
        """
```

这个方法使用 `@think` 表示这是一个推理步骤，然后将分析结果作为输入，从上下文中检索原始代码，最后创建一个结构化的改进建议提示。

## 10、命令行界面

现在让我们创建一个用户友好的界面。我们将其分解为几个部分：

```
def main():
    # 1. Set up logging
    logger = logging.getLogger(__name__)

    # 2. Configure Ollama server
    config = OllamaServerConfig(
        host="127.0.0.1",  # Local machine
        port=11434,        # Default Ollama port
        gpu_layers=35,     # Adjust based on your GPU
        cpu_threads=8,     # Adjust based on your CPU
    )
```

第一部分设置错误日志记录，使用合理的默认值配置 Ollama 服务器并允许自定义GPU 和 CPU 使用率的优化。

接下来，我们创建 AI 客户端和助手：

```
    # Use context manager for Ollama server
    with OllamaManager(config) as manager:
        # Initialize ClientAI with Ollama
        client = ClientAI(
            "ollama", 
            host=f"http://{config.host}:{config.port}"
        )

        # Create code review assistant with tools
        assistant = CodeReviewAssistant(
            client=client,
            default_model="llama3",
            tools=create_review_tools(),
            tool_confidence=0.8,  # How confident the AI should be before using tools
            max_tools_per_step=2, # Maximum tools to use per step
        )
```

关于此设置的关键点：

- 上下文管理器 (with) 确保正确的服务器清理
- 我们连接到本地 Ollama 实例
- 助手配置：我们的自定义工具、工具使用置信度阈值和每步工具数量限制，以防止过度使用

最后，我们创建交互式循环：

```
        print("Code Review Assistant (Local AI)")
        print("Enter Python code to review, or 'quit' to exit.")
        print("End input with '###' on a new line.")

        while True:
            try:
                print("\n" + "=" * 50 + "\n")
                print("Enter code:")
                
                # Collect code input
                code_lines = []
                while True:
                    line = input()
                    if line == "###":
                        break
                    code_lines.append(line)

                code = "\n".join(code_lines)
                if code.lower() == "quit":
                    break

                # Process the code
                result = assistant.run(code, stream=True)

                # Handle both streaming and non-streaming results
                if isinstance(result, str):
                    print(result)
                else:
                    for chunk in result:
                        print(chunk, end="", flush=True)
                print("\n")

            except Exception as e:
                logger.error(f"Unexpected error: {e}")
                print("\nAn unexpected error occurred. Please try again.")
```

此接口：

- 收集多行代码输入，直到看到 `###`
- 处理流式和非流式输出
- 提供干净的错误处理
- 允许使用 `quit`轻松退出

让我们将其制作成我们可以运行的脚本：

```
if __name__ == "__main__":
    main()
```

## 11、使用助手

让我们看看助手如何处理真实代码。让我们运行它：

```
python code_analyzer.py
```

这是一个需要查找问题的示例：

```
def calculate_total(values,tax_rate):
    Total = 0
    for Val in values:
        if Val > 0:
            if tax_rate > 0:
                Total += Val + (Val * tax_rate)
            else:
                Total += Val
    return Total
```

助手将分析多个方面：

- 结构问题（嵌套 if 语句增加复杂性、缺少类型提示、没有输入验证）。
- 风格问题（变量不一致命名、逗号后缺少空格、缺少文档字符串）。

## 12、扩展思路

以下是增强助手的一些详细方法：

- 其他分析工具
- 增强样式检查
- 文档改进
- 自动修复功能

可以通过创建新工具函数、将其包装在适当的 JSON 格式中、将其添加到 `create_review_tools()` 函数，然后更新助手的提示以使用新工具来添加这些功能。

---

原文链接：[Building a Code Analysis Assistant with Ollama: A Step-by-Step Guide to Local LLMs](https://medium.com/@igorbenav/building-a-code-analysis-assistant-with-ollama-a-step-by-step-guide-to-local-llms-3d855bc68443?ref=hubwiz.com)

汇智网翻译整理，转载请标明出处