mirofish-offline-simulation
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseMiroFish-Offline Skill
MiroFish-Offline Skill
Skill by ara.so — Daily 2026 Skills collection.
MiroFish-Offline is a fully local multi-agent swarm intelligence engine. Feed it any document (press release, policy draft, financial report) and it generates hundreds of AI agents with unique personalities that simulate public reaction on social media — posts, arguments, opinion shifts — hour by hour. No cloud APIs required: Neo4j CE 5.15 handles graph memory, Ollama serves the LLMs.
由ara.so开发的Skill — 属于Daily 2026 Skills合集。
MiroFish-Offline是一款全本地化的多智能体群体智能引擎。向其输入任意文档(新闻稿、政策草案、财务报告),它就能生成数百个具有独特人格的AI Agent,逐小时模拟公众在社交媒体上的反应——包括发帖、争论、观点转变等全过程。无需调用云端API:Neo4j CE 5.15负责图内存存储,Ollama提供大语言模型支持。
Architecture Overview
架构概述
Document Input
│
▼
Graph Build (NER + relationship extraction via Ollama LLM)
│
▼
Neo4j Knowledge Graph (entities, relations, embeddings via nomic-embed-text)
│
▼
Env Setup (generate hundreds of agent personas with personalities + memory)
│
▼
Simulation (agents post, reply, argue, shift opinions on simulated platforms)
│
▼
Report (ReportAgent interviews focus group, queries graph, generates analysis)
│
▼
Interaction (chat with any individual agent, full memory persists)Backend: Flask + Python 3.11
Frontend: Vue 3 + Node 18
Graph DB: Neo4j CE 5.15 (bolt protocol)
LLM: Ollama (OpenAI-compatible endpoint)
Embeddings: (768-dimensional, via Ollama)
Search: Hybrid — 0.7 × vector similarity + 0.3 × BM25
Frontend: Vue 3 + Node 18
Graph DB: Neo4j CE 5.15 (bolt protocol)
LLM: Ollama (OpenAI-compatible
/v1Embeddings:
nomic-embed-textSearch: Hybrid — 0.7 × vector similarity + 0.3 × BM25
文档输入
│
▼
图构建(通过Ollama LLM实现命名实体识别+关系提取)
│
▼
Neo4j知识图谱(实体、关系、基于nomic-embed-text的嵌入向量)
│
▼
环境搭建(生成数百个带有人格与记忆的Agent角色)
│
▼
仿真模拟(Agent在模拟平台上发帖、回复、争论、转变观点)
│
▼
报告生成(ReportAgent访谈焦点群体、查询图谱、生成分析报告)
│
▼
交互功能(与任意单个Agent对话,完整记忆持久化存储)后端:Flask + Python 3.11
前端:Vue 3 + Node 18
图数据库:Neo4j CE 5.15(bolt协议)
大语言模型:Ollama(兼容OpenAI的端点)
嵌入模型:(768维,通过Ollama提供)
搜索方式:混合搜索 — 0.7×向量相似度 + 0.3×BM25
前端:Vue 3 + Node 18
图数据库:Neo4j CE 5.15(bolt协议)
大语言模型:Ollama(兼容OpenAI的
/v1嵌入模型:
nomic-embed-text搜索方式:混合搜索 — 0.7×向量相似度 + 0.3×BM25
Installation
安装步骤
Option A: Docker (Recommended)
选项A:Docker(推荐)
bash
git clone https://github.com/nikmcfly/MiroFish-Offline.git
cd MiroFish-Offline
cp .env.example .envbash
git clone https://github.com/nikmcfly/MiroFish-Offline.git
cd MiroFish-Offline
cp .env.example .envStart Neo4j + Ollama + MiroFish backend + frontend
启动Neo4j + Ollama + MiroFish后端 + 前端
docker compose up -d
docker compose up -d
Pull required models into the Ollama container
拉取所需模型到Ollama容器中
docker exec mirofish-ollama ollama pull qwen2.5:32b
docker exec mirofish-ollama ollama pull nomic-embed-text
docker exec mirofish-ollama ollama pull qwen2.5:32b
docker exec mirofish-ollama ollama pull nomic-embed-text
Check all services are healthy
检查所有服务是否健康运行
docker compose ps
Open `http://localhost:3000`.docker compose ps
打开`http://localhost:3000`即可访问。Option B: Manual Setup
选项B:手动安装
1. Neo4j
bash
docker run -d --name neo4j \
-p 7474:7474 -p 7687:7687 \
-e NEO4J_AUTH=neo4j/mirofish \
neo4j:5.15-community2. Ollama
bash
ollama serve &
ollama pull qwen2.5:32b # Main LLM (~20GB, requires 24GB VRAM)
ollama pull qwen2.5:14b # Lighter option (~10GB VRAM)
ollama pull nomic-embed-text # Embeddings (small, fast)3. Backend
bash
cp .env.example .env1. 安装Neo4j
bash
docker run -d --name neo4j \
-p 7474:7474 -p 7687:7687 \
-e NEO4J_AUTH=neo4j/mirofish \
neo4j:5.15-community2. 安装Ollama
bash
ollama serve &
ollama pull qwen2.5:32b # 主LLM(约20GB,需要24GB显存)
ollama pull qwen2.5:14b # 轻量化选项(约10GB显存)
ollama pull nomic-embed-text # 嵌入模型(体积小、速度快)3. 安装后端
bash
cp .env.example .envEdit .env (see Configuration section)
编辑.env文件(参考配置章节)
cd backend
pip install -r requirements.txt
python run.py
cd backend
pip install -r requirements.txt
python run.py
Backend starts on http://localhost:5000
**4. Frontend**
```bash
cd frontend
npm install
npm run dev
**4. 安装前端**
```bash
cd frontend
npm install
npm run devFrontend starts on http://localhost:3000
---
---Configuration (.env
)
.env配置(.env
)
.envbash
undefinedbash
undefined── LLM (Ollama OpenAI-compatible endpoint) ──────────────────────────
── LLM(Ollama兼容OpenAI端点) ──────────────────────────
LLM_API_KEY=ollama
LLM_BASE_URL=http://localhost:11434/v1
LLM_MODEL_NAME=qwen2.5:32b
LLM_API_KEY=ollama
LLM_BASE_URL=http://localhost:11434/v1
LLM_MODEL_NAME=qwen2.5:32b
── Neo4j ─────────────────────────────────────────────────────────────
── Neo4j ─────────────────────────────────────────────────────────────
NEO4J_URI=bolt://localhost:7687
NEO4J_USER=neo4j
NEO4J_PASSWORD=mirofish
NEO4J_URI=bolt://localhost:7687
NEO4J_USER=neo4j
NEO4J_PASSWORD=mirofish
── Embeddings (Ollama) ───────────────────────────────────────────────
── 嵌入模型(Ollama) ───────────────────────────────────────────────
EMBEDDING_MODEL=nomic-embed-text
EMBEDDING_BASE_URL=http://localhost:11434
EMBEDDING_MODEL=nomic-embed-text
EMBEDDING_BASE_URL=http://localhost:11434
── Optional: swap Ollama for any OpenAI-compatible provider ─────────
── 可选:替换Ollama为任意兼容OpenAI的服务商 ─────────
LLM_API_KEY=$OPENAI_API_KEY
LLM_API_KEY=$OPENAI_API_KEY
LLM_BASE_URL=https://api.openai.com/v1
LLM_BASE_URL=https://api.openai.com/v1
LLM_MODEL_NAME=gpt-4o
LLM_MODEL_NAME=gpt-4o
---
---Core Python API
核心Python API
GraphStorage Interface
GraphStorage接口
The abstraction layer between MiroFish and the graph database:
python
from backend.storage.base import GraphStorage
from backend.storage.neo4j_storage import Neo4jStorageMiroFish与图数据库之间的抽象层:
python
from backend.storage.base import GraphStorage
from backend.storage.neo4j_storage import Neo4jStorageInitialize storage (typically done via Flask app.extensions)
初始化存储(通常通过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"],
)
undefinedstorage = 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"],
)
undefinedBuilding a Knowledge Graph from a Document
从文档构建知识图谱
python
from backend.services.graph_builder import GraphBuilder
builder = GraphBuilder(storage=storage)python
from backend.services.graph_builder import GraphBuilder
builder = GraphBuilder(storage=storage)Feed a document string
读取文档字符串
with open("press_release.txt", "r") as f:
document_text = f.read()
with open("press_release.txt", "r") as f:
document_text = f.read()
Extract entities + relationships, store in Neo4j
提取实体+关系,存储到Neo4j
graph_id = builder.build(
content=document_text,
title="Q4 Earnings Report",
source_type="financial_report",
)
print(f"Graph built: {graph_id}")
graph_id = builder.build(
content=document_text,
title="Q4 Earnings Report",
source_type="financial_report",
)
print(f"图谱构建完成: {graph_id}")
Returns a graph_id used for subsequent simulation runs
返回的graph_id用于后续的仿真运行
undefinedundefinedCreating and Running a Simulation
创建并运行仿真
python
from backend.services.simulation import SimulationService
sim = SimulationService(storage=storage)python
from backend.services.simulation import SimulationService
sim = SimulationService(storage=storage)Create a simulation environment from an existing graph
从现有图谱创建仿真环境
sim_id = sim.create_environment(
graph_id=graph_id,
agent_count=200, # Number of agents to generate
simulation_hours=24, # Simulated time span
platform="twitter", # "twitter" | "reddit" | "weibo"
)
sim_id = sim.create_environment(
graph_id=graph_id,
agent_count=200, # 要生成的Agent数量
simulation_hours=24, # 仿真时间跨度
platform="twitter", # 可选值:"twitter" | "reddit" | "weibo"
)
Run the simulation (blocking — use async wrapper for production)
运行仿真(阻塞式——生产环境请使用异步封装)
result = sim.run(sim_id=sim_id)
print(f"Simulation complete. Posts generated: {result['post_count']}")
print(f"Sentiment trajectory: {result['sentiment_over_time']}")
undefinedresult = sim.run(sim_id=sim_id)
print(f"仿真完成。生成帖子数量: {result['post_count']}")
print(f"情绪变化轨迹: {result['sentiment_over_time']}")
undefinedQuerying Simulation Results
查询仿真结果
python
from backend.services.report import ReportAgent
report_agent = ReportAgent(storage=storage)python
from backend.services.report import ReportAgent
report_agent = ReportAgent(storage=storage)Generate a structured analysis report
生成结构化分析报告
report = report_agent.generate(
sim_id=sim_id,
focus_group_size=10, # Number of agents to interview
include_graph_search=True,
)
print(report["summary"])
print(report["key_narratives"])
print(report["sentiment_shift"])
print(report["influential_agents"])
undefinedreport = report_agent.generate(
sim_id=sim_id,
focus_group_size=10, # 要访谈的Agent数量
include_graph_search=True,
)
print(report["summary"])
print(report["key_narratives"])
print(report["sentiment_shift"])
print(report["influential_agents"])
undefinedChatting with a Simulated Agent
与仿真Agent对话
python
from backend.services.agent_chat import AgentChatService
chat = AgentChatService(storage=storage)python
from backend.services.agent_chat import AgentChatService
chat = AgentChatService(storage=storage)List agents from a completed simulation
列出已完成仿真中的Agent
agents = chat.list_agents(sim_id=sim_id, limit=10)
agent_id = agents[0]["id"]
print(f"Chatting with: {agents[0]['persona']['name']}")
print(f"Personality: {agents[0]['persona']['traits']}")
agents = chat.list_agents(sim_id=sim_id, limit=10)
agent_id = agents[0]["id"]
print(f"正在与以下Agent对话: {agents[0]['persona']['name']}")
print(f"人格特质: {agents[0]['persona']['traits']}")
Send a message — agent responds in-character with full memory
发送消息——Agent会结合完整记忆以符合其人格的方式回复
response = chat.send(
agent_id=agent_id,
message="Why did you post that criticism about the earnings report?",
)
print(response["reply"])
response = chat.send(
agent_id=agent_id,
message="你为什么要发布那篇批评收益报告的帖子?",
)
print(response["reply"])
→ Agent responds using its personality, opinion bias, and post history
→ Agent会结合自身人格、观点倾向及发帖历史进行回复
undefinedundefinedHybrid Search on the Knowledge Graph
知识图谱混合搜索
python
from backend.services.search import SearchService
search = SearchService(storage=storage)python
from backend.services.search import SearchService
search = SearchService(storage=storage)Hybrid search: 0.7 * vector similarity + 0.3 * BM25
混合搜索:0.7×向量相似度 + 0.3×BM25
results = search.query(
text="executive compensation controversy",
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"])
undefinedresults = 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"])
undefinedImplementing a Custom GraphStorage Backend
自定义GraphStorage后端实现
python
from backend.storage.base import GraphStorage
from typing import List, Dict, Any
class MyCustomStorage(GraphStorage):
"""
Swap Neo4j for any graph DB by implementing this interface.
Register via Flask app.extensions['neo4j_storage'] = MyCustomStorage(...)
"""
def store_entity(self, entity: Dict[str, Any]) -> str:
# Store entity, return 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 NotImplementedErrorpython
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 NotImplementedErrorFlask App Integration Pattern
Flask应用集成模式
python
undefinedpython
undefinedbackend/app.py — how storage is wired via dependency injection
backend/app.py — 如何通过依赖注入实现存储组件的连接
from flask import Flask
from backend.storage.neo4j_storage import Neo4jStorage
import os
def create_app():
app = Flask(name)
# Single storage instance, injected everywhere via 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 appundefinedfrom 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 appundefinedAccessing Storage in a Flask Route
在Flask路由中访问存储组件
python
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)python
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 Reference
REST API参考
| Method | Endpoint | Description |
|---|---|---|
| | Upload document, build knowledge graph |
| | Get graph entities and relationships |
| | Create simulation environment |
| | Execute simulation |
| | Get posts, sentiment, metrics |
| | List generated agents |
| | Generate ReportAgent analysis |
| | Chat with a specific agent |
| | Hybrid search the knowledge graph |
Example: Build graph from document
bash
curl -X POST http://localhost:5000/api/graph/build \
-H "Content-Type: application/json" \
-d '{
"content": "Acme Corp announces record Q4 earnings, CFO resigns...",
"title": "Q4 Press Release",
"source_type": "press_release"
}'| 方法 | 端点 | 描述 |
|---|---|---|
| | 上传文档,构建知识图谱 |
| | 获取图谱的实体与关系 |
| | 创建仿真环境 |
| | 执行仿真 |
| | 获取帖子、情绪数据及指标 |
| | 列出生成的Agent |
| | 生成ReportAgent分析报告 |
| | 与指定Agent对话 |
| | 对知识图谱执行混合搜索 |
示例:从文档构建图谱
bash
curl -X POST http://localhost:5000/api/graph/build \
-H "Content-Type: application/json" \
-d '{
"content": "Acme Corp宣布第四季度收益创新高,首席财务官辞职...",
"title": "Q4新闻稿",
"source_type": "press_release"
}'→ {"graph_id": "g_abc123", "entities": 47, "relationships": 89}
→ {"graph_id": "g_abc123", "entities": 47, "relationships": 89}
**Example: Run a simulation**
```bash
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"
}'
**示例:运行仿真**
```bash
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"}
→ {"sim_id": "s_xyz789", "status": "running"}
---
---Hardware Selection Guide
硬件选型指南
| Use Case | Model | VRAM | RAM |
|---|---|---|---|
| Quick test / dev | | 6 GB | 16 GB |
| Balanced quality | | 10 GB | 16 GB |
| Production quality | | 24 GB | 32 GB |
| CPU-only (slow) | | None | 16 GB |
Switch model by editing :
.envbash
LLM_MODEL_NAME=qwen2.5:14bThen restart the backend — no other changes needed.
| 使用场景 | 模型 | 显存 | 内存 |
|---|---|---|---|
| 快速测试/开发 | | 6 GB | 16 GB |
| 平衡性能与质量 | | 10 GB | 16 GB |
| 生产环境质量 | | 24 GB | 32 GB |
| 仅CPU运行(速度慢) | | 无 | 16 GB |
通过编辑文件切换模型:
.envbash
LLM_MODEL_NAME=qwen2.5:14b然后重启后端即可,无需其他修改。
Common Patterns
常见使用模式
PR Crisis Test Pipeline
公关危机测试流水线
python
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. Build knowledge graph
builder = GraphBuilder(storage=storage)
graph_id = builder.build(content=text, title="Draft PR", source_type="press_release")
# 2. Simulate public reaction
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. Generate report
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"],
}python
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="Draft PR", 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"],
}Usage
使用示例
with open("draft_announcement.txt") as f:
result = test_press_release(f.read())
print(f"Risk score: {result['risk_score']}/10")
print(f"Top narrative: {result['key_narratives'][0]}")
undefinedwith open("draft_announcement.txt") as f:
result = test_press_release(f.read())
print(f"风险评分: {result['risk_score']}/10")
print(f"核心舆论倾向: {result['key_narratives'][0]}")
undefinedUse Any OpenAI-Compatible Provider
使用任意兼容OpenAI的服务商
bash
undefinedbash
undefinedClaude via Anthropic (or any proxy)
通过Anthropic使用Claude(或任意代理)
LLM_API_KEY=$ANTHROPIC_API_KEY
LLM_BASE_URL=https://api.anthropic.com/v1
LLM_MODEL_NAME=claude-3-5-sonnet-20241022
LLM_API_KEY=$ANTHROPIC_API_KEY
LLM_BASE_URL=https://api.anthropic.com/v1
LLM_MODEL_NAME=claude-3-5-sonnet-20241022
OpenAI
OpenAI官方服务
LLM_API_KEY=$OPENAI_API_KEY
LLM_BASE_URL=https://api.openai.com/v1
LLM_MODEL_NAME=gpt-4o
LLM_API_KEY=$OPENAI_API_KEY
LLM_BASE_URL=https://api.openai.com/v1
LLM_MODEL_NAME=gpt-4o
Local LM Studio
本地LM Studio
LLM_API_KEY=lm-studio
LLM_BASE_URL=http://localhost:1234/v1
LLM_MODEL_NAME=your-loaded-model
---LLM_API_KEY=lm-studio
LLM_BASE_URL=http://localhost:1234/v1
LLM_MODEL_NAME=your-loaded-model
---Troubleshooting
故障排查
Neo4j connection refused
Neo4j连接被拒绝
bash
undefinedbash
undefinedCheck Neo4j is running
检查Neo4j是否在运行
docker ps | grep neo4j
docker ps | grep neo4j
Check bolt port
检查bolt端口
nc -zv localhost 7687
nc -zv localhost 7687
View Neo4j logs
查看Neo4j日志
docker logs neo4j --tail 50
undefineddocker logs neo4j --tail 50
undefinedOllama model not found
Ollama模型未找到
bash
undefinedbash
undefinedList available models
列出可用模型
ollama list
ollama list
Pull missing models
拉取缺失的模型
ollama pull qwen2.5:32b
ollama pull nomic-embed-text
ollama pull qwen2.5:32b
ollama pull nomic-embed-text
Check Ollama is serving
检查Ollama是否正常提供服务
undefinedundefinedOut of VRAM
显存不足
bash
undefinedbash
undefinedSwitch to smaller model in .env
在.env中切换为更小的模型
LLM_MODEL_NAME=qwen2.5:14b # or qwen2.5:7b
LLM_MODEL_NAME=qwen2.5:14b # 或qwen2.5:7b
Restart backend
重启后端
cd backend && python run.py
undefinedcd backend && python run.py
undefinedEmbeddings dimension mismatch
嵌入向量维度不匹配
bash
undefinedbash
undefinednomic-embed-text produces 768-dim vectors
nomic-embed-text生成768维向量
If you switch embedding models, drop and recreate the Neo4j vector index:
若切换嵌入模型,需删除并重新创建Neo4j向量索引:
In Neo4j browser (http://localhost:7474):
在Neo4j浏览器(http://localhost:7474)中执行:
DROP INDEX entity_embedding IF EXISTS;
DROP INDEX entity_embedding IF EXISTS;
Then restart MiroFish — it recreates the index with correct dimensions.
然后重启MiroFish——它会自动以正确维度重建索引。
undefinedundefinedDocker Compose: Ollama container can't access GPU
Docker Compose:Ollama容器无法访问GPU
yaml
undefinedyaml
undefineddocker-compose.yml — add GPU reservation:
docker-compose.yml — 添加GPU资源预留:
services:
ollama:
deploy:
resources:
reservations:
devices:
- driver: nvidia
count: 1
capabilities: [gpu]
undefinedservices:
ollama:
deploy:
resources:
reservations:
devices:
- driver: nvidia
count: 1
capabilities: [gpu]
undefinedSlow simulation on CPU
CPU运行仿真速度慢
- Use for faster (lower quality) inference
qwen2.5:7b - Reduce to 50–100 for testing
agent_count - Reduce to 6–12
simulation_hours - CPU inference with 7b model: expect ~5–10 tokens/sec
- 使用模型以获得更快(质量稍低)的推理速度
qwen2.5:7b - 测试时将减少到50–100
agent_count - 将减少到6–12
simulation_hours - 7b模型的CPU推理速度:预计约5–10 tokens/秒
Frontend can't reach backend
前端无法连接后端
bash
undefinedbash
undefinedCheck VITE_API_BASE_URL in frontend/.env
检查frontend/.env中的VITE_API_BASE_URL
VITE_API_BASE_URL=http://localhost:5000
VITE_API_BASE_URL=http://localhost:5000
Verify backend is up
验证后端是否正常运行
---
---Project Structure
项目结构
MiroFish-Offline/
├── backend/
│ ├── run.py # Entry point
│ ├── app.py # Flask factory, DI wiring
│ ├── storage/
│ │ ├── base.py # GraphStorage abstract interface
│ │ └── neo4j_storage.py # Neo4j implementation
│ ├── services/
│ │ ├── graph_builder.py # NER + relationship extraction
│ │ ├── simulation.py # Agent simulation engine
│ │ ├── report.py # ReportAgent + focus group
│ │ ├── agent_chat.py # Per-agent chat interface
│ │ └── search.py # Hybrid vector + BM25 search
│ └── routes/
│ ├── graph.py
│ ├── simulation.py
│ └── report.py
├── frontend/ # Vue 3 (fully English UI)
├── docker-compose.yml
├── .env.example
└── README.mdMiroFish-Offline/
├── backend/
│ ├── run.py # 项目入口
│ ├── app.py # Flask工厂、依赖注入配置
│ ├── storage/
│ │ ├── base.py # GraphStorage抽象接口
│ │ └── neo4j_storage.py # Neo4j实现
│ ├── services/
│ │ ├── graph_builder.py # 命名实体识别+关系提取服务
│ │ ├── simulation.py # Agent仿真引擎
│ │ ├── report.py # ReportAgent+焦点群体分析服务
│ │ ├── agent_chat.py # 单Agent对话接口
│ │ └── search.py # 混合向量+BM25搜索服务
│ └── routes/
│ ├── graph.py
│ ├── simulation.py
│ └── report.py
├── frontend/ # Vue 3实现(全英文UI)
├── docker-compose.yml
├── .env.example
└── README.md