Automaattiset PR-katselmointikommentit
PR-diffien lukeminen ja rivinsisäisten katselmointikommenttien julkaiseminen API:n kautta
Automaattiset PR-katselmointikommentit on ilmainen Tekoälyagentit-oppitunti CoddyKitissä. Tämä on oppitunti 3/4. Voit lukea koko oppitunnin alta ilmaiseksi ja harjoitella sen jälkeen käytännössä selaimessa sisäänrakennetulla koodieditorilla ja ympäri vuorokauden käytettävissä olevan tekoälytuutorin avulla. Oppitunti kuuluu Tekoälyagentit-oppimispolkuun, ja edistymisesi synkronoituu verkon ja CoddyKit-sovelluksen välillä. Tekoälyagentit-kurssilla on yhteensä 4 oppituntia.
Mitä on automaattinen PR-katselmointi?
Automaattinen PR-katselmointiin tarkoitettu agentti lukee muuttuneet tiedostot, analysoi diffit ja julkaisee kohdennettuja katselmuskommentteja — aivan kuten ihmiskatselmoija, mutta nopeammin ja johdonmukaisemmin. Käyttökohteita ovat tietoturvan tarkistus, tyylisääntöjen valvonta, yleisten virheiden havaitseminen, testikattavuuden tarkistaminen ja PR:n sisällön yhteenveto.
from github import Github
import os
g = Github(token=os.environ['GITHUB_TOKEN'])
repo = g.get_repo('myorg/myrepo')
# Get a specific pull request by number
pr = repo.get_pull(42)
print(f'PR #{pr.number}: {pr.title}')
print(f'Author: {pr.user.login}')
print(f'Base branch: {pr.base.ref} <- Head: {pr.head.ref}')
print(f'State: {pr.state}')
print(f'Changed files: {pr.changed_files}')
print(f'Additions: +{pr.additions}')
print(f'Deletions: -{pr.deletions}')Avoimien pull requestien listaaminen
Käyttäkää komentoa repo.get_pulls() PR:ien listaamiseen. Suodattakaa niitä tilan (open, closed), kohdehaaran tai lajittelujärjestyksen perusteella. Katselmointibotti käsittelee yleensä avoimia PR:iä, joita ei ole vielä katselmoitu.
from github import Github
import os
g = Github(token=os.environ['GITHUB_TOKEN'])
repo = g.get_repo('myorg/myrepo')
# All open PRs targeting main
open_prs = repo.get_pulls(
state='open',
base='main',
sort='created',
direction='desc'
)
for pr in open_prs:
# Check if agent has already reviewed it
reviews = list(pr.get_reviews())
agent_reviewed = any(
r.user.login == 'my-agent-bot[bot]'
for r in reviews
)
if not agent_reviewed:
print(f'Needs review: PR #{pr.number} - {pr.title}')Muuttuneiden tiedostojen hakeminen
pr.get_files() palauttaa luettelon File-olioita — kukin niistä edustaa PR:ssä muuttunutta tiedostoa. Tärkeitä ominaisuuksia ovat filename, status (added/modified/removed), additions, deletions ja patch (yhtenäisen diffin teksti).
from github import Github
import os
g = Github(token=os.environ['GITHUB_TOKEN'])
repo = g.get_repo('myorg/myrepo')
pr = repo.get_pull(42)
changed_files = list(pr.get_files())
print(f'Changed files: {len(changed_files)}')
for file in changed_files:
print(f'\n{file.status.upper()}: {file.filename}')
print(f' +{file.additions} -{file.deletions}')
# The patch is the unified diff
if file.patch:
print(f' Patch preview: {file.patch[:200]}')
# Filter to only Python files
python_files = [
f for f in changed_files
if f.filename.endswith('.py') and f.status != 'removed'
]Tiedoston patchin (diffin) lukeminen
file.patch on yhtenäinen diff-merkkijono. Merkillä + alkavat rivit on lisätty ja merkillä - alkavat rivit poistettu. Otsikko @@ näyttää, mistä rivinumerosta hunk alkaa. Jäsentäkää tämä, jotta tunnistatte täsmälleen, missä uuden koodin ongelmat ovat.
def parse_patch_hunks(patch):
if not patch:
return []
hunks = []
current_hunk = None
new_line_num = 0
for line in patch.split('\n'):
if line.startswith('@@'):
# Parse: @@ -old_start,old_count +new_start,new_count @@
import re
match = re.search(r'\+([0-9]+)', line)
if match:
new_line_num = int(match.group(1))
current_hunk = {'header': line, 'lines': [], 'start_line': new_line_num}
hunks.append(current_hunk)
elif current_hunk is not None:
if line.startswith('+'):
current_hunk['lines'].append({'type': 'add', 'content': line[1:], 'line': new_line_num})
new_line_num += 1
elif line.startswith('-'):
current_hunk['lines'].append({'type': 'remove', 'content': line[1:], 'line': None})
else:
new_line_num += 1
return hunks
# --- demo ---
patch = (
'@@ -10,3 +10,4 @@\n'
' def add(a, b):\n'
'- return a - b\n'
'+ return a + b\n'
'+ # fixed subtraction bug\n'
)
for hunk in parse_patch_hunks(patch):
print(hunk['header'])
for line in hunk['lines']:
print(' ', line)
PR:n komittien hakeminen
pr.get_commits() palauttaa PR:n komitit. Jokaisella komitilla on viesti, tekijä ja luettelo muuttuneista tiedostoista. Komittiviestien analysointi auttaa katselmointibottia tekemään yhteenvedon PR:n sisällöstä ja tarkistamaan komittikäytännöt.
from github import Github
import os
g = Github(token=os.environ['GITHUB_TOKEN'])
repo = g.get_repo('myorg/myrepo')
pr = repo.get_pull(42)
commits = list(pr.get_commits())
print(f'Commits in PR: {len(commits)}')
for commit in commits:
print(f'\nCommit: {commit.sha[:8]}')
print(f'Author: {commit.commit.author.name}')
print(f'Message: {commit.commit.message[:80]}')
print(f'Changes: +{commit.stats.additions} -{commit.stats.deletions}')
# Check commit message quality
poor_messages = [
c for c in commits
if len(c.commit.message.split()[0] if c.commit.message else '') < 3
or c.commit.message.lower().startswith(('wip', 'fix', 'test'))
]
if poor_messages:
print(f'{len(poor_messages)} commits have poor message quality')PR-katselmuksen lähettäminen
Käyttäkää komentoa pr.create_review() muodollisen katselmuksen lähettämiseen. Parametri event määrittää katselmuksen tyypin: 'APPROVE', 'REQUEST_CHANGES' tai 'COMMENT'. Katselmukseen voi sisältyä yleinen leipäteksti ja luettelo tiettyihin riveihin kohdistuvista kommenteista.
from github import Github
import os
g = Github(token=os.environ['GITHUB_TOKEN'])
repo = g.get_repo('myorg/myrepo')
pr = repo.get_pull(42)
# Submit a review with a general comment (no inline comments)
pr.create_review(
body=(
'**Automated Code Review**\n\n'
'I analyzed this PR and found the following:\n'
'- No security issues detected\n'
'- Style conforms to project guidelines\n'
'- Test coverage looks adequate\n\n'
'_This review was generated automatically._'
),
event='COMMENT' # 'APPROVE', 'REQUEST_CHANGES', or 'COMMENT'
)Rivikohtaisten katselmuskommenttien lähettäminen
Rivikohtaiset kommentit näkyvät suoraan diffin tietyillä riveillä. Jokainen kommentti määrittää path-arvon (tiedostopolku), line-arvon (uuden tiedoston rivinumero) ja body-arvon. Välittäkää kommenttiluettelo kutsulle pr.create_review(comments=[...]).
from github import Github
import os
g = Github(token=os.environ['GITHUB_TOKEN'])
repo = g.get_repo('myorg/myrepo')
pr = repo.get_pull(42)
# Build inline comments from analysis results
inline_comments = [
{
'path': 'src/auth.py',
'line': 47,
'body': (
':warning: **Security Issue:** Using `md5` for password hashing.\n'
'Use `bcrypt` or `argon2` instead.\n'
'Example: `import bcrypt; bcrypt.hashpw(password.encode(), bcrypt.gensalt())`'
)
},
{
'path': 'src/utils.py',
'line': 23,
'body': (
':bulb: Consider using `pathlib.Path` instead of `os.path.join` '
'for better cross-platform compatibility.'
)
}
]
pr.create_review(
body='Security and style review complete. See inline comments.',
event='REQUEST_CHANGES',
comments=inline_comments
)
print(f'Review submitted with {len(inline_comments)} inline comments')LLM-pohjainen koodianalyysi
Syöttäkää tiedoston diff LLM:lle tietoturva- ja laatuarviointiin tarkoitetun kehotteen kanssa. Pyytäkää jäsennelty JSON-vastaus, jossa luetellaan ongelmat sekä tiedostopolku, rivinumero, vakavuus ja kuvaus. Rakentakaa vastausten avulla rivikohtaisten kommenttien luettelo.
import anthropic
import json
import os
client = anthropic.Anthropic(api_key=os.environ['ANTHROPIC_API_KEY'])
def analyze_file_with_llm(filename, patch):
prompt = (
f'Review this Python code diff for issues.\n'
f'File: {filename}\n\n'
f'Return JSON array: [{{"line": N, "severity": "error|warning|info", '
f'"message": "..."}}]\n'
f'Focus on: security vulnerabilities, bugs, and anti-patterns.\n'
f'Diff:\n{patch[:3000]}'
)
response = client.messages.create(
model='claude-opus-4-5',
max_tokens=1000,
messages=[{'role': 'user', 'content': prompt}]
)
try:
issues = json.loads(response.content[0].text)
return [
{'path': filename, 'line': i.get('line', 1),
'body': f'[{i.get("severity", "info").upper()}] {i.get("message", "")}'}
for i in issues if isinstance(i, dict)
]
except json.JSONDecodeError:
return []PR-katselmointiagentin täysi työnkulku
Yhdistäkää kaikki osat täydelliseksi PR-katselmoinnin työnkuluksi: hakekaa tiedostot, suodattakaa katselmoitavat tiedostot, analysoikaa kukin LLM:n avulla, kerätkää kommentit ja lähettäkää katselmus yhdellä API-kutsulla. Rajoittakaa käsittely kymmeneen ensimmäiseen muuttuneeseen tiedostoon LLM-kustannusten hallitsemiseksi.
def run_pr_review(g, repo_name, pr_number):
repo = g.get_repo(repo_name)
pr = repo.get_pull(pr_number)
print(f'Reviewing PR #{pr_number}: {pr.title}')
# Get changed files (limit to 10)
files = [
f for f in list(pr.get_files())[:10]
if f.filename.endswith(('.py', '.js', '.ts'))
and f.patch
]
all_comments = []
for file in files:
print(f' Analyzing: {file.filename}')
comments = analyze_file_with_llm(file.filename, file.patch)
all_comments.extend(comments)
# Determine review event based on findings
critical = [c for c in all_comments if '[ERROR]' in c['body']]
event = 'REQUEST_CHANGES' if critical else 'COMMENT'
summary = (
f'Analyzed {len(files)} files. '
f'Found {len(all_comments)} issues '
f'({len(critical)} critical).'
)
pr.create_review(body=summary, event=event, comments=all_comments[:25])
print(f'Review submitted: {event} with {len(all_comments)} comments')Olemassa olevien agenttikatselmusten tarkistaminen
Tarkistakaa ennen katselmuksen lähettämistä, onko agenttibottinne jo katselmoinut tämän PR:n nykyisessä tilassa. Välttäkää katselmusten kaksoiskappaleet tarkistamalla olemassa olevien katselmusten luettelosta bottinne käyttäjänimi.
def should_review_pr(pr, bot_username):
reviews = list(pr.get_reviews())
# Check if bot already reviewed this PR
bot_reviews = [
r for r in reviews
if r.user and r.user.login == bot_username
]
if not bot_reviews:
return True # No previous review, proceed
# Check if new commits were pushed since last review
last_review_time = max(r.submitted_at for r in bot_reviews)
commits = list(pr.get_commits())
latest_commit_time = commits[-1].commit.author.date if commits else None
if latest_commit_time and latest_commit_time > last_review_time:
print(f'New commits since last review, re-reviewing...')
return True
print(f'PR #{pr.number} already reviewed by {bot_username}')
return False
# --- demo: minimal stand-ins for PyGithub-style PR/review/commit objects ---
import datetime
from types import SimpleNamespace
class _FakeCommit:
def __init__(self, date):
self.commit = SimpleNamespace(author=SimpleNamespace(date=date))
class _FakeReview:
def __init__(self, login, submitted_at):
self.user = SimpleNamespace(login=login)
self.submitted_at = submitted_at
class _FakePR:
def __init__(self, number, reviews, commits):
self.number = number
self._reviews = reviews
self._commits = commits
def get_reviews(self):
return self._reviews
def get_commits(self):
return self._commits
pr_never_reviewed = _FakePR(101, [], [_FakeCommit(datetime.datetime(2026, 8, 1))])
print('PR #101 needs review:', should_review_pr(pr_never_reviewed, 'coddy-bot'))
review_time = datetime.datetime(2026, 8, 1, 10, 0)
pr_up_to_date = _FakePR(102, [_FakeReview('coddy-bot', review_time)],
[_FakeCommit(review_time - datetime.timedelta(hours=1))])
print('PR #102 needs review:', should_review_pr(pr_up_to_date, 'coddy-bot'))
PR-katselmoinnin tietoturvatarkistusmalli
Yleinen automaattisen katselmoinnin malli on etsiä lisätyiltä riveiltä tietoturvan vastaisia käytäntöjä: kovakoodattuja salaisuuksia, vanhentuneiden funktioiden käyttöä, SQL-merkkijonojen yhdistämistä tai eval()-kutsuja. Tarkista jokainen lisätty rivi mallijoukkoa vasten ja merkitse löydökset rivinsisäisiksi kommenteiksi.
import re
SECURITY_PATTERNS = [
(r'eval\s*\(', 'Dangerous eval() call — code injection risk'),
(r'os\.system\s*\(', 'os.system() is unsafe; use subprocess with a list'),
(r'pickle\.loads?\s*\(', 'pickle.load is unsafe with untrusted data'),
(r'execute\(.*%\s*', 'Possible SQL injection — use parameterized queries'),
(r'md5\s*\(|hashlib\.md5', 'MD5 is cryptographically broken for passwords; use bcrypt'),
]
def scan_patch_for_security(filename, patch):
if not patch:
return []
comments = []
new_line = 0
for line in patch.split('\n'):
if line.startswith('@@'):
import re as _re
m = _re.search(r'\+([0-9]+)', line)
if m:
new_line = int(m.group(1))
elif line.startswith('+') and not line.startswith('+++'):
content = line[1:]
for pattern, message in SECURITY_PATTERNS:
if re.search(pattern, content):
comments.append({'path': filename, 'line': new_line,
'body': f':rotating_light: **Security:** {message}'})
new_line += 1
elif not line.startswith('-'):
new_line += 1
return comments
# --- demo ---
patch = (
'@@ -1,2 +1,3 @@\n'
' def run(cmd):\n'
'+ os.system(cmd)\n'
'+ hashlib.md5(cmd.encode())\n'
)
for c in scan_patch_for_security('run.py', patch):
print(c)
Pikatarkistus: katselmointitapahtumat
Testaa ymmärrystäsi PR-katselmoinnin vaihtoehdoista.
Automaattisen PR-katselmoinnin yhteenveto
Agenttisi osaa nyt katselmoida pull requesteja ohjelmallisesti:
- repo.get_pull(number) — hae PR-objekti metatietoineen
- pr.get_files() — luettele muuttuneet tiedostot, joiden tiedot ovat
filename,patchjaadditions - pr.get_commits() — tarkastele commit-viestejä ja tilastoja
- pr.create_review(body, event, comments) — lähetä katselmointi valinnaisine rivinsisäisine kommentteineen
- Rivinsisäisen kommentin muoto:
{'path': filename, 'line': N, 'body': markdown} - Käytä
'COMMENT'-arvoa havainnoille ja'REQUEST_CHANGES'-arvoa estäville ongelmille - Tarkista aiemmat katselmoinnit ennen uutta katselmointia päällekkäisyyksien välttämiseksi
- Rajoita tiedostojen analyysi 10 tiedostoon ja rivinsisäiset kommentit 25 kommenttiin API-rajojen puitteissa pysymiseksi
Opi Tekoälyagentit tekoälytuutorin avulla — ilmaiseksi
Kirjoita ja suorita oikeaa koodia selaimessa, saa välitöntä apua tekoälytuutorilta ympäri vuorokauden ja jatka siitä, mihin jäit, verkossa tai sovelluksessa.
- Kurssit
- 60
- Oppitunnit
- 239
Usein kysytyt kysymykset
Onko oppitunti ”Automaattiset PR-katselmointikommentit” ilmainen?
Kyllä – oppitunnin ”Automaattiset PR-katselmointikommentit” koko tekstin voi lukea täällä verkossa ilmaiseksi. Jos haluat harjoitella interaktiivisesti sisäänrakennetulla koodieditorilla ja ympäri vuorokauden käytettävissä olevan tekoälytuutorin avulla sekä avata koko Tekoälyagentit-kurssin, päivitä CoddyKit PROhon. Tekoälyagentit-kurssilla on yhteensä 4 oppituntia.
Mitä opin oppitunnilla ”Automaattiset PR-katselmointikommentit”?
PR-diffien lukeminen ja rivinsisäisten katselmointikommenttien julkaiseminen API:n kautta Harjoittelet Tekoälyagentit-aihetta koodilla, jonka suoritat suoraan selaimessa. Ympäri vuorokauden käytettävissä oleva tekoälytuutori vastaa kysymyksiisi oppitunnin aikana.
Tarvitsenko kokemusta aloittaakseni Tekoälyagentit-opiskelun?
Aiempi kokemus ei ole tarpeen. CoddyKitin Tekoälyagentit-oppimispolku sopii vasta-alkajista edistyneisiin, joten voit aloittaa tästä tai alusta ja edetä omaan tahtiisi. Tämä on oppitunti 3/4.
Kuinka kauan ”Automaattiset PR-katselmointikommentit”-oppitunnin suorittaminen kestää?
Useimmat CoddyKitin oppitunnit kestävät noin 5–10 minuuttia. Jokainen oppitunti on lyhyt ja interaktiivinen, joten edistyt tasaisesti ja voit jatkaa siitä, mihin jäit – sekä verkossa että sovelluksessa.
Voinko kirjoittaa ja suorittaa koodia tällä Tekoälyagentit-oppitunnilla?
Kyllä. Jokainen Tekoälyagentit-oppitunti sisältää sisäänrakennetun koodieditorin, joten voit kirjoittaa ja suorittaa oikeaa koodia suoraan selaimessa ja saada välitöntä palautetta tekoälyltä – paikallista asennusta ei tarvita.
Kaikki tämän kurssin oppitunnit
- GitHub REST API:n yleiskatsaus
- Issuejen luettelointi ja hallinta
- Automaattiset PR-katselmointikommentit
- Commit-historian ja diffien analyysi