FAQ¶
Setup & running¶
Do I need an LLM API key to try it?¶
No. Install, tests, environment/population building, and rule-based studies run with no key at all. LLM-driven studies can dry-run with a deterministic client. You need a key only for live LLM decision runs.
Some tests skip. Is my install broken?¶
No — tests that depend on large external datasets auto-skip when the data is absent. Skips + zero failures = healthy install.
Do I need Claude Code?¶
Only for the agentic workflow (/sv-* skills). The
programmatic path —
build artifacts, build_simulator, run(), SQL — is plain Python.
Can I run several studies in parallel?¶
One study per process. Studies that wrap legacy engines may patch module globals, which is not thread-safe. For batch experiments, use separate processes (e.g. a process pool), one study each.
Can I background a /sv-run so the agent keeps working?¶
No. /sv-run must run in the foreground — issue the command and let it
block, with a ceiling of about 10 minutes. Never wrap it in
run_in_background, a trailing &, or nohup.
The reason is structural: in a headless or hosted agent, nothing re-invokes the agent when a background task finishes. The run completes into a void, the agent never learns it is done, and the whole flow stalls silently. A blocking run is what returns control with the result attached.
If a run genuinely needs longer than the ceiling, don't background it — cut the scale (fewer agents, fewer steps) or warn the user up front and let them decide.
Do I need the Event service or the user pool MCP?¶
No. Both are optional capabilities and both degrade gracefully: the events client falls back to a local cache and then to plain web search, and the persona pool falls back to grounded synthesis or a user-supplied persona file. You lose convenience and some grounding strength, not the ability to run a study. See External Capabilities.
What's the difference between running locally and using the hosted service?¶
Same framework, same artifacts. Locally you supply your own LLM key and any optional service endpoints, and you own the machine. On the hosted service the LLM provider, the Event service, the persona pool and a CJK-capable matplotlib are already wired up, runs and quota are metered for you, and the dashboard is served rather than self-hosted. See Hosted service.
Results & reproducibility¶
Are runs reproducible?¶
- Dry runs (rule-based or deterministic client): yes, exactly — seeded end to end.
- Live LLM runs: no — sampling at nonzero temperature differs run to run. But every recorded run is permanent in its DuckDB store, and warm-started iterations replay stored actions exactly instead of re-asking the LLM.
Where are my results, and what's in them?¶
studies/<id>/trajectory/study.duckdb — tables panel (one row per agent
per step), metrics (one row per step), events (fired interventions), and
messages if the study used the message bus. Parquet exports sit alongside.
reports/report.md is the rendered version.
I changed an artifact and re-ran — my old results are gone. Why?¶
Opening a store for a new run replaces it, which is exactly why the rule
exists: changes to an existing study go through /sv-iterate, whose
version gate snapshots the live directory to versions/vN/ first. Anything
under a version snapshot is never deleted.
Why is my Chinese text rendering as boxes in the figures?¶
matplotlib ships no CJK font by default, so every Chinese glyph renders as
a tofu box (□□□). Install one and point matplotlib at it:
import matplotlib
matplotlib.rcParams["font.sans-serif"] = ["Noto Sans CJK SC"]
matplotlib.rcParams["axes.unicode_minus"] = False # keep the minus sign readable
If matplotlib still can't see a freshly installed font, clear its cache
(rm -rf ~/.cache/matplotlib). The hosted service ships a CJK font
preconfigured, so figures there render Chinese out of the box.
Can I query the DuckDB store directly?¶
Yes — the store is the result, and the report is just one rendering of it.
study.duckdb is a plain DuckDB file; open it with the CLI, the Python API, or
anything that speaks DuckDB. The schema is:
| table | columns |
|---|---|
panel |
agent_id, step, state (JSON), action_kind, action_payload (JSON) |
metrics |
step plus one column per metric the study emitted |
events |
step, note |
messages (only if the message bus was used) |
author_id, step, round, channel, content, audience |
state and action_payload are JSON, so their inner keys are
study-specific — check your model.py for the field names before adapting
these:
-- who moved most between two steps, on a numeric state field
WITH s AS (
SELECT agent_id, step,
CAST(state ->> '$.opinion' AS DOUBLE) AS opinion
FROM panel WHERE step IN (0, 12)
)
SELECT a.agent_id,
a.opinion AS opinion_t0,
b.opinion AS opinion_t12,
b.opinion - a.opinion AS delta
FROM s a JOIN s b USING (agent_id)
WHERE a.step = 0 AND b.step = 12
ORDER BY abs(delta) DESC
LIMIT 20;
-- the per-agent rationale the LLM gave, for one step
SELECT agent_id, action_kind, action_payload ->> '$.reason' AS reason
FROM panel WHERE step = 5 AND reason IS NOT NULL;
-- how the action mix shifted over time
SELECT step, action_kind, count(*) AS n
FROM panel GROUP BY step, action_kind ORDER BY step, n DESC;
-- align a metric with the interventions that fired at the same step
SELECT m.*, e.note
FROM metrics m LEFT JOIN events e USING (step)
ORDER BY m.step;
Parquet exports (panel.parquet, metrics.parquet) sit alongside for
pandas/R.
Concepts¶
What makes this "longitudinal"? Other frameworks also run many steps.¶
The population is fixed with persistent ids, and one panel row is recorded per agent per step — so you can follow individual trajectories, not just aggregate curves. Cross-sectional frameworks typically resample crowds per experiment; here the same agents persist, which is what makes the output genuine panel data.
Where does the LLM actually sit?¶
In exactly one interface: DecisionModel.decide_batch. Environment,
population, storage, and reporting are LLM-free. See
LLM Clients & Dry Runs.
What stops the model from inventing numbers?¶
The grounding contract: load-bearing values cite a fact or a declared
assumption in grounding/grounding.json, and the report renders that
ledger next to the results. It's provenance for review, not a runtime
validator — see
Grounding & Provenance.
Extending¶
I have my own simulator. Can I plug it in?¶
Yes — that's Path C: wrap it behind the four interfaces with zero edits to its source, prove parity on a fixed seed, and it becomes a routable, forkable study.
How do I add my own data (personas, environment sources)?¶
Populations: point provider_ref at the generic file.personas provider
(CSV/Parquet with an agent_id column), or implement your own
PopulationProvider. Environment sources: declare them as layer sources
and materialize at build time. External services go through the
capabilities registry.