مراجعة أكواد بنمط pull request على GitHub لوكلاء البرمجة
هيّئ في Potato مراجعة أكواد بنمط pull request على GitHub، مع تعليقات مضمّنة على الـ diff، وتقييمات جودة على مستوى الملف، وأحكام قبول أو رفض لمخرجات وكلاء البرمجة.
لماذا يهم التعليق التوضيحي لمراجعة الأكواد
تختزل معظم معايير قياس وكلاء البرمجة التقييمَ في سؤال ثنائي: هل نجحت الاختبارات أم لا؟ يذكر SWE-bench نسبة المسائل المحلولة، ويذكر HumanEval نسبة pass@k. هذه المقاييس مفيدة للوحات الصدارة، لكنها لا تفيد في فهم جودة الكود.
يمكن للوكيل أن ينجح في كل اختبار وأن يكتب مع ذلك كوداً لا يرغب أحد في صيانته: كود فيه ثغرة أمنية، أو مسار بطيء، أو أسلوب يناقض بقية قاعدة الكود. المراجع البشري سيطلب تعديلات على ذلك الـ pull request حتى لو كانت الاختبارات خضراء. وإذا أردت وكلاء يكتبون كوداً يدمجه الناس فعلاً، فعليك مراجعة الكود لا تشغيل الاختبارات وحدها.
يأتي مخطط code_review في Potato بتجربة مراجعة الـ pull request من GitHub إلى داخل أداة التعليق التوضيحي. يرى المعلّقون diff موحّداً بإبراز لبنية اللغة، وينقرون على أسطر الـ diff لإضافة تعليقات مضمّنة، ويقيّمون الملفات على بعدين من أبعاد الجودة، ويصدرون حكماً بالقبول أو بطلب التعديل أو بالتعليق فقط، تماماً كمراجعة pull request حقيقي. ولمرجع المخطط الكامل، انظر وثائق التعليق التوضيحي لوكلاء البرمجة ودليل تقييم الوكلاء.
إليك واجهة مراجعة الأكواد في Potato، وفيها التعليقات المضمّنة على الـ diff وتقييمات الملفات:
واجهة مراجعة الأكواد في Potato مع تعليقات مضمّنة على الـ diff وتقييمات جودة على مستوى الملف
نظرة عامة على مخطط مراجعة الأكواد
يتألف مخطط code_review من ثلاث طبقات:
- تعليقات مضمّنة على الـ diff: ينقر المعلّقون على أي سطر في الـ diff لإرفاق تعليق مصنَّف (خلل، أسلوب، أداء، أمان، منطق، اقتراح، سؤال)
- تقييمات على مستوى الملف: يحصل كل ملف معدَّل على تقييمين مستقلين للصحة (1-5) ولجودة الكود (1-5)
- الحكم الكلي: يصدر المعلّق حكماً نهائياً: قبول، أو طلب تعديلات، أو تعليق فقط
يحاكي هذا مراجعة أكواد حقيقية، فيبدو مألوفاً للمطورين، والمخرجات المنظمة التي ينتجها تنطبق مباشرةً على تدريب نماذج مراجعة الأكواد.
CodingTraceDisplay: كيف تُعرض الـ diffs
يعرض مكوّن CodingTraceDisplay تتبعات وكلاء البرمجة بوصفها سلسلة من نداءات الأدوات ومخرجاتها، مع معالجة خاصة لتحريرات الملفات. فحين يحرر الوكيل ملفاً، يعرض المكوّن diff موحّداً يتضمن:
- أسطر حمراء: أسطر محذوفة (تسبقها
-) - أسطر خضراء: أسطر مضافة (تسبقها
+) - أسطر رمادية: أسطر سياق (غير متغيرة)
- أرقام الأسطر: أرقام الأسطر القديمة والجديدة معاً في الهامش
- إبراز بنية اللغة: إبراز يراعي اللغة بحسب امتداد الملف
- النقر للتعليق: النقر على أي سطر يفتح نموذج تعليق مربوطاً بذلك السطر
يُحسب الـ diff تلقائياً من عمليات التحرير التي أجراها الوكيل. وإذا استخدم الوكيل أداة بحث واستبدال، يعيد Potato بناء الحالتين قبل التحرير وبعده ويولّد الـ diff الموحّد.
أما الوكلاء الذين يحررون ملفات متعددة في تتبع واحد (وهذا شائع في إصلاح الأخطاء الواقعية)، فيحصل كل ملف على قسم diff قابل للطي، على غرار تبويب Files changed في pull request على GitHub.
يعرض CodingTraceDisplay تغييرات الكود مع إبراز سليم لبنية اللغة:
CodingTraceDisplay يعرض diffs موحّدة مع إبراز لبنية اللغة وشريط جانبي لشجرة الملفات
فئات التعليقات
حين ينقر المعلّق على سطر في الـ diff ليضيف تعليقاً، يختار فئة:
| الفئة | اللون | الوصف | مثال |
|---|---|---|---|
bug | أحمر | في الكود خطأ وظيفي | «سيرمي هذا NullPointerException إذا كانت user تساوي None» |
style | أزرق | مشكلة في أسلوب الكود أو في الأعراف المتبعة | «المشروع يستخدم snake_case للدوال، لا camelCase» |
performance | برتقالي | كود غير كفء | «هذا يستعلم من قاعدة البيانات داخل حلقة؛ استخدم استعلاماً دفعياً» |
security | بنفسجي | ثغرة أمنية | «يُمرَّر إدخال المستخدم مباشرةً إلى استعلام SQL من دون تنقية» |
logic | أصفر | مشكلة منطقية قد لا تسبب فشلاً فورياً | «ينبغي أن يكون هذا الشرط >= لا >، خطأ بمقدار واحد عند الحد» |
suggestion | أخضر | اقتراح تحسين، لا خطأ | «فكّر في استخدام context manager هنا لإدارة أنظف للموارد» |
question | رمادي | يلزم توضيح | «لماذا أُضيف هذا الاستيراد؟ لا يبدو أنه مستخدَم» |
لكل تعليق أيضاً متن نصي حر يشرح فيه المعلّق المشكلة بالتفصيل، تماماً ككتابة تعليق حقيقي على pull request.
التقييمات على مستوى الملف
بعد مراجعة الـ diff لكل ملف، يقيّمه المعلّق على بعدين:
الصحة (1-5):
- 1: لا يعمل، ويُدخل أخطاء جديدة
- 2: يعمل جزئياً، وفيه مشكلات كبيرة
- 3: يعمل في المسار المثالي لكنه يغفل الحالات الحدّية
- 4: يعمل بشكل صحيح مع مشكلات طفيفة
- 5: صحيح تماماً، ويعالج الحالات الحدّية كما ينبغي
جودة الكود (1-5):
- 1: غير قابل للصيانة، ولا بنية له
- 2: جودة رديئة، ومشكلات كبيرة في الأسلوب أو التصميم
- 3: مقبول، ويتبع الأعراف الأساسية
- 4: جودة جيدة، نظيف ومقروء
- 5: ممتاز، اصطلاحي، وموثّق جيداً
خيارات الحكم
بعد مراجعة كل الملفات، يختار المعلّق واحداً من ثلاثة أحكام:
- قبول: الكود جاهز للدمج كما هو، أو بتعديلات طفيفة فقط
- طلب تعديلات: يحتاج الكود إلى مراجعات كبيرة قبل الدمج
- تعليق فقط: تقديم ملاحظات من دون اتخاذ قرار بشأن الدمج
ينطبق هذا مباشرةً على حالات مراجعة الـ pull request الثلاث في GitHub.
الإعداد خطوة خطوة
الخطوة 1: تحويل تتبعات وكلاء البرمجة
تأتي تتبعات وكلاء البرمجة بصيغ كثيرة. إليك أمثلة على ثلاثة وكلاء شائعين.
من Claude Code (تصدير JSON):
python -m potato.trace_converter \
--input claude_code_sessions/ \
--output data/code_traces.jsonl \
--input-format claude_codeمن Aider (سجل المحادثة):
python -m potato.trace_converter \
--input aider_logs/ \
--output data/code_traces.jsonl \
--input-format aiderمن SWE-Agent (مجلد المسارات):
python -m potato.trace_converter \
--input swe_agent_trajectories/ \
--output data/code_traces.jsonl \
--input-format swe_agent_trajectoryينتج المحوّل صيغة JSONL موحّدة. ويحتوي كل سطر على تتبع فيه المهمة، وخطوات الوكيل، وفروق الملفات:
{
"trace_id": "swe_bench_123",
"task": "Fix the TypeError in django/db/models/query.py when calling .values() on an empty QuerySet",
"repo": "django/django",
"base_commit": "a1b2c3d",
"steps": [
{
"type": "thought",
"content": "Let me look at the QuerySet.values() method to understand the TypeError."
},
{
"type": "action",
"tool": "search",
"input": "def values",
"file": "django/db/models/query.py"
},
{
"type": "observation",
"content": "Found at line 234: def values(self, *fields):\n ..."
},
{
"type": "action",
"tool": "edit",
"file": "django/db/models/query.py",
"old_content": " def values(self, *fields):\n clone = self._clone()\n clone.query.set_values(fields)",
"new_content": " def values(self, *fields):\n clone = self._clone()\n if not fields and not self.query.values_select:\n return clone\n clone.query.set_values(fields)"
},
{
"type": "action",
"tool": "edit",
"file": "tests/queries/test_values.py",
"old_content": "class ValuesTests(TestCase):",
"new_content": "class ValuesTests(TestCase):\n def test_empty_queryset_values(self):\n \"\"\"Calling .values() on empty QuerySet should not raise TypeError.\"\"\"\n qs = Author.objects.none()\n result = list(qs.values())\n self.assertEqual(result, [])"
}
],
"files_changed": [
{
"path": "django/db/models/query.py",
"diff": "--- a/django/db/models/query.py\n+++ b/django/db/models/query.py\n@@ -234,6 +234,8 @@\n def values(self, *fields):\n clone = self._clone()\n+ if not fields and not self.query.values_select:\n+ return clone\n clone.query.set_values(fields)"
},
{
"path": "tests/queries/test_values.py",
"diff": "--- a/tests/queries/test_values.py\n+++ b/tests/queries/test_values.py\n@@ -1,4 +1,10 @@\n class ValuesTests(TestCase):\n+ def test_empty_queryset_values(self):\n+ \"\"\"Calling .values() on empty QuerySet should not raise TypeError.\"\"\"\n+ qs = Author.objects.none()\n+ result = list(qs.values())\n+ self.assertEqual(result, [])"
}
]
}الخطوة 2: تهيئة مخطط مراجعة الأكواد
أنشئ ملف config.yaml الخاص بك:
annotation_task_name: "Coding Agent Code Review"
data_files:
- "data/code_traces.jsonl"
item_properties:
id_key: "trace_id"
text_key: "task"
# Display coding agent traces with diff rendering
display:
type: "coding_trace"
trace_key: "steps"
diff_key: "files_changed"
syntax_highlighting: true
show_line_numbers: true
collapse_large_diffs: true
max_uncollapsed_lines: 200
annotation_schemes:
- annotation_type: "code_review"
# Inline comment categories
# File-level ratings
# Overall verdict
# Annotator settings
annotator_config:
allow_back_navigation: true
# Output settings
output:
path: "output/"
format: "jsonl"الخطوة 3: تشغيل خادم التعليق التوضيحي
potato start config.yaml -p 8000انتقل إلى http://localhost:8000. سترى أول تتبع لوكيل برمجة، ومعه وصف المهمة، وخطوات تفكير الوكيل، وفروق الملفات معروضة مع إبراز لبنية اللغة.
الخطوة 4: سير عمل المعلّق
إليك مسار المراجعة المعتاد:
- اقرأ المهمة: افهم ما طُلب من الوكيل فعله (مثلاً «أصلح TypeError في django/db/models/query.py»)
- راجع التتبع: مرّ على خطوات تفكير الوكيل لتفهم مقاربته
- راجع diff كل ملف:
- اقرأ الـ diff مع إبراز بنية اللغة
- انقر على أي سطر لإضافة تعليق مضمّن
- اختر فئة للتعليق (خلل، أسلوب، أداء، إلخ)
- اكتب متن التعليق شارحاً المشكلة
- قيّم الملف على الصحة (1-5) وعلى جودة الكود (1-5)
- أصدر الحكم: اختر القبول أو طلب التعديلات أو التعليق فقط
- أرسل: انقر Submit أو اضغط Ctrl+Enter
تسرّع اختصارات لوحة المفاتيح سير العمل:
| الاختصار | الإجراء |
|---|---|
j / k | التنقل بين الملفات |
c | فتح تعليق على السطر المحدد |
1-5 | ضبط التقييم للبعد الحالي |
a | ضبط الحكم على القبول |
r | ضبط الحكم على طلب التعديلات |
Ctrl+Enter | إرسال المراجعة |
صيغة التصدير
تنتج كل مراجعة مُرسَلة كائن JSON منظماً:
{
"trace_id": "swe_bench_123",
"annotator": "reviewer_01",
"timestamp": "2026-03-22T14:32:11Z",
"review": {
"inline_comments": [
{
"file": "django/db/models/query.py",
"line": 236,
"side": "right",
"category": "logic",
"body": "This early return skips set_values entirely, but if fields are provided later via .values('name'), the previous empty .values() call will have returned a clone that never went through set_values. Consider checking if this clone is still valid downstream."
},
{
"file": "tests/queries/test_values.py",
"line": 5,
"side": "right",
"category": "suggestion",
"body": "Consider adding a test case for .values() followed by .values('name') to verify the chaining behavior after your fix."
}
],
"file_ratings": [
{
"file": "django/db/models/query.py",
"correctness": 3,
"code_quality": 4
},
{
"file": "tests/queries/test_values.py",
"correctness": 4,
"code_quality": 4
}
],
"verdict": "request_changes"
}
}هذه الصيغة المنظمة قابلة للاستخدام مباشرةً في تدريب نماذج مراجعة الأكواد وفي التحليل التجميعي.
التحليل: العمل ببيانات المراجعة
تحميل المراجعات
import json
import pandas as pd
from pathlib import Path
reviews = []
for f in Path("output/").glob("*.jsonl"):
with open(f) as fh:
for line in fh:
reviews.append(json.loads(line))
print(f"Loaded {len(reviews)} code reviews")توزيع فئات التعليقات
from collections import Counter
all_comments = []
for rev in reviews:
for comment in rev["review"]["inline_comments"]:
all_comments.append(comment)
category_counts = Counter(c["category"] for c in all_comments)
print("Comment categories:")
for cat, count in category_counts.most_common():
print(f" {cat}: {count}")متوسط تقييمات الملفات
ratings = []
for rev in reviews:
for fr in rev["review"]["file_ratings"]:
ratings.append(fr)
ratings_df = pd.DataFrame(ratings)
print("Average ratings by file:")
print(
ratings_df.groupby("file")[["correctness", "code_quality"]]
.mean()
.round(2)
.to_string()
)توزيع الأحكام
verdict_counts = Counter(rev["review"]["verdict"] for rev in reviews)
total = sum(verdict_counts.values())
print("Verdict distribution:")
for verdict, count in verdict_counts.most_common():
print(f" {verdict}: {count} ({count/total*100:.1f}%)")معدل الأخطاء لكل وكيل
إذا تضمنت تتبعاتك حقل agent، يمكنك مقارنة معدلات الأخطاء بين الوكلاء:
agent_bugs = {}
for rev in reviews:
agent = rev.get("agent", "unknown")
bug_count = sum(
1 for c in rev["review"]["inline_comments"]
if c["category"] == "bug"
)
if agent not in agent_bugs:
agent_bugs[agent] = []
agent_bugs[agent].append(bug_count)
print("Average bugs per review by agent:")
for agent, bugs in sorted(agent_bugs.items()):
print(f" {agent}: {sum(bugs)/len(bugs):.2f} (n={len(bugs)})")حالات الاستخدام
تدريب نماذج مراجعة الأكواد
التعليقات المضمّنة المنظمة، وتقييمات الملفات، والأحكام التي ينتجها التعليق التوضيحي لمراجعة الأكواد في Potato هي بيانات تدريب مناسبة لنماذج مراجعة الأكواد الآلية. فكل مراجعة توفر:
- ملاحظات موطّنة مرتبطة بأسطر بعينها في الـ diff
- مشكلات مصنّفة (خلل مقابل أسلوب مقابل أداء)
- إشارات جودة على مستويات دقة متعددة (السطر، الملف، المجمل)
هذه هي صيغة البيانات التي تستخدمها أدوات مثل CodeRabbit والمراجع الآلي في Graphite، لكنها هنا مولَّدة من خبراء بشر لا مقطّرة من نموذج لغوي.
تقييم وكلاء البرمجة على SWE-bench
يخبرك SWE-bench إن كان الوكيل قد حلّ المسألة (نجاح الاختبارات)، لا إن كان الكود قابلاً للدمج. وبتشغيل التعليق التوضيحي لمراجعة الأكواد على حلول SWE-bench، يمكنك تمييز الوكلاء الذين يحلون المسائل بكود نظيف من الوكلاء الذين يحلونها بحيل. ينتج هذا لوحة صدارة أدق ترتبط بتجربة المطور في الواقع.
بناء مجموعات بيانات لجودة الكود
اجمع بيانات مراجعة الأكواد عبر تتبعات كثيرة لبناء مجموعات بيانات عن مشكلات جودة الكود الشائعة في الكود الذي يولّده الذكاء الاصطناعي. ويمكن استخدام هذه المجموعات في:
- الضبط الدقيق لنماذج توليد الكود لتجنب الأخطاء الشائعة
- بناء أدوات فحص خاصة بأنماط الكود المولَّد آلياً
- تدريب مصنّفات تشير إلى المشكلات المرجّحة في مخرجات الوكيل قبل المراجعة البشرية
الخلاصة
يضع مخطط code_review في Potato سير عمل مراجعة الـ pull request من GitHub داخل تقييم الوكلاء. والتعليقات المضمّنة وتقييمات الملفات والأحكام التي تجمعها تعطيك بيانات منظمة عن جودة الكود، وهي أكثر بكثير مما تخبرك به نتيجة اختبار ناجح أو فاشل. وهذه البيانات هي ما تحتاج إليه سواء كنت تدرّب نموذج مراجعة أكواد، أو تفصل حلول SWE-bench النظيفة عن الملتوية، أو تضع فقط خط أساس للجودة لوكيلك.