# RFT Grader Design Guide ## Grader Type Selection | Grader Type | Best For | Tradeoffs | |------------|---------|-----------| | **Python grader** (default) | Most tasks incl. tool-calling. Accesses `output_text` and `output_tools`. | Can't call external APIs or execute code. | | **Multi grader** | Combining multiple scoring dimensions. | `score_model` component adds LLM cost per rollout. | | **Endpoint grader** | Tasks requiring external API calls (test suites, DB queries). | HTTP latency, scaling risk. Under-provisioned endpoints can hang jobs. | | **String check** | Exact-match tasks (classification, yes/no, numeric). | Binary 0/1 only — no partial credit. | Start with Python grader unless you need external API calls. Python graders are fast, deterministic, reliable, and tool-aware (`sample.output_tools` provides tool call metadata). ## Partial Credit Pattern Binary pass/fail gives sparse reward. Decompose into 2–4 scored dimensions: ```python def grade(sample, item): output_text = sample.get("output_text", "") or "" expected = item.get("expected_answer", "") score = 0.0 # Core correctness (highest weight) if correct_action(output_text, expected): score += 0.4 # Precision (exact amounts, specific values) score += 0.3 * precision_score(output_text, expected) # Reasoning quality (cited correct rules/facts) score += 0.2 * reasoning_score(output_text, expected) # Process quality (used the right tools) if used_correct_tools(sample.get("output_tools", [])): score += 0.1 return round(min(score, 1.0), 3) ``` ### Weight Guidelines | Dimension | Typical Weight | Examples | |-----------|---------------|----------| | Core correctness | 0.3–0.5 | Right action/answer/classification | | Precision | 0.2–0.3 | Exact amounts, correct format | | Reasoning | 0.1–0.2 | Cited correct rules, justified decision | | Process quality | 0.05–0.1 | Used right tools, followed steps | ## Threshold Calibration Workflow The `pass_threshold` determines what score counts as pass vs fail — the most important RFT hyperparameter. 1. Run the **base model** on your training/validation set 2. Score every output with your grader 3. Compute pass rates at multiple thresholds: ```python for threshold in [0.5, 0.6, 0.7, 0.8, 0.85, 0.9, 0.95]: pass_rate = sum(1 for s in scores if s >= threshold) / len(scores) print(f" @{threshold}: pass={pass_rate:.0%}, fail={1 - pass_rate:.0%}") ``` 4. Choose where **25–50% of base model rollouts fail**: | Failure Rate | Signal Quality | |-------------|----------------| | < 10% | ❌ Too easy — no learning signal | | 10–25% | ⚠️ Weak signal | | **25–50%** | ✅ Good — enough failures to learn from | | 50–70% | ⚠️ Harsh — mostly negative reward | | > 70% | ❌ Too hard — training may diverge | **Always re-run calibration when you change your dataset.** ## Consistency Rules When using multiple graders (Python for training, endpoint for debugging, local script for eval): 1. **Identical scoring logic** — same weights, keywords, dimension breakdown 2. **Identical default scores** — same behavior when no action found, no amounts expected 3. **Test with same examples** — run 10 samples through all graders and verify scores match Mismatched scoring causes the model to learn different behavior than what your evaluation measures.