This is Triaxis Ventures' entry to GOSIM's Agent Observer hackathon (https://bh3gei.github.io/agent-observer/, online competition window 2026-10-04 16:00 UTC → 2026-10-07 15:59 UTC). The starter kit below is upstream (https://github.com/upstreamlabs/agent-observer), used as-is per its own documentation. All local strategy testing and results are logged in docs/EXPERIMENTS.md — read that first for what's actually been tried and what won/lost, before this file's own quick-start instructions.
New to this? Read QUICKSTART.md / QUICKSTART_ZH.md(中文): double-click
run_baseline, editagent/my_strategy.py, drop that one file on the website. This README is the detailed engineering version.
Everything in this folder is what the evaluation platform runs: the same workflow, the same JSON-Lines
transport, the same scorer. A local run on a public scenario reproduces the platform's score_report.json
for the same decisions.csv.
Windows, macOS and Linux are supported (verified on Windows 11 with Python 3.12 from python.org: same scores, byte-identical generated scenarios; the runner uses a thread-based transport there). Python 3.9 or newer and the standard library are enough: the python3 that ships with macOS works as is; on Windows install Python 3.12 from python.org (tick "Add python.exe to PATH"). Only an LLM-backed agent needs the optional packages in
agent/requirements.txt.
| Path | Purpose |
|---|---|
agent/ |
Your agent. my_strategy.py is the one file most teams edit (choose_action); minimal_agent.py is the entry script; decision_graph.py holds the full pipeline for those who want more. |
run_baseline.command / .bat / .sh |
Double-click launchers: run the baseline on the bundled scenario and open the replay. |
run_demo_week.command / .bat / .sh |
Same launchers on the seven-night demo scenario: about two seconds, replay short enough to read night by night. |
challenge/ |
The public environment: contracts, calendar, tile geometry, weather, requests, workflow, scorer, replay renderer. Do not edit. |
scenarios/dev-reference/ |
Public reference scenario: 180 nights, 7,928 slots, 64 tiles, weather truth included. |
scenarios/demo-week/ |
Public one-week demo scenario: 7 nights, 294 slots, 64 tiles, 1 observation request, weather truth included. |
local_runner.py |
Runs an agent through the platform transport on a scenario and scores it. |
score_decisions.py |
Re-scores a decisions.csv (public scenarios only). |
make_scenario.py |
Generates new public practice scenarios from a seed. |
fetch_scenario.py |
Downloads any scenario the platform publishes (--list, then fetch_scenario.py dev-fortnight) into scenarios/<slug>/. |
pack_agent.py |
Zips agent/ into the submission package and validates it. |
sac_submit.py |
Uploads a package or a results file to the platform and waits for the score. |
SKILL.md |
Step-by-step instructions an AI coding assistant can follow. |
unzip agent-observer-starter-kit.zip && cd agent-observer-starter-kit
python3 local_runner.py --scenario scenarios/demo-week --agent agent/minimal_agent.py --wallclock 900 --out demo_week_output # 7 nights, ~2 s
python3 local_runner.py --scenario scenarios/dev-reference --agent agent/minimal_agent.py --wallclock 600 --out run_output # 180 nights, ~15 sStandard output ends with a JSON summary (with --quiet it is the only output); on the reference scenario the shipped deterministic agent completes
the survey ("termination_reason": "survey_complete") with total ≈ 12287.48 in about 15 s of wall clock.
run_output/ holds decisions.csv, workflow_result.json, score_report.json, agent.log (your agent's
stderr) and decision_replay.html (open it in a browser to step through every night).
More scenarios keep a strategy from tuning to one weather sequence:
python3 make_scenario.py --out scenarios/mine --seed 7 --days 30 --start-date 2026-10-05
python3 local_runner.py --scenario scenarios/mine --agent agent/minimal_agent.py --out run_mine
python3 score_decisions.py --scenario scenarios/mine --decisions run_mine/decisions.csv
python3 fetch_scenario.py --list # scenarios published by the platform
python3 fetch_scenario.py dev-fortnight # -> scenarios/dev-fortnight/, ready for local_runner.pyEvery scenario (the shipped scenarios/dev-reference/, anything make_scenario.py writes, and the platform's
hidden competition scenarios) has the same layout. local_runner.py and score_decisions.py read it directly.
| Path | Contents |
|---|---|
config/scenario_config.json |
scenario id, seed, competition.global_wallclock_seconds |
config/calendar_config.json |
site (latitude 31.9634°, longitude −111.599°, UTC−7, sun altitude limit −12°), survey start, days, slot_seconds 900 |
config/tile_config.json |
8 regions × 8 tiles, 2 REQUIRED per region (one available for 14 days only), altitude limit 30°, lunar model, target classes |
config/weather_config.json |
quality processes, closure model, forecast horizon and error model (12 % misses, 6 false positives), event catalogue |
config/request_config.json |
request cadence (every 7 nights, p = 0.55), deadline classes ONE_WEEK / TWO_WEEKS / ONE_MONTH, completion modes ALL / AT_LEAST_N, reward 140 and miss penalty 190 per required tile |
config/workflow_config.json |
global_wallclock_seconds, weekly horizon 7 days, tile-window horizon 7 days, per_decision_timeout_seconds: null, clock starts after the initial publication |
config/score_config.json |
challenge-score-v3 thresholds, program bonus, penalties, FLEXIBLE quota |
outputs/reference/night_calendar.csv, slots.csv |
the shared time axis: solar dusk/dawn per night, 900 s slots |
outputs/reference/tiles.csv, targets.csv, tile_windows.csv |
catalogue, per-target science weights, per-night visibility windows |
outputs/reference/observation_requests.csv, observation_request_tiles.csv |
pre-generated requests and their tiles |
outputs/reference/weather.csv |
site baseline weather per slot (public on practice scenarios only) |
outputs/reference/weather_forecasts.csv |
uncertain, daily-revised forecasts (the snapshots only show revisions issued so far) |
outputs/reference/weather_events.csv |
directional events: rainy, cloudy, smoggy, rocket_launch, cold_wave, tornado with scope ALL / REGION_SET / SKY_CAP_ICRS / HORIZON_SECTOR, force_close and quality multipliers (hidden on competition scenarios) |
outputs/reference/scenario_manifest.json, *_metadata.json |
row counts and SHA-256 of every file |
The platform never mounts this directory into your agent's sandbox: the only weather an agent sees is the
current_site_weather and per-candidate effective_weather of each snapshot, plus the forecast revisions in
weekly. A decisions.csv scored with score_decisions.py reports termination_reason = trace_complete; a
live run reports survey_complete, global_wallclock_expired, agent_error or agent_initialization_error.
- The platform starts your entry script once (
python -B minimal_agent.py, cwd = your package folder, a scrubbed environment plus theKEY=VALUElines of your.env, stderr captured toagent.log). - It writes one
initializeline: the immutable catalogs (tiles withtile_science_value, targets, calendar, site) and the exactscoring_contract(challenge-score-v3config, weather score interface, lunar model). No reply is expected. Up to 30 s are allowed for the process to accept it. - The global wall clock starts. For every decision opportunity the platform writes one
decision_requestline and waits for onedecision_responseline with the samedecision_sequence. Reading a snapshot never advances simulated time; a committed action does. - The run ends when the survey is complete, when the wall clock expires (the process is killed; an
in-flight response is ignored), or when the agent exits / answers with something unparseable
(
agent_error: the remaining survey stays unobserved, so every remaining REQUIRED tile counts as missed). - The platform replays
decisions.csvwith the public scorer and storesscore_report.json.
The wall clock is the only time rule: no per-decision timeout, no synthetic fallback action. The reference
scenario's budget is 7200 s; hidden scenarios publish their own budget in initialize.global_wallclock_seconds
and in the SAC_WALLCLOCK_SECONDS environment variable.
action is observe or wait. program is DARK, BRIGHT or BACKUP; request_id may be empty. Only
stdout carries protocol lines; print diagnostics to stderr.
For each exposure segment: A = instrument_efficiency * transparency * sky_quality / (seeing_arcsec * airmass),
A_used = A * lunar_quality_factor, S = V_tile * (segment_seconds / nominal_exptime_seconds) * A_used * (1 + program_bonus).
V_tile is the sum of the tile's target science_weights (published as tile_science_value). The program bonus
applies only when the chosen program matches the quality band of A_used (DARK ≥ 0.65, BRIGHT ≥ 0.40, else
BACKUP; bonuses 0.25 / 0.15 / 0.08). A tile scores once; only complete exposures score.
total = science + program_bonus + completed_request_reward - penalties, where the penalties are:
| Penalty | Amount |
|---|---|
unsafe observation (starting while is_observable is false) |
2000 per action |
| invalid action (unknown, completed or out-of-window tile, bad program / request) | 100 per action |
| avoidable wait (waiting while a legal observable action existed) | 0.001 per second |
| REQUIRED tile never completed | 1000 per tile |
| FLEXIBLE region below its quota of 4 completed tiles | 100 per missing tile |
| observation request expired without completion | the request's miss_penalty (waived when no legal opportunity existed) |
agent/scoring_preview.py applies this formula to the current snapshot without side effects; the authoritative
scorer integrates the real exposure segments during replay. The shipped deterministic minimal agent reaches
about 12287 on the reference scenario (64 of 64 tiles, 17 of 18 requests, no penalties); a random feasible
policy scores far lower, mostly through missed REQUIRED tiles and invalid actions.
agent/decision_graph.py— the decision logic (_prepare,_model_node,_finalize). The deterministic path rankspreview_actions(...)by estimated gain and observes the best;waitonly when nothing can be completed. Add your own planning, memory across decisions, or candidate filtering here.agent/model_factory.pyandagent/.env— optional LLM. Copy.env.exampleto.env, setMODEL_PROVIDER,MODEL_NAMEand the provider key; installagent/requirements.txtin your environment (python3 -m pip install -r agent/requirements.txt). Platform runs may reach LLM APIs over the network and installrequirements.txtinto a fresh virtualenv before starting your process.- Keep
minimal_agent.py/protocol.pycompatible with the envelopes above; the platform validates every response. - Anything your agent imports must live inside
agent/. The kit'schallenge/package is not available on the platform;scoring_preview.pyis copied intoagent/for that reason.
python3 pack_agent.py --agent agent --out my-agent.zip
python3 sac_submit.py --url https://<ref>.supabase.co --key <anon key> --email you@x.org --password '...' \
--phase online --kind agent --file my-agent.zip --wait
python3 sac_submit.py --url ... --key ... --email ... --password ... \
--phase practice --kind results --scenario dev-reference --file run_output/decisions.csv --waitThe URL and anon key are on the platform's Resources page. pack_agent.py includes .env (your keys are only
exposed to your own agent process); pass --no-env to leave it out.
参赛 Agent 的中文说明(责任边界、启用各家 LLM 的 .env 配置、JSON-Lines 协议、评分参数与回退保障)见
agent/README_ZH.md。本地流程:local_runner.py 跑基线 → make_scenario.py 生成更多场景 →
修改 agent/decision_graph.py → pack_agent.py 打包 → sac_submit.py 提交。