diff --git a/.gitea/workflows/ai-review.yml b/.gitea/workflows/ai-review.yml index 01cda5d..a77be51 100644 --- a/.gitea/workflows/ai-review.yml +++ b/.gitea/workflows/ai-review.yml @@ -1,40 +1,102 @@ -name: AI Code Review +name: "AI Code Review (Skill-Driven)" -# 触发条件:当向 main 分支发起 PR,或者 PR 有新代码提交时触发 on: pull_request: types: [opened, synchronize] - branches: - - main jobs: - code-review: + review_with_skill: runs-on: ubuntu-latest steps: - # 1. 拉取代码 - - name: Checkout Repository - uses: actions/checkout@v3 + # 1. 拉取代码及 Skill 规则文件 + - name: Checkout Code & Skills + uses: actions/checkout@v4 with: - fetch-depth: 0 # 必须设置为 0,获取完整提交历史,以便 AI 对比代码差异 + fetch-depth: 0 - # 2. 运行 AI 代码审查 - - name: Run AI Code Review - uses: anc95/ChatGPT-CodeReview@main + # 2. 运行 AI 审查脚本 + - name: Run Review Engine env: - # Gitea 自带的 Token,用于让机器人能自动在你的 PR 下方发评论(系统自带,无需你手动配置) - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - # 刚刚你在 Gitea "工作流 -> 密钥" 里配置的中转站 API Key - OPENAI_API_KEY: ${{ secrets.LLM_API_KEY }} - - # 你的中转站 API 基础地址(务必保留结尾的 /v1) - OPENAI_API_ENDPOINT: "https://api.maxmodel.com/v1" - - # 你想使用的模型名称(请填入你中转站支持的模型,比如 gpt-4o 或 deepseek-chat) - MODEL: "gpt-4o" - - # 设置 AI 评论的语言 - LANGUAGE: "Chinese" - - # 给 AI 的设定提示词,让它重点关注你关心的漏洞 - SYSTEM_MESSAGE: "你是一个资深的 Python 架构师。请严格检查代码中的安全隐患(如硬编码密钥)、性能问题(如在 async 中使用同步阻塞 I/O)以及代码健壮性缺失,并提供具体的修复建议和代码示例。" \ No newline at end of file + # 🔑 中转站 API Key(在 Gitea Secrets 配置) + API_KEY: ${{ secrets.LLM_API_KEY }} + # 🌐 中转站 Base URL(在 Gitea Secrets 配置) + BASE_URL: ${{ secrets.LLM_BASE_URL }} + # 🤖 选择的模型名称(按需修改) + MODEL_NAME: "gpt-4o" + # Gitea 自动注入的 Token,无需手动配置 + GITEA_TOKEN: ${{ gitea.token }} + GITEA_SERVER_URL: ${{ gitea.server_url }} + GITEA_REPOSITORY: ${{ gitea.repository }} + GITEA_PR_NUMBER: ${{ gitea.event.number }} + GITEA_BASE_REF: ${{ gitea.base_ref }} + GITEA_HEAD_REF: ${{ gitea.head_ref }} + run: | + # 提取当前 PR 的代码差异 + git diff origin/$GITEA_BASE_REF...origin/$GITEA_HEAD_REF > diff.txt + + # 使用 Python 执行审查(纯标准库,无需 pip install) + python3 -c " + import os, json, urllib.request, urllib.error + + # ========== 1. 读取 Skill 规则书 ========== + skill_base_path = '.claude/skills/code-review-skill/' + try: + skill_config = open(os.path.join(skill_base_path, 'SKILL.md'), encoding='utf-8').read() + python_rules = open(os.path.join(skill_base_path, 'reference/python.md'), encoding='utf-8').read() + except FileNotFoundError as e: + print(f'❌ 未找到 Skill 规则文件: {e}'); exit(1) + + full_skill_prompt = f'{skill_config}\n\n以下是针对当前语言的补充规范:\n{python_rules}' + + # ========== 2. 读取代码变动 ========== + diff_data = open('diff.txt', encoding='utf-8').read() + if not diff_data.strip(): + print('✅ 没有检测到代码变更,跳过审查。'); exit(0) + + # ========== 3. 调用中转站大模型 API ========== + base_url = os.getenv('BASE_URL', 'https://api.maxmodel.com/v1').rstrip('/') + api_url = f'{base_url}/chat/completions' + model_name = os.getenv('MODEL_NAME', 'gpt-4o') + + print(f'🚀 正在调用模型: {model_name} @ {base_url}') + + headers = { + 'Authorization': f'Bearer {os.getenv(\"API_KEY\")}', + 'Content-Type': 'application/json' + } + payload = { + 'model': model_name, + 'messages': [ + {'role': 'system', 'content': full_skill_prompt}, + {'role': 'user', 'content': f'请严格对照上述 Skill 规范,评审以下 FastAPI 代码改动,并用中文输出评审意见:\n\n{diff_data}'} + ], + 'temperature': 0.2 + } + + req = urllib.request.Request(api_url, data=json.dumps(payload).encode('utf-8'), headers=headers, method='POST') + try: + with urllib.request.urlopen(req, timeout=120) as response: + res = json.loads(response.read().decode('utf-8')) + comment_body = res['choices'][0]['message']['content'] + print('✅ 大模型已返回审查意见') + except urllib.error.HTTPError as e: + error_body = e.read().decode('utf-8') + print(f'❌ 请求中转站失败 [{e.code}]: {error_body}'); exit(1) + except Exception as e: + print(f'❌ 请求中转站异常: {e}'); exit(1) + + # ========== 4. 发表到 Gitea PR 评论区 ========== + gitea_url = f'{os.getenv(\"GITEA_SERVER_URL\")}/api/v1/repos/{os.getenv(\"GITEA_REPOSITORY\")}/issues/{os.getenv(\"GITEA_PR_NUMBER\")}/comments' + gitea_headers = { + 'Authorization': f'token {os.getenv(\"GITEA_TOKEN\")}', + 'Content-Type': 'application/json' + } + gitea_payload = {'body': f'### 🤖 AI 自动化审查报告 (基于 Code Review Skill)\n\n> 模型: \`{model_name}\` | 中转站: maxmodel\n\n{comment_body}'} + + gitea_req = urllib.request.Request(gitea_url, data=json.dumps(gitea_payload).encode('utf-8'), headers=gitea_headers, method='POST') + try: + urllib.request.urlopen(gitea_req, timeout=30) + print('🎉 Skill 审查报告已成功发表到 PR!') + except Exception as e: + print(f'❌ 发表评论失败: {e}'); exit(1) + " \ No newline at end of file