跳转至

总览 — 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

同一个循环,把真正流经它的东西画出来 —— 包括外生通道,以及它留下的面板数据:

主循环:E_t → B_t → E_{t+1},外生通道与逐步落盘的面板

循环的三个性质是刻意为之的:

  1. P 只构建一次。 persona 在构建时获得确定性的持久 id,运行途中池子 永不变化——这正是输出成为面板数据的原因。
  2. 决策是批式的。 decide_batch 一次接收全部观察;不存在热循环里的 逐 agent LLM 调用。
  3. 每一步都被记录。 每个 agent 每步一行面板数据,连同聚合指标与已触发 事件,落入持久化的 DuckDB 存储。

类型化的交接

阶段之间流动的数据端到端类型化:

Persona[]  →  Observation[]  →  Action[]  →  metrics

其中每一个都是 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>/ 里有什么。