Research log Small Model Experimentation
GitHub

Qwen3.5-4B Tool State Policy LoRA

Two cheap checks beat a fine-tuned model

The one idea you need

For each table puzzle the model gives two answers: a quick guess, and a small program it writes and test-runs on one example whose answer is known. A referee commits just one, trusting the program only when it passes that test and disagrees with the guess.

The question

When a small model can either answer a table task directly or run a program it wrote, what is the best way to decide which answer to submit?

What we found

Surprisingly, yes, and with almost no learning. Trusting the program only when it passes a worked example and disagrees with the quick answer lifted accuracy from 56% to 66%, exactly matching the best any picker could reach, and never overwriting a correct answer. Fine-tuning learned most of the signal but landed lower, at 64%. Letting the model commit its own program every time collapsed to 24%, because programs that pass the example are often still secretly wrong.

Why it matters

Before fine-tuning a selector, try cheap tool signals: does the generated program pass its worked example, and does it disagree with the direct answer? Those two checks captured every recoverable win here. Never let a small model commit its own code unchecked.

Two-check rule vs answering directly56% → 66%correct on 50 unseen table tasks: 28 of 50 up to 33 of 50
Best any picker could reach66%perfect-hindsight ceiling, which the simple rule tied
Fine-tuned model64%one task below the free rule and the ceiling
Letting the model commit its own program24%worse than never using it; overwrote 21 of 50 correct answers
On this page
  1. Results at a glance
  2. Overview
  3. Report
    1. Summary
    2. Split
    3. Held-Out Test Result
    4. Gate Verdict
    5. Learned Rule
    6. LoRA Training
    7. Figures
    8. Interpretation
    9. Limitations
  4. Experiment log
  5. Figures
  6. Data files
  7. Reproduce
  8. Related

Results at a glance 3

Answer accuracy for six ways of choosing which answer to submit

How to read

Each bar is test accuracy on unseen tasks for one commit policy, left to right: answer directly, commit the program blindly, fine-tune on scrambled labels, the fine-tuned policy, the two-check rule, and a perfect-hindsight ceiling. Taller is better.

0%20%40%60%80%always directalways direct56%base zero-shotbase zero-shot24%shuffled-label LoRAshuffled-label LoRA46%LoRA action policyLoRA action policy64%learned rulelearned rule66%oracle ceilingoracle ceiling66%

Takeaway → The two-check rule reaches the perfect-hindsight ceiling, while committing the program blindly sits lowest, below even always answering directly. Knowing when to override beats raw effort.

Data table
commit policytest accuracy
always direct56%
base zero-shot24%
shuffled-label LoRA46%
LoRA action policy64%
learned rule66%
oracle ceiling66%

Numbers from reports/final_summary.json (policy_results.*.test.accuracy)

Technical framing

Held-out accuracy: can a policy pick DIRECT vs PROGRAM from tool state? — A simple tool-state rule hits the oracle ceiling (66%); the LoRA policy learns most of the signal (64%) but not all.

Wrong answers rescued versus right answers ruined, per policy

How to read

For five policies, two bars each out of 50 tasks: wrong direct answers the program rescued, and correct direct answers a bad program overwrote. More rescues with zero overwrites is best.

0102030base zero-shotbase zero-shot521shuffled-label LoRAshuffled-label LoRA510LoRA action policyLoRA action policy40learned rulelearned rule50oracle ceilingoracle ceiling50

Takeaway → The two-check rule and fine-tuned policy rescue about 5 tasks with zero overwrites, while committing the program blindly destroys 10 to 21 correct answers.

Data table
commit policydirect-miss recoveriesdirect-correct losses
base zero-shot521
shuffled-label LoRA510
LoRA action policy40
learned rule50
oracle ceiling50

Numbers from reports/final_summary.json (policy_results.*.test.direct_miss_recoveries / direct_correct_losses)

Technical framing

Recoveries vs losses on held-out test (out of 50 tasks) — The learned rule and LoRA policy recover direct misses without ever overwriting a correct direct answer; controls lose 10-21 tasks.

Does the tool state hold a real, learnable choose signal?

How to read

