BenchGen Fugu-Lite

Fugu-Lite is a small, runnable model router for one local GPU. A frozen local language model reads a task, a custom linear head selects one worker, and the selected worker is called through OpenRouter (or through a local OpenAI-compatible server).

This is Fugu-inspired research code, not Sakana AI's proprietary Fugu implementation. The useful overlap is the learned worker selection, offline trajectory/reward collection, and an optional separable CMA-ES stage. Version 0.1 performs one learned routing decision; it does not yet learn recursive, multi-turn delegation or answer synthesis.

Project documentation

The repository includes the V1 task/reward snapshot and trained routing-head artifacts so the experiment can be audited and resumed. The real API key is never stored in the project.

What trains where

Part Runs on What changes API cost during training?
Reward-data generation OpenRouter/local workers Nothing; records every worker's answer, score, cost, and latency Yes, once per task × worker
SFT warm-start Your GPU Router LayerNorm + classification head No
Contextual-bandit RL Your GPU Same small routing head No
Sep-CMA-ES (optional) CPU after one GPU embedding pass Final linear routing layer No
Inference Your GPU + selected worker Nothing One worker call per prompt, unless fallback is needed

The default backbone is Qwen/Qwen3-0.6B. With three workers, its trainable head is only a few thousand parameters. The backbone stays frozen, so the project is practical on modest GPUs. The exact memory depends on sequence length and software, but the default normally fits comfortably above roughly 4 GiB of VRAM.

Architecture

flowchart TD
    A[BenchGen tasks] --> B[Call every worker once]
    B --> C[Quality, cost, latency rewards]
    C --> D[Reward matrix JSONL]
    D --> E[SFT warm-start]
    E --> F[Contextual-bandit RL]
    F --> G[Optional Sep-CMA-ES]
    G --> H[Local router checkpoint]
    H --> I[Single API endpoint]
    I --> J[Selected OpenRouter or local worker]

1. Install on the remote GPU server

Python 3.10–3.13 and a working NVIDIA/PyTorch setup are expected.

Install the PyTorch CUDA build recommended for your server's driver from https://pytorch.org/get-started/locally/ first. If python -c "import torch; print(torch.cuda.is_available())" already prints True, keep that installation.

unzip BenchGen_Fugu_Lite.zip
cd fugu-lite
python3 -m venv .venv
source .venv/bin/activate
python -m pip install --upgrade pip
pip install -e '.[dev]'
cp .env.example .env
chmod 600 .env

Edit .env on the server and set OPENROUTER_API_KEY. Never paste the real key into source code, YAML, a Git commit, or chat. Check the machine:

fugu-lite doctor

If PyTorch cannot see CUDA, fix the server's NVIDIA driver/PyTorch installation before training. The router can run on CPU, but it will be much slower.

2. Prove the whole data path without spending credits

The mock pool contains a math, code, and general expert. It deterministically creates a reward matrix from the sample tasks.

fugu-lite generate \
  --workers configs/workers.mock.yaml \
  --tasks data/tasks.sample.jsonl \
  --output data/rewards.mock.jsonl

Run the tests:

pytest -q

3. Choose real OpenRouter workers

OpenRouter model availability changes, so query the live catalog rather than relying on an old list:

fugu-lite list-models --contains qwen --limit 30
fugu-lite list-models --contains free --limit 30

Copy and edit the worker file:

cp configs/workers.openrouter.example.yaml configs/workers.yaml

Choose meaningfully different workers—for example, a low-cost fast model, a strong reasoning model, and a coding specialist. If all workers produce the same reward on every task, the router has no learning signal. Three or four workers are a good first experiment.

To include a model hosted on the same server, add an openai_compatible worker. Any server that exposes /v1/chat/completions can be used; an example vLLM entry is already commented in the YAML.

4. Prepare real BenchGen tasks

Input is JSON Lines, one object per task:

{"task_id":"math-101","prompt":"Solve ...","domain":"math","reference_answer":"42","grader":{"type":"numeric","tolerance":0.0},"split":"train","tags":["algebra"]}

Supported local graders are exact, contains, numeric, and regex. For an open-ended task, use llm_judge, add a clear grader.rubric, and configure reward.judge_model in the worker YAML. LLM judging adds one judge call for each worker response and should be audited on a human-labeled sample.

Do not place answers, benchmark labels, or worker scores in prompt; only the grader sees the reference answer. Keep benchmark test sets out of training. A useful first real run is:

  • 200–1,000 training tasks covering your production distribution;
  • at least 50 validation and 100 held-out test tasks;
  • balanced domains, including tasks on which each worker can actually win;
  • deterministic executable graders whenever possible.

If BenchGen already produces worker rewards, skip API generation and write the matrix shape shown in data/rewards.schema.example.jsonl. worker_ids order must remain identical in every row, and rewards[i] must belong to worker_ids[i].

5. Generate the real reward matrix

First use a small limit to verify prompts, grading, and billing:

fugu-lite generate \
  --workers configs/workers.yaml \
  --tasks data/tasks.jsonl \
  --output data/rewards.real.jsonl \
  --limit 10

Inspect the JSONL. When it looks correct, run the complete job (preferably to a new output path). Records are appended after each task, so an interrupted job can continue safely with --resume. The number of primary calls is number_of_tasks × number_of_workers; llm_judge doubles that count. OpenRouter's non-streaming usage object is recorded when it provides token and cost fields.

fugu-lite generate \
  --workers configs/workers.yaml \
  --tasks data/tasks.jsonl \
  --output data/rewards.real.jsonl \
  --resume

The default utility is:

[ U = w_q Q - w_c \min(C/C_0,5) - w_l \min(L/L_0,5) ]

where quality (Q) is 0–1, cost (C) is the reported request cost, and latency (L) is seconds. Adjust the weights and scales in the worker YAML to match your product. If quality matters much more than price, keep the cost and latency weights small.

6. Train the routing head

Start with SFT. It converts each reward vector into a soft target distribution and trains the custom head with cross-entropy:

fugu-lite train-sft \
  --config configs/train_sft.yaml \
  --data data/rewards.real.jsonl \
  --output artifacts/router-sft

Then run real offline contextual-bandit RL. The default REINFORCE estimator samples worker actions and uses the full-information expected reward as a baseline:

fugu-lite train-rl \
  --config configs/train_rl.yaml \
  --data data/rewards.real.jsonl \
  --checkpoint artifacts/router-sft \
  --output artifacts/router-rl

For smaller or noisy data, estimator: expected_reward in train_rl.yaml is lower variance. For a closer analogue to the reported Sakana optimization style, optionally run separable CMA-ES. It caches backbone features once and evolves only the last linear layer:

fugu-lite train-es \
  --config configs/train_es.yaml \
  --data data/rewards.real.jsonl \
  --checkpoint artifacts/router-rl \
  --output artifacts/router-es

Use SFT alone as the first baseline. Add RL only when workers have different rewards and the validation utility improves. Add CMA-ES when you want to optimize a discrete or otherwise non-differentiable end metric; keep it only if held-out performance improves.

7. Evaluate correctly

fugu-lite evaluate \
  --checkpoint artifacts/router-rl \
  --data data/rewards.real.jsonl \
  --split test \
  --output artifacts/router-rl/test_metrics.json

The report includes:

Metric Meaning
router_utility Mean reward obtained by the learned greedy route
oracle_utility Upper bound if the best worker were known for every task
best_fixed_utility Strongest single worker used for every task
random_utility Uniform random routing baseline
regret Oracle utility minus router utility; lower is better
oracle_route_accuracy Fraction of tasks routed to an oracle-best worker

Do not deploy merely because route accuracy looks high. The router should beat the best fixed worker on held-out utility and on your actual cost/latency guardrails.

8. Use the trained router

Route without calling any worker:

fugu-lite route \
  --checkpoint artifacts/router-rl \
  --domain code \
  --prompt "Find the bug in this Python function ..."

Route and call the selected worker:

fugu-lite ask \
  --checkpoint artifacts/router-rl \
  --workers configs/workers.yaml \
  --domain code \
  --prompt "Find the bug in this Python function ..."

Serve a single OpenAI-compatible endpoint:

fugu-lite serve \
  --checkpoint artifacts/router-rl \
  --workers configs/workers.yaml \
  --host 0.0.0.0 \
  --port 8080
curl http://127.0.0.1:8080/v1/chat/completions \
  -H 'Content-Type: application/json' \
  -d '{"model":"fugu-lite","domain":"code","messages":[{"role":"user","content":"Explain this stack trace..."}]}'

The response includes a fugu_lite field with route probabilities, the actual served model, latency, cost when available, and fallback errors. In production, put authentication and TLS in front of this service; do not expose it directly to the public internet.

What to improve next

Version 0.1 is deliberately narrow. The next meaningful step toward a fuller Fugu-like system is to define a small action grammar such as CALL(worker, subtask), VERIFY(worker), SYNTHESIZE, and STOP; collect complete multi-turn trajectories in a sandbox; and optimize the final verified task reward plus cost/latency penalties. Do that only after this one-step router reliably beats fixed-worker baselines—otherwise multi-agent complexity hides basic data and reward problems.

References

Downloads last month

-

Downloads are not tracked for this model. How to track
Inference Providers NEW
This model isn't deployed by any Inference Provider. 🙋 Ask for provider support

Model tree for tahsinsoyak/fugu-lite

Finetuned
Qwen/Qwen3-0.6B
Finetuned
(1167)
this model