← TinyGPT · docs · devlog · roadmap · speedup
source: docs/recipes/from-traces.md · view on GitHub ↗

From .atraj rollouts to a trained specialist

This recipe walks the substrate-refinement cycle that closes between B22 (token-preserving trajectory recorder), B29 (trace-to-training-data), B30 (reasoning-depth classifier for mix balancing), and tinygpt sft.

Inputs in / model out, no LLM judge in V1, no DPO.

The loop

agent rollouts → .atraj files (B22)

tinygpt traces-to-data        (B29)   →  raw-sft.jsonl

tinygpt reasoning-classify --train    →  reason.tgfr
tinygpt reasoning-classify --score    →  scored.jsonl     (B30)
tinygpt reasoning-classify --filter   →  balanced.jsonl

tinygpt sft --data balanced.jsonl --base <gallery-pin>

Step 1 — collect trajectories

tinygpt agent specialist.tinygpt --tools tools.json \
  --trajectory-dir ~/.cache/tinygpt/atraj/tool-call-v1 \
  --trajectory-task "tool-call" \
  --single "look up the weather in Paris"

Every rollout emits one <uuid>.atraj. Keep them under a per-task directory so B29 can --task tool-call them as a batch. See docs/agent_runtime.md §“Token-preserving trajectories (B22)” for the format.

Step 2 — turn the trajectories into SFT JSONL

tinygpt traces-to-data ~/.cache/tinygpt/atraj/tool-call-v1 \
  --task tool-call \
  --out raw-sft.jsonl

Default filters: tool-echo drop (assistant turns that emitted a tool-call JSON but never reached {"answer": ...} are excluded — those are training-data noise, not training-data signal), exact dedup on (prompt, response), and MinHash near-dedup on the prompt at Jaccard ≥ 0.85.

Tune the MinHash threshold per corpus:

tinygpt traces-to-data ~/.cache/tinygpt/atraj/tool-call-v1 \
  --task tool-call \
  --minhash-threshold 0.7 \
  --out raw-sft.jsonl

Run with --dry-run first to preview the per-stage drop counts before committing to a write.

Step 3 — balance the depth mix

Trace dumps are single-hop heavy by default (those are the prompts that succeed soonest in production). Without balancing, the trained specialist regresses on multi-hop and comparison prompts even though both are present in raw-sft.jsonl. B30 fixes this:

# Train the depth classifier ONCE per corpus family.
tinygpt reasoning-classify \
  --train labeled-seed.jsonl \
  --heldout labeled-heldout.jsonl \
  --out reason.tgfr

tinygpt reasoning-classify --score raw-sft.jsonl \
  --model reason.tgfr --out scored.jsonl --field user

tinygpt reasoning-classify --filter scored.jsonl \
  --target-mix "single=0.3,multi=0.5,comparison=0.2,other=0.0" \
  --out balanced.jsonl

The Castform-aligned 0.3/0.5/0.2 mix biases toward the depth that regresses most often. Adjust per the per-class evals you care about. See balanced-training-mix.md for the full B30 details.

Step 4 — fine-tune

tinygpt sft --base <pin-from-gallery> --data balanced.jsonl ...

What V1 is NOT

These are explicit deferrals, all flagged in the CLI so the operator knows what’s missing:

Where this lives in code

FileRole
Sources/TinyGPTModel/AgentTrajectory.swiftB22 — record types + Codable I/O
Sources/TinyGPT/AgentLoop.swiftB22 — recorder hooks at every turn boundary
Sources/TinyGPT/TracesToData.swiftB29 — orchestrator
Sources/TinyGPT/ReasoningClassify.swiftB30 — depth classifier + filter
evals/traces-to-data-fixtures/*.atrajsmoke fixture (5 trajectories)
evals/traces-to-data-smoke.shend-to-end smoke (rows in / rows out + filter-summary asserts)

Pairs with