You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
mirosociety/docs/new_plan/2026-09-18-real-city-and-di...

9.4 KiB

Real City Inference & Direct Agent Control — Design

2026-09-18 位置:docs/new_plan/(区别于 docs/plans/ 里已归档的设计) 记录两个产品增强方向:(1) 规则文本里提到城市时自动启用真实人口结构;(2) 用户在模拟运行中可直接与居民交互(私聊、塞情报),把 MiroSociety 从「观察实验」升级为「可介入实验」。

背景

当前 MiroSociety 的产品定位是「看涌现」—— 用户定义规则,系统跑出结果。但在真实使用中,玩家常自然产生两类诉求:

  1. 我希望自己的规则能更接近真实:写「旧金山房租涨 30%」时,期望模拟的是真实的旧金山人,而不是 10 个随机抽样的居民。
  2. 我希望在中途介入:加天灾、跟某个人说句话、给某个人一条情报,看看这个社会对「定向刺激」会怎么反应。

这两类诉求都对应明确的代码改动,且与现有架构兼容。详见下方的「补齐方案」。

现状梳理

真实城市相关

当前行为:用户在规则里写「旧金山」,系统不会自动匹配,智能体人口结构完全随机。这与「随便写就行」的产品直觉不符。

直接控制相关

玩家想做 当前能力 代码位置
加天灾 / 黑天鹅 支持,广播式 engine.py:2116-2147
居民互相打听 支持(内部 INVESTIGATE 动作) engine.py:1992-2047
居民互相私聊 支持(内部 SPEAK_PRIVATE 动作) engine.py:1636
用户定向跟某居民说话 无接口
用户给某居民塞情报 / 改信念 无接口
暂停状态下介入 暂停期间只能 inject 事件,不能定向传话

技术上,运行时改智能体的 beliefs / core_memory / working_memory 已经支持(deliver_enriched_agents 是先例,见 engine.py:329)。缺的只是 API + 前端交互

补齐方案

1. 规则文本中城市名自动匹配

思路

run_pipeline 的早期(状态切到 GENERATING_WORLD 之前),用轻量规则解析扫描 rules 文本:

  • req.city 已有值 → 跳过,优先级用户显式 > 文本推断
  • req.city 为空 → 在文本里找已知城市列表的别名(包括中英文、缩写),命中则自动填入 req.city
  • 若未命中 → 行为不变,完全随机人口

数据:已知城市的别名表

census.py 里新增 CITY_ALIASES:

CITY_ALIASES: dict[str, str] = {
    # 英文
    "san francisco": "san francisco, ca",
    "sf": "san francisco, ca",
    "new york": "new york, ny",
    "nyc": "new york, ny",
    "los angeles": "los angeles, ca",
    "la": "los angeles, ca",
    # 中文
    "旧金山": "san francisco, ca",
    "纽约": "new york, ny",
    "洛杉矶": "los angeles, ca",
    "芝加哥": "chicago, il",
    "西雅图": "seattle, wa",
    "奥斯汀": "austin, tx",
    "波士顿": "boston, ma",
    "迈阿密": "miami, fl",
    "亚特兰大": "atlanta, ga",
    "底特律": "detroit, mi",
    # ... 覆盖 20 个城市
}

改动范围

风险与缓解

  • 误识别:「我旧金山的表弟今天结婚」这句话里出现「旧金山」,但用户是想讨论亲戚婚礼,不是想测试旧金山人 —— 缓解方式:把识别结果作为提示,前端弹出「我们识别到旧金山,是否启用真实人口结构?」让用户确认一次
  • 歧义:某些城市名(如 Portland)在多个州都有 → 已在 CITY_FIPS 里标注,匹配时选人口最多的那个
  • 非美城市:目前只支持美国,匹配不到就不报错,行为不变

不做的事

  • 不做模糊匹配(避免误识别),只做精确子串匹配
  • 不做 NLP 抽取,只做字符串扫描 — 快、可控、易调试

2. 用户定向与居民交互

目标

让玩家可以在模拟运行中:

  • 对某个居民说一句话(类似神谕/上帝视角 whisper),该居民下一轮会按自己性格回应
  • 给某个居民塞一条情报(改她的 belief / core_memory),影响她后续判断

接口设计

新增两个 FastAPI 接口,都在 /simulation/{sim_id} 下:

class DirectSpeakRequest(BaseModel):
    agent_id: int
    message: str
    as: Literal["god", "voice_in_head", "neighbor"] = "voice_in_head"

@router.post("/simulation/{sim_id}/direct-speak")
async def direct_speak(sim_id, req, request): ...

class PlantInfoRequest(BaseModel):
    agent_id: int
    claim: str                    # 例如「Alice 偷了你的东西」
    target_agent_id: int | None   # 可选,情报是关于某个人的
    source_label: str = "reliable neighbor"

@router.post("/simulation/{sim_id}/plant-info")
async def plant_info(sim_id, req, request): ...

后端逻辑

direct-speak:

  1. store.get_agent(sim_id, agent_id) 拿到该居民
  2. message 写入 agent.working_memory 顶部:
    "Day {day}: You suddenly hear a voice in your head saying: \"{message}\". 
    You're not sure where it came from, but it feels real."
    
  3. 触发该居民在下一轮进入激活列表(active_agents 强制包含)
  4. save_agents_batch 持久化
  5. SSE 推 direct_speak_delivered 事件

plant-info:

  1. 拿到目标居民
  2. claim 作为新信念加入 agent.beliefs:
    "Day {day}: A {source_label} told you: {claim}"
    
  3. 如果指定了 target_agent_id,同时降低目标居民在该居民 relationships 里的 sentiment(0.2-0.4)
  4. 持久化 + SSE 事件 info_planted

改动范围

状态机注意事项

  • 暂停时调用:应当入队,resume 后下一轮再触发(避免和 main loop 抢锁)
  • 取消时调用:直接拒绝,返回 409 simulation_cancelled
  • 已完成时调用:返回 410 simulation_complete,不允许干预
  • 重复调用:同一居民同一轮多次 whisper,合并成一条 working_memory(避免爆栈)

隐私 / 公平设计

  • 所有直接干预记入 action_log,标记来源是 user_direct_speak / user_planted_info,便于事后复盘「这条信念是被植入的」
  • 前端 UI 显示一个「 用户干预」角标,让观众知道这个居民在某些点上被外力影响过
  • 报告生成器(narrator.generate_report)把用户干预事件作为单独章节列出

不做的事

  • 不做多选批量干预(一次只针对一个居民,保持简单)
  • 不做定时任务(不能在第 10 天自动给她塞情报),玩家手动控制时机
  • 不做语音输入,只用文字

优先级建议

如果分阶段上线:

阶段 内容 预估工作量
P0 城市自动匹配(方案 1) 1-2 天,改动小,立刻提升「随手写规则」的体验
P1 direct-speak 接口 + 前端按钮 3-4 天,核心玩法升级
P2 plant-info 接口 2-3 天,在 P1 基础上复用持久化逻辑
P3 干预标记 + 报告章节 1 天,可观测性补完

P0 单独立项即可上线,P1+P2 一起做可以串成「上帝模式」主题更新。

后续可能扩展

  • 多选 / 批量干预:一次对一组人说话(类似群体喊话)
  • 定时干预:cron 风格,在第 N 天自动触发某事件
  • 干预模板:把常用的「告诉你邻居是坏人」「告诉你老板要裁员」做成快捷按钮
  • 其他国家的城市:接入中国/欧洲的人口数据源,扩大可玩范围
  • 真实天气事件 API:对接 NOAA / 和风天气,让天灾更真实