跳转至

常见问题

安装与运行

试用需要 LLM API key 吗?

不需要。安装、测试、环境/人群构建、以及规则式决策的研究完全无 key 运行, python scripts/demo_offline.py 还能离线端到端跑完一个完整研究。从零构建的研究用 llm_kind: "scripted" 干跑。 只有真实的 LLM 决策运行才需要 key。

直接 pip install socioverse2 可以吗?

核心库可以:pip install socioverse2 会从 PyPI 安装 socioverse(schema、接口、引擎、DuckDB 存储、校验)。/sv-* 工作流 skill、 仪表盘、参考研究和演示脚本不在这个包里,需要 git clone https://github.com/sii-research/SocioVerse2.git 获得。见 安装。

有些测试跳过了,安装坏了吗?

没坏——需要配套仓库(SocioVerse-ABM、ConsumerSim、HiSim)、可选 extra 或真实 LLM 的 测试在缺失时自动跳过。「有跳过、零失败」= 健康的安装。

必须用 Claude Code 吗?

只有 agentic 工作流(/sv-* skill)需要。 编程路径——写产物、 build_simulator、run()、SQL——是纯 Python。

Claude Code 需要它自己的 Claude 登录(Claude 订阅)或 ANTHROPIC_API_KEY。 .env 里的 SV_LLM_* key 驱动不了它,那个 key 只用于模拟中的 agent。如果只有 OpenAI key,可以尝试 Codex CLI,它读取仓库随附的 AGENTS.md 和 .codex/skills/;这条路径属于实验性,尚未做过完整的端到端测试。见 工作流 skill 由什么驱动。

可以并行跑多个研究吗?

一个进程一个研究。 包装遗留引擎的研究可能 patch 模块全局量, 不是线程安全的。批量实验用独立进程(例如进程池),每个进程一个研究。

可以把 /sv-run 丢到后台,让 agent 继续干别的吗?

不可以。 /sv-run 必须在前台跑——发出命令、让它阻塞,上限约 10 分钟。 不要用 run_in_background、结尾 & 或 nohup 把它包起来。

原因是结构性的:在 headless / 托管的 agent 里,后台任务结束时没有任何东西会 重新唤起 agent。运行完成在一片虚空里,agent 永远不知道它跑完了,整条流程就 这样静默卡住。前台阻塞的运行才会带着结果把控制权交回来。

如果某次运行确实需要超过这个上限:不要丢后台,而是把规模压下来(更少 agent、 更少步数),或者提前告知用户、由他决定。

一定要有事件服务或 user pool MCP 吗?

不需要。两者都是可选能力,且都会优雅降级:事件客户端会退到本地缓存、再退到 普通网络检索;persona 池不可用时退到「先锚定再合成」或用户自备的 persona 文件。 你损失的是便利和一部分锚定强度,而不是跑研究的能力。见 外部能力。

本地跑和用托管服务有什么区别?

框架一样、产物一样。本地跑:LLM key 和可选服务端点都由你自己提供,机器也归你。 托管服务:LLM provider、事件服务、persona 池、以及一份配好中文字体的 matplotlib 都是现成接好的,运行与配额替你计量,dashboard 是服务出来的而不用自己起。见 托管服务。

结果与可复现性

运行可以复现吗?

  • 干跑(llm_kind: "scripted"、规则式模型,或 chicago_schelling 的确定性客户端): 可以,精确复现——端到端由种子决定。
  • 真实 LLM 运行:不行——非零温度采样每次不同。但每次已记录的运行都 永久保存在它的 DuckDB 存储里,而且 分支会重放父版本 已存储的动作,精确复现,不重新询问 LLM。

我的结果在哪里?里面有什么?

studies/<id>/trajectory/study.duckdb —— 表 panel(每个 agent 每步 一行)、metrics(每步一行)、events(触发的干预),若研究用了消息 总线还有 messages。旁边有 Parquet 导出。reports/report.md 是渲染版。运行库被 gitignore,所以刚 clone 下来的参考研究带着报告,但在你跑之前没有 trajectory/。

我改了产物重跑了一次,旧结果没了。为什么?

为新一次运行打开存储会替换它——这正是那条规则存在的原因:修改既有 研究要走 /sv-iterate,它的版本门会先把现行目录快照到 versions/vN/。凡在版本快照之下的内容永不删除。

分支和版本有什么区别?什么时候只能建版本?

两者都是该 study versions.json 里的条目,条目的 kind 说明它是哪一类:

  • "branch"(分支)——由 create_branch(study_dir, note, *, fork_step=t*, source=None, resume_from_stage="sv-build-environment") 写入。它记下 warm_start = {"source": "<父版本>", "resume_from": t*-1} 与 fork_step: t*。 跑批时 SimulationLoop.run()(socioverse/engine/loop.py)读到这份规格, 先调 _replay_inherited(),用父版本已存的面板动作把 1..t*-1 重建出来 ——env.advance_to(t) 后 env.apply(actions),不走 decide_batch、不调 LLM——然后才从 t* 进入真正的循环。分支允许的改动:新增 at_step >= t* 的 scheduled_events / broadcasts、为承载新广播新增一个环境层、以及把 n_steps 拉长。
  • "version"(版本)——由 create_version(...) 写入,从第 0 步冷跑。凡是 动到 population/population.json 或 roster.jsonl、研究自己的 *.py / adapter/*.py(也就是 f)、seed、decision_ref / decision_args、 collector_*、store_*、engine_args、interaction_rounds,或 at_step < t* 的环境条目,都只能是版本。n_steps 被刻意排除在外,所以分支可以比父版本跑得更长。

check_branch_invariant() 会拿这张清单逐项去比父版本的冻结快照,而 resolve_warm_start() 在每条通往跑批的路径上都会调用它。违约时降级而不报错: kind 变回 "version"、warm_start 与 fork_step 被清空、改成冷跑,并在条目上留下 downgraded_from_branch(来源、分叉步、每个越界产物一行)作为审计留痕。

重放本身还要求 interaction_rounds == 1,以及 env.apply() 是所传动作的纯函数; 因此包装遗留模拟器的研究(chicago_schelling 的移动意图挂在隐藏的遗留 agent 上) 和任何多轮研究都只能建版本。完整说明见 迭代:版本与分支与 版本与可控研究循环。

图里的中文为什么变成方块了?

matplotlib 默认不带 CJK 字体,所以每个汉字都渲染成豆腐块(□□□)。 装一个中文字体并让 matplotlib 指过去:

# Debian/Ubuntu
sudo apt-get install fonts-noto-cjk
import matplotlib
matplotlib.rcParams["font.sans-serif"] = ["Noto Sans CJK SC"]
matplotlib.rcParams["axes.unicode_minus"] = False   # 负号也别变方块

刚装的字体如果 matplotlib 还看不见,清一下它的缓存 (rm -rf ~/.cache/matplotlib)。托管服务已预装并配好中文字体,图里的中文 开箱即正常。

可以直接查 DuckDB 存储吗?

可以——存储才是结果本身,报告只是它的一种渲染。study.duckdb 就是一个普通的 DuckDB 文件,用 CLI、Python API、或任何会说 DuckDB 的工具都能打开。表结构:

表 列
panel agent_id、step、state(JSON)、action_kind、action_payload(JSON)
metrics step 加上研究发出的每个指标各一列
events step、note
messages(仅当用了消息总线) author_id、step、round、channel、content、audience

state 与 action_payload 是 JSON,里面的键是各研究自定的——照抄下面的 例子前,先去自己的 model.py 里确认字段名:

-- 在某个数值型 state 字段上,看谁在两步之间变化最大
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;

-- 某一步里每个 agent 给出的理由
SELECT agent_id, action_kind, action_payload ->> '$.reason' AS reason
FROM panel WHERE step = 5 AND reason IS NOT NULL;

-- 行为构成随时间怎么变
SELECT step, action_kind, count(*) AS n
FROM panel GROUP BY step, action_kind ORDER BY step, n DESC;

-- 把指标和同一步触发的干预对齐起来看
SELECT m.*, e.note
FROM metrics m LEFT JOIN events e USING (step)
ORDER BY m.step;

旁边还有 Parquet 导出(panel.parquet、metrics.parquet)供 pandas / R 用。

概念

别的框架也能跑很多步,「纵向」特殊在哪?

人群是固定的、带持久 id 的,且每个 agent 每步记录一行面板数据—— 所以你能追踪个体轨迹,而不只是聚合曲线。横截面框架通常每次实验重新 抽样人群;这里同一批 agent 一直存在,这才是输出成为真正面板数据的原因。

LLM 到底在哪个位置?

只在一个接口里:DecisionModel.decide_batch。环境、人群、存储、报告 都不含 LLM。见 LLM 接入与干跑。

是什么防止模型凭空编数字?

grounding 契约:承重数值要引用 grounding/grounding.json 里的事实或 声明的假设,而报告会把这份台账渲染在结果旁边。它是供审阅的溯源, 不是运行时校验器——见 真实世界锚定与溯源。

扩展

我有自己的模拟器,能接进来吗?

能——那就是 Path C:把它包装到四个接口 后面(源码零改动),在固定种子上证明一致性,它就成为一个可路由、可 fork 的研究。

怎么接入我自己的数据(personas、环境数据源)?

人群:把 provider_ref 指向通用的 file.personas provider(带 agent_id 列的 CSV/Parquet),或实现你自己的 PopulationProvider。 环境数据源:声明为层的 source 并在构建时物化。外部服务走 能力注册表。