Two training-loss curves over 80 steps: fitting the real pick-the-answer labels versus randomly shuffled labels. Lower loss means the model found a pattern; the shuffled line is the cannot-learn-noise baseline.

0123420406080shuffled labelsreal labels

Takeaway → Real-label loss plunges toward zero while shuffled-label loss stays stuck high, confirming the tool state carries a genuine choose signal, not memorized noise.

Data table
training stepreal labelsshuffled labels
23.242.69
40.3770.497
60.2740.378
80.410.344
100.0230.12
120.06460.21
140.02840.394
167.8e-040.293
180.1340.701
200.003520.36
222.2e-040.308
245.7e-040.358
263.7e-040.342
280.00140.278
300.01330.177
320.01470.33
340.007030.188
360.01780.38
380.04260.277
400.05020.354
420.001510.276
440.001130.558
464.6e-040.311
484.8e-040.38
503.9e-040.2
523.1e-040.333
542.0e-040.382
562.2e-040.351
581.7e-040.325
601.3e-040.398
621.2e-040.437
641.1e-040.144
661.0e-040.496
686.0e-050.359
706.0e-050.26
727.0e-050.289
746.0e-050.236
766.0e-050.745
787.0e-050.371
806.0e-050.129

Numbers from reports/final_summary.json (lora.losses, lora_shuffled.losses; every 2nd step)

Technical framing

LoRA training loss: real labels vs shuffled-label control — Real-label loss collapses to near zero while shuffled labels stay unlearnable, confirming the tool-state signal is genuine, not memorization.

In the author’s words from the Report · “Summary”

This standalone experiment tests whether a small action policy can use executable-tool observations to choose between a direct table answer and a repaired program output. The policy is not asked to produce a table or a program. It sees a compact tool state and chooses DIRECT or PROGRAM.

Overview

Standalone experiment for tool-state action-policy learning.

The package uses precomputed Qwen3.5-4B table-transformation traces. Each trace contains:

  • a direct JSON answer,
  • an executable transform(table) program attempt,
  • visible-example execution observations,
  • repair-loop observations,
  • hidden held-out exactness labels used only for training labels and evaluation.

The policy sees only deployable tool-state observations and chooses:

  • DIRECT: commit the direct JSON table,
  • PROGRAM: commit the final visible-verified executable program output.

Run the fast non-neural baselines:

python scripts/run_tool_state_policy.py \
  --root /workspace/experiments/qwen35_4b_tool_state_policy_lora \
  --skip-lora

Run the LoRA action-policy arm:

python scripts/run_tool_state_policy.py \
  --root /workspace/experiments/qwen35_4b_tool_state_policy_lora \
  --train-lora \
  --max-steps 80

Outputs are written under reports/.

Report

Rendered from reports/report.md

Summary

This standalone experiment tests whether a small action policy can use executable-tool observations to choose between a direct table answer and a repaired program output.

The policy is not asked to produce a table or a program. It sees a compact tool state and chooses DIRECT or PROGRAM.

Split

  • Train: 150 records across 30 families
  • Dev: 50 records across 10 families
  • Test: 50 records across 10 families

Held-Out Test Result

PolicyExactAccuracyProgram commitsRecoveriesLossesProgram precision
direct_only28/5056.0%000n/a
program_if_visible_else_direct33/5066.0%145085.7%
program_if_visible_and_disagrees_else_direct33/5066.0%550100.0%
parse_fallback_program_else_direct32/5064.0%440100.0%
learned_rule33/5066.0%550100.0%
base_zero_shot_action12/5024.0%5052124.0%
lora_action_policy32/5064.0%440100.0%
lora_shuffled_labels23/5046.0%2751044.4%
oracle_action33/5066.0%550100.0%

Gate Verdict

The environment state contains a deployable selection signal. The selected rule reaches the oracle action ceiling on held-out test: 33/50, with 5 direct-miss recoveries and 0 losses. The rule is simple: choose PROGRAM only when the program passes the public example and disagrees with the direct output on the new input.

The LoRA action-policy arm is a partial positive. It improves over direct-only (32/50 vs 28/50), beats the shuffled-label control (32/50 vs 23/50), and has 0 losses, but it does not match the simple rule/oracle ceiling (33/50). The posttraining signal is real but not the best controller in this small split.

Learned Rule

The non-neural rule search selected:

{
  "dev": {
    "accuracy": 0.66,
    "action_counts": {
      "DIRECT": 43,
      "PROGRAM": 7
    },
    "direct_correct_losses": 0,
    "direct_miss_recoveries": 5,
    "exact": 33,
    "n": 50,
    "policy": "rule",
    "program_commits": 7,
    "program_correct": 5,
    "program_precision": 0.7142857142857143,
    "split": "dev"
  },
  "false_action": "DIRECT",
  "feature": "direct_program_disagree_visible_pass",
  "train": {
    "accuracy": 0.6,
    "action_counts": {
      "DIRECT": 136,
      "PROGRAM": 14
    },
    "direct_correct_losses": 0,
    "direct_miss_recoveries": 8,
    "exact": 90,
    "n": 150,
    "policy": "rule",
    "program_commits": 14,
    "program_correct": 8,
    "program_precision": 0.5714285714285714,
    "split": "train"
  },
  "true_action": "PROGRAM"
}

LoRA Training

  • Real-label LoRA train examples: 122
  • Real-label LoRA tokenized examples: 122
  • Real-label final loss: 6.317748193396255e-05
  • Shuffled-label LoRA train examples: 122
  • Shuffled-label final loss: 0.12911884486675262

Figures

Held-out accuracy

Recoveries and losses

Family headroom

Interpretation

The decisive comparison is whether the learned action policy converts program-only headroom into held-out recoveries without committing hidden-wrong visible-pass programs on direct-correct tasks.

The oracle row is an upper bound that uses hidden labels to pick PROGRAM exactly when the program is correct and the direct answer is not. Deployable policies do not see that label.

The main positive result is not that LoRA is necessary; it is that a small deployable tool-state observation collapses the selection problem on this held-out split. Posttraining learned most of that signal, but the rule exposes the cleaner mechanism.

Limitations

  • The environment traces are precomputed; this package trains and evaluates the commit policy over those observed states.
  • The policy observes the final repair-loop state, so policies that use tool observations pay the full repair-loop generation cost even when they choose DIRECT.
  • The family-disjoint split is deterministic but still small. Repeat across multiple split seeds before treating the learned policy as stable.

Experiment log 1

Show the running log (1 entry, 2026-06-28)

2026-06-28

  • Created a standalone tool-state action-policy package.
  • Copied table-transformation cases and precomputed Qwen3.5-4B tool-environment traces into data/.
  • Chose a family-disjoint split seed (6137) that keeps direct-miss program-recovery labels present in train, dev, and test.
  • Planned a two-stage run: first validate non-neural baselines and oracle ceilings, then run the LoRA action-policy arm if the package compiles and the split is sane.
  • Ran the fast baseline/rule stage. The selected rule was PROGRAM only when the final program passed the visible example and disagreed with the direct output.
  • The selected rule reached 33/50 on held-out test, exactly matching the oracle action ceiling and recovering 5 direct misses with 0 losses.
  • Ran a one-step LoRA smoke test. Model loading, adapter attachment, training, scoring, and artifact writing all worked.
  • Started an 80-step LoRA run with autoregressive action generation, then interrupted it during post-train evaluation because generation was too slow for a two-action classifier.
  • Patched evaluation to score DIRECT vs PROGRAM by next-token logits. Re-ran the full 80-step real-label LoRA plus 80-step shuffled-label control.
  • Final LoRA result: real-label LoRA reached 32/50, recovering 4 direct misses with 0 losses. Shuffled-label LoRA reached 23/50, with 10 direct-correct losses. The adapter learned useful state signal but did not match the simpler rule/oracle ceiling.

Figures 3

family headroom
family headroom · reports/figures/
heldout accuracy
heldout accuracy · reports/figures/
recoveries losses
recoveries losses · reports/figures/

Data files 1

Result tables and metrics copied from the experiment folder — preview inline or open the raw file.

Reproduce

Run steps are documented inside the experiment folder (README and scripts).

Browse the experiment folder on GitHub ↗