专题结论
运行时并非只有“多个提示词并发调用”:它会把图谱实体转成人设,为每个 Agent 生成活动和立场配置,启动独立子进程,在 Twitter/Reddit 两套 OASIS 环境和 SQLite 数据库中逐轮执行模型行动,并把落地动作增量写入日志。问题在于运行结果没有随机种子、两个平台只是并行的独立世界、界面展示的推荐/传播参数没有被执行脚本读取,以及子进程在轮次结束后默认等待访谈命令,导致报告阶段的终态衔接存在静态可见的断点。
原始类与方法
| 原始符号 | 真实职责 | 源码定义 | 固定提交行级证据 |
|---|---|---|---|
SimulationManager.prepare_simulation()(串联实体读取、人设生成和配置生成的准备方法,约第 244 行) |
把已完成图谱转成可运行模拟目录 | 模拟管理文件 | 固定版本第 244-478 行 |
OasisProfileGenerator.generate_profiles_from_entities()(并行把图谱实体转成人设文件的方法,约第 895 行) |
为 Twitter/Reddit 生成账号、性格、职业和背景字段 | 人设生成文件 | 固定版本第 895-1020 行 |
PlatformConfig(声明平台推荐、传播和回音室字段的数据类,约第 131 行) |
保存第二步界面展示的平台参数 | 配置生成文件 | 固定版本第 131-145 行 |
SimulationConfigGenerator.generate_config()(生成时间、事件、Agent 和平台配置的方法,约第 244 行) |
汇总运行输入并保存固定的平台参数 | 配置生成文件 | 固定版本第 244-375 行 |
SimulationRunner.start_simulation()(声明运行态并启动平台子进程的方法,约第 371 行) |
读取配置、计算轮数、建立终态锁并启动脚本 | 模拟运行文件 | 固定版本第 371-617 行 |
get_active_agents_for_round()(按时段、活动水平和随机抽样选择 Agent 的函数,约第 1040 行) |
决定每轮哪些 Agent 执行动作 | 双平台脚本 | 固定版本第 1040-1090 行 |
run_twitter_simulation()(创建并运行 Twitter OASIS 环境的异步函数,约第 1101 行) |
管理平台数据库、初始帖子、轮次和行动日志 | 双平台脚本 | 固定版本第 1101-1292 行 |
run_reddit_simulation()(创建并运行 Reddit OASIS 环境的异步函数,约第 1293 行) |
管理另一套独立平台数据库、轮次和行动日志 | 双平台脚本 | 固定版本第 1293-1490 行 |
SimulationRunner._monitor_simulation()(增量读取行动并在进程退出后发布终态的方法,约第 620 行) |
聚合平台进度、提交图谱记忆并排空写入器 | 模拟运行文件 | 固定版本第 620-765 行 |
ZepGraphMemoryUpdater.add_activity()(把成功行动加入异步图谱写入队列的方法,约第 350 行) |
将模拟行为转换为可写入 Zep 的自然语言图谱事件 | 图谱记忆写入文件 | 固定版本第 350-409 行 |
doStartSimulation()(页面挂载后以强制重启和图谱写回参数启动模拟的前端函数,约第 383 行) |
标准网页入口固定选择双平台并开启记忆写回 | 第三步组件 | 固定版本第 383-438 行 |
原始调用链
SimulationManager.prepare_simulation()(模拟准备总入口,约第 244 行)从图谱读取实体后调用OasisProfileGenerator.generate_profiles_from_entities()(批量生成人设的方法,约第 895 行)。- 同一准备入口随后调用
SimulationConfigGenerator.generate_config()(生成时间、事件和 Agent 配置的方法,约第 244 行)并把结果保存为运行配置。 doStartSimulation()(标准网页的启动函数,约第 383 行)调用后端启动接口,最终进入SimulationRunner.start_simulation()(创建状态并启动子进程的方法,约第 371 行)。- 双平台脚本并发运行
run_twitter_simulation()(Twitter 轮次循环,约第 1101 行)和run_reddit_simulation()(Reddit 轮次循环,约第 1293 行);两者每轮都调用get_active_agents_for_round()(随机选择活跃 Agent 的函数,约第 1040 行)。 SimulationRunner._monitor_simulation()(增量监控与终态发布方法,约第 620 行)读取两套行动日志,并在标准前端开启记忆时将动作交给ZepGraphMemoryUpdater.add_activity()(排队写入模拟活动的方法,约第 350 行)。
源码片段一:平台参数被生成并保存
原项目关键片段。来源:SimulationConfigGenerator.generate_config()(构造固定 Twitter/Reddit 平台参数的方法,约第 339-372 行);固定提交第 339-372 行。
原项目关键片段:
twitter_config = None
reddit_config = None
if enable_twitter:
twitter_config = PlatformConfig(
platform="twitter",
recency_weight=0.4,
popularity_weight=0.3,
relevance_weight=0.3,
viral_threshold=10,
echo_chamber_strength=0.5
)
if enable_reddit:
reddit_config = PlatformConfig(
platform="reddit",
recency_weight=0.3,
popularity_weight=0.4,
relevance_weight=0.3,
viral_threshold=15,
echo_chamber_strength=0.6
)
这些参数不是模型推导结果,而是源码常量。对仓库中的字段使用点做静态检索后,它们只出现在配置模型、配置生成、配置摘要和第二步界面展示;两个平台执行函数没有读取这些字段。因此“平台算法差异”目前是界面表达,不是运行机制。
源码片段二:Agent 激活依赖未记录种子的随机过程
原项目关键片段。来源:get_active_agents_for_round()(根据时段和随机数选择活跃 Agent 的函数,约第 1047-1080 行);固定提交第 1047-1080 行。
原项目关键片段:
time_config = config.get("time_config", {})
agent_configs = config.get("agent_configs", [])
base_min = time_config.get("agents_per_hour_min", 5)
base_max = time_config.get("agents_per_hour_max", 20)
target_count = int(random.uniform(base_min, base_max) * multiplier)
candidates = []
for cfg in agent_configs:
agent_id = cfg.get("agent_id", 0)
active_hours = cfg.get("active_hours", list(range(8, 23)))
activity_level = cfg.get("activity_level", 0.5)
if current_hour not in active_hours:
continue
if random.random() < activity_level:
candidates.append(agent_id)
selected_ids = random.sample(
candidates,
min(target_count, len(candidates))
) if candidates else []
源码证明即使材料、模型和配置相同,每轮参与者也会变化。仓库中未见为该随机模块设置或持久化 seed 的运行路径,所以结果无法严格重放。PoC 应记录随机种子、模型标识、提示版本、依赖锁和运行 id,并用多次运行分布替代单次故事线。
源码片段三:轮次结束后默认保留环境
原项目关键片段 A。来源:SimulationRunner.start_simulation()(构造子进程命令的方法,约第 511-525 行);固定提交第 511-525 行。
原项目关键片段:
cmd = [
sys.executable,
script_path,
"--config", config_path,
]
if max_rounds is not None and max_rounds > 0:
cmd.extend(["--max-rounds", str(max_rounds)])
原项目关键片段 B。来源:main()(轮次结束后进入命令等待循环的脚本入口,约第 1591-1624 行);固定提交第 1591-1624 行。
原项目关键片段:
if wait_for_commands:
ipc_handler = ParallelIPCHandler(
simulation_dir=simulation_dir,
twitter_env=twitter_result.env if twitter_result else None,
twitter_agent_graph=twitter_result.agent_graph if twitter_result else None,
reddit_env=reddit_result.env if reddit_result else None,
reddit_agent_graph=reddit_result.agent_graph if reddit_result else None
)
ipc_handler.update_status("alive")
while not _shutdown_event.is_set():
should_continue = await ipc_handler.process_commands()
if not should_continue:
break
启动命令没有携带“轮次后立即退出”开关,而脚本默认等待采访/关闭命令。这解释了为什么 OASIS 环境可以支持后续采访,也构成报告阶段必须等待终态的冲突前提。这里是静态推断;应通过网页端到端运行确认实际是否会停留在运行态。
双平台边界
| 观察 | 源码事实 | 选型含义 |
|---|---|---|
| 并发方式 | main()(使用并发协程同时启动两平台的入口函数,约第 1492 行)并发调用两个平台函数 |
并行不等于共享状态 |
| 数据库 | run_twitter_simulation()(创建 Twitter SQLite 的函数,约第 1151 行)与 run_reddit_simulation()(创建 Reddit SQLite 的函数,约第 1342 行)使用不同文件 |
帖子、评论和推荐环境相互隔离 |
| 人设 | 两个平台读取同一准备阶段生成的人设与 Agent 配置 | 初始身份可比,但行为历史不共享 |
| 行动 | 两个平台分别写自己的行动日志 | 可做并排展示,不能声称发生了跨平台传播 |
| 图谱记忆 | SimulationRunner._read_action_log()(读取平台行动并提交图谱记忆的方法,约第 767 行)可把两个平台活动汇入同一 Zep 图谱 |
合并方便报告检索,但平台/运行来源必须保留并可过滤 |
对当前需求的取舍
建议局部引入子进程隔离、行动日志、进度监控和图谱写入终态屏障;不建议直接引入现有随机选择、固定但未接线的平台参数和自动强制重跑页面。最小 PoC 应先完成:随机种子与运行版本记录、平台参数真正接入、双平台是否需要共享传播的产品决策、刷新不重跑、显式关闭环境,以及 10/40/100 Agent 的时间、费用与失败率测试。