外观
MiroFish:把一份素材变成群体舆论模拟
资料依据:MiroFish 原项目
666ghj/MiroFish、社区本地化 forktt-a1i/MiroFish-local,以及本地部署实测。本文讲清它的运行逻辑,不把它误写成角色戏剧引擎。
1. 它预测的不是“未来答案”,而是“群体会怎么反应”
MiroFish 的基本问题是:
给定一份材料,如果把其中的人、机构、媒体和事件放进一个模拟社交世界,他们接下来会怎样发言、站队、扩散和反应?
它的输入不是一套台词,也不是一个游戏地图,而是一份材料:
text
新闻 / 报告 / 剧本设定 / 事件说明
↓
实体与关系
↓
Agent 人设与记忆
↓
Twitter / Reddit 式互动
↓
舆论演化 + 报告因此它更像:
| 系统 | 基本问题 |
|---|---|
| AI Town | 角色今天在世界里做什么? |
| MiroFish | 一群人会如何讨论同一件事? |
| 戏剧工坊 | 角色下一场会如何演? |
这个区别决定了后面的所有架构。
2. 原项目的总体管线
每个阶段的产物不同:
| 阶段 | 主要产物 | 作用 |
|---|---|---|
| 本体生成 | Entity types / Edge types | 决定图谱如何理解材料 |
| 建图 | Nodes / Edges / Episodes | 保存实体、关系、事件 |
| 人设生成 | Agent Profile | 把实体变成能发言的角色 |
| 配置生成 | 时间和平台参数 | 决定模拟如何运行 |
| OASIS | Actions / Posts / Comments | 产生舆论行为 |
| 报告 | Markdown 报告 | 汇总趋势、风险和可能结果 |
| Interview | 单 Agent 回答 | 事后追问角色立场 |
3. 本体生成:先决定“世界里有什么”
MiroFish 不直接把全文塞给一群 Agent。第一步会让模型从素材里抽出:
text
实体类型:Person / Organization / MediaOutlet / Event / Location
关系类型:KNOWS / WORKS_FOR / ACCUSES / REPORTS / INVOLVES例如一份“地铁停运报告”可能变成:
text
实体:
某市地铁3号线
地铁运营方
交通委
受影响乘客
信号系统故障
微博
关系:
地铁运营方 ──负责──> 某市地铁3号线
信号系统故障 ──导致──> 地铁停运事件
受影响乘客 ──发布──> 微博本体是一个重要的控制点
本体太粗:所有东西都变成 Entity,后续 Agent 类型混乱。 本体太细:图谱变得复杂,Agent 反而抓不住重点。
我们实测过一个坑:DeepSeek 的 json_object 模式没有正确输出 Graphiti 需要的类型标签,导致节点只有 :Entity,人设筛选拿不到角色。最后通过补 Cypher 类型标签解决。
结论:本体不是装饰,它决定了后面的角色生成和过滤能不能工作。
4. 图谱记忆:原项目为什么依赖 Zep
原版 MiroFish 的关键依赖是 Zep Cloud。它负责保存:
text
Graph
├─ Nodes:人物、机构、事件、物品
├─ Edges:关系
└─ Episodes:按时间写入的材料和 Agent 活动Agent 不是只看最近几条帖子,而是可以从图谱里检索与当前行为有关的实体、关系和事件。
不过原项目明确依赖 Zep Cloud,ZEP_API_URL 也不是一个真正可替换的本地后端入口。Zep Community Edition 已经废弃,因此我们没有继续围绕 Zep Cloud 配置。
5. 我们的本地化改造
text
原版:MiroFish → Zep Cloud → OASIS
本地版:MiroFish-local
├─ Graphiti 适配层
├─ Neo4j 5.26(本机)
├─ Ollama nomic-embed-text(本机)
├─ DeepSeek / OpenAI 兼容网关
└─ OASIS(本地 Python 环境)已验证的本地组件
| 组件 | 用途 |
|---|---|
| Neo4j | 图节点、关系、Episode 存储 |
| Graphiti | 图谱写入、实体关系抽取、检索适配 |
| Ollama | 本地 embedding,nomic-embed-text,768 维 |
| DeepSeek | 本体、建图、Agent、报告生成 |
| OASIS | Twitter / Reddit 行为模拟 |
我们最终放弃了 Graphiti 的 Kuzu 路线:构造阶段补完 _database 后,继续遇到 Table Entity doesn't exist。因此本地方案选 Neo4j,而不是继续修不完整的嵌入式驱动。
6. OASIS:MiroFish 的行为世界
OASIS 不是一个通用剧场,而是社交平台环境。Agent 的主要动作是:
text
Twitter:发帖 / 点赞 / 转发 / 关注 / 引用
Reddit :发帖 / 评论 / 搜索 / 点赞 / 点踩 / 关注模拟运行大致是:
Twitter 与 Reddit 的价值
同一个 Agent 在两个平台上的表达可能不同:
| 平台 | 更像什么 | 常见表现 |
|---|---|---|
| 公开平台 | 口径更谨慎、重视形象 | |
| 匿名论坛 | 更容易泄露怀疑、情绪和细节 |
这能产生“同一个人两套说法”的矛盾,但它仍然是公开舆论矛盾,不是正式审讯。
7. 报告和 Interview 的边界
Report Agent
报告不是简单把帖子拼起来。它会:
text
模拟动作
↓
图谱检索
↓
规划报告大纲
↓
逐章节深度检索与撰写
↓
完整报告本地实测中,完整报告会明显慢于模拟,因为它要逐章节做长上下文分析;报告文件会分章节保存,防止中途失败后完全没有产物。
Interview
原生 Interview 是:
text
系统 / 外部调用者 → 单个 Agent → 文本回答它不是 Agent 之间的原生对话。我们后来加了另一个模式:
text
Agent A 生成问题
↓
Agent B 按自己的人设回答
↓
Agent A 读取回答并决定是否追问这正是后来戏剧工坊采用的角色交互基础。
8. 它适合和不适合什么
适合
- 舆情演化;
- 多方站队;
- 谣言扩散;
- 公开讨论中的立场变化;
- 研究某种回应策略会引发什么反应;
- 把一份报告变成多个利益相关者的讨论。
不适合直接做
- 连续场景戏;
- 角色动作与台词;
- 复杂审讯;
- 固定镜头节奏;
- 长篇剧集的场次管理;
- 直接导出分镜。
它的基本单位是:
text
一次平台行为而不是:
text
一次戏剧交锋9. 我们从 MiroFish 留下什么
最终保留下来的不是 OASIS,而是三种思想:
9.1 事件应该进入长期结构化记忆
不是只留聊天文本,而是记录:
text
谁出现
谁说了什么
谁因此知道了什么
这件事发生在什么时间9.2 Agent 的知识应该有来源
一个角色知道某件事,应该能追溯到:
text
亲眼看到
别人告诉
从公开材料读到
自己的秘密9.3 舆情模拟和角色演戏应该分开
MiroFish 继续作为:
text
群体智能预测 / 舆情实验 / 反应模拟器戏剧工坊负责:
text
场景 / 动作 / 台词 / 旁白 / 剧情推进两者共享图谱、模型和部分记忆思想,但不应该强行使用同一套行为循环。
10. 一句话总结
MiroFish 把一份材料变成一个会讨论它的群体;它的核心不是“生成答案”,而是让不同 Agent 在图谱记忆和社交平台规则里产生分化反应。
它最值得借鉴的地方是:
先把世界中的实体、关系和事件结构化,再让 Agent 在这个结构上行动。