Anthral Labs — Open weights

Anthral Research.

LoRA adapters fine-tuned with GRPO on top of Qwen/Qwen3.5-9B-Base. Trained on retrieval-grounded forecasting questions derived from GDELT events, with strict leakage controls — see the training datasets on Hugging Face: gdelt-forecast-binary and gdelt-forecast-freeform.


Benchmark · pg-320

Phase 2 outperforms gpt-4o-mini by +5.0 accuracy points on a 320-question politics/geopolitics forecasting set, given identical retrieval and prompt. Reproduce locally: rajatagarwal457/pg320-forecast-eval.

ModelAccBrierReward
Anthral Phase 2 (Qwen3.5-9B-Base + LoRA)60.9 %0.259+0.350
Anthral Phase 1 (binary-only LoRA)57.5 %0.269+0.306
Qwen3.5-9B-Base (untrained)56.3 %0.256+0.307
gpt-4o-mini55.9 %0.278+0.281

Using the adapters

These are LoRA adapters, not standalone models — they sit on top of Qwen/Qwen3.5-9B-Base and only take effect once attached to it. To use one: download an adapter folder below, load the base model with the peft library, and apply the adapter on top. The model expects a forecasting question plus a short bundle of news articles dated before the question's resolution; that retrieval context is how it was trained, and it matters. Phase 1 is tuned for binary yes/no questions; Phase 2 also handles free-form answers. Inference fits on a single 24 GB GPU at bfloat16.

Python · transformers + peft
from peft import PeftModel
from transformers import AutoModelForCausalLM, AutoTokenizer

base  = AutoModelForCausalLM.from_pretrained("Qwen/Qwen3.5-9B-Base", torch_dtype="bfloat16", device_map="auto")
tok   = AutoTokenizer.from_pretrained("Qwen/Qwen3.5-9B-Base")
model = PeftModel.from_pretrained(base, "./qwen3.5-9b-gdelt-binary-grpo-phase1")

Serving with vLLM

For higher-throughput inference, serve through vLLM. It loads adapters once at startup and exposes an OpenAI-compatible HTTP API, so existing client code drops in unchanged. Both adapters can be served at the same time and selected per-request by the model field. A single A100 80 GB will comfortably handle dozens of concurrent forecasting calls.

Shell · start the server
vllm serve Qwen/Qwen3.5-9B-Base \
  --enable-lora \
  --lora-modules phase1=./qwen3.5-9b-gdelt-binary-grpo-phase1 \
                 phase2=./qwen3.5-9b-gdelt-mixed-grpo-phase2 \
  --max-loras 2 --max-lora-rank 16 \
  --dtype bfloat16 --max-model-len 32768
Python · OpenAI-compatible client
from openai import OpenAI

client = OpenAI(base_url="http://localhost:8000/v1", api_key="dummy")

resp = client.chat.completions.create(
    model="phase2",   # or "phase1"
    messages=[{"role": "user", "content": prompt}],
    temperature=0.0,
)
print(resp.choices[0].message.content)

I.

Phase 1 — binary forecasting

qwen3.5-9b-gdelt-binary-grpo-phase1

GRPO on 1,015 binary yes/no forecasting questions. Paranoid retrieval — EXPLICIT and IMPLIED leakage stripped. LoRA r=16, α=32. 500 steps.

II.

Phase 2 — mixed binary & free-form

qwen3.5-9b-gdelt-mixed-grpo-phase2

Continued from Phase 1. GRPO on a mixed set of binary and free-form forecasting questions, paranoid retrieval. Same LoRA configuration.