5.7 KiB
问题记录:硬编码与"千人社会"模拟的业务一致性
记录日期:2026-09-27 记录人:项目相关方 状态:仅记录问题,不附解决方案
背景
我想搞清楚一个问题:在当前的项目实现里,如果用户设置 1000 个市民,整个模拟引擎到底是怎么跑的?我担心两个事情:
- 供应商的 TPS 顶不顶得住;
- 这个所谓的"千人社会模拟",在业务层面到底是不是真的有意义。
下面是我一边看代码一边问出来的问题。
一、先确认运行机制
我看到的是这样一条路径:
- 模拟每一天被分成三轮(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 里。但它却对"千人模拟"的语义有决定性影响。
这到底是一个用户应该配置的旋钮,还是一个工程内部值?我越看越觉得,它应该是前者。
三、我没有结论,只有问题
到目前为止我没找到一份文档说明:
- 为什么
active_agents_per_round_min/max默认是 3–10; - 这个区间在不同人口规模下是怎么被论证过的;
- 它是被当作"LLM 成本阀门"还是"模拟质量参数"设计的;
- 是否有人为"千人社会"做过代表性 / 抽样率的论证。
如果不回答这四个问题,"千人社会模拟"四个字,目前在产品上只是启动时填了一个数字而已。
四、补充记录:跟 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.pyTimeConfig(约 14–17 行) - 决策并发:
backend/app/services/llm.pyLLMClient.generate_batch - 轮循环入口:
backend/app/services/engine.pyrun主循环(while round_num < total_rounds)