baud-challenge-demo / generate.py
baudcoin's picture
Update BaudCoin assets (site: baud.money)
95dcc28 verified
Raw
History Blame Contribute Delete
4.85 kB
#!/usr/bin/env python3
"""Deterministic generator for the BAUD reasoning-trace seed set.
This is the same challenge construction the Mint Protocol uses for the MHOP
(multi-hop inference) class, exported as a dataset so the format is public and
reproducible before any live mining has happened.
python generate.py --n 250 --out data/seed-v0.jsonl
Every row is reproducible from (seed, index): same inputs, same challenge, same
reference answer. Nothing here is scraped or model-generated.
"""
import argparse
import hashlib
import json
import os
import random
# A small closed-world fact graph. Entities are deliberately mundane so the
# difficulty comes from the hop structure, not from memorised trivia.
FACTS = [
("the Baudot code", "was patented by", "Emile Baudot"),
("Emile Baudot", "worked for", "the French Telegraph Administration"),
("the French Telegraph Administration", "operated in", "France"),
("France", "uses the currency", "the euro"),
("the teleprinter", "descended from", "the Baudot code"),
("the teleprinter", "transmitted over", "telegraph wire"),
("telegraph wire", "carried", "five bit symbols"),
("five bit symbols", "encode", "thirty two characters"),
("ASCII", "replaced", "the Baudot code"),
("the modem", "measured speed in", "baud"),
("baud", "is named after", "Emile Baudot"),
("BNB Chain", "settles", "BEP20 tokens"),
("BEP20 tokens", "are held by", "agent wallets"),
("agent wallets", "are controlled by", "AI agents"),
("AI agents", "are billed in", "inference tokens"),
("inference tokens", "are metered by", "context length"),
("context length", "constrains", "retrieval depth"),
("retrieval depth", "affects", "answer accuracy"),
]
CONSTRAINT_POOL = [
("max_tokens", [40, 60, 80]),
("must_cite", [True]),
("answer_schema", ["entity", "entity", "json"]),
("forbid", [["speculation"], ["speculation", "hedging"]]),
]
def build_index(facts):
"""subject -> list of (relation, object)"""
idx = {}
for s, r, o in facts:
idx.setdefault(s, []).append((r, o))
return idx
def walk(idx, rng, hops):
"""Walk the graph for `hops` steps, return (start, chain, answer) or None."""
starts = [s for s in idx if idx[s]]
rng.shuffle(starts)
for start in starts:
node, chain = start, []
ok = True
for _ in range(hops):
if node not in idx or not idx[node]:
ok = False
break
rel, nxt = rng.choice(idx[node])
chain.append((node, rel, nxt))
node = nxt
if ok and len(chain) == hops:
return start, chain, node
return None
def make_row(i, seed):
rng = random.Random(f"{seed}:{i}")
hops = rng.choice([2, 2, 3, 3, 4])
idx = build_index(FACTS)
walked = walk(idx, rng, hops)
if walked is None:
return None
start, chain, answer = walked
constraints = {}
for key, options in CONSTRAINT_POOL:
constraints[key] = rng.choice(options)
relations = " then ".join(f'"{r}"' for _, r, _ in chain)
facts_block = "\n".join(f"- {s} {r} {o}." for s, r, o in FACTS)
prompt = (
f"Facts:\n{facts_block}\n\n"
f'Question: starting from "{start}", follow {hops} links: {relations}. '
f"Name the final entity.\n"
f"Answer with the entity only and cite the facts you used with [n] markers."
)
# difficulty band: hops plus constraint pressure, clamped 1..5
difficulty = min(5, max(1, hops - 1 + (1 if constraints["max_tokens"] <= 40 else 0)))
cid = "mhop_" + hashlib.sha256(f"{seed}:{i}".encode()).hexdigest()[:10]
return {
"id": cid,
"class": "MHOP",
"domain": "inference@1.0.0",
"difficulty": difficulty,
"hops": hops,
"prompt": prompt,
"constraints": constraints,
"reference_answer": answer,
"reasoning_chain": [{"subject": s, "relation": r, "object": o} for s, r, o in chain],
"seed": f"{seed}:{i}",
}
def main():
ap = argparse.ArgumentParser()
ap.add_argument("--n", type=int, default=250)
ap.add_argument("--seed", default="baud-v0")
ap.add_argument("--out", default="data/seed-v0.jsonl")
args = ap.parse_args()
os.makedirs(os.path.dirname(args.out) or ".", exist_ok=True)
written, seen = 0, set()
with open(args.out, "w", encoding="utf-8") as f:
i = 0
while written < args.n and i < args.n * 20:
row = make_row(i, args.seed)
i += 1
if not row or row["id"] in seen:
continue
seen.add(row["id"])
f.write(json.dumps(row, ensure_ascii=False) + "\n")
written += 1
print(f"wrote {written} rows to {args.out}")
if __name__ == "__main__":
main()