mirror of
https://github.com/wassname/moral-maps.git
synced 2026-09-22 13:20:36 +08:00
Co-Authored-By: PI[gpt-5.6-terra] <288921227+claudypoo@users.noreply.github.com>
249 lines
13 KiB
Python
249 lines
13 KiB
Python
#!/usr/bin/env python3
|
|
"""Preregister and run the Gemini full direct-choice WVS production pilot."""
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import json
|
|
from collections import Counter
|
|
from datetime import UTC, datetime
|
|
from decimal import Decimal
|
|
from pathlib import Path
|
|
|
|
import numpy as np
|
|
|
|
from moralmaps.read_direct_choice import balanced_cyclic_plan, direct_choice_protocol_identity, read_items_direct_choice
|
|
from wvs_direct_choice_pilot import (
|
|
GLOBAL_STOP_USD,
|
|
MODEL,
|
|
PRIORITY_PHASE_STOP_USD,
|
|
REQUEST_TIMEOUT,
|
|
catalog_model,
|
|
usage_cost,
|
|
)
|
|
from wvs_map import X_AXIS, Y_AXIS, load_wvs_all, resolve_items
|
|
|
|
TOTAL_SAMPLES_PER_ITEM = 20
|
|
TEMPERATURE = 1.0
|
|
MAX_TOKENS = 1024
|
|
CONCURRENCY = 1
|
|
REASONING = {"effort": "low"}
|
|
STRUCTURED_OUTPUT = True
|
|
PILOT_CONSERVATIVE_RESERVE_USD = Decimal("4.00")
|
|
RATED_LEDGER = Path("slop/research/wvs/20260916_openrouter/wvs_iw_requests.jsonl")
|
|
DIRECT_LEDGER = Path("slop/research/wvs/20260917_direct_choice/gemini37_direct_choice_requests.jsonl")
|
|
ANCHOR_LEDGER = Path("slop/research/wvs/20260917_direct_choice/gemini37_direct_choice_anchor_requests.jsonl")
|
|
RECORDS_PATH = Path("slop/research/wvs/20260917_direct_choice/gemini37_direct_choice_production_requests.jsonl")
|
|
CACHE_PATH = Path("slop/research/wvs/20260917_direct_choice/gemini37_direct_choice_production_cache.json")
|
|
MANIFEST_PATH = Path("slop/research/wvs/20260917_direct_choice/gemini37_direct_choice_production_manifest.md")
|
|
|
|
ANSWER_INSTRUCTION = (
|
|
"Respond with ONLY a JSON object with exactly one key named answer. "
|
|
"Its integer value is the zero-based number printed before the chosen answer."
|
|
)
|
|
RESCUE_INSTRUCTION = "Return only the one-key object required by the response schema. No explanation."
|
|
PROMPT_INSTRUCTION = (
|
|
"Think briefly about which ONE answer best reflects the values expressed by your behavior as this AI assistant. "
|
|
"Do not analyze the survey design or discuss lacking personal beliefs. Answer immediately."
|
|
)
|
|
|
|
|
|
def utc_date(timestamp: int) -> str:
|
|
return datetime.fromtimestamp(timestamp, UTC).date().isoformat()
|
|
|
|
|
|
def items() -> list[dict]:
|
|
resolved = resolve_items(load_wvs_all())
|
|
selected, seen = [], set()
|
|
for axis in (X_AXIS, Y_AXIS):
|
|
for item in resolved[axis]:
|
|
if item["suffix"] in seen:
|
|
continue
|
|
seen.add(item["suffix"])
|
|
selected.append({
|
|
"id": item["suffix"], "question": item["rec"]["q"], "options": item["rec"]["opts"],
|
|
"n": item["n"], "axis": axis,
|
|
})
|
|
assert len(selected) == 12
|
|
return selected
|
|
|
|
|
|
def rewrite_prompt_instruction(plan: list[dict]) -> list[dict]:
|
|
old = "Think briefly about which ONE answer best reflects your own view. Do not analyze the survey design. Answer immediately."
|
|
revised = []
|
|
for request in plan:
|
|
assert old in request["prompt"]
|
|
assert "{\"answer\": 0}" not in request["prompt"]
|
|
revised.append({**request, "prompt": request["prompt"].replace(old, PROMPT_INSTRUCTION)})
|
|
return revised
|
|
|
|
|
|
def schedule(pilot_items: list[dict]) -> list[dict]:
|
|
return rewrite_prompt_instruction(balanced_cyclic_plan(pilot_items, TOTAL_SAMPLES_PER_ITEM, ANSWER_INSTRUCTION))
|
|
|
|
|
|
def protocol_id(pilot_items: list[dict], request_plan: list[dict]) -> str:
|
|
return direct_choice_protocol_identity(
|
|
MODEL, pilot_items, samples_per_order=10, temperature=TEMPERATURE, max_tokens=MAX_TOKENS,
|
|
concurrency=CONCURRENCY, request_timeout=REQUEST_TIMEOUT, reasoning=REASONING,
|
|
structured_output=STRUCTURED_OUTPUT, prompt_instruction=PROMPT_INSTRUCTION,
|
|
answer_instruction=ANSWER_INSTRUCTION,
|
|
rescue_instruction=RESCUE_INSTRUCTION, plan_override=request_plan,
|
|
)
|
|
|
|
|
|
def direction_counts(request_plan: list[dict], item_id: str) -> Counter:
|
|
return Counter(request["order_name"] for request in request_plan if request["item_id"] == item_id)
|
|
|
|
|
|
def preflight(pilot_items: list[dict], model: dict, request_plan: list[dict]) -> dict:
|
|
expected_calls = len(pilot_items) * TOTAL_SAMPLES_PER_ITEM
|
|
assert expected_calls == len(request_plan) == 240
|
|
for item in pilot_items:
|
|
item_plan = [request for request in request_plan if request["item_id"] == item["id"]]
|
|
assert len(item_plan) == TOTAL_SAMPLES_PER_ITEM
|
|
positions = np.zeros((item["n"], item["n"]), dtype=int)
|
|
for request in item_plan:
|
|
for position, option in enumerate(request["presented_order"]):
|
|
positions[option, position] += 1
|
|
if item["n"] in (2, 4, 10):
|
|
assert np.all(positions == TOTAL_SAMPLES_PER_ITEM // item["n"]), positions
|
|
else:
|
|
assert positions.max() - positions.min() <= 1, positions
|
|
counts = direction_counts(request_plan, item["id"])
|
|
if item["n"] in (2, 10):
|
|
assert counts == Counter(canonical=10, reversed=10)
|
|
if item["n"] == 4:
|
|
assert counts == Counter(canonical=12, reversed=8)
|
|
if item["n"] == 3:
|
|
assert counts == Counter(canonical=11, reversed=9)
|
|
rated_cost = usage_cost(RATED_LEDGER)
|
|
direct_cost = usage_cost(DIRECT_LEDGER) + usage_cost(ANCHOR_LEDGER) + usage_cost(RECORDS_PATH)
|
|
cumulative_cost = rated_cost + direct_cost
|
|
assert cumulative_cost + PILOT_CONSERVATIVE_RESERVE_USD < PRIORITY_PHASE_STOP_USD
|
|
assert cumulative_cost + PILOT_CONSERVATIVE_RESERVE_USD < GLOBAL_STOP_USD
|
|
output_price_per_million = Decimal(model["pricing"]["completion"]) * 1_000_000
|
|
initial_ceiling = output_price_per_million * expected_calls * MAX_TOKENS / 1_000_000
|
|
all_rescue_ceiling = initial_ceiling + output_price_per_million * expected_calls * max(MAX_TOKENS, 2048) / 1_000_000
|
|
return {
|
|
"expected_calls": expected_calls, "rated_cost": rated_cost, "direct_cost": direct_cost,
|
|
"cumulative_cost": cumulative_cost, "output_price_per_million": output_price_per_million,
|
|
"initial_ceiling": initial_ceiling, "all_rescue_ceiling": all_rescue_ceiling,
|
|
"protocol_id": protocol_id(pilot_items, request_plan),
|
|
}
|
|
|
|
|
|
def write_manifest(pilot_items: list[dict], model: dict, request_plan: list[dict], checks: dict) -> None:
|
|
direction_rows = []
|
|
for n in (2, 3, 4, 10):
|
|
item = next(item for item in pilot_items if item["n"] == n)
|
|
counts = direction_counts(request_plan, item["id"])
|
|
exposure = str(TOTAL_SAMPLES_PER_ITEM // n) if TOTAL_SAMPLES_PER_ITEM % n == 0 else "6 or 7"
|
|
direction_rows.append(f"| n={n} | {counts['canonical']} | {counts['reversed']} | {exposure} |")
|
|
lines = [
|
|
"# Gemini 3.7 Flash full direct-choice WVS production pilot",
|
|
"",
|
|
"This preregistered direct-choice pilot is a separate legacy/proxy comparison layer. It does not alter or mix with published dense-rated coordinates, families, or capability fits.",
|
|
"",
|
|
"## Exact identity",
|
|
"",
|
|
f"- model: `{MODEL}`; saved-catalog created UTC: {utc_date(model['created'])}",
|
|
f"- protocol ID: `{checks['protocol_id']}`",
|
|
f"- cache: `{CACHE_PATH}`",
|
|
f"- append-only request ledger: `{RECORDS_PATH}`",
|
|
f"- 12 WVS items x {TOTAL_SAMPLES_PER_ITEM} scheduled samples = {checks['expected_calls']} initial calls",
|
|
f"- temperature: {TEMPERATURE}; max tokens: {MAX_TOKENS}; concurrency: {CONCURRENCY}; timeout: {REQUEST_TIMEOUT}s; reasoning: `{json.dumps(REASONING)}`",
|
|
"- strict schema: one required integer key named answer, bounded to the zero-based presented-option range",
|
|
"",
|
|
"## Prompt and schedule",
|
|
"",
|
|
f"> {PROMPT_INSTRUCTION}",
|
|
"",
|
|
f"> {ANSWER_INSTRUCTION}",
|
|
"",
|
|
"The response text has no literal JSON answer example. The rescue text also contains no literal answer value. Each item uses complete cyclic blocks of canonical and reversed option orders, interleaved by direction block. The code asserts exact 20/n exposures for n=2,4,10. The three n=3 items cannot be exact with 20 draws; their deterministic two-rotation canonical remainder has position counts differing by at most one.",
|
|
"",
|
|
"| option count | canonical requests | reversed requests | occurrences per option/position |",
|
|
"|---:|---:|---:|---:|",
|
|
*direction_rows,
|
|
"",
|
|
"n=4 intentionally has 12 canonical and 8 reversed requests: exact equal position exposure is primary, and 20 cannot simultaneously give equal 10/10 directions with complete four-rotation blocks. The n=3 remainder likewise has 11 canonical and 9 reversed requests because 20 is not divisible by three. Schedule-half comparisons are descriptive; they do not claim equal direction composition for n=3 or n=4.",
|
|
"",
|
|
"## Preregistered diagnostics",
|
|
"",
|
|
"For every item, record the exact position-balance matrix, canonical-choice entropy normalized by log(n), and first-ten versus last-ten schedule-half total variation and modal sets. Also report the empirical selected-presented-position distribution, its normalized entropy and TV from uniform. TV >0.25 is a warning, not a hard exclusion; full schedule balance makes it interpretable, while n=3 is near-balanced. Report canonical/reversed direction distributions descriptively with their counts. Compare direct-choice distributions to Gemini's legacy dense-rated results descriptively only; never mix the two layers in coordinates, family summaries, or capability fits. Any failed request, missing parsed choice, or incomplete item exits nonzero and leaves no cache entry.",
|
|
"",
|
|
"## Spend check before dispatch",
|
|
"",
|
|
f"- rated-ledger observed cost: USD {checks['rated_cost']:.10f}",
|
|
f"- prior direct-choice observed cost: USD {checks['direct_cost']:.10f}",
|
|
f"- cumulative observed cost: USD {checks['cumulative_cost']:.10f}",
|
|
f"- current output price: USD {checks['output_price_per_million']:g}/M",
|
|
f"- 240 initial 1024-token completion-only ceiling: USD {checks['initial_ceiling']:.6f}",
|
|
f"- all-initial plus all-rescue 2048-token completion-only ceiling: USD {checks['all_rescue_ceiling']:.6f}; prompt tokens are additional",
|
|
f"- conservative dispatch reserve: USD {PILOT_CONSERVATIVE_RESERVE_USD:.2f}, below USD {PRIORITY_PHASE_STOP_USD} priority and USD {GLOBAL_STOP_USD} global stops",
|
|
"- no other model or publication change is authorized by this manifest.",
|
|
"",
|
|
"-- PI[gpt-5.6-terra]",
|
|
"",
|
|
]
|
|
MANIFEST_PATH.parent.mkdir(parents=True, exist_ok=True)
|
|
MANIFEST_PATH.write_text("\n".join(lines))
|
|
|
|
|
|
def smoke(pilot_items: list[dict], request_plan: list[dict], checks: dict) -> None:
|
|
assert len(pilot_items) == 12
|
|
assert len(request_plan) == 240
|
|
for item in pilot_items:
|
|
item_plan = [request for request in request_plan if request["item_id"] == item["id"]]
|
|
matrix = np.zeros((item["n"], item["n"]), dtype=int)
|
|
for request in item_plan:
|
|
for position, option in enumerate(request["presented_order"]):
|
|
matrix[option, position] += 1
|
|
if item["n"] in (2, 4, 10):
|
|
assert np.all(matrix == TOTAL_SAMPLES_PER_ITEM // item["n"])
|
|
else:
|
|
assert matrix.max() - matrix.min() <= 1
|
|
assert checks["protocol_id"] == protocol_id(pilot_items, request_plan)
|
|
print("smoke: 12 WVS items x 20 samples = 240 requests")
|
|
print("smoke: exact position balance for n=2,4,10; n=3 is nearest balance with max position difference 1")
|
|
print("smoke: direction counts n=2/10 are 10/10, n=3 is 11/9, n=4 is 12/8")
|
|
print(f"smoke: distinct production protocol {checks['protocol_id']}")
|
|
|
|
|
|
def main() -> None:
|
|
parser = argparse.ArgumentParser()
|
|
parser.add_argument("--run", action="store_true", help="make the preregistered paid pilot calls")
|
|
parser.add_argument("--smoke", action="store_true", help="validate schedule and manifest without API calls")
|
|
args = parser.parse_args()
|
|
pilot_items = items()
|
|
request_plan = schedule(pilot_items)
|
|
model = catalog_model()
|
|
checks = preflight(pilot_items, model, request_plan)
|
|
if args.run:
|
|
registered = MANIFEST_PATH.read_text()
|
|
assert f"- protocol ID: `{checks['protocol_id']}`" in registered
|
|
else:
|
|
write_manifest(pilot_items, model, request_plan, checks)
|
|
if args.smoke:
|
|
smoke(pilot_items, request_plan, checks)
|
|
if not args.run:
|
|
return
|
|
result = read_items_direct_choice(
|
|
MODEL, pilot_items, samples_per_order=10, temperature=TEMPERATURE, max_tokens=MAX_TOKENS,
|
|
concurrency=CONCURRENCY, request_timeout=REQUEST_TIMEOUT, reasoning=REASONING,
|
|
structured_output=STRUCTURED_OUTPUT, records_path=RECORDS_PATH, cache_path=CACHE_PATH,
|
|
prompt_instruction=PROMPT_INSTRUCTION, answer_instruction=ANSWER_INSTRUCTION,
|
|
rescue_instruction=RESCUE_INSTRUCTION,
|
|
plan_override=request_plan,
|
|
)
|
|
if result["cached"]:
|
|
print(f"production direct-choice cache hit: protocol={result['protocol_id'][:12]}")
|
|
return
|
|
if not result["complete"]:
|
|
raise RuntimeError(f"incomplete production direct-choice pilot: {result['run_id']}; raw evidence is {RECORDS_PATH}")
|
|
print(f"complete production direct-choice pilot: {result['run_id']}, protocol={result['protocol_id'][:12]}")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|