Files
mirosociety/docs/问题记录/问题记录-硬编码与千人模拟.md
T

5.7 KiB
Raw Blame History

问题记录:硬编码与"千人社会"模拟的业务一致性

记录日期:2026-09-27 记录人:项目相关方 状态:仅记录问题,不附解决方案


背景

我想搞清楚一个问题:在当前的项目实现里,如果用户设置 1000 个市民,整个模拟引擎到底是怎么跑的?我担心两个事情:

  1. 供应商的 TPS 顶不顶得住;
  2. 这个所谓的"千人社会模拟",在业务层面到底是不是真的有意义。

下面是我一边看代码一边问出来的问题。


一、先确认运行机制

我看到的是这样一条路径:

  • 模拟每一天被分成三轮(morning / afternoon / evening),全年 365 天,共 1095 轮。
  • 每一轮里,并不是所有人都去问 LLM,而是有一个 _select_active_agents 的机制去抽样。
  • 抽样的基准是 active_agents_per_round_min = 3、active_agents_per_round_max = 10,带情绪、事件、派系、沉睡等权重加成。
  • 然后这一批被抽中的人,去并发地打 LLM,做决策。
  • 之后再走反应、研究、叙事等若干个 LLM 调用。

看到这里的时候,我的第一个问题浮出来了。


二、问题

问题 1:抽样率随人口规模塌缩

1000 人的社区,每一轮只有 3–10 个人被问,这是什么比例?

  • 100 人社区:3%–10%(看起来是合理的"采样")
  • 1000 人社区:0.3%–1%
  • 10000 人社区:0.03%

我卖的产品叫"千人社会模拟"。可是跑出来,每一轮只有不到 10 个人在表态,那剩下 990 多个人在干什么?我看到的"社会"是从 3–10 个人里推出来的。

问题 2:业务参数和成本参数混在同一个常量里

那 3 和 10 到底是产品决策还是工程决策?

  • 如果是产品决策 —— 它凭什么不随人口规模变?
  • 如果是工程决策 —— 它为什么硬编码在 TimeConfig 这样一个看起来是"世界设定"的数据类里?

我觉得这两个里总有一个不对劲。

问题 3:上限被钳到 60%,但实际上根本用不到

我注意到有这么一行:

max_active = max(base_count, int(len(agents) * 0.6))
target = min(base_count + (3 if event_occurred else 0), max_active)

看起来是给 1000 人准备的"60% 上限",但由于 base_count 只有 3–10,那个 0.6 的上限永远摸不到。也就是说这个上限是一个"为更大规模准备的逻辑",但在没有人为改 3–10 之前,它永远是死代码。

那它在暗示什么?是不是原来设想里,这个 3–10 本来就应该随规模动起来?

问题 4:3–10 个人的表态,能代表什么?

我再往深想一层。民意、情绪、派系分布、冲突演化 —— 整个产品叙事都建立在"涌现"两个字上。

那 3–10 个人做出的决策,能涌现出什么?尤其是当 active 抽样里还包含了"沉默偏向"(默认 w=0.3,平静的人权重天然偏低),抽样结果会进一步偏向情绪激动的小群体。

所以这到底是"千人社会的涌现",还是"6 个最激动的人的噪声"?

问题 5:业务承诺和实际行为之间,是不是有错配?

用户为"千人社会模拟"付费。系统在生成阶段认真生成了 1000 个市民的 backstory、性格、记忆、关系。一轮跑下来,认真读了 prompt 的只有 6 个人。

这种错配在业务上叫什么?我不太确定。但我直觉它不只是"性能问题"。

问题 6:除了 active 数,还有哪些是硬编码的"看似业务参数"?

我在 world.py 里看到了:

total_days: int = 365
rounds_per_day: int = 3
active_agents_per_round_min: int = 3
active_agents_per_round_max: int = 10

这四个数字看起来都是默认值。但 total_days 和 rounds_per_day 是用户故事的核心节奏参数 —— 用户应该在产品层能感知到它们。

而 active_agents_per_round_min/max 这个东西,用户感知不到,因为它从来没出现在 UI 里。但它却对"千人模拟"的语义有决定性影响。

这到底是一个用户应该配置的旋钮,还是一个工程内部值?我越看越觉得,它应该是前者。


三、我没有结论,只有问题

到目前为止我没找到一份文档说明:

  1. 为什么 active_agents_per_round_min/max 默认是 3–10;
  2. 这个区间在不同人口规模下是怎么被论证过的;
  3. 它是被当作"LLM 成本阀门"还是"模拟质量参数"设计的;
  4. 是否有人为"千人社会"做过代表性 / 抽样率的论证。

如果不回答这四个问题,"千人社会模拟"四个字,目前在产品上只是启动时填了一个数字而已。


四、补充记录:跟 TPS 那条线是分开的两件事

我一开始问的是 TPS(供应商限流),得到的答案是:

  • semaphore 默认并发 10;
  • 每轮实际只打 3–10 次决策 LLM,加若干反应 / 研究 / 叙事;
  • 1000 人下瞬时并发不会超过 semaphore 上限。

这条线结论清楚:当前实现下,TPS 没问题。

但顺着这个问题我又往下看了一眼抽样代码,发现了上面那些"业务没意义"的疑问。所以这两件事其实是分开的两件事:

  • TPS:没风险(这是工程结论)
  • 千人模拟是否成立:存疑(这是产品结论)

我把它们分开记在这里,免得有人把"TPS 没问题"误读成"千人模拟没问题"。


五、相关代码位置

  • 抽样逻辑:backend/app/services/engine.py _select_active_agents(约 765–833 行)
  • 配置常量:backend/app/models/world.py TimeConfig(约 14–17 行)
  • 决策并发:backend/app/services/llm.py LLMClient.generate_batch
  • 轮循环入口:backend/app/services/engine.py run 主循环(while round_num < total_rounds)