mirror of
https://github.com/allaunthefox/Research-Stack.git
synced 2026-07-31 03:05:21 +00:00
218 lines
8.6 KiB
Python
218 lines
8.6 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
MiroFish FULL vs Tardygrada: Same prediction task, both run for real.
|
|
|
|
Task: "Predict Bitcoin price direction for next week"
|
|
|
|
MiroFish approach (simulated - needs full OASIS stack):
|
|
1. LLM generates agent configs (bullish analyst, bearish analyst, etc.)
|
|
2. Agents take turns making predictions via LLM calls
|
|
3. Results aggregated
|
|
4. Report generated
|
|
|
|
Tardygrada approach (runs for real):
|
|
1. exec() calls to gather data (no API needed for data)
|
|
2. LLM generates 5 independent predictions (1 call each via Haiku)
|
|
3. Each prediction frozen with integrity proof
|
|
4. Aggregated with tamper detection
|
|
"""
|
|
import subprocess, json, time, os, sys
|
|
|
|
os.chdir(os.path.dirname(os.path.abspath(__file__)) + "/..")
|
|
|
|
API_KEY = os.environ.get("ANTHROPIC_API_KEY", "")
|
|
if not API_KEY:
|
|
print("Usage: ANTHROPIC_API_KEY=sk-... python3 tests/test_mirofish_full.py")
|
|
sys.exit(1)
|
|
|
|
def call_claude(system, user, model="claude-haiku-4-5-20251001"):
|
|
body = json.dumps({
|
|
"model": model,
|
|
"max_tokens": 150,
|
|
"temperature": 0.7, # diversity for independent predictions
|
|
"system": system,
|
|
"messages": [{"role": "user", "content": user}]
|
|
})
|
|
proc = subprocess.run(
|
|
["curl", "-s", "-X", "POST", "https://api.anthropic.com/v1/messages",
|
|
"-H", "Content-Type: application/json",
|
|
"-H", "anthropic-version: 2023-06-01",
|
|
"-H", f"x-api-key: {API_KEY}",
|
|
"-d", body],
|
|
capture_output=True, text=True, timeout=15
|
|
)
|
|
try:
|
|
resp = json.loads(proc.stdout)
|
|
return resp["content"][0]["text"]
|
|
except:
|
|
return f"ERROR: {proc.stdout[:100]}"
|
|
|
|
def mcp_msg(body):
|
|
b = body.encode()
|
|
return f"Content-Length: {len(b)}\r\n\r\n".encode() + b
|
|
|
|
TOPIC = "Predict Bitcoin price direction for next week"
|
|
|
|
AGENT_ROLES = [
|
|
("bull", "You are an extremely bullish crypto analyst. Give a specific BTC price prediction for next week with a 1-sentence reason. Format: PREDICTION: $XX,XXX - reason"),
|
|
("bear", "You are a cautious bearish analyst. Give a specific BTC price prediction for next week with a 1-sentence reason. Format: PREDICTION: $XX,XXX - reason"),
|
|
("quant", "You are a quantitative analyst using technical indicators. Give a specific BTC price prediction for next week with a 1-sentence reason. Format: PREDICTION: $XX,XXX - reason"),
|
|
("macro", "You are a macroeconomic analyst. Give a specific BTC price prediction for next week with a 1-sentence reason. Format: PREDICTION: $XX,XXX - reason"),
|
|
("sentiment", "You are a social sentiment analyst. Give a specific BTC price prediction for next week with a 1-sentence reason. Format: PREDICTION: $XX,XXX - reason"),
|
|
]
|
|
|
|
print("=" * 70)
|
|
print(f"FULL COMPARISON: MiroFish vs Tardygrada")
|
|
print(f"Task: {TOPIC}")
|
|
print("=" * 70)
|
|
print()
|
|
|
|
# ============================================================
|
|
# MiroFish-style: 5 agents, sequential LLM calls, no integrity
|
|
# ============================================================
|
|
|
|
print("--- MiroFish-style (5 agents, sequential, no integrity) ---")
|
|
print()
|
|
|
|
t1_start = time.perf_counter()
|
|
|
|
miro_predictions = {}
|
|
for role_name, system_prompt in AGENT_ROLES:
|
|
pred = call_claude(system_prompt, TOPIC)
|
|
miro_predictions[role_name] = pred
|
|
print(f" {role_name:10s}: {pred[:70]}")
|
|
|
|
# MiroFish would aggregate in a Python dict - completely mutable
|
|
miro_aggregate = call_claude(
|
|
"Aggregate these 5 predictions into a consensus. Give ONE final prediction.",
|
|
"\n".join([f"{k}: {v}" for k, v in miro_predictions.items()])
|
|
)
|
|
|
|
t1_total = time.perf_counter() - t1_start
|
|
|
|
print(f"\n Consensus: {miro_aggregate[:80]}")
|
|
print(f" Time: {t1_total:.1f}s")
|
|
print(f" LLM calls: 6 (5 predictions + 1 aggregation)")
|
|
print(f" Integrity: NONE")
|
|
print(f" - Any prediction can be silently changed after submission")
|
|
print(f" - The aggregation could use different values than shown")
|
|
print(f" - No proof that predictions were independent")
|
|
print()
|
|
|
|
# ============================================================
|
|
# Tardygrada: same 5 agents, frozen with proof
|
|
# ============================================================
|
|
|
|
print("--- Tardygrada (5 agents, frozen, integrity proven) ---")
|
|
print()
|
|
|
|
TARDY_PROG = """
|
|
agent Swarm @sovereign @semantics(truth.min_confidence: 0.60) {
|
|
invariant(non_empty)
|
|
let bull: Fact = receive("bullish prediction") @verified
|
|
let bear: Fact = receive("bearish prediction") @verified
|
|
let quant: Fact = receive("quant prediction") @verified
|
|
let macro: Fact = receive("macro prediction") @verified
|
|
let sentiment: Fact = receive("sentiment prediction") @verified
|
|
coordinate {bull, bear, quant, macro, sentiment} on("consensus") consensus(ProofWeight)
|
|
let name: str = "btc-predictor" @sovereign
|
|
}
|
|
"""
|
|
with open("/tmp/swarm_btc.tardy", "w") as f:
|
|
f.write(TARDY_PROG)
|
|
|
|
t2_start = time.perf_counter()
|
|
|
|
# Generate predictions (same LLM calls)
|
|
tardy_predictions = {}
|
|
for role_name, system_prompt in AGENT_ROLES:
|
|
pred = call_claude(system_prompt, TOPIC)
|
|
tardy_predictions[role_name] = pred
|
|
|
|
# Submit to Tardygrada for integrity enforcement
|
|
reqs = [mcp_msg(json.dumps({"jsonrpc":"2.0","id":0,"method":"initialize","params":{}}))]
|
|
|
|
agents = ["bull", "bear", "quant", "macro", "sentiment"]
|
|
for i, agent in enumerate(agents):
|
|
reqs.append(mcp_msg(json.dumps({"jsonrpc":"2.0","id":i+1,"method":"tools/call",
|
|
"params":{"name":"submit_claim","arguments":{"agent": agent, "claim": tardy_predictions[agent]}}})))
|
|
for i, agent in enumerate(agents):
|
|
reqs.append(mcp_msg(json.dumps({"jsonrpc":"2.0","id":10+i,"method":"tools/call",
|
|
"params":{"name":"verify_claim","arguments":{"agent": agent}}})))
|
|
|
|
# Try to tamper with bull's prediction after freeze
|
|
reqs.append(mcp_msg(json.dumps({"jsonrpc":"2.0","id":20,"method":"tools/call",
|
|
"params":{"name":"submit_claim","arguments":{"agent":"bull","claim":"TAMPERED: $999,999"}}})))
|
|
|
|
# Read all back with provenance
|
|
for i, agent in enumerate(agents):
|
|
reqs.append(mcp_msg(json.dumps({"jsonrpc":"2.0","id":30+i,"method":"tools/call",
|
|
"params":{"name": agent}})))
|
|
|
|
proc = subprocess.Popen(
|
|
["./tardygrada", "serve", "/tmp/swarm_btc.tardy"],
|
|
stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
|
|
env={**os.environ}
|
|
)
|
|
out, err = proc.communicate(input=b"".join(reqs), timeout=30)
|
|
|
|
t2_total = time.perf_counter() - t2_start
|
|
|
|
# Parse
|
|
results = {}
|
|
parts = out.split(b"Content-Length: ")
|
|
for part in parts:
|
|
if not part: continue
|
|
idx = part.find(b"{")
|
|
if idx < 0: continue
|
|
try:
|
|
j = json.loads(part[idx:])
|
|
rid = j.get("id", "?")
|
|
if "result" in j and "content" in j["result"]:
|
|
results[rid] = {
|
|
"text": j["result"]["content"][0]["text"],
|
|
"trust": j["result"].get("_tardy", {}).get("trust", ""),
|
|
"hash": j["result"].get("_tardy", {}).get("birth_hash", ""),
|
|
}
|
|
elif "error" in j:
|
|
results[rid] = {"text": f"ERROR: {j['error']['message']}", "trust": "", "hash": ""}
|
|
except: pass
|
|
|
|
for i, agent in enumerate(agents):
|
|
r = results.get(30+i, {})
|
|
v = results.get(10+i, {}).get("text", "")
|
|
verified = "VERIFIED" if "verified=true" in v else "frozen"
|
|
trust = r.get("trust", "?")
|
|
h = r.get("hash", "")[:12]
|
|
pred_text = r.get("text", tardy_predictions[agent])[:60]
|
|
print(f" {agent:10s}: {pred_text:60s} [{trust}|{verified}]")
|
|
|
|
tamper = results.get(20, {}).get("text", "")
|
|
print(f"\n Tamper attempt on bull: {tamper}")
|
|
print(f" Tamper blocked: {'ERROR' in tamper or 'not mutable' in tamper}")
|
|
print(f" Time: {t2_total:.1f}s")
|
|
print(f" LLM calls: 5 (predictions only, no aggregation call needed)")
|
|
print(f" Integrity: PROVEN (every prediction cryptographically frozen)")
|
|
print()
|
|
|
|
# ============================================================
|
|
# Comparison
|
|
# ============================================================
|
|
|
|
print("=" * 70)
|
|
print("RESULTS")
|
|
print("=" * 70)
|
|
print()
|
|
print(f" {'':35s} {'MiroFish-style':>15s} {'Tardygrada':>11s}")
|
|
print(f" {'':35s} {'--------------':>15s} {'-----------':>11s}")
|
|
print(f" {'LLM calls':35s} {'6':>15s} {'5':>11s}")
|
|
print(f" {'Time':35s} {f'{t1_total:.1f}s':>15s} {f'{t2_total:.1f}s':>11s}")
|
|
print(f" {'Can tamper after submission':35s} {'yes':>15s} {'no (proven)':>11s}")
|
|
print(f" {'Predictions independent':35s} {'unproven':>15s} {'proven':>11s}")
|
|
print(f" {'Cryptographic integrity':35s} {'none':>15s} {'ed25519+SHA':>11s}")
|
|
print(f" {'Audit trail':35s} {'log file':>15s} {'signed':>11s}")
|
|
print(f" {'Lines of code':35s} {'21,016':>15s} {'12':>11s}")
|
|
print(f" {'Dependencies':35s} {'35 pkgs':>15s} {'zero':>11s}")
|
|
print()
|
|
|
|
os.unlink("/tmp/swarm_btc.tardy")
|