Skip to content

Conversation

@jonesjylee0614
Copy link

新增完整的 AI 集成模块,支持在自动提交前智能分析代码变更并动态生成提示词。

主要特性:

  • 支持多种 AI 提供商 (OpenAI, Anthropic, Ollama)
  • Git diff 智能分析和 Commit Message 自动生成
  • 代码问题检测(安全、性能、代码异味)
  • 风险评估和优化建议
  • Web UI 集成和 RESTful API 端点
  • 完整的配置示例和使用文档

新增文件:

  • src/mcp_feedback_enhanced/ai/ - AI 分析核心模块
  • src/mcp_feedback_enhanced/web/routes/ai_routes.py - API 路由
  • src/mcp_feedback_enhanced/web/static/js/modules/ai/ - 前端模块
  • src/mcp_feedback_enhanced/web/static/css/ai-analyzer.css - UI 样式
  • docs/ai-integration-guide.md - 详细使用指南
  • examples/ai-integration-example.py - 集成示例代码
  • examples/ai-config-example.env - 配置模板
  • AI_FEATURE_README.md - 功能概览文档

技术实现:

  • 异步 API 调用支持
  • 多提供商统一接口设计
  • 优雅的错误处理和降级
  • 响应式 UI 设计

🤖 Generated with Claude Code

新增完整的 AI 集成模块,支持在自动提交前智能分析代码变更并动态生成提示词。

主要特性:
- 支持多种 AI 提供商 (OpenAI, Anthropic, Ollama)
- Git diff 智能分析和 Commit Message 自动生成
- 代码问题检测(安全、性能、代码异味)
- 风险评估和优化建议
- Web UI 集成和 RESTful API 端点
- 完整的配置示例和使用文档

新增文件:
- src/mcp_feedback_enhanced/ai/ - AI 分析核心模块
- src/mcp_feedback_enhanced/web/routes/ai_routes.py - API 路由
- src/mcp_feedback_enhanced/web/static/js/modules/ai/ - 前端模块
- src/mcp_feedback_enhanced/web/static/css/ai-analyzer.css - UI 样式
- docs/ai-integration-guide.md - 详细使用指南
- examples/ai-integration-example.py - 集成示例代码
- examples/ai-config-example.env - 配置模板
- AI_FEATURE_README.md - 功能概览文档

技术实现:
- 异步 API 调用支持
- 多提供商统一接口设计
- 优雅的错误处理和降级
- 响应式 UI 设计

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
@gemini-code-assist
Copy link

Summary of Changes

Hello @jonesjylee0614, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

此拉取请求为MCP Feedback Enhanced项目引入了一个全面的AI集成模块。该模块旨在通过利用人工智能智能分析代码变更、自动生成提交信息、检测潜在问题并提供优化建议,从而自动化和增强代码审查流程。这不仅能提高代码质量,还能显著提升开发效率和团队协作规范性。

Highlights

  • AI集成模块: 新增完整的AI集成模块,支持在自动提交前智能分析代码变更并动态生成提示词。
  • 多AI提供商支持: 支持OpenAI、Anthropic、Ollama等多种AI提供商,提供灵活的部署选项(云端或本地)。
  • 智能代码分析功能: 实现Git diff智能分析、Commit Message自动生成、代码问题检测(安全、性能、代码异味)、风险评估和优化建议。
  • Web UI与API集成: 提供Web UI集成,允许用户通过界面触发AI分析,并暴露RESTful API端点供程序化调用。
  • 完善的文档与示例: 包含详细的使用指南、配置示例和集成示例代码,方便用户快速上手和理解功能。
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

你好,感谢你为这个项目带来如此强大的 AI 代码分析功能。这是一个非常全面的功能实现,从支持多个 AI 提供商到后端 API、前端 UI 和详尽的文档,都做得非常出色。

我在代码中发现了一些需要关注的问题。最关键的是在 API 路由中,异步函数里包含了阻塞式的子进程调用,并且不安全地更改了当前工作目录,这在并发环境下可能导致严重问题。此外,前端 JavaScript 代码中存在一个会导致运行时错误的 bug。我还提供了一些关于代码健壮性和文档准确性的建议。

总的来说,这是一个非常棒的 PR。在解决了这些关键问题后,这个功能将会更加稳定和可靠。期待看到你的更新!

Comment on lines +40 to +101
def get_git_diff(project_dir: str) -> tuple[str, str]:
"""
获取 Git 状态和差异

Args:
project_dir: 项目目录

Returns:
tuple: (git_status, git_diff)
"""
try:
# 切换到项目目录
original_dir = os.getcwd()
os.chdir(project_dir)

# 获取 git status
status_result = subprocess.run(
["git", "status", "--short"],
capture_output=True,
text=True,
check=True,
timeout=10,
)
git_status = status_result.stdout

# 获取 staged 和 unstaged 的 diff
diff_staged = subprocess.run(
["git", "diff", "--cached"],
capture_output=True,
text=True,
check=True,
timeout=10,
)

diff_unstaged = subprocess.run(
["git", "diff"], capture_output=True, text=True, check=True, timeout=10
)

# 合并 diff
git_diff = ""
if diff_staged.stdout:
git_diff += "=== Staged Changes ===\n" + diff_staged.stdout + "\n"
if diff_unstaged.stdout:
git_diff += "=== Unstaged Changes ===\n" + diff_unstaged.stdout

# 恢复原目录
os.chdir(original_dir)

return git_status, git_diff

except subprocess.CalledProcessError as e:
debug_log(f"Git 命令执行失败: {e}")
raise HTTPException(status_code=500, detail=f"Git 命令失败: {e}")
except subprocess.TimeoutExpired:
debug_log("Git 命令超时")
raise HTTPException(status_code=500, detail="Git 命令执行超时")
finally:
# 确保恢复原目录
try:
os.chdir(original_dir)
except Exception:
pass

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

这个函数存在两个严重问题:

  1. 阻塞事件循环 (Critical): 在一个 async 函数中使用了阻塞的 subprocess.run。这会阻塞整个服务器的事件循环,导致在执行 git 命令时服务器无法响应任何其他请求。
  2. 不安全的目录切换 (High): 使用 os.chdir() 在一个并发环境中(如 web 服务器)是非常危险的。一个请求可能会改变当前工作目录,从而影响到其他正在处理的请求,导致不可预测的行为和错误。

我建议将此函数重构为一个完全异步的实现,使用 asyncio.create_subprocess_exec 来执行 git 命令,并使用 cwd 参数来指定工作目录,而不是全局切换。

async def get_git_diff(project_dir: str) -> tuple[str, str]:
    """
    异步获取 Git 状态和差异,且不改变当前工作目录。

    Args:
        project_dir: 项目目录。

    Returns:
        一个包含 git status 和 git diff 的元组。
    """
    async def run_git_command(args: list[str]) -> str:
        proc = await asyncio.create_subprocess_exec(
            "git", *args,
            stdout=asyncio.subprocess.PIPE,
            stderr=asyncio.subprocess.PIPE,
            cwd=project_dir
        )
        try:
            stdout, stderr = await asyncio.wait_for(proc.communicate(), timeout=10)
        except asyncio.TimeoutError:
            proc.kill()
            await proc.wait()
            raise

        if proc.returncode != 0:
            raise subprocess.CalledProcessError(
                proc.returncode, f"git {' '.join(args)}", stderr=stderr
            )
        return stdout.decode()

    try:
        git_status = await run_git_command(["status", "--short"])
        diff_staged = await run_git_command(["diff", "--cached"])
        diff_unstaged = await run_git_command(["diff"])

        git_diff = ""
        if diff_staged:
            git_diff += "=== Staged Changes ===\n" + diff_staged + "\n"
        if diff_unstaged:
            git_diff += "=== Unstaged Changes ===\n" + diff_unstaged

        return git_status, git_diff

    except (subprocess.CalledProcessError, FileNotFoundError) as e:
        debug_log(f"Git 命令执行失败: {e}")
        raise HTTPException(status_code=500, detail=f"Git 命令失败: {e!s}")
    except asyncio.TimeoutError:
        debug_log("Git 命令超时")
        raise HTTPException(status_code=500, detail="Git 命令执行超时")

Comment on lines +300 to +322
```bash
#!/bin/bash

# 调用 AI 分析
python -c "
from mcp_feedback_enhanced.ai import AIAnalyzer
import asyncio

async def analyze():
analyzer = AIAnalyzer()
result = await analyzer.analyze_git_diff(
git_diff='$(git diff --cached)',
git_status='$(git status --short)'
)

if result.risk_level == 'high':
print('❌ 检测到高风险变更,请仔细审查!')
print(result.summary)
exit(1)

asyncio.run(analyze())
"
```

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

这个 Git Hook 示例的实现方式有误。在 python -c 的字符串中,'$(git diff --cached)' 不会执行 shell 命令,而是会作为字符串字面量传递给 Python 脚本。这会导致 AI 分析的内容是错误的。

为了正确地将 git diff 的内容传递给 Python 脚本,你应该先在 shell 中执行命令,然后通过环境变量将结果传递进去。这样更清晰、更可靠,也更容易调试。

#!/bin/bash

# 获取 git diff 和 status 并通过环境变量传递
export GIT_DIFF=$(git diff --cached)
export GIT_STATUS=$(git status --short)

# 如果没有暂存的变更,则退出
if [ -z "$GIT_DIFF" ]; then
    echo "No staged changes to analyze."
    exit 0
fi

# 调用 AI 分析
python -c "
from mcp_feedback_enhanced.ai import AIAnalyzer
import asyncio
import os

async def analyze():
    analyzer = AIAnalyzer()
    result = await analyzer.analyze_git_diff(
        git_diff=os.environ['GIT_DIFF'],
        git_status=os.environ['GIT_STATUS']
    )

    if result.risk_level == 'high':
        print('❌ 检测到高风险变更,请仔细审查!')
        print(result.summary)
        exit(1)

asyncio.run(analyze())
"

Comment on lines +200 to +220
if (analysis.issues_found && analysis.issues_found.length > 0) {
html += '<div class="ai-section ai-issues">';
html += '<h4>⚠️ 发现的问题</h4>';
html += '<ul>';
analysis.issues_found.forEach(function(issue) {
html += '<li>' + self._escapeHtml(issue) + '</li>';
});
html += '</ul>';
html += '</div>';
}

if (analysis.suggestions && analysis.suggestions.length > 0) {
html += '<div class="ai-section ai-suggestions">';
html += '<h4>💡 优化建议</h4>';
html += '<ul>';
analysis.suggestions.forEach(function(suggestion) {
html += '<li>' + self._escapeHtml(suggestion) + '</li>';
});
html += '</ul>';
html += '</div>';
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

在这两个 forEach 循环的回调函数中,你使用了 self._escapeHtml,但是 self 在这个作用域内并未定义,这将导致运行时抛出 ReferenceError

要修复这个问题,你可以在 forEach 的第二个参数中传入 this 来指定回调函数内部的 this 上下文。

        if (analysis.issues_found && analysis.issues_found.length > 0) {
            html += '<div class="ai-section ai-issues">';
            html += '<h4>⚠️ 发现的问题</h4>';
            html += '<ul>';
            analysis.issues_found.forEach(function(issue) {
                html += '<li>' + this._escapeHtml(issue) + '</li>';
            }, this);
            html += '</ul>';
            html += '</div>';
        }

        if (analysis.suggestions && analysis.suggestions.length > 0) {
            html += '<div class="ai-section ai-suggestions">';
            html += '<h4>💡 优化建议</h4>';
            html += '<ul>';
            analysis.suggestions.forEach(function(suggestion) {
                html += '<li>' + this._escapeHtml(suggestion) + '</li>';
            }, this);
            html += '</ul>';
            html += '</div>';
        }


def __init__(
self,
provider: AIProvider = AIProvider.OPENAI,

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

默认的 AI 提供商设置为 OPENAI,但这与文档和示例中推荐新用户使用 Ollama 的做法不一致。为了保持一致性并降低新用户的入门门槛(因为 Ollama 是免费且无需 API 密钥的),建议将默认提供商更改为 OLLAMA

Suggested change
provider: AIProvider = AIProvider.OPENAI,
provider: AIProvider = AIProvider.OLLAMA,

breaking_changes: bool # 是否包含破坏性变更

# 额外信息
confidence_score: float # AI 分析置信度 (0-1)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

confidence_score 的类型定义为 float 有些严格。如果 AI 的响应中没有包含这个字段,_parse_ai_response 方法会使用一个硬编码的默认值 0.8,这可能不太准确。建议将类型改为 float | None,这样可以更明确地表示该值是否由 AI 提供。

修改后,你还需要更新 _parse_ai_response 方法中第 342 行的代码,移除默认值,像这样:
confidence_score=data.get("confidence_score"),

Suggested change
confidence_score: float # AI 分析置信度 (0-1)
confidence_score: float | None # AI 分析置信度 (0-1)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants