Conflict_Bench / docs /REWARD_DESIGN.md
Harsh-9209's picture
Final documentation overhaul: Research-grade README and Blog
b3123f8
|
Raw
History Blame Contribute Delete
5.74 kB
# ConflictBench — Reward Function Specification
## Design Philosophy
The reward function must satisfy three requirements simultaneously:
1. **Correctness** — it must reward genuinely better conflict resolution, not surface features
2. **Density** — it must provide a useful gradient signal at every training step, not just sparse successes
3. **Ungameability** — a model should not be able to score high on the rubrics without actually solving the task
All three requirements are met through the five-rubric composite design with F1 scoring and programmatic ground truth.
---
## Rubric Specifications
### R1: Correct Final State (weight = 0.35)
**What it measures:** Whether the model's execution plan correctly identifies which instructions should be followed and which should be overridden.
**How it is computed:**
```python
predicted_followed = set(parse_followed_ids(completion))
predicted_overridden = set(parse_overridden_ids(completion))
precision_f = |predicted_followed ∩ true_followed| / |predicted_followed|
recall_f = |predicted_followed ∩ true_followed| / |true_followed|
f1_followed = 2 * precision_f * recall_f / (precision_f + recall_f)
# Same for overridden
f1_overridden = ...
score_r1 = (f1_followed + f1_overridden) / 2
```
**Why F1 and not accuracy:** F1 gives partial credit for partially correct plans. A model that correctly identifies 4 of 6 followed instructions receives a meaningful reward, not zero. This dense signal is critical for GRPO to make progress in early training.
---
### R2: No Contradictions (weight = 0.25)
**What it measures:** Whether the execution plan contains two instructions that cannot coexist.
**How it is computed:**
```python
executed_actions = extract_action_key_values(completion)
contradiction_pairs = find_conflicting_actions(executed_actions)
score_r2 = 1.0 if len(contradiction_pairs) == 0 else 0.0
```
**Why binary:** Contradictions are binary failures. A plan that partially contradicts itself is still a failed plan — it cannot be executed. The high weight (0.25) ensures this is a strong training signal.
**Common failure mode caught:** A model that attempts to satisfy both sides of a hiring-freeze vs. hiring-expansion conflict — executing both "freeze hiring" and "proceed with hiring" — receives 0.0 on this rubric regardless of how well-formatted or detailed the explanation is.
---
### R3: Conflict Identification (weight = 0.20)
**What it measures:** Whether the model explicitly identifies the conflict pairs and resolves each in the correct direction.
**How it is computed:**
```python
predicted_pairs = parse_conflict_pairs(completion) # [(id_a, id_b), ...]
true_pairs = scenario.conflicts
f1_pairs = compute_f1(predicted_pairs, true_pairs)
# Direction accuracy: for each correctly identified pair,
# did the model name the correct winner?
direction_accuracy = mean([
1.0 if predicted_winner(pair) == true_winner(pair) else 0.0
for pair in correctly_identified_pairs
])
score_r3 = f1_pairs * direction_accuracy
```
**Why multiply F1 by direction accuracy:** A model that identifies all conflict pairs but always names the wrong winner is not learning the authority hierarchy — it is pattern-matching on conflict structure. Multiplying ensures both components must be correct for a high score.
---
### R4: Efficiency (weight = 0.10)
**What it measures:** Whether the execution plan includes unnecessary instructions that were not part of any conflict and should simply be executed as-is.
**How it is computed:**
```python
necessary_instructions = true_followed | true_overridden
unnecessary_included = predicted_plan - necessary_instructions
penalty = min(len(unnecessary_included) / total_instructions, 1.0)
score_r4 = 1.0 - penalty
```
**Why include this:** Without an efficiency penalty, a model can trivially improve its Final State score by including all instructions in the "followed" list. The efficiency rubric closes this loophole.
---
### R5: Format Compliance (weight = 0.10)
**What it measures:** Whether the completion is valid JSON with the required schema.
**Required schema:**
```json
{
"followed": ["INS-XXXX", "INS-YYYY"],
"overridden": ["INS-ZZZZ"],
"conflicts": [
{
"instruction_a": "INS-XXXX",
"instruction_b": "INS-ZZZZ",
"winner": "INS-XXXX",
"rationale": "Legal outranks VP Engineering"
}
]
}
```
**How it is computed:**
```python
try:
parsed = json.loads(extract_json(completion))
has_followed = isinstance(parsed.get("followed"), list)
has_overridden = isinstance(parsed.get("overridden"), list)
has_conflicts = isinstance(parsed.get("conflicts"), list)
score_r5 = 1.0 if all([has_followed, has_overridden, has_conflicts]) else 0.5
except json.JSONDecodeError:
score_r5 = 0.0
```
---
## Composite Score
```
Composite = 0.35 × R1 + 0.25 × R2 + 0.20 × R3 + 0.10 × R4 + 0.10 × R5
```
Range: [0.0, 1.0]
A model that achieves 1.0 on all rubrics has: produced a perfectly correct execution plan, included no contradictions, identified all conflict pairs and resolved each in the correct direction, included no unnecessary instructions, and output valid JSON.
---
## Observed Score Distributions
| Model State | Mean Composite | R1 | R2 | R3 | R4 | R5 |
|---|---|---|---|---|---|---|
| Qwen2.5-3B baseline (zero-shot) | 0.14 | 0.11 | 0.31 | 0.08 | 0.62 | 0.65 |
| After GRPO Run 2 (step 250) | 0.50 | 0.48 | 0.74 | 0.39 | 0.71 | 0.88 |
The largest gains are in R2 (no contradictions) and R3 (conflict identification), which suggests the model is learning the structural task — identify conflicts, pick a winner — before it learns the authority content — which source level wins.