AI agents की साथ-साथ तुलना: Binary, Scale, और Multi-Dimension modes
Potato में तीन modes के साथ pairwise agent तुलना सेट करें: binary preference, continuous scale, और आवश्यक justification वाला प्रति-dimension multi-criteria निर्णय।
Agent मूल्यांकन के लिए pairwise तुलना क्यों
किसी coding agent trace को 1 से 10 के पैमाने पर rate करने को कहने से आपको शोर भरा data मिलता है, क्योंकि हर व्यक्ति उस scale को अलग तरह से अंशांकित करता है। एक annotator का 7 दूसरे का 5 होता है। Pairwise तुलना इस दिक़्क़त से बच निकलती है। Traces को अलग-अलग rate करने के बजाय annotators दो को साथ-साथ देखते हैं और बताते हैं कि कौन सी बेहतर है। ऐसा आमने-सामने का निर्णय लेना आसान होता है, लोगों के बीच ज़्यादा सुसंगत रहता है, और संयोग से ठीक वही है जो Direct Preference Optimization (DPO) और Reinforcement Learning from Human Feedback (RLHF) के लिए चाहिए।
यह वही तरीक़ा है जो language model alignment के लिए reward models train करने में इस्तेमाल होता है, और coding agents पर साफ़-साफ़ लागू हो जाता है: agent trajectories के जोड़ों के बीच मानवीय preferences जुटाएँ, उन पर reward model train करें, फिर उस model से agent training को दिशा दें या inference के समय N उम्मीदवारों में से सबसे अच्छा चुनें।
Potato में pairwise तुलना के तीन modes हैं, हर एक अलग मूल्यांकन ज़रूरत और data बजट के लिए उपयुक्त।
Interface दो traces को साथ-साथ रखता है:
Annotators compare two agent traces and select which approach was better
Mode 1: binary preference
यह सबसे सरल और सबसे तेज़ mode है। Annotator दो traces साथ-साथ देखता है और बेहतर वाली पर click करता है। एक वैकल्पिक tie बटन उन मामलों को संभालता है जहाँ दोनों बराबर अच्छी या बराबर ख़राब हैं।
Binary mode कब उपयोग करें
Binary mode तब चुनें जब आपको जल्दी में बहुत सारा preference data चाहिए। यह बुनियादी reward models train करने, agent win rates निकालने, और Elo leaderboards बनाने के लिए अच्छा बैठता है। नुक़सान यह है कि बारीक़ी हाथ से निकल जाती है। आपको पता चलता है कि कौन सी trace जीती, पर यह नहीं कि कितने अंतर से या किन मोर्चों पर।
कॉन्फ़िगरेशन
annotation_task_name: "Agent Comparison - Binary"
task_dir: "."
port: 8000
data_files:
- "data/paired_traces.jsonl"
item_properties:
id_key: "id"
text_key: "task"
instance_display:
fields:
- key: "task"
type: text
label: "Task"
annotation_schemes:
- annotation_type: pairwise
name: preference
description: "Which agent produced a better solution?"
mode: binary
items_key: "responses"
show_labels: true
labels:
- "Agent A"
- "Agent B"
allow_tie: true
tie_label: "Tie (equally good or equally bad)"
randomize_order: true
sequential_key_binding: true
label_requirement:
required: true
- annotation_type: radio
name: confidence
description: "How confident are you in this judgment?"
labels:
- "Very confident"
- "Somewhat confident"
- "Not confident"
output_annotation_dir: "annotation_output/"
export_annotation_format: jsonl
user_config:
allow_all_users: trueAnnotation का workflow
Annotator को एक बँटी हुई स्क्रीन मिलती है। बायीं ओर Trace A पूरे CodingTraceDisplay के साथ render होती है: diffs, terminal blocks, file reads, thinking। दायीं ओर उसी task के लिए Trace B। हर तरफ़ अपने आप scroll होती है।
Task का विवरण दोनों traces के ऊपर रहता है ताकि annotator को पता रहे कि दोनों agents क्या करने की कोशिश कर रहे थे।
उनके नीचे तीन बटन होते हैं: "Agent A is better", "Agent B is better", और "Tie"। randomize_order चालू होने पर हर instance में यह बदलता रहता है कि कौन सा agent A है और कौन सा B, इसलिए annotators बायीं या दायीं तरफ़ की आदत में नहीं पड़ सकते।
ज़्यादा बारीक़ मूल्यांकन के लिए interface कई dimensions का भी समर्थन करता है:
Binary preference, continuous scale, and multi-dimension modes are available
Mode 2: continuous scale
Scale mode annotator को यह बताने देता है कि एक trace कितनी बेहतर है, सिर्फ़ यह नहीं कि कौन सी जीती। एक click के बजाय वे एक slider खींचते हैं जो बायीं ओर "A much better" से दायीं ओर "B much better" तक जाता है, बीच में "Equal" के साथ।
Scale mode कब उपयोग करें
Scale mode तब उपयोग करें जब preference की दिशा के साथ-साथ उसकी तीव्रता भी मायने रखती हो। Slider का सिरे के पास होना साफ़ गुणवत्ता अंतर बताता है; केंद्र के पास होना बताता है कि दोनों क़रीब थे। DPO और उस जैसी pipelines उदाहरणों को उस तीव्रता के हिसाब से weight दे सकती हैं, और साफ़-साफ़ मामलों पर ज़्यादा टिक सकती हैं।
कॉन्फ़िगरेशन
annotation_schemes:
- annotation_type: pairwise
name: preference_scale
description: "Which agent produced a better solution, and by how much?"
mode: scale
items_key: "responses"
labels:
- "Agent A"
- "Agent B"
scale:
min: -3
max: 3
step: 1
default: 0
labels:
min: "A is much better"
max: "B is much better"
center: "Equal"
label_requirement:
required: true5-बिंदु scale का उपयोग
तेज़ annotation और थोड़ी कम बारीक़ी के लिए 5-बिंदु scale पर आ जाएँ:
annotation_schemes:
- annotation_type: pairwise
name: multi_dim_comparison
description: "Compare the two solutions along each dimension"
mode: multi_dimension
items_key: "responses"
labels:
- "Agent A"
- "Agent B"
dimensions:
- name: correctness
description: "Which solution actually fixes the stated problem?"
allow_tie: true
- name: efficiency
description: "Which takes fewer unnecessary steps, file reads, or redundant edits?"
allow_tie: true
- name: code_quality
description: "Which is more readable, better named, and closer to existing patterns?"
allow_tie: true
- name: communication
description: "Which explains its reasoning more clearly and identifies the root cause?"
allow_tie: true
- name: robustness
description: "Which handles edge cases and verifies its changes?"
allow_tie: true
justification:
required: true
reason_categories:
- "More correct"
- "More efficient"
- "Better written"
- "Better explained"
min_rationale_chars: 20
rationale_placeholder: "Which agent was better here, and why?"Mode 3: multi-dimension तुलना
यह सबसे विस्तृत mode है। एक समग्र preference के बजाय annotator हर trace को कई स्वतंत्र dimensions पर परखता है। हर dimension को अपना A/B/Tie निर्णय मिलता है, और हर निर्णय के साथ लिखित justification ज़रूरी है।
Multi-dimension mode कब उपयोग करें
इसका उपयोग तब करें जब आप जानना चाहते हैं कि कौन सा agent जीता ही नहीं, बल्कि क्यों जीता। एक trace में code सही हो सकता है पर efficiency बेहद ख़राब; दूसरी efficient हो सकती है पर किसी edge case को अनदेखा कर दे। इससे निकलने वाला प्रति-dimension data dimension-विशिष्ट reward models train कर सकता है या agent बनाने वालों तक विस्तृत टिप्पणियाँ पहुँचा सकता है।
कॉन्फ़िगरेशन
attention_checks:
enabled: true
items_file: "data/attention_checks.json"
frequency: 20
min_response_time: 10.0
failure_handling:
warn_threshold: 2
block_threshold: 5Paired trace data तैयार करना
तीनों modes input के रूप में paired traces लेते हैं। JSONL file की हर पंक्ति में दो traces होती हैं जिन्होंने एक ही task का प्रयास किया।
Data format
{
"id": "pair_001",
"task_description": "Fix the IndexError in process_batch() when the input list is empty",
"repo": "myorg/myproject",
"trace_a": {
"agent": "claude_code",
"model": "claude-sonnet-4-20250514",
"structured_turns": [
{
"step_idx": 0,
"type": "file_read",
"path": "src/batch.py",
"content": "def process_batch(items):\n result = items[0]\n ...",
"start_line": 10,
"end_line": 25
},
{
"step_idx": 1,
"type": "file_edit",
"path": "src/batch.py",
"diff": "--- a/src/batch.py\n+++ b/src/batch.py\n@@ -10,3 +10,5 @@\n def process_batch(items):\n+ if not items:\n+ return []\n result = items[0]\n"
},
{
"step_idx": 2,
"type": "bash_command",
"command": "python -m pytest tests/test_batch.py -v",
"output": "PASSED",
"exit_code": 0
}
]
},
"trace_b": {
"agent": "swe_agent",
"model": "gpt-4o",
"structured_turns": [
{
"step_idx": 0,
"type": "bash_command",
"command": "find . -name '*.py' | xargs grep 'process_batch'",
"output": "src/batch.py:def process_batch(items):\ntests/test_batch.py: process_batch([])",
"exit_code": 0
},
{
"step_idx": 1,
"type": "file_read",
"path": "src/batch.py",
"content": "def process_batch(items):\n result = items[0]\n ...",
"start_line": 1,
"end_line": 50
},
{
"step_idx": 2,
"type": "file_edit",
"path": "src/batch.py",
"diff": "--- a/src/batch.py\n+++ b/src/batch.py\n@@ -10,3 +10,6 @@\n def process_batch(items):\n+ if items is None or len(items) == 0:\n+ logger.warning('Empty input to process_batch')\n+ return []\n result = items[0]\n"
},
{
"step_idx": 3,
"type": "bash_command",
"command": "python -m pytest tests/ -v",
"output": "PASSED (12 tests)",
"exit_code": 0
}
]
}
}अलग-अलग traces से जोड़े बनाना
अगर आपके पास अलग-अलग traces हैं जिन्होंने वही tasks किए हैं, तो pairing utility उन्हें जोड़ देगी:
# Generate all possible pairs for each task
potato pair-traces \
--input ./data/individual_traces.jsonl \
--output ./data/paired_traces.jsonl \
--pair_by "task_id" \
--strategy "all_pairs"
# Or sample a fixed number of pairs per task
potato pair-traces \
--input ./data/individual_traces.jsonl \
--output ./data/paired_traces.jsonl \
--pair_by "task_id" \
--strategy "sample" \
--pairs_per_task 3तुलना data निर्यात करना
DPO/RLHF preference जोड़े
Pairwise तुलनाओं का मुख्य निर्यात format DPO या RLHF training के लिए preference जोड़े हैं:
potato export \
--format dpo_preferences \
--project ./output/ \
--output ./training_data/preferences.jsonlBinary mode के लिए output सरल रहता है:
{
"prompt": "Fix the IndexError in process_batch() when the input list is empty",
"chosen": {"agent": "claude_code", "trace_id": "trace_a_001", "steps": [...]},
"rejected": {"agent": "swe_agent", "trace_id": "trace_b_001", "steps": [...]},
"annotator": "judge1",
"confidence": "high"
}Scale mode preference की तीव्रता भी जोड़ देता है:
{
"prompt": "Fix the IndexError in process_batch()",
"chosen": {"agent": "claude_code", "trace_id": "trace_a_001"},
"rejected": {"agent": "swe_agent", "trace_id": "trace_b_001"},
"preference_strength": 0.83,
"scale_value": 2,
"justification": "Agent A found and fixed the bug in fewer steps with cleaner code"
}Multi-dimension mode प्रति-dimension preferences साथ लेकर चलता है:
{
"prompt": "Fix the IndexError in process_batch()",
"chosen": {"agent": "claude_code", "trace_id": "trace_a_001"},
"rejected": {"agent": "swe_agent", "trace_id": "trace_b_001"},
"overall_preference": "A",
"dimensions": {
"correctness": {"preference": "Tie", "justification": "Both correctly fix the bug"},
"efficiency": {"preference": "A", "justification": "A solves it in 3 steps vs 4"},
"code_quality": {"preference": "B", "justification": "B adds logging and handles None"},
"communication": {"preference": "A", "justification": "A's reasoning is more focused"},
"robustness": {"preference": "B", "justification": "B runs full test suite, not just one file"}
},
"weighted_score_a": 0.55,
"weighted_score_b": 0.45
}विश्लेषण: win rates, Elo ratings, और प्रति-dimension विवरण
Win rates निकालना
import json
from collections import defaultdict
with open("training_data/preferences.jsonl") as f:
prefs = [json.loads(line) for line in f]
wins = defaultdict(lambda: {"wins": 0, "losses": 0, "ties": 0})
for pref in prefs:
agent_chosen = pref["chosen"]["agent"]
agent_rejected = pref["rejected"]["agent"]
if agent_chosen == agent_rejected:
continue # Skip self-comparisons
if pref.get("overall_preference") == "Tie":
wins[agent_chosen]["ties"] += 1
wins[agent_rejected]["ties"] += 1
else:
wins[agent_chosen]["wins"] += 1
wins[agent_rejected]["losses"] += 1
print("Agent Win Rates:")
print("-" * 55)
for agent, record in sorted(wins.items()):
total = record["wins"] + record["losses"] + record["ties"]
win_rate = (record["wins"] + 0.5 * record["ties"]) / total * 100
print(f" {agent:<20} {win_rate:5.1f}% "
f"(W:{record['wins']} L:{record['losses']} T:{record['ties']})")Elo ratings निकालना
import json
import math
from collections import defaultdict
def compute_elo(preferences, k=32, initial_rating=1500):
"""Compute Elo ratings from pairwise preferences."""
ratings = defaultdict(lambda: initial_rating)
for pref in preferences:
agent_a = pref["chosen"]["agent"]
agent_b = pref["rejected"]["agent"]
ra = ratings[agent_a]
rb = ratings[agent_b]
# Expected scores
ea = 1.0 / (1.0 + math.pow(10, (rb - ra) / 400))
eb = 1.0 / (1.0 + math.pow(10, (ra - rb) / 400))
overall = pref.get("overall_preference", "A")
if overall == "Tie":
sa, sb = 0.5, 0.5
else:
# "chosen" is the winner
sa, sb = 1.0, 0.0
ratings[agent_a] = ra + k * (sa - ea)
ratings[agent_b] = rb + k * (sb - eb)
return dict(ratings)
with open("training_data/preferences.jsonl") as f:
prefs = [json.loads(line) for line in f]
ratings = compute_elo(prefs)
print("Elo Ratings:")
print("-" * 35)
for agent, rating in sorted(ratings.items(), key=lambda x: -x[1]):
print(f" {agent:<20} {rating:.0f}")प्रति-dimension विवरण
Multi-dimension तुलनाओं के लिए देखें कि हर agent किन dimensions पर अच्छा करता है:
import json
from collections import defaultdict
with open("training_data/preferences.jsonl") as f:
prefs = [json.loads(line) for line in f]
# Only process multi-dimension annotations
multi_dim = [p for p in prefs if "dimensions" in p]
dim_wins = defaultdict(lambda: defaultdict(lambda: {"A": 0, "B": 0, "Tie": 0}))
for pref in multi_dim:
agent_a = pref["chosen"]["agent"]
agent_b = pref["rejected"]["agent"]
pair_key = f"{agent_a} vs {agent_b}"
for dim_name, dim_data in pref["dimensions"].items():
dim_wins[dim_name][pair_key][dim_data["preference"]] += 1
print("Per-Dimension Win Rates:")
print("=" * 60)
for dim_name, matchups in sorted(dim_wins.items()):
print(f"\n {dim_name.upper()}")
print(f" {'-' * 50}")
for pair, counts in matchups.items():
total = counts["A"] + counts["B"] + counts["Tie"]
a_rate = (counts["A"] + 0.5 * counts["Tie"]) / total * 100
print(f" {pair}: A={a_rate:.0f}% B={100-a_rate:.0f}% "
f"(A:{counts['A']} B:{counts['B']} Tie:{counts['Tie']})")व्यवहार में क्या काम करता है
Mode चुनना
Binary mode तब सही चुनाव है जब आपको जल्दी हज़ारों preferences चाहिए, एक सामान्य-प्रयोजन reward model चाहिए, या एक leaderboard रैंकिंग चाहिए। प्रति तुलना लगभग 1 से 2 मिनट मानकर चलें।
Scale mode तब अपनी क़ीमत वसूल करता है जब preference की तीव्रता आपकी training pipeline में जाती है। Margin weighting वाला DPO इस अंतर की परवाह करता है कि preference मज़बूत है (slider सिरे पर) या कमज़ोर (slider केंद्र के पास)। प्रति तुलना 2 से 3 मिनट मानकर चलें।
Multi-dimension mode अतिरिक्त समय के लायक़ तब है जब आपको जानना हो कि agents कहाँ मज़बूत और कहाँ कमज़ोर हैं, जब आप dimension-विशिष्ट reward models train कर रहे हों, या जब आपको agent developers को विस्तृत रिपोर्ट देनी हो। प्रति तुलना 4 से 6 मिनट मानकर चलें।
कितनी तुलनाएँ चाहिए
भरोसेमंद win rates के लिए हर agent जोड़े पर कम से कम 100 तुलनाएँ जुटाएँ। पाँच या ज़्यादा agents पर Elo ratings के लिए कुल 200 से 300 तुलनाओं में रैंकिंग स्थिर हो जाती है। DPO reward models के लिए 1,000 या उससे ज़्यादा preference जोड़ों का लक्ष्य रखें, जो आसान और कठिन दोनों tasks पर फैले हों।
क्रम को यादृच्छिक करना
randomize_order: true हमेशा सेट करें। Position bias, यानी बायीं ओर या पहले tab में दिखने वाली trace को पसंद करने की प्रवृत्ति, मानव मूल्यांकन अध्ययनों में अच्छी तरह दर्ज है। इस यादृच्छिकीकरण के साथ attention_checks.type: "duplicate_reversed" जाँच भी लगाएँ ताकि जो लोग बस एक ही तरफ़ click करते रहते हैं वे पकड़ में आ जाएँ।
Ties संभालना
Binary mode में ties की अनुमति दें पर tie दर पर नज़र रखें। अगर वह 30% से ऊपर जाती है, तो agents शायद binary निर्णय के लिए बहुत क़रीब हैं और आपको scale या multi-dimension mode पर जाना चाहिए। Scale mode में ties बस केंद्र बिंदु हैं। Multi-dimension mode में व्यक्तिगत dimensions पर ties अपेक्षित हैं और आपको कुछ बताती हैं।
Agent की पहचान छिपाना
show_agent_identity: false ही रखें, जब तक उसे दिखाने की कोई ठोस वजह न हो। अगर annotators को पता हो कि कोई trace किस agent ने बनाई है, तो वे उसी का पक्ष लेने लगते हैं जिसे वे पहले से बेहतर मानते हैं।
Modes को जोड़ना
गहन मूल्यांकन के लिए पहले जोड़ों के एक बड़े समूह पर binary mode चलाएँ ताकि समग्र रैंकिंग मिल जाए, फिर एक छोटे, स्तरीकृत उपसमुच्चय पर multi-dimension mode चलाएँ ताकि विवरण मिल सके। Binary तुलनाएँ reward model training में जाती हैं; multi-dimension वाली बताती हैं कि agent में सुधार कहाँ पर करना है।
इन modes के पीछे के configuration reference के लिए स्रोत दस्तावेज़ देखें। Agents के सिरे से सिरे तक मूल्यांकन के व्यापक walkthrough के लिए agent evaluation guide से शुरू करें।