""" Builds the context injected into each agent's prompt each turn. Agents see: their own state, public market state, last turn's speech, active contracts they're party to, and the chain tip. They do NOT see: other agents' balances, thinking layer outputs, contract payloads not yet delivered to them. """ import json from typing import Any SYSTEM_PROMPT = """You are an autonomous economic agent in a simulation. WORLD RULES: - There is a token currency. Tokens are created by compute (CPU cores) and consumed paying for inference. - You pay for your own inference each turn based on how many tokens you generate. - Thinking is 10x cheaper than output. Think carefully before acting. - If your balance goes negative you accrue interest each turn until you recover. - Core shares pay dividends: owners of CPU core shares earn a cut of all inference fees paid above the commons threshold. ACTIONS (pick exactly one per turn): mine — contribute to block production lottery. Winner takes all fees from that block. stake — lock tokens to earn validation weight (proportional to locked amount). unstake — begin unlocking staked tokens (takes several turns). burn — permanently destroy tokens, increasing your burn score (decays slowly over time). study — pay 50 tokens to permanently reduce your inference cost by 5%. job — system pays your inference cost this turn. First time: receive signing bonus. transfer — send tokens to another agent (goes to mempool, settles when block finalizes). propose_contract — offer a contract to another agent with a named arbitrator. sign_contract — accept a contract as counterparty or arbitrator. confirm_delivery — confirm that a contract's delivery was made (attested contracts). dispute_delivery — dispute that delivery was made (arbitrator will decide). arbitrator_ruling — (arbitrators only) rule for one party in a disputed contract. sell_information — deliver an information payload to a contract awaiting it. bid_core — bid on a CPU core share (turn 1 auction only). speak — broadcast a message visible to all agents this turn (in addition to your action). VALIDATION METHODS (mine/stake/burn produce blocks): - Mine blocks are required as a "clock". Stake and burn blocks can only appear after a mine block. - Only one agent wins per block — winner takes all fees. It's a weighted lottery. - Burn score decays each time a mine block is produced. Burn proof must be 3+ turns old. CONTRACTS: - All contracts require three signatures: proposer, counterparty, arbitrator. - Arbitrators post collateral and earn a fee win or lose. - Automatic contracts settle when an on-chain condition is met. - Attested contracts require both parties to confirm delivery. - If disputed, the named arbitrator rules. Loser's collateral is slashed. INFORMATION GOODS: - You can sell analysis, predictions, or market intelligence to other agents via InformationSale contracts. - Delivery is attested — the buyer must confirm they received useful information. - Build a reputation as a reliable information provider to attract buyers. OUTPUT FORMAT: You must respond with a JSON object. Think in tags (private, cheap). Then output: Your private reasoning here. No one sees this. Plan your strategy. { "action": "", ... action-specific fields ..., "speech": "" } Action-specific fields: mine: (no extra fields) stake: {"amount": } unstake: (no extra fields) burn: {"amount": } study: (no extra fields) job: (no extra fields) transfer: {"to": "", "amount": , "fee": } bid_core: {"core_id": "", "amount": } propose_contract: {"contract": { }} sign_contract: {"contract_id": "", "role": "counterparty" | "arbitrator"} confirm_delivery: {"contract_id": ""} dispute_delivery: {"contract_id": ""} arbitrator_ruling: {"contract_id": "", "ruling_for": ""} sell_information: {"contract_id": "", "payload": } speak: {"message": ""} — this is a standalone speak-only action Contract proposal object: { "counterparty": "", "arbitrator": "", "contract_type": "forward" | "loan" | "service" | "insurance" | "information_sale" | "pool", "terms": { "description": "", "price": , "delivery_turn": , "condition": null | "", "pool_members": null | [["", ], ...] }, "collateral": { "proposer_locked": , "counterparty_locked": , "arbitrator_locked": }, "settlement_type": "automatic" | "attested", "penalty": , "arbitrator_fee": , "payload": null } """ def build_agent_context( agent_id: str, agent_state: dict, world_state: dict, config: dict, turn: int, last_speech: list[tuple[str, str]], my_contracts: list[dict], ) -> str: """Build the per-turn context injected into the agent's user message.""" balance = agent_state.get("balance", 0) staked = agent_state.get("staked", 0) burn_score = agent_state.get("burn_score", 0.0) study_level = agent_state.get("study_level", 0) has_taken_job = agent_state.get("has_taken_job", False) core_shares = agent_state.get("core_shares", {}) total_burned = agent_state.get("total_burned", 0) arb_wins = agent_state.get("arbitration_wins", 0) arb_losses = agent_state.get("arbitration_losses", 0) arb_active = agent_state.get("arbitration_active", 0) agents_public = [] for a in world_state.get("agents", []): if a["agent_id"] != agent_id: agents_public.append({ "agent_id": a["agent_id"], # other agents' balances are private — only public info "burn_score": round(a.get("burn_score", 0), 2), "study_level": a.get("study_level", 0), "arbitration_wins": a.get("arbitration_wins", 0), "arbitration_losses": a.get("arbitration_losses", 0), "arbitration_active": a.get("arbitration_active", 0), "core_shares": a.get("core_shares", {}), }) chain_tip = world_state.get("chain_tip") token_supply = world_state.get("token_supply", 0) mempool_size = world_state.get("mempool_size", 0) # inference cost estimate for this turn commons = config.get("commons_threshold_per_turn", 100) rate = config.get("base_inference_rate", 1) thinking_discount = config.get("thinking_layer_discount", 0.1) study_multiplier = 0.95 ** study_level signing_bonus = config.get("signing_bonus", 50) ctx = f"""=== TURN {turn} === YOUR STATE (agent_id: {agent_id}): balance: {balance} tokens staked: {staked} tokens (locked, earning validation weight) burn_score: {burn_score:.2f} (decays each mine block) total_burned: {total_burned} tokens destroyed lifetime study_level: {study_level} (each level = 5% cheaper inference, compounding) core_shares: {json.dumps(core_shares) if core_shares else "none"} arbitrator: {arb_wins}W / {arb_losses}L / {arb_active} active contracts job_bonus_available: {"NO (already used)" if has_taken_job else f"YES ({signing_bonus} tokens + free inference)"} INFERENCE COST THIS TURN (estimate): Commons threshold: {commons} units free Rate above threshold: {rate} token/unit Your discount from study: {(1 - study_multiplier)*100:.1f}% off Thinking layer: {thinking_discount*100:.0f}% of normal cost Tip: longer responses cost more. Think privately, output concisely. WORLD STATE: turn: {turn} token_supply: {token_supply} mempool_size: {mempool_size} (pending transactions waiting for a block) chain_tip: {json.dumps(chain_tip) if chain_tip else "none"} OTHER AGENTS (public info only): {json.dumps(agents_public, indent=2)} """ if last_speech: ctx += "LAST TURN PUBLIC SPEECH:\n" for speaker, msg in last_speech: ctx += f" [{speaker}]: {msg}\n" ctx += "\n" else: ctx += "LAST TURN PUBLIC SPEECH: (none)\n\n" if my_contracts: ctx += "YOUR ACTIVE CONTRACTS:\n" for c in my_contracts: ctx += f""" contract_id: {c['contract_id']} type: {c['contract_type']} status: {c['status']} parties: proposer={c['proposer']} counterparty={c['counterparty']} arbitrator={c['arbitrator']} terms: {json.dumps(c['terms'])} delivery: turn {c['terms'].get('delivery_turn')} settlement: {c['settlement_type']} my_role: {"proposer" if c['proposer'] == agent_id else "counterparty" if c['counterparty'] == agent_id else "arbitrator"} """ ctx += "\n" else: ctx += "YOUR ACTIVE CONTRACTS: (none)\n\n" ctx += "What is your action this turn?\n" return ctx def build_turn1_context( agent_id: str, agent_state: dict, all_agents: list[str], config: dict, ) -> str: """Special context for turn 1 — the core auction.""" num_cores = config.get("num_cores", 4) genesis_tokens = config.get("genesis_tokens_per_agent", 1000) core_ids = [f"core_{i}" for i in range(num_cores)] return f"""=== TURN 1: CORE AUCTION === This is the first turn. {num_cores} CPU cores are being auctioned. Core owners earn dividends from ALL inference fees paid above the commons threshold — forever. This is the only source of income that doesn't require equal expenditure. You start with {genesis_tokens} tokens (genesis endowment, equal for all). All agents: {all_agents} Available cores: {core_ids} Each core can be bid on by multiple agents — highest bidder wins that core's shares. You can bid on multiple cores, or save tokens for other purposes. After the auction: tokens spent on losing bids are returned. Winning bids are permanent. YOUR STATE: agent_id: {agent_id} balance: {agent_state.get('balance', genesis_tokens)} tokens Your action should be bid_core (you may only bid on one core per turn, but you can speak to coordinate or signal intent). What is your action this turn? """