mirofish-offline-simulation

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

MiroFish-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
/v1
endpoint)
Embeddings:
nomic-embed-text
(768-dimensional, via Ollama)
Search: 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的
/v1
端点)
嵌入模型
nomic-embed-text
(768维,通过Ollama提供)
搜索方式:混合搜索 — 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 .env
bash
git clone https://github.com/nikmcfly/MiroFish-Offline.git
cd MiroFish-Offline
cp .env.example .env

Start 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-community
2. 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 .env
1. 安装Neo4j
bash
docker run -d --name neo4j \
  -p 7474:7474 -p 7687:7687 \
  -e NEO4J_AUTH=neo4j/mirofish \
  neo4j:5.15-community
2. 安装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 .env

Edit .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

**4. Frontend**
```bash
cd frontend
npm install
npm run dev

**4. 安装前端**
```bash
cd frontend
npm install
npm run dev

---

---

Configuration (
.env
)

配置(
.env

bash
undefined
bash
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_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 Neo4jStorage
MiroFish与图数据库之间的抽象层:
python
from backend.storage.base import GraphStorage
from backend.storage.neo4j_storage import Neo4jStorage

Initialize 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"], )
undefined
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"], )
undefined

Building 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用于后续的仿真运行

undefined
undefined

Creating 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']}")
undefined
result = sim.run(sim_id=sim_id)
print(f"仿真完成。生成帖子数量: {result['post_count']}") print(f"情绪变化轨迹: {result['sentiment_over_time']}")
undefined

Querying 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"])
undefined
report = 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"])
undefined

Chatting 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会结合自身人格、观点倾向及发帖历史进行回复

undefined
undefined

Hybrid 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"])
undefined
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"])
undefined

Implementing 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 NotImplementedError
python
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 App Integration Pattern

Flask应用集成模式

python
undefined
python
undefined

backend/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 app
undefined
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
undefined

Accessing 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参考

MethodEndpointDescription
POST
/api/graph/build
Upload document, build knowledge graph
GET
/api/graph/:id
Get graph entities and relationships
POST
/api/simulation/create
Create simulation environment
POST
/api/simulation/run
Execute simulation
GET
/api/simulation/:id/results
Get posts, sentiment, metrics
GET
/api/simulation/:id/agents
List generated agents
POST
/api/report/generate
Generate ReportAgent analysis
POST
/api/agent/:id/chat
Chat with a specific agent
GET
/api/search
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"
  }'
方法端点描述
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
列出生成的Agent
POST
/api/report/generate
生成ReportAgent分析报告
POST
/api/agent/:id/chat
与指定Agent对话
GET
/api/search
对知识图谱执行混合搜索
示例:从文档构建图谱
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 CaseModelVRAMRAM
Quick test / dev
qwen2.5:7b
6 GB16 GB
Balanced quality
qwen2.5:14b
10 GB16 GB
Production quality
qwen2.5:32b
24 GB32 GB
CPU-only (slow)
qwen2.5:7b
None16 GB
Switch model by editing
.env
:
bash
LLM_MODEL_NAME=qwen2.5:14b
Then restart the backend — no other changes needed.

使用场景模型显存内存
快速测试/开发
qwen2.5:7b
6 GB16 GB
平衡性能与质量
qwen2.5:14b
10 GB16 GB
生产环境质量
qwen2.5:32b
24 GB32 GB
仅CPU运行(速度慢)
qwen2.5:7b
16 GB
通过编辑
.env
文件切换模型:
bash
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]}")
undefined
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]}")
undefined

Use Any OpenAI-Compatible Provider

使用任意兼容OpenAI的服务商

bash
undefined
bash
undefined

Claude 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
undefined
bash
undefined

Check 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
undefined
docker logs neo4j --tail 50
undefined

Ollama model not found

Ollama模型未找到

bash
undefined
bash
undefined

List 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是否正常提供服务

Out of VRAM

显存不足

bash
undefined
bash
undefined

Switch 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
undefined
cd backend && python run.py
undefined

Embeddings dimension mismatch

嵌入向量维度不匹配

bash
undefined
bash
undefined

nomic-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):

DROP INDEX entity_embedding IF EXISTS;

DROP INDEX entity_embedding IF EXISTS;

Then restart MiroFish — it recreates the index with correct dimensions.

然后重启MiroFish——它会自动以正确维度重建索引。

undefined
undefined

Docker Compose: Ollama container can't access GPU

Docker Compose:Ollama容器无法访问GPU

yaml
undefined
yaml
undefined

docker-compose.yml — add GPU reservation:

docker-compose.yml — 添加GPU资源预留:

services: ollama: deploy: resources: reservations: devices: - driver: nvidia count: 1 capabilities: [gpu]
undefined
services: ollama: deploy: resources: reservations: devices: - driver: nvidia count: 1 capabilities: [gpu]
undefined

Slow simulation on CPU

CPU运行仿真速度慢

  • Use
    qwen2.5:7b
    for faster (lower quality) inference
  • Reduce
    agent_count
    to 50–100 for testing
  • Reduce
    simulation_hours
    to 6–12
  • CPU inference with 7b model: expect ~5–10 tokens/sec
  • 使用
    qwen2.5:7b
    模型以获得更快(质量稍低)的推理速度
  • 测试时将
    agent_count
    减少到50–100
  • simulation_hours
    减少到6–12
  • 7b模型的CPU推理速度:预计约5–10 tokens/秒

Frontend can't reach backend

前端无法连接后端

bash
undefined
bash
undefined

Check 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.md
MiroFish-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