Agentic AI Code Reviewer
An autonomous AI agent that reads GitHub Pull Requests, analyzes code changes with a local LLM, and posts automated review comments — simulating a senior engineer’s review process.
Full project source on GitHub: Click Here
What Is This Project For?
The Agentic AI Code Reviewer is a Python-based autonomous agent that monitors a GitHub Pull Request, extracts the code diff, feeds it to a locally-running Large Language Model (Ollama), and posts an AI-generated code review directly as a comment on the PR — all without any manual intervention.
“It simulates how a senior engineer reviews pull requests — detecting bugs, code quality issues, performance risks, and improvement suggestions.”
How It Works — System Architecture
What the AI Reviews
The agent instructs the LLM to focus on four pillars:
Bugs
Logic errors, null dereferences, off-by-one errors, and missing edge case handling.
Code Quality
Naming conventions, type hints, docstrings, function decomposition, and readability.
Performance
Inefficient algorithms, unnecessary re-computation, redundant API calls.
Security
Injection risks, exposed secrets, insecure defaults, and authentication issues.
Project Requirements
This project does not call OpenAI, Anthropic, or any cloud AI API. It runs a local LLM through Ollama. You must install Ollama and pull a model before running the agent.
1 · Python 3.10+
The project uses async/await (asyncio) and modern type hints, requiring Python 3.10 or newer.
# Check your version python --version # Should show Python 3.10.x or higher # Install on Ubuntu/Debian sudo apt update && sudo apt install python3.11 python3-pip -y # Install on macOS via Homebrew brew install python@3.11
2 · Ollama — Local LLM Runtime
Ollama runs large language models locally on your machine. The project defaults to llama3.2:3b, a fast and capable 3-billion-parameter model.
Install Ollama
# macOS / Linux (one-liner installer) curl -fsSL https://ollama.com/install.sh | sh # Windows winget install Ollama.Ollama
Pull the LLaMA 3.2 3B model
ollama pull llama3.2:3b
Start the Ollama server
ollama serve
# Server now runs at http://localhost:11434
Verify it works
curl http://localhost:11434/api/generate
-d '{"model":"llama3.2:3b","prompt":"hello","stream":false}'
3 · Python Dependencies
The requirements.txt only lists two packages, but you’ll also need python-dotenv (used in test files):
httpx — async HTTP client used to call Ollama’s REST API
PyGithub — GitHub REST API wrapper for reading PRs & posting comments
# Create and activate a virtual environment (recommended) python -m venv venv source venv/bin/activate # macOS/Linux venvScriptsactivate # Windows # Install required packages pip install httpx PyGithub python-dotenv
The test_github.py file uses from dotenv import load_dotenv to load your .env file. You should install python-dotenv even though it’s not listed in requirements.txt.
4 · Git (optional but recommended)
# Clone the repository
git clone https://github.com/gauravsinghshl/agentic-code-reviewer.git
cd agentic-code-reviewer
External Keys & Configuration
This project requires exactly one external credential: a GitHub Personal Access Token. There is no cloud LLM key because all AI inference runs locally via Ollama.
A GitHub Personal Access Token (PAT) with permissions to read repository contents and post PR comments. Used by GitHubService in services/github_service.py.
- Go to github.com → Settings → Developer settings → Personal access tokens → Tokens (classic)
- Click “Generate new token (classic)”
- Give it a descriptive name, e.g. ai-code-reviewer
- Set expiration (90 days recommended for testing)
- Under Scopes, check:
repo(full control of private repositories) — this covers both reading PRs and posting comments - Click “Generate token” and copy the token immediately (it won’t be shown again)
Setting Up Your .env File
The project reads GITHUB_TOKEN from environment variables. Create a .env file in the project root:
# Create .env file in project root touch .env # Add your token echo 'GITHUB_TOKEN=ghp_your_actual_token_here' >> .env
Or create the file manually with this content:
# .env — DO NOT commit this file to git GITHUB_TOKEN=ghp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
The .env file is already listed in .gitignore — never commit it. Never hardcode the token in any source file. If a token is accidentally exposed, revoke it immediately at github.com/settings/tokens.
How the Token Is Consumed
import os from github import Github class GitHubService: def __init__(self): token = os.getenv("GITHUB_TOKEN") # reads from environment if not token: raise Exception("GITHUB_TOKEN not set") self.github = Github(token)
All Variables At a Glance
| Variable | Where Used | Required? | Description |
|---|---|---|---|
| GITHUB_TOKEN | github_service.py |
REQUIRED | GitHub Personal Access Token with repo scope |
| model | llm_service.py (constructor arg) |
OPTIONAL | Ollama model name. Default: llama3.2:3b. Change to any pulled Ollama model. |
| url (Ollama) | llm_service.py (hardcoded) |
OPTIONAL | Ollama server endpoint. Default: http://localhost:11434/api/generate |
| repo_name | test_pr_reader.py, test_pr_review.py |
REQUIRED | Target GitHub repository in OWNER/REPO format |
| pr_number | test_pr_reader.py, test_pr_review.py |
REQUIRED | Pull Request number integer to review |
File-by-File Reference
Project Structure
├─ agents/
│ └─ review_agent.py Core agent — builds prompt, calls LLM
├─ services/
│ ├─ github_service.py GitHub API wrapper — reads PRs, posts comments
│ └─ llm_service.py Ollama client — sends prompts, returns text
├─ prompts/
│ └─ review_prompt.txt System prompt template (for reference)
├─ test_agent.py Test: agent on a hardcoded mock diff
├─ test_github.py Test: verify GitHub token is valid
├─ test_pr_reader.py Test: read a real PR diff only
├─ test_pr_review.py ⭐ Main runner: end-to-end review & post comment
├─ test_token.py Test: print current GITHUB_TOKEN value
├─ requirements.txt
├─ README.MD
└─ .gitignore
Role: The central orchestrator — the “agent brain”. It holds a LocalLLM instance and exposes review_diff(diff), an async method that builds the review prompt and calls the LLM.
| Name | Type | Description |
|---|---|---|
| self.llm | LocalLLM | Instance of the Ollama client, injected at construction |
| diff | str | Raw GitHub diff text passed into review_diff() |
| prompt | str (f-string) | Full instruction + diff text sent to the LLM. Instructs it to act as a senior developer and focus on Bugs, Code quality, Performance, Security |
| review | str | The LLM’s response text, returned from review_diff() |
class CodeReviewAgent: def __init__(self): self.llm = LocalLLM() # instantiates Ollama client async def review_diff(self, diff: str): prompt = f""" You are a senior developer reviewing a GitHub pull request. ... GitHub Diff: {diff} Return feedback in bullet points.""" review = await self.llm.generate(prompt) return review
Role: A thin async HTTP wrapper around Ollama’s /api/generate endpoint. Sends a prompt and returns the response text. Uses httpx.AsyncClient with a 120-second timeout (LLM inference can be slow).
| Name | Type | Description |
|---|---|---|
| self.url | str | Ollama API endpoint — hardcoded as http://localhost:11434/api/generate |
| self.model | str | Ollama model name. Default llama3.2:3b. Passed in constructor — change here to use a different model. |
| prompt | str | The full text prompt received from the agent |
| temperature | float (0) | LLM generation temperature. Set to 0 for deterministic/consistent reviews. |
| stream | bool (False) | Streaming disabled — waits for complete response before returning |
| timeout | int (120) | HTTP timeout in seconds for the Ollama call |
class LocalLLM: def __init__(self, model="llama3.2:3b"): self.url = "http://localhost:11434/api/generate" self.model = model async def generate(self, prompt: str): async with httpx.AsyncClient(timeout=120) as client: response = await client.post( self.url, json={ "model": self.model, "prompt": prompt, "stream": False, "options": {"temperature": 0} } ) return response.json()["response"]
Role: GitHub API abstraction layer. Provides two capabilities: (1) reading the unified diff of all changed files in a PR, and (2) posting a text comment on that PR. Uses the PyGithub library and reads the token from environment.
| Name | Type | Description |
|---|---|---|
| token | str | Read from os.getenv("GITHUB_TOKEN") — raises Exception if missing |
| self.github | Github | Authenticated PyGithub client instance |
| repo_name | str | “OWNER/REPO” format, e.g. "gauravsinghshl/my-project" |
| pr_number | int | Pull Request number, e.g. 1 |
| diff | str | Accumulated diff string built by iterating pr.get_files() |
| file.filename | str | Name of each changed file in the PR |
| file.patch | str | The actual unified diff (lines added/removed) for each file |
| comment | str | The AI-generated review text to post on the PR |
def get_pull_request_diff(self, repo_name, pr_number): repo = self.github.get_repo(repo_name) pr = repo.get_pull(pr_number) diff = "" for file in pr.get_files(): diff += f"File: {file.filename}n" diff += file.patch + "n" return diff def comment_on_pr(self, repo_name, pr_number, comment): repo = self.github.get_repo(repo_name) pr = repo.get_pull(pr_number) pr.create_issue_comment(comment)
Role: A standalone prompt template for reference or alternative use. It is not currently imported by any code — the inline prompt in review_agent.py is what’s actually used. However, this file documents the intended review structure and can be extended for more structured output.
You are a senior software engineer reviewing code. Analyze the following code and provide: 1. Bugs 2. Code quality issues 3. Security risks 4. Suggested improvements Code: {code} Return structured feedback.
To use this template instead of the hardcoded prompt, read it in review_agent.py via open("prompts/review_prompt.txt").read() and format it with the diff string.
Test & Runner Scripts
This is the primary script to run. It connects all three components: reads a real GitHub PR, sends the diff to the LLM, and posts the review as a PR comment.
| Variable | You Must Change? | Description |
|---|---|---|
| repo_name | YES | Change to your target repo: "OWNER/REPO" |
| pr_number | YES | Change to the PR number you want reviewed |
Tests the agent with a hardcoded mock diff. Useful for verifying Ollama is running correctly without needing a GitHub token. Run this first to sanity-check your LLM setup.
Calls GitHub with your token and prints your username. If this works, your token is valid and correctly loaded from .env.
Reads a PR diff and prints it to console without calling the LLM. Useful for checking what code the agent sees before sending it for review.
Simply prints the value of os.getenv("GITHUB_TOKEN"). If this prints None, your .env file is not being loaded — remember to use python-dotenv or export the variable manually.
How to Start the Project
Clone the repository
git clone https://github.com/gauravsinghshl/agentic-code-reviewer.git cd agentic-code-reviewer
Create & activate a Python virtual environment
python -m venv venv source venv/bin/activate # macOS / Linux venvScriptsactivate # Windows PowerShell
Install Python dependencies
pip install httpx PyGithub python-dotenv
Install Ollama and pull the LLM model
# Install Ollama (macOS/Linux) curl -fsSL https://ollama.com/install.sh | sh # Windows winget install Ollama.Ollama # Pull the default model (~2GB download) ollama pull llama3.2:3b # Start the Ollama server (keep this terminal open) ollama serve # Verify: http://localhost:11434/api/tags
Create your .env file with your GitHub token
# In the project root directory cat > .env << 'EOF' GITHUB_TOKEN=ghp_your_github_personal_access_token EOF
Verify your setup — run the quick tests first
# 6a: Check token is loaded python test_token.py # Expected: prints your token (not None) # 6b: Verify GitHub authentication python test_github.py # If certificate issue: pip install pip-system-certs # Expected: prints your GitHub username # 6c: Test the LLM (no GitHub needed) python test_agent.py # Expected: AI review printed to console
Edit test_pr_review.py with your target PR
# Open test_pr_review.py and change these two lines: repo_name = "YOUR_GITHUB_USERNAME/YOUR_REPO_NAME" pr_number = 1 # the PR number you want reviewed
Run the full end-to-end agent
python test_pr_review.py
The agent will: read the PR diff → send to Ollama → generate review → post as GitHub PR comment. This may take 30–120 seconds depending on diff size and your hardware.
When the run completes successfully you should see: AI review posted to GitHub PR! printed in the terminal, and a new comment on your GitHub Pull Request from your bot account containing the AI code review.
Troubleshooting Common Errors
| Error | Cause | Fix |
|---|---|---|
GITHUB_TOKEN not set |
.env not loaded or missing |
Load env manually: export GITHUB_TOKEN=ghp_xxx or use python-dotenv |
ConnectionRefusedError localhost:11434 |
Ollama server not running | Run ollama serve in a separate terminal |
404 Not Found from Ollama |
Model not pulled | Run ollama pull llama3.2:3b |
GithubException 401 |
Invalid or expired token | Generate a new token at github.com/settings/tokens |
GithubException 403 |
Token lacks required scope | Regenerate token with repo scope checked |
AttributeError: 'NoneType' has no 'patch' |
A PR file has no diff (e.g. binary file) | Add a check: if file.patch: diff += file.patch |
Sample Demo Walkthrough
Scenario
A developer opens PR #1 on the repository gauravsinghshl/genai-rag-document-qa. The PR changes a Python utility function. We run the agent to get an automated review.
Step 1 — The Pull Request Code Change
The PR modifies a function — here is what the diff looks like:
File: utils/calculator.py - def calculate(x,y): - return x+y + def add_numbers(x: int, y: int) -> int: + return x + y
Step 2 — Running the Agent
PR number: 1
Pull Request Code Changes:
File: utils/calculator.py
– def calculate(x,y):
– return x+y
+ def add_numbers(x: int, y: int) -> int:
+ return x + y
Sending diff to Ollama (llama3.2:3b)…
Waiting for LLM response… [~45 seconds]
AI Code Review:
• Bugs:
– No input validation: the function does not check if x or y
are integers; passing strings or floats could cause unexpected behavior.
• Code Quality:
– Function rename from `calculate` to `add_numbers` is a good
improvement — the name is now self-documenting.
– Type hints (x: int, y: int) and return annotation (-> int)
significantly improve readability and tooling support. Good change.
– Missing docstring: add a brief description of the function’s purpose.
• Performance:
– No performance concerns for this simple operation.
• Security:
– No security risks identified in this diff.
• Suggested Improvement:
– Add a docstring: `”””Add two integers and return their sum.”””`
– Consider raising `TypeError` if inputs are not integers.
AI review posted to GitHub PR!
Step 3 — The GitHub PR Comment (What It Looks Like)
After the script completes, a comment is automatically posted by your GitHub account on the Pull Request:
• No input validation — passing strings or floats could cause unexpected behavior.
Code Quality:
• ✅ Function rename from `calculate` to `add_numbers` improves clarity.
• ✅ Type hints improve readability and IDE/tooling support.
• ⚠ Missing docstring — add a description of the function’s purpose.
Performance:
• No performance concerns for this operation.
Security:
• No security risks identified.
Suggested Improvements:
• Add docstring: `”””Add two integers and return their sum.”””`
• Consider raising `TypeError` if inputs are not integers.
Testing with the Offline Mock (No GitHub Required)
To test just the LLM part without touching GitHub, run test_agent.py:
python test_agent.py # Output: AI PR Review • Bugs: - No input validation on x and y parameters. • Code Quality: - Good: added type hints and descriptive function name. - Missing docstring for the function. • Performance: - No issues for this simple operation. • Security: - No security concerns.
The agent is fully functional. To review any PR on any repository you have access to, simply update repo_name and pr_number in test_pr_review.py and run it again.
Customization Tips
Change the LLM Model
In llm_service.py, change model="llama3.2:3b" to any Ollama model you’ve pulled — e.g. codellama:7b or mistral:7b.
Customize the Review Prompt
Edit the inline prompt string in review_agent.py or load prompts/review_prompt.txt to change the focus areas or output format.
Add Loop / Automation
Wrap test_pr_review.py logic in a polling loop or GitHub webhook handler to automatically review every new PR.
Different LLM Temperature
Change "temperature": 0 in llm_service.py to e.g. 0.3 for slightly more varied, creative review suggestions.
Clone or download the full project:Click Here