跪拜 Guibai
← Back to the summary

A 200-Line Python Flask Service That Posts AI Code Reviews Straight to GitLab MRs

A practical guide to a small Python project written for frontend developers

  1. # Practical: Building an AI Code Review Automated Pipeline
  2. # GitLab-Runner + AI Code Review Service + Remote Large Model Full Deployment and Operations Practice

Origin: Why I Wrote These Notes

A few days ago, I built an AI code review service using Python + Flask with about 200 lines of code. It achieves: When GitLab receives an MR, it automatically calls AI to analyze the code diff, then posts the review comments back to the MR comment section.

Several frontend colleagues on the team were curious: "I don't know Python, can I learn this?" So I thought, why not write a set of zero-basics learning notes, recording all the pitfalls I encountered and the process of understanding it.


1. What Exactly Does This Service Do?

Don't worry about the code yet. Let's first clarify what problem it aims to solve:

So the service flow is:

  1. GitLab CI triggers a task →
  2. Calls this Python service (passing the project name, MR number) →
  3. The Python service pulls the code changes (diff) for this MR from GitLab →
  4. Sends the diff to a large model (like GPT-4) →
  5. The large model returns review comments according to our specifications →
  6. The service then posts these comments to the MR comment section via the GitLab API.

You can think of it as: an AI colleague who can read code, online 24/7, on call anytime.


2. What Technologies Does This Project Use? (Made Understandable for Frontend Devs)

Technology Role Analogy (Frontend Perspective)
Python 3 Backend language Similar to Node.js
Flask Web framework Similar to Express
OpenAI SDK Calling large models Similar to calling a third-party API
requests Sending HTTP requests Similar to axios
python-dotenv Reading .env config Similar to the dotenv package
git command Cloning the specification file repository Just git clone

So even if you don't know Python, as long as you know JavaScript, understanding these concepts isn't hard—it's just different syntax.


3. Step-by-Step to Get It Running (Mac / Linux)

1. Install Python (if not already present)

Mac users (usually comes pre-installed):

brew install python3

Verify:

python3 --version   # Should display 3.9 or higher

2. Download the Code

Save the app.py file above into a folder, e.g., ~/ai-review-service/.

Also create a requirements.txt with the following content:

flask
openai
python-dotenv
requests

3. Install Dependencies

cd ~/ai-review-service
pip3 install -r requirements.txt

If pip3 is not found, try pip. Or use python3 -m pip install ...

4. Configure Environment Variables (Create .env file)

Create a .env file in the same directory with the following content (replace the angle brackets with your own):

OPENAI_API_KEY=sk-xxxxxxxxxxxxxx
OPENAI_BASE_URL=https://api.openai.com/v1
OPENAI_MODEL=gpt-4o-mini
GITLAB_TOKEN=glpat-xxxxxxxxxxxx
GITLAB_URL=https://gitlab.com
REVIEW_SPEC_PATH=./specs/frontend-code-review.md
AUTO_SYNC_SPEC=true
SKILLS_REPO_URL=https://gitlab.com/your-team/skills.git

I will explain the meaning of each field in detail later.

5. Start the Service

python3 app.py

Success looks like the following output:

🚀 AI Review Service starting...
📡 Service address: http://localhost:5001
🔑 Test token: test-token-123
📝 Review endpoint: POST /api/review

4. Detailed Explanation of the .env Configuration File (Must-Read for Beginners)

Variable Meaning Where to Get It
OPENAI_API_KEY Key for calling OpenAI (or compatible interface) Apply on the OpenAI official website or Alibaba Cloud DashScope
OPENAI_BASE_URL API address Default is OpenAI official; if using Tongyi Qianwen domestically, change to the Alibaba Cloud address
OPENAI_MODEL Model name used e.g., gpt-4o-mini, qwen3.7-max
GITLAB_TOKEN GitLab personal access token GitLab → Settings → Access Tokens, check api or read_api permission
GITLAB_URL Your GitLab server address Your company's internal GitLab address, e.g., https://gitlab.your-company.com
REVIEW_SPEC_PATH Path to store the code review specification file Can be default; it will be downloaded from the skills repo on startup
AUTO_SYNC_SPEC Whether to auto-sync the specification true or false, recommended true
SKILLS_REPO_URL Git repository address storing the review specifications Create a repo yourself, put frontend-code-review/SKILL.md inside

Special Note:


5. Code Analysis Section by Section (You don't need to know Python, just understand what it does)

I'll break down app.py into several core functions and explain them in plain language.

1. Import Dependencies and Read Configuration

from flask import Flask, request, jsonify
import os
import subprocess
# ... other imports
load_dotenv()   # Automatically reads the .env file

2. Sync Review Specification Files

def sync_specs_from_git():
    # Uses git clone to pull a repository, then copies the SKILL.md inside to local

Why is this needed? Team code specifications may update. We put them separately in a Git repository, and the service automatically pulls the latest version on startup. This way, you don't need to change the code, just the specification file, and the AI will review according to the new rules.

Analogy for frontend: It's like you npm install a dependency package, except here it's git clone for a document.

3. Get MR Diff from GitLab

def get_mr_diff(project_path, mr_iid):
    # Calls GitLab API: GET /projects/:id/merge_requests/:iid/changes
    # Returns a string containing the code additions and deletions

Core Logic:

What if it fails? The code has detailed error prompts, e.g., 401 tells you the token permission is insufficient, 403 tells you no permission to access the project.

4. Call AI for Review

def get_real_ai_review(mr_info, code_diff):
    # 1. Load the team specification file content
    # 2. Construct a very long prompt
    # 3. Call the OpenAI API
    # 4. Parse the returned JSON

This is the core function. What it does can be understood as: giving instructions to the AI.

The instruction content is roughly:

You are a review expert. Below are the code changes for the MR. Please check according to the following specifications: ... The output format must be JSON, and each issue must indicate the file, line number, priority, and category.

The JSON structure returned by the AI is similar to:

{
  "score": 75,
  "suggestions": [
    {"type": "error", "file": "src/App.vue", "line": 12, "message": "Null pointer risk, suggest adding ?."}
  ]
}

Note for frontend colleagues: This prompt is your "requirements document". How you want the AI to review can be completely controlled by modifying the prompt. You can even translate ESLint rules into natural language and write them in.

5. Define HTTP Interface (Routes)

@app.route('/api/review', methods=['POST'])
def review_code():
    # 1. Verify the Bearer token in the Authorization header
    # 2. Parse the request body (JSON)
    # 3. Call get_mr_diff to pull the code
    # 4. Call get_real_ai_review
    # 5. Return the result

6. Health Check and Test Interface

@app.route('/health')
def health_check():
    return {"status": "ok", ...}

Purpose: Lets you confirm whether the service is alive and whether the configuration is correct (e.g., checking if the API Key exists).

There is also a /test/gitlab interface to manually test if the GitLab token is valid.


6. How to Call This Service in GitLab CI?

In your frontend project's root directory, modify .gitlab-ci.yml and add a job:

ai-review:
  stage: review
  tags:
    - mac-runner          # If you use a local runner
  script:
    - |
      curl -X POST http://localhost:5001/api/review \
        -H "Authorization: Bearer test-token-123" \
        -H "Content-Type: application/json" \
        -d "{
            \"project\": \"$CI_PROJECT_PATH\",
            \"mr_iid\": $CI_MERGE_REQUEST_IID,
            \"source_branch\": \"$CI_MERGE_REQUEST_SOURCE_BRANCH_NAME\",
            \"target_branch\": \"$CI_MERGE_REQUEST_TARGET_BRANCH_NAME\"
          }"
  rules:
    - if: '$CI_PIPELINE_SOURCE == "merge_request_event"'

If you don't want to use a local runner, you can also deploy this service to a server with a public IP, then replace http://localhost:5001 with the corresponding IP.


7. Pitfall Records (Where I Fell)

1. Insufficient Token Permission → 401

2. Local Service Running on 5001, but GitLab Runner Cannot Access It

3. Python Dependency Installation Failed

4. AI Returned JSON Parsing Failed


8. Extension Ideas (Features You Can Add Yourself)


9. Final Words

As a former frontend developer, I initially found Python very unfamiliar too. But after actually writing it, I discovered that Flask is even simpler than Express: no need to configure middleware, no need to handle route parameter parsing, and not even async/await (it's synchronous by default, which is very suitable for this kind of small tool).

Moreover, the pattern for this kind of "small AI service" is very fixed: Receive request → Call third-party API → Process data → Return JSON. As long as you can write JavaScript, switching to Python is just a difference in dictionary and list syntax.

I hope these notes give you the confidence to modify the code above, or even write your own. If you encounter problems while trying, feel free to leave a comment in the comment section (or raise an issue), and I will add new pitfall experiences.

One last nagging point: The code is not important, the method of solving the problem is. Although this service is only 200 lines, it truly implements the team's code standards, which is the most valuable part.