Socra Small for hub4: a 0.8B model that runs a truck port on its own

Socra Small is a fast decision model ("System 1") for hub4, the simulated Serviceport truck port: a logistics hub where long-haul, autonomous and local trucks sign on, charge or swap batteries, rest, load and leave. Every one of the hub's 14 decision types is answered by this model, with no hand-written rule, guard or fallback switch.

It is Qwen3.5-0.8B with a LoRA adapter (r 16) and the Clef joint schema head, which reads a short JSON description of the situation and scores every open option of the decision with one logit. About 110 ms per decision on one GPU.

Result: the model alone vs the hub's rules

160 development test days (test hubs 801-840 x 4 day seeds; hubs and seeds never seen in training). The model and the hub4 rules play the same days with the same random draws.

hub4 rules Socra alone (this model) Socra alone, 2nd training seed
hard-limit violations 0 0 0
long-haul trucks served per day 489.1 489.4 489.5
trucks turned away per day 24.8 24.2 24.1
total cost per day (EUR) 34,488 34,255 33,310

The model serves at least as many long-haul trucks as the rules at a lower cost, with no violation. Read the cost with care: one of the 160 days (h0803-s2000002) is a very expensive day for the rules (about 338,000 EUR more than the model). Without that day the model costs +1,891 EUR per day (2nd seed: +999) more than the rules, while still serving more long-haul trucks (+0.08 and +0.25 per day). The 95 % intervals of the served and cost differences include zero.

The 14 decisions

The 14 hub4 decisions along a truck's way through the hub, with their options

The model answers all 14 decision types along a truck's way through the hub, from sign-on on the road to departure. Colours show the asking agent: blue the tower, orange the gate, green energy (charger or battery swap, charging power, charge target), purple the site stations (buffer parking, inspection, trailer yard, local handover). Each ask lists only the options open in that state; a medium hub day has about 6,000-12,000 asks.

Mermaid source of the diagram
flowchart LR
    S(["`Truck signs on
still on the road`"]) --> R

    subgraph R ["1 ยท Before arrival"]
        direction TB
        admit("`**admit**
admit ยท refuse`")
        plan("`**plan** ยท service order
energy first ยท inspection first
cleaning first ยท energy last`")
        pickup("`**pickup** ยท trailer here?
come ยท pass`")
        speed("`**speed** ยท arrival
on time ยท 10 / 20 / 30 min later
park before the hub ยท break before`")
        admit --> plan --> pickup --> speed
    end

    subgraph H ["2 ยท At the hub"]
        direction TB
        gate("`**gate**
let in ยท hold ยท turn away`")
        waiting("`**waiting** ยท where to wait
station line ยท buffer parking
another free station`")
        place("`**place** ยท who gets a free place
booked slot ยท earliest deadline
least drive left ยท earliest promise
keep free`")
        gate --> waiting --> place
    end

    subgraph V ["3 ยท Service, in the order set by plan"]
        direction TB
        energy_mode("`**energy_mode**
battery swap ยท MCS ยท CCS
slow ยท charge during rest`")
        power("`**power**
full ยท mid ยท floor`")
        target("`**target_soc**
60 ยท 70 ยท 80 ยท 85 ยท 90 % ยท none`")
        inspect("`**inspect**
at the charger ยท in the hall
at the next hub`")
        energy_mode --> power --> target ~~~ inspect
    end

    subgraph D ["4 ยท Departure"]
        direction TB
        load("`**load** ยท trailer
couple now ยท wait for the paired one
leave empty`")
        release("`**release**
drive on ยท hold`")
        local_round("`**local_round** ยท local trucks
start a round now ยท wait`")
        load --> release ~~~ local_round
    end

    R --> H --> V --> D --> E(["`Truck leaves the hub`"])

    classDef tower fill:#dbeafe,stroke:#3b82f6,color:#0f172a
    classDef gatec fill:#ffedd5,stroke:#f97316,color:#0f172a
    classDef energy fill:#dcfce7,stroke:#22c55e,color:#0f172a
    classDef site fill:#f3e8ff,stroke:#a855f7,color:#0f172a
    classDef term fill:#f8fafc,stroke:#94a3b8,color:#334155
    class admit,plan,pickup,speed,place,release tower
    class gate gatec
    class energy_mode,power,target energy
    class waiting,inspect,load,local_round site
    class S,E term
    style R fill:#ffffff,stroke:#cbd5e1,color:#334155
    style H fill:#ffffff,stroke:#cbd5e1,color:#334155
    style V fill:#ffffff,stroke:#cbd5e1,color:#334155
    style D fill:#ffffff,stroke:#cbd5e1,color:#334155

How it was trained: seven rounds, DAgger from round 2

One-step labels are not enough: a model that is right on most single decisions can still run the hub into the ground when it meets the states its own choices create. Every round was therefore tested in closed loop, and from round 2 training ran DAgger: the model plays training days itself, a teacher labels every situation it reaches, and the model is retrained on those rows.

round what changed result vs rules (dev days)
0 one-step model, labels from simulated option costs (fast-all-s1/) +79,346 EUR/day, 0.9 vs 489 long-haul served
1 labels anchored on the rule's option unless another is clearly cheaper +696 EUR/day, served -1.1 (40-day screen)
2 DAgger: the model plays, the Track 2 hub4 decider labels every ask -306 EUR/day, served -1.9 (40-day screen)
3 a second DAgger set plus one more copy of every missed row -12 EUR/day, served -1.7 (40-day screen)
4 new input: the weather forecast (on, end, whether it still holds the truck when ready); DAgger on snow and fog days -1,943 EUR/day, served -0.9 (160 days)
5 new inputs: the tower's own hold plan for the truck and the hold ask -296 EUR/day, served -0.5 (160 days)
6 on hold asks, the rule's option (park before the hub) as label -233 EUR/day, served +0.2 (160 days)

What made the difference on the last mile: the missing long-haul trucks were all on snow days. There the rule parks autonomous trucks before the hub when the weather will hold them, which keeps the hub free; the model first could not see the weather at all (round 4), then could not tell which ask was the one raised for the hold (round 5), and then had learned from a teacher that almost never parked (round 6).

This run: 454,344 training decisions, 1 epoch, batch 64, lr 3e-4, bf16 base weights, 44 min on one NVIDIA B300 (Curiosity cluster).

Input and output

One hub4 decision: the situation record goes into Socra Small, which scores the open options and answers park_before

Each time the hub needs a decision, the model gets the situation as compact JSON and a question that lists only the options open in that state. It scores every option with one logit and answers with the best option's id.

part of the record what it carries
objective the goal: lowest total hub cost in EUR over the next 8 hours
decision, context decision type, asking agent and ask group, clock, shift, slot, day type, hub size, competitors
peak contract peak, 15-minute peak so far, grid draw, capacity price, tariff
weather the tower's forecast (on, end); for autonomous trucks the hold plan and whether this is the hold ask
obs the decision's own observation, here the truck and its timing
question the open options, each with a one-line description
The full record of this ask (peak values shortened)
{"system": {"name": "hub4", "version": "b4218365c9ee9e61"},
 "objective": "lowest total hub cost in EUR over the next 8 hours",
 "decision": "speed",
 "context": {"agent": "tower", "group": "pace", "clock": "02:55", "shift": 0, "slot": 0, "day_type": "weekday",
             "hub_size": "medium", "competitors": 2},
 "peak": {"contract_peak_kw": ..., "peak15_so_far_kw": ..., "grid_kw": ..., "capacity_eur_per_kw": ...,
          "tariff": ..., "policy_after": "model"},
 "weather": {"on": true, "end_in_min": 1323, "holds_truck_when_ready": true, "hold_plan": true, "hold_ask": true},
 "obs": {"truck": {"group": "auto", "reason": null, "soc": 0.2149, "stay_min": 0, "need": 1.177,
                   "arrive_in_min": 100.1, "deadline_in_min": 1194, "drive_before_min": null,
                   "drive_left_min": null, "reach_ok": false, "booked": null},
         "slack_min": 31.27, "early_min": 0, "first_wait_min": 0}}

The question with the open options:

{"type": "choice", "instructions": "Which option should the hub tower choose now?",
 "criteria": {"on_time": "Let the truck arrive as planned.",
              "later_10": "Slow the truck so it arrives 10 minutes later.",
              "later_20": "Slow the truck so it arrives 20 minutes later.",
              "later_30": "Slow the truck so it arrives 30 minutes later.",
              "park_before": "Autonomous truck parks before the hub and comes in later."}}

Use

import json
from unsloth import FastDecisionModel     # Unsloth with decision-model support

model, tok = FastDecisionModel.from_pretrained("chrispie/socra-small-hub4", max_seq_length=4096, load_in_4bit=False)
FastDecisionModel.for_inference(model)

def decide(record):
    state = json.dumps({k: v for k, v in record.items() if k not in ("question", "id")},
                       ensure_ascii=False, separators=(",", ":"))
    return FastDecisionModel.predict(model, tok, state, {"action": record["question"]})["action"]["answer"]

Records must be built exactly as in training (same fields, the open options only, the weather block). The hub4 simulation, the record builder (hub4model/socra.py), the live decider (hub4model/socra_decider.py) and the evaluation (python3 -m hub4model.evaluate --socra ADAPTERS --socra-weather) are in the truckport repository; the training code (tools/train_socra.py) is in predictive-decision-distillation.

Limitations

  • Trained and tested only on the hub4 simulation (engine b4218365c9ee9e61); it is not a general logistics model.
  • The cost lead over the rules depends on one test day; on the other days it is slightly more expensive, with more late departures (+0.9 per day).
  • Some snow days still lose long-haul trucks (up to -28 on one day); other days win them back.
  • The teacher for most decisions is the Track 2 hub4 decider, so the model inherits its choices; on planned weather holds the hub's rule is the teacher.

Files

file content
adapter_model.safetensors, adapter_config.json LoRA adapter (r 16)
joint_head.safetensors, joint_head_config.json Clef joint schema head
tokenizer.json, tokenizer_config.json, chat_template.jinja, processor_config.json tokenizer
socra.json, unsloth_decision_config.json decision-model settings
metrics.json, eval.json training metrics and one-step validation
closed_loop_summary.json the 160-day closed-loop evaluation
assets/decisions.svg, assets/input-output.svg the two diagrams
fast-all-s1/ round 0: the earlier one-step model
Downloads last month
27
Inference Providers NEW
This model isn't deployed by any Inference Provider. ๐Ÿ™‹ Ask for provider support

Model tree for chrispie/socra-small-hub4

Adapter
(683)
this model