总览 — B = f(P, E)¶
SocioVerse 围绕一个抽象构建,借自 Kurt Lewin 的场论:行为是人与环境的
函数,B = f(P, E)。运行时里的一切,都是在具体回答「P 是什么、E 是什么、
f 如何随时间闭合成环」。
纵向,而非横截面¶
AgentSociety、OneSim 这类框架擅长横截面研究:大量单轮实验,常常每轮 重新抽样一批人。SocioVerse 瞄准的是互补的问题——同一批人如何随时间 变化?
这倒转了两个设计选择:
| 横截面 | SocioVerse(纵向) | |
|---|---|---|
| 人群 | 每次实验重新抽样 | 固定池、持久 id |
| 环境 | 每轮基本静态 | 动态——环境才是变化的主体 |
| 输出 | 结果分布 | 面板数据:每个 agent 每步一行 |
持久的 agent id 就是纵向研究的主键:有了它,你问的可以是
「把 x-042 这户在全部十二步里的轨迹给我看」,而不只是
「整体发生了什么」。
两轴环境¶
E 沿两条独立的轴动态展开,让每个 agent 在每一步都拥有一个四象限的世界视图:
| 宏观 | 局部 | |
|---|---|---|
| 物理 | 世界的全局状态 | 该 agent 的邻域状态 |
| 信息 | 所有人都会收到的广播 | 只投递给该 agent 的信息流与消息 |
推动 E 前进的变化有两类:
- 外生——你在环境产物里声明的定时干预与信息广播 (「第 3 步发布一条政策公告」)。
- 内生——agent 自身行为的反馈
(
apply(actions)闭合E_t → E_{t+1})。
主循环¶
sequenceDiagram
participant Eng as 引擎(循环)
participant E as 环境(E)
participant P as 人群(P)
participant D as 决策模型(f)
participant S as 面板存储
Note over Eng: t = 0 —— P 只构建一次,E 重置
loop 每一步 t = 1..N
Eng->>E: advance_to(t) —— 外生事件与广播
Eng->>E: observe_batch() → Observation[](四象限视图)
Eng->>D: decide_batch(observations) → Action[](= B)
Eng->>E: apply(actions) —— 内生反馈
Eng->>S: 采集指标 + 记录面板行
end
同一个循环,把真正流经它的东西画出来 —— 包括外生通道,以及它留下的面板数据:

循环的三个性质是刻意为之的:
- P 只构建一次。 persona 在构建时获得确定性的持久 id,运行途中池子 永不变化——这正是输出成为面板数据的原因。
- 决策是批式的。
decide_batch一次接收全部观察;不存在热循环里的 逐 agent LLM 调用。 - 每一步都被记录。 每个 agent 每步一行面板数据,连同聚合指标与已触发 事件,落入持久化的 DuckDB 存储。
类型化的交接¶
阶段之间流动的数据端到端类型化:
其中每一个都是 socioverse/schemas/ 里的 Pydantic 模型,且每个阶段边界
都有严格校验把守——畸形的产物在门口就失败,而不是三个阶段之后才爆雷。
从构建之初就有据可依¶
一个研究的可信度取决于它据以构建的数字。每个研究都带一份 grounding
侧车文件(grounding/grounding.json):有来源的事实、建模参考、以及
显式声明的假设。构建阶段会查阅并扩充它——环境与人群里承重的数值要引用
一个事实 id 或假设 id,无法溯源的内容被声明为假设而非悄悄编造。见
真实世界锚定与溯源。
概念在代码中的位置¶
| 概念 | 代码位置 |
|---|---|
| 研究要实现的接口 | socioverse/abc/ |
| 驱动接口的循环 | socioverse/engine/ |
| 类型化契约 | socioverse/schemas/ |
| 可复用的信息轴层 | socioverse/env_layers/ |
| 持久化面板存储 | socioverse/io/duckdb_store.py |
下一篇:一个 Study 的解剖 —— studies/<id>/ 里有什么。