打造自己的代码审查助手
你是否曾经想要拥有完全在本地机器上运行的 AI 代码审查器?在本教程中,我们将使用 ClientAI 和 Ollama 构建该工具。

你是否曾经想要拥有完全在本地机器上运行的 AI 代码审查器?在本教程中,我们将使用 ClientAI 和 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 的网站获取它。
现在让我们创建将代码写入其中的文件:
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
汇智网翻译整理,转载请标明出处