🚀 快速安装
复制以下命令并运行,立即安装此 Skill:
npx skills add https://skills.sh/aradotso/trending-skills/mirofish-offline-simulation
💡 提示:需要 Node.js 和 NPM
MiroFish-Offline 技能
技能由 ara.so 提供 — Daily 2026 Skills 系列。
MiroFish-Offline 是一个完全本地的多智能体群智能引擎。输入任何文档(新闻稿、政策草案、财务报告),它就会生成数百个具有独特个性的 AI 智能体,在社交媒体上模拟公众反应——发帖、争论、观点转变——按小时模拟。无需云 API:Neo4j CE 5.15 处理图内存,Ollama 提供 LLM 服务。
架构概述
文档输入
│
▼
图构建(通过 Ollama LLM 进行命名实体识别 + 关系提取)
│
▼
Neo4j 知识图谱(实体、关系、通过 nomic-embed-text 生成的嵌入向量)
│
▼
环境设置(生成数百个具有个性和记忆的智能体角色)
│
▼
模拟(智能体在模拟平台上发帖、回复、争论、转变观点)
│
▼
报告(报告智能体采访焦点小组、查询图谱、生成分析)
│
▼
交互(与任何单个智能体聊天,完整记忆持久化)
后端: Flask + Python 3.11
前端: Vue 3 + Node 18
图数据库: Neo4j CE 5.15(bolt 协议)
LLM: Ollama(OpenAI 兼容的 /v1 端点)
嵌入模型: nomic-embed-text(768 维,通过 Ollama)
搜索: 混合搜索 — 0.7 × 向量相似度 + 0.3 × BM25
安装
选项 A:Docker(推荐)
git clone https://github.com/nikmcfly/MiroFish-Offline.git
cd MiroFish-Offline
cp .env.example .env
# 启动 Neo4j + Ollama + MiroFish 后端 + 前端
docker compose up -d
# 将所需模型拉取到 Ollama 容器中
docker exec mirofish-ollama ollama pull qwen2.5:32b
docker exec mirofish-ollama ollama pull nomic-embed-text
# 检查所有服务是否健康
docker compose ps
打开 http://localhost:3000。
选项 B:手动设置
1. Neo4j
docker run -d --name neo4j \
-p 7474:7474 -p 7687:7687 \
-e NEO4J_AUTH=neo4j/mirofish \
neo4j:5.15-community
2. Ollama
ollama serve &
ollama pull qwen2.5:32b # 主 LLM(约 20GB,需要 24GB 显存)
ollama pull qwen2.5:14b # 轻量选项(约 10GB 显存)
ollama pull nomic-embed-text # 嵌入模型(小,快速)
3. 后端
cp .env.example .env
# 编辑 .env(见配置部分)
cd backend
pip install -r requirements.txt
python run.py
# 后端启动于 http://localhost:5000
4. 前端
cd frontend
npm install
npm run dev
# 前端启动于 http://localhost:3000
配置 (.env)
# ── LLM (Ollama OpenAI 兼容端点) ──────────────────────────
LLM_API_KEY=ollama
LLM_BASE_URL=http://localhost:11434/v1
LLM_MODEL_NAME=qwen2.5:32b
# ── Neo4j ─────────────────────────────────────────────────────────────
NEO4J_URI=bolt://localhost:7687
NEO4J_USER=neo4j
NEO4J_PASSWORD=mirofish
# ── 嵌入模型 (Ollama) ───────────────────────────────────────────────
EMBEDDING_MODEL=nomic-embed-text
EMBEDDING_BASE_URL=http://localhost:11434
# ── 可选:将 Ollama 替换为任何 OpenAI 兼容的提供商 ─────────
# LLM_API_KEY=$OPENAI_API_KEY
# LLM_BASE_URL=https://api.openai.com/v1
# LLM_MODEL_NAME=gpt-4o
核心 Python API
GraphStorage 接口
MiroFish 和图数据库之间的抽象层:
from backend.storage.base import GraphStorage
from backend.storage.neo4j_storage import Neo4jStorage
# 初始化存储(通常通过 Flask app.extensions 完成)
storage = Neo4jStorage(
uri=os.environ["NEO4J_URI"],
user=os.environ["NEO4J_USER"],
password=os.environ["NEO4J_PASSWORD"],
embedding_model=os.environ["EMBEDDING_MODEL"],
embedding_base_url=os.environ["EMBEDDING_BASE_URL"],
llm_base_url=os.environ["LLM_BASE_URL"],
llm_api_key=os.environ["LLM_API_KEY"],
llm_model=os.environ["LLM_MODEL_NAME"],
)
从文档构建知识图谱
from backend.services.graph_builder import GraphBuilder
builder = GraphBuilder(storage=storage)
# 输入文档字符串
with open("press_release.txt", "r") as f:
document_text = f.read()
# 提取实体 + 关系,存储到 Neo4j
graph_id = builder.build(
content=document_text,
title="Q4 Earnings Report",
source_type="financial_report",
)
print(f"图构建完成:{graph_id}")
# 返回用于后续模拟运行的 graph_id
创建并运行模拟
from backend.services.simulation import SimulationService
sim = SimulationService(storage=storage)
# 从现有图谱创建模拟环境
sim_id = sim.create_environment(
graph_id=graph_id,
agent_count=200, # 要生成的智能体数量
simulation_hours=24, # 模拟时间跨度
platform="twitter", # "twitter" | "reddit" | "weibo"
)
# 运行模拟(阻塞式 — 生产环境使用异步包装器)
result = sim.run(sim_id=sim_id)
print(f"模拟完成。生成的帖子数:{result['post_count']}")
print(f"情感轨迹:{result['sentiment_over_time']}")
查询模拟结果
from backend.services.report import ReportAgent
report_agent = ReportAgent(storage=storage)
# 生成结构化分析报告
report = report_agent.generate(
sim_id=sim_id,
focus_group_size=10, # 要采访的智能体数量
include_graph_search=True,
)
print(report["summary"])
print(report["key_narratives"])
print(report["sentiment_shift"])
print(report["influential_agents"])
与模拟智能体聊天
from backend.services.agent_chat import AgentChatService
chat = AgentChatService(storage=storage)
# 列出已完成模拟中的智能体
agents = chat.list_agents(sim_id=sim_id, limit=10)
agent_id = agents[0]["id"]
print(f"正在与以下智能体聊天:{agents[0]['persona']['name']}")
print(f"个性:{agents[0]['persona']['traits']}")
# 发送消息 — 智能体以角色身份回复,保留完整记忆
response = chat.send(
agent_id=agent_id,
message="你为什么发布那条关于财报的批评性内容?",
)
print(response["reply"])
# → 智能体使用其个性、观点偏见和发帖历史进行回复
知识图谱的混合搜索
from backend.services.search import SearchService
search = SearchService(storage=storage)
# 混合搜索:0.7 * 向量相似度 + 0.3 * BM25
results = search.query(
text="高管薪酬争议",
graph_id=graph_id,
top_k=5,
vector_weight=0.7,
bm25_weight=0.3,
)
for r in results:
print(r["entity"], r["relationship"], r["score"])
实现自定义 GraphStorage 后端
from backend.storage.base import GraphStorage
from typing import List, Dict, Any
class MyCustomStorage(GraphStorage):
"""
通过实现此接口,将 Neo4j 替换为任何图数据库。
通过 Flask app.extensions['neo4j_storage'] = MyCustomStorage(...) 注册
"""
def store_entity(self, entity: Dict[str, Any]) -> str:
# 存储实体,返回 entity_id
raise NotImplementedError
def store_relationship(
self,
source_id: str,
target_id: str,
relation_type: str,
properties: Dict[str, Any],
) -> str:
raise NotImplementedError
def vector_search(
self, embedding: List[float], top_k: int = 5
) -> List[Dict[str, Any]]:
raise NotImplementedError
def keyword_search(
self, query: str, top_k: int = 5
) -> List[Dict[str, Any]]:
raise NotImplementedError
def get_agent_memory(self, agent_id: str) -> Dict[str, Any]:
raise NotImplementedError
def update_agent_memory(
self, agent_id: str, memory_update: Dict[str, Any]
) -> None:
raise NotImplementedError
Flask 应用集成模式
# backend/app.py — 如何通过依赖注入连接存储
from flask import Flask
from backend.storage.neo4j_storage import Neo4jStorage
import os
def create_app():
app = Flask(__name__)
# 单个存储实例,通过 app.extensions 注入到各处
storage = Neo4jStorage(
uri=os.environ["NEO4J_URI"],
user=os.environ["NEO4J_USER"],
password=os.environ["NEO4J_PASSWORD"],
embedding_model=os.environ["EMBEDDING_MODEL"],
embedding_base_url=os.environ["EMBEDDING_BASE_URL"],
llm_base_url=os.environ["LLM_BASE_URL"],
llm_api_key=os.environ["LLM_API_KEY"],
llm_model=os.environ["LLM_MODEL_NAME"],
)
app.extensions["neo4j_storage"] = storage
from backend.routes import graph_bp, simulation_bp, report_bp
app.register_blueprint(graph_bp)
app.register_blueprint(simulation_bp)
app.register_blueprint(report_bp)
return app
在 Flask 路由中访问存储
from flask import Blueprint, current_app, request, jsonify
simulation_bp = Blueprint("simulation", __name__)
@simulation_bp.route("/api/simulation/run", methods=["POST"])
def run_simulation():
storage = current_app.extensions["neo4j_storage"]
data = request.json
sim = SimulationService(storage=storage)
sim_id = sim.create_environment(
graph_id=data["graph_id"],
agent_count=data.get("agent_count", 200),
simulation_hours=data.get("simulation_hours", 24),
)
result = sim.run(sim_id=sim_id)
return jsonify(result)
REST API 参考
| 方法 | 端点 | 描述 |
|---|---|---|
POST |
/api/graph/build |
上传文档,构建知识图谱 |
GET |
/api/graph/:id |
获取图谱实体和关系 |
POST |
/api/simulation/create |
创建模拟环境 |
POST |
/api/simulation/run |
执行模拟 |
GET |
/api/simulation/:id/results |
获取帖子、情感、指标 |
GET |
/api/simulation/:id/agents |
列出生成的智能体 |
POST |
/api/report/generate |
生成 ReportAgent 分析 |
POST |
/api/agent/:id/chat |
与特定智能体聊天 |
GET |
/api/search |
对知识图谱进行混合搜索 |
示例:从文档构建图谱
curl -X POST http://localhost:5000/api/graph/build \
-H "Content-Type: application/json" \
-d '{
"content": "Acme Corp 宣布创纪录的第四季度收益,CFO 辞职...",
"title": "Q4 新闻稿",
"source_type": "press_release"
}'
# → {"graph_id": "g_abc123", "entities": 47, "relationships": 89}
示例:运行模拟
curl -X POST http://localhost:5000/api/simulation/run \
-H "Content-Type: application/json" \
-d '{
"graph_id": "g_abc123",
"agent_count": 150,
"simulation_hours": 12,
"platform": "twitter"
}'
# → {"sim_id": "s_xyz789", "status": "running"}
硬件选择指南
| 使用场景 | 模型 | 显存 | 内存 |
|---|---|---|---|
| 快速测试 / 开发 | qwen2.5:7b |
6 GB | 16 GB |
| 平衡质量 | qwen2.5:14b |
10 GB | 16 GB |
| 生产质量 | qwen2.5:32b |
24 GB | 32 GB |
| 仅 CPU(慢) | qwen2.5:7b |
无 | 16 GB |
通过编辑 .env 切换模型:
LLM_MODEL_NAME=qwen2.5:14b
然后重启后端 — 无需其他更改。
常见模式
公关危机测试流程
import os
from backend.storage.neo4j_storage import Neo4jStorage
from backend.services.graph_builder import GraphBuilder
from backend.services.simulation import SimulationService
from backend.services.report import ReportAgent
storage = Neo4jStorage(
uri=os.environ["NEO4J_URI"],
user=os.environ["NEO4J_USER"],
password=os.environ["NEO4J_PASSWORD"],
embedding_model=os.environ["EMBEDDING_MODEL"],
embedding_base_url=os.environ["EMBEDDING_BASE_URL"],
llm_base_url=os.environ["LLM_BASE_URL"],
llm_api_key=os.environ["LLM_API_KEY"],
llm_model=os.environ["LLM_MODEL_NAME"],
)
def test_press_release(text: str) -> dict:
# 1. 构建知识图谱
builder = GraphBuilder(storage=storage)
graph_id = builder.build(content=text, title="草案", source_type="press_release")
# 2. 模拟公众反应
sim = SimulationService(storage=storage)
sim_id = sim.create_environment(graph_id=graph_id, agent_count=300, simulation_hours=48)
sim.run(sim_id=sim_id)
# 3. 生成报告
report = ReportAgent(storage=storage).generate(sim_id=sim_id, focus_group_size=15)
return {
"sentiment_peak": report["sentiment_over_time"][0],
"key_narratives": report["key_narratives"],
"risk_score": report["risk_score"],
"recommended_edits": report["recommendations"],
}
# 用法
with open("draft_announcement.txt") as f:
result = test_press_release(f.read())
print(f"风险评分:{result['risk_score']}/10")
print(f"主要叙事:{result['key_narratives'][0]}")
使用任何 OpenAI 兼容的提供商
# 通过 Anthropic 使用 Claude(或任何代理)
LLM_API_KEY=$ANTHROPIC_API_KEY
LLM_BASE_URL=https://api.anthropic.com/v1
LLM_MODEL_NAME=claude-3-5-sonnet-20241022
# OpenAI
LLM_API_KEY=$OPENAI_API_KEY
LLM_BASE_URL=https://api.openai.com/v1
LLM_MODEL_NAME=gpt-4o
# 本地 LM Studio
LLM_API_KEY=lm-studio
LLM_BASE_URL=http://localhost:1234/v1
LLM_MODEL_NAME=your-loaded-model
故障排除
Neo4j 连接被拒绝
# 检查 Neo4j 是否正在运行
docker ps | grep neo4j
# 检查 bolt 端口
nc -zv localhost 7687
# 查看 Neo4j 日志
docker logs neo4j --tail 50
Ollama 找不到模型
# 列出可用模型
ollama list
# 拉取缺失的模型
ollama pull qwen2.5:32b
ollama pull nomic-embed-text
# 检查 Ollama 是否正在服务
curl http://localhost:11434/api/tags
显存不足
# 在 .env 中切换到更小的模型
LLM_MODEL_NAME=qwen2.5:14b # 或 qwen2.5:7b
# 重启后端
cd backend && python run.py
嵌入向量维度不匹配
# nomic-embed-text 生成 768 维向量
# 如果切换嵌入模型,请删除并重新创建 Neo4j 向量索引:
# 在 Neo4j 浏览器中 (http://localhost:7474):
# DROP INDEX entity_embedding IF EXISTS;
# 然后重启 MiroFish — 它会以正确的维度重新创建索引。
Docker Compose:Ollama 容器无法访问 GPU
# docker-compose.yml — 添加 GPU 预留:
services:
ollama:
deploy:
resources:
reservations:
devices:
- driver: nvidia
count: 1
capabilities: [gpu]
CPU 上模拟速度慢
- 使用
qwen2.5:7b进行更快的(质量较低)推理 - 将
agent_count减少到 50–100 用于测试 - 将
simulation_hours减少到 6–12 - 使用 7b 模型的 CPU 推理:预计约 5–10 tokens/秒
前端无法访问后端
# 检查 frontend/.env 中的 VITE_API_BASE_URL
VITE_API_BASE_URL=http://localhost:5000
# 验证后端是否已启动
curl http://localhost:5000/api/health
项目结构
MiroFish-Offline/
├── backend/
│ ├── run.py # 入口点
│ ├── app.py # Flask 工厂,依赖注入
│ ├── storage/
│ │ ├── base.py # GraphStorage 抽象接口
│ │ └── neo4j_storage.py # Neo4j 实现
│ ├── services/
│ │ ├── graph_builder.py # 命名实体识别 + 关系提取
│ │ ├── simulation.py # 智能体模拟引擎
│ │ ├── report.py # ReportAgent + 焦点小组
│ │ ├── agent_chat.py # 每个智能体的聊天接口
│ │ └── search.py # 混合向量 + BM25 搜索
│ └── routes/
│ ├── graph.py
│ ├── simulation.py
│ └── report.py
├── frontend/ # Vue 3(完全英文 UI)
├── docker-compose.yml
├── .env.example
└── README.md
📄 原始文档
完整文档(英文):
https://skills.sh/aradotso/trending-skills/mirofish-offline-simulation
💡 提示:点击上方链接查看 skills.sh 原始英文文档,方便对照翻译。
声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。

评论(0)