Cara MiroFish Menciptakan Dunia Paralel Digital

Ashley Innocent

Ashley Innocent

19 March 2026

Cara MiroFish Menciptakan Dunia Paralel Digital

Pendahuluan

Media sosial bergerak cepat. Satu postingan dapat memicu serangkaian reaksi, pembentukan ulang, dan gerakan balasan yang tidak terduga. Bagaimana jika Anda dapat melihat bagaimana sebuah skenario terungkap sebelum terjadi di dunia nyata?

MiroFish melakukan hal itu. Ini adalah mesin kecerdasan kelompok yang menciptakan dunia paralel digital di mana ribuan agen AI dengan kepribadian, memori, dan pola perilaku yang berbeda berinteraksi secara bebas. Anda mengunggah materi awal—artikel berita, draf kebijakan, bahkan novel—dan MiroFish membangun simulasi akurasi tinggi tentang bagaimana peristiwa dapat terungkap.

💡
Membangun MiroFish membutuhkan fondasi pengujian API yang andal. Tim menggunakan Apidog untuk merancang, melakukan debug, dan mendokumentasikan semua API backend sebelum menulis logika simulasi. Ini berhasil mendeteksi masalah endpoint sejak dini dan menjaga backend Python serta frontend Vue tetap sinkron selama pengembangan.
tombol

Postingan ini menguraikan arsitektur teknis di balik MiroFish. Anda akan mempelajari bagaimana sistem mengubah dokumen mentah menjadi simulasi yang hidup, bagaimana agen membuat keputusan, dan bagaimana alur kerja lima langkah mengatur segalanya mulai dari pembangunan grafik pengetahuan hingga pemantauan waktu nyata.

gambar-189

Ikhtisar Sistem: Alur Kerja Lima Langkah

MiroFish memproses simulasi melalui lima fase yang berbeda:

┌─────────────┐     ┌─────────────┐     ┌─────────────┐     ┌─────────────┐     ┌─────────────┐
│   Langkah 1   │ ──► │   Langkah 2   │ ──► │   Langkah 3   │ ──► │   Langkah 4   │ ──► │   Langkah 5   │
│  Pembuatan  │     │ Pembangunan │     │   Penyiapan │     │   Jalankan  │     │   Pembuatan │
│  Ontologi   │     │  GraphRAG   │     │   Lingkungan│     │   Simulasi  │     │   Laporan   │
└─────────────┘     └─────────────┘     └─────────────┘     └─────────────┘     └─────────────┘

Langkah 1: Pembuatan Ontologi

Sistem menganalisis dokumen masukan dan persyaratan simulasi Anda, kemudian menggunakan LLM untuk menghasilkan ontologi khusus. Ini mendefinisikan:

Generator ontologi memberlakukan struktur dua tingkat: 8 jenis spesifik berdasarkan konten Anda, ditambah 2 jenis cadangan (Orang dan Organisasi) untuk menangkap apa pun yang tidak sesuai di tempat lain.

Langkah 2: Pembangunan GraphRAG

Dokumen dipotong-potong (500 karakter, 50 tumpang tindih) dan dikirim ke Zep Cloud dalam batch. Sistem:

  1. Membuat grafik mandiri dengan ID unik
  2. Mengatur ontologi khusus
  3. Mengirim batch teks untuk ekstraksi entitas dan hubungan
  4. Menunggu Zep memproses setiap episode
  5. Mengambil grafik akhir dengan node dan edge

Langkah 3: Penyiapan Lingkungan

Generator konfigurasi simulasi menganalisis grafik pengetahuan dan membuat parameter agen yang terperinci:

Langkah 4: Jalankan Simulasi

Agen bangun sesuai jadwal aktivitas mereka dan mulai memposting, berkomentar, dan bereaksi. Sistem menjalankan simulasi paralel di Twitter dan Reddit, mencatat setiap tindakan ke file JSONL secara waktu nyata.

Langkah 5: Pembuatan Laporan

Agen Laporan menggunakan tiga alat pengambilan inti untuk menganalisis apa yang terjadi:

Pembahasan Teknis Mendalam: Pembuatan Ontologi

Generator ontologi berada di backend/app/services/ontology_generator.py. Ini menggunakan perintah sistem yang dibuat dengan cermat yang memberlakukan aturan ketat.

Perintah sistem mencakup panduan ekstensif tentang apa yang dianggap sebagai entitas yang valid (orang, organisasi, media) versus apa yang tidak (konsep abstrak, tema, sudut pandang). Perbedaan ini penting karena simulasi membutuhkan agen yang benar-benar dapat berbicara dan bertindak di media sosial.

Setelah LLM menghasilkan ontologi, metode _validate_and_process memberlakukan batasan:

def _validate_and_process(self, result: Dict[str, Any]) -> Dict[str, Any]:
    # Batasan API Zep: maksimal 10 jenis entitas, maksimal 10 jenis edge
    MAX_ENTITY_TYPES = 10
    MAX_EDGE_TYPES = 10

    # Pastikan jenis cadangan ada
    fallbacks_to_add = []
    if "Person" not in entity_names:
        fallbacks_to_add.append(person_fallback)
    if "Organization" not in entity_names:
        fallbacks_to_add.append(organization_fallback)

    # Pangkas jika penambahan cadangan akan melebihi batas
    if current_count + needed_slots > MAX_ENTITY_TYPES:
        result["entity_types"] = result["entity_types"][:-to_remove]

    result["entity_types"].extend(fallbacks_to_add)
    return result

Lapisan validasi ini memastikan output selalu berfungsi dengan batasan API Zep sambil mempertahankan struktur dua tingkat.

Pembangunan Grafik Pengetahuan: Integrasi Zep

Layanan pembangun grafik (backend/app/services/graph_builder.py) menangani alur kerja asinkron:

def _build_graph_worker(self, task_id: str, text: str, ontology: Dict, ...):
    # 1. Buat grafik
    graph_id = self.create_graph(graph_name)

    # 2. Atur ontologi
    self.set_ontology(graph_id, ontology)

    # 3. Potong teks
    chunks = TextProcessor.split_text(text, chunk_size, chunk_overlap)

    # 4. Kirim batch
    episode_uuids = self.add_text_batches(graph_id, chunks, batch_size)

    # 5. Tunggu pemrosesan Zep
    self._wait_for_episodes(episode_uuids, progress_callback)

    # 6. Ambil grafik akhir
    graph_info = self._get_graph_info(graph_id)

Pembuatan Model Pydantic Dinamis

Satu bagian cerdik: sistem secara dinamis membuat model Pydantic untuk setiap jenis entitas pada saat runtime:

def set_ontology(self, graph_id: str, ontology: Dict[str, Any]):
    RESERVED_NAMES = {'uuid', 'name', 'group_id', 'name_embedding', 'summary', 'created_at'}

    def safe_attr_name(attr_name: str) -> str:
        if attr_name.lower() in RESERVED_NAMES:
            return f"entity_{attr_name}"
        return attr_name

    entity_types = {}
    for entity_def in ontology.get("entity_types", []):
        name = entity_def["name"]
        attrs = {"__doc__": description}
        annotations = {}

        for attr_def in entity_def.get("attributes", []):
            attr_name = safe_attr_name(attr_def["name"])
            attrs[attr_name] = Field(description=attr_desc, default=None)
            annotations[attr_name] = Optional[EntityText]

        attrs["__annotations__"] = annotations
        entity_class = type(name, (EntityModel,), attrs)
        entity_types[name] = entity_class

Ini memungkinkan Zep memvalidasi atribut entitas terhadap skema khusus tanpa memerlukan model yang telah ditentukan sebelumnya.

Paginasi Melalui Grafik Besar

Zep mengembalikan hasil yang dipaginasi. Utilitas zep_paging.py mengambil semuanya:

def fetch_all_nodes(client: Zep, graph_id: str) -> List[Node]:
    nodes = []
    cursor = None
    while True:
        result = client.graph.get_nodes(graph_id=graph_id, cursor=cursor, limit=100)
        nodes.extend(result.nodes)
        if not result.next_cursor:
            break
        cursor = result.next_cursor
    return nodes

Simulasi Aktivitas Agen Berbasis Waktu

Generator konfigurasi simulasi (backend/app/services/simulation_config_generator.py) membuat pola aktivitas realistis berdasarkan perilaku zona waktu Tiongkok:

CHINA_TIMEZONE_CONFIG = {
    "dead_hours": [0, 1, 2, 3, 4, 5],           # Hampir tidak ada orang di dini hari
    "morning_hours": [6, 7, 8],                  # Secara bertahap aktif di pagi hari
    "work_hours": [9, 10, 11, 12, 13, 14, 15, 16, 17, 18],
    "peak_hours": [19, 20, 21, 22],              # Puncak malam hari
    "night_hours": [23],
    "activity_multipliers": {
        "dead": 0.05,
        "morning": 0.4,
        "work": 0.7,
        "peak": 1.5,
        "night": 0.5
    }
}

Jenis agen yang berbeda mendapatkan pola yang berbeda:

Jenis Agen Tingkat Aktivitas Jam Aktif Penundaan Respons Pengaruh
University 0.2 9-17 60-240 min 3.0
MediaOutlet 0.5 7-23 5-30 min 2.5
Student 0.8 8-12, 18-23 1-15 min 0.8
Professor 0.4 8-21 15-90 min 2.0

Generator konfigurasi menggunakan panggilan LLM untuk menyesuaikan nilai-nilai ini berdasarkan skenario spesifik Anda, kemudian kembali ke nilai default berbasis aturan jika LLM gagal.

Pelacakan Aksi Waktu Nyata

Runner simulasi (backend/app/services/simulation_runner.py) memantau aktivitas agen dengan mengalirkan log JSONL:

def _read_action_log(self, log_path: str, position: int, state: SimulationRunState, platform: str):
    with open(log_path, 'r', encoding='utf-8') as f:
        f.seek(position)
        for line in f:
            action_data = json.loads(line)

            # Tangani peristiwa
            if "event_type" in action_data:
                if action_data["event_type"] == "simulation_end":
                    state.twitter_completed = True  # or reddit
                elif action_data["event_type"] == "round_end":
                    state.current_round = action_data["round"]
                continue

            # Uraikan tindakan agen
            action = AgentAction(
                round_num=action_data.get("round", 0),
                platform=platform,
                agent_id=action_data.get("agent_id", 0),
                action_type=action_data.get("action_type", ""),
                ...
            )
            state.add_action(action)

        return f.tell()

Ini berjalan dalam thread latar belakang, memperbarui status simulasi setiap 2 detik. Frontend melakukan polling status ini untuk menampilkan kemajuan waktu nyata.

Manajemen Proses Lintas-Platform

Menghentikan simulasi memerlukan manajemen proses yang cermat di Windows dan Unix:

def _terminate_process(cls, process: subprocess.Popen, simulation_id: str, timeout: int = 10):
    if IS_WINDOWS:
        # Windows: gunakan taskkill untuk mematikan pohon proses
        subprocess.run(['taskkill', '/PID', str(process.pid), '/T'], ...)
    else:
        # Unix: matikan grup proses (dibuat dengan start_new_session=True)
        os.killpg(os.getpgid(process.pid), signal.SIGTERM)

Penangan pembersihan mendaftarkan penangan sinyal untuk SIGINT, SIGTERM, dan SIGHUP:

def register_cleanup(cls):
    def cleanup_handler(signum, frame):
        cls.cleanup_all_simulations()
        # Kemudian panggil penangan asli

    signal.signal(signal.SIGTERM, cleanup_handler)
    signal.signal(signal.SIGINT, cleanup_handler)
    if has_sighup:
        signal.signal(signal.SIGHUP, cleanup_handler)

    atexit.register(cls.cleanup_all_simulations)

Ini memastikan simulasi berhenti dengan baik saat server dimatikan.

Pembuatan Laporan: Pengambilan Tiga Tingkat

Layanan alat Zep (backend/app/services/zep_tools.py) menyediakan tiga fungsi pengambilan:

InsightForge (Penelusuran Mendalam)

Menguraikan pertanyaan kompleks menjadi sub-kueri, mencari masing-masing, lalu mengagregasi:

def insight_forge(self, graph_id: str, query: str, simulation_requirement: str):
    # 1. Hasilkan sub-kueri menggunakan LLM
    sub_queries = self._generate_sub_queries(query, simulation_requirement)

    # 2. Cari setiap sub-kueri
    for sub_query in sub_queries:
        search_result = self.search_graph(graph_id, query=sub_query)
        all_facts.extend(search_result.facts)

    # 3. Ekstrak UUID entitas dari edge
    entity_uuids = set(edge['source_node_uuid'] for edge in all_edges)

    # 4. Ambil info entitas terperinci
    for uuid in entity_uuids:
        node = self.get_node_detail(uuid)
        entity_insights.append({...})

    # 5. Bangun rantai hubungan
    for edge in all_edges:
        chain = f"{source_name} --[{relation_name}]--> {target_name}"
        relationship_chains.append(chain)

PanoramaSearch (Cakupan Penuh)

Mengambil semuanya termasuk fakta historis yang kedaluwarsa/tidak valid:

def panorama_search(self, graph_id: str, query: str, include_expired: bool = True):
    all_nodes = self.get_all_nodes(graph_id)
    all_edges = self.get_all_edges(graph_id, include_temporal=True)

    for edge in all_edges:
        is_historical = edge.is_expired or edge.is_invalid
        if is_historical:
            historical_facts.append(f"[{valid_at} - {invalid_at}] {edge.fact}")
        else:
            active_facts.append(edge.fact)

InterviewAgents (Waktu Nyata)

Memanggil API wawancara OASIS yang sebenarnya untuk berbicara dengan agen aktif:

def interview_agents(self, simulation_id: str, interview_requirement: str):
    # 1. Muat profil agen dari CSV/JSON
    profiles = self._load_agent_profiles(simulation_id)

    # 2. Gunakan LLM untuk memilih agen yang relevan
    selected_agents, selected_indices, reasoning = self._select_agents_for_interview(...)

    # 3. Hasilkan pertanyaan wawancara
    questions = self._generate_interview_questions(...)

    # 4. Panggil API wawancara nyata (dua-platform)
    api_result = SimulationRunner.interview_agents_batch(
        simulation_id=simulation_id,
        interviews=[{"agent_id": idx, "prompt": combined_prompt} for idx in selected_indices],
        platform=None,  # Interview both Twitter and Reddit
        timeout=180.0
    )

    # 5. Uraikan dan format hasil
    for i, agent_idx in enumerate(selected_indices):
        twitter_response = results_dict.get(f"twitter_{agent_idx}", {})
        reddit_response = results_dict.get(f"reddit_{agent_idx}", {})
        response_text = f"[Twitter]\n{twitter_response}\n\n[Reddit]\n{reddit_response}"

Keputusan Rekayasa Utama

1. Manajemen Tugas Asinkron

Operasi yang berjalan lama (pembangunan grafik, jalankan simulasi) menggunakan tugas asinkron dengan pelacakan kemajuan:

def build_graph_async(self, text: str, ontology: Dict, ...) -> str:
    task_id = self.task_manager.create_task(task_type="graph_build", metadata={...})

    thread = threading.Thread(
        target=self._build_graph_worker,
        args=(task_id, text, ontology, ...)
    )
    thread.daemon = True
    thread.start()

    return task_id

Frontend melakukan polling status tugas melalui /api/graph/task/{task_id}.

2. Panggilan LLM Batch dengan Coba Lagi

Pembuatan konfigurasi membagi daftar agen besar menjadi batch 15:

num_batches = math.ceil(len(entities) / self.AGENTS_PER_BATCH)
for batch_idx in range(num_batches):
    batch_entities = entities[start_idx:end_idx]
    batch_configs = self._generate_agent_configs_batch(context, batch_entities)
    all_agent_configs.extend(batch_configs)

Setiap batch mencakup logika perbaikan JSON untuk output yang terpotong:

def _fix_truncated_json(self, content: str) -> str:
    open_braces = content.count('{') - content.count('}')
    open_brackets = content.count('[') - content.count(']')

    if content and content[-1] not in '",}]':
        content += '"'

    content += ']' * open_brackets
    content += '}' * open_braces
    return content

3. Simulasi Paralel Dua-Platform

Twitter dan Reddit berjalan secara paralel dengan database terpisah dan log tindakan:

uploads/simulations/{simulation_id}/
├── twitter/
│   ├── actions.jsonl
│   └── twitter_simulation.db
├── reddit/
│   ├── actions.jsonl
│   └── reddit_simulation.db
├── simulation_config.json
├── run_state.json
└── simulation.log

Runner mendeteksi penyelesaian per-platform melalui peristiwa simulation_end.

Pertimbangan Kinerja

Manajemen Memori

Isolasi Basis Data

Setiap platform menggunakan database SQLite-nya sendiri untuk menghindari pertentangan kunci selama penulisan paralel.

Penurunan Kinerja yang Elegan (Graceful Degradation)

Ketika API Pencarian Zep gagal, sistem kembali menggunakan pencocokan kata kunci lokal:

try:
    search_results = self.client.graph.search(...)
except Exception as e:
    logger.warning(f"API Pencarian Zep gagal, kembali ke pencarian lokal: {e}")
    return self._local_search(graph_id, query, limit, scope)

Kesimpulan

MiroFish menunjukkan bagaimana membangun sistem simulasi multi-agen lengkap dari awal. Alur kerja lima langkah mengubah dokumen mentah menjadi dunia digital yang hidup di mana ribuan agen berinteraksi sesuai dengan pola perilaku realistis.

Poin-poin penting:

  1. Desain ontologi itu penting: Struktur dua tingkat (8 jenis spesifik + 2 jenis cadangan) memastikan cakupan tanpa melebihi batasan API
  2. Alur kerja asinkron memungkinkan operasi yang panjang: Pelacakan tugas dengan pembaruan kemajuan membuat pengguna tetap terinformasi selama operasi yang berlangsung beberapa menit
  3. Aktivitas berbasis waktu menciptakan realisme: Pola zona waktu Tiongkok dan jadwal khusus jenis agen menghasilkan perilaku yang meyakinkan
  4. Simulasi dua-platform menyediakan perbandingan: Menjalankan Twitter dan Reddit secara paralel menunjukkan bagaimana dinamika platform memengaruhi hasil
  5. Pengambilan tiga tingkat melayani kebutuhan yang berbeda: InsightForge untuk kedalaman, PanoramaSearch untuk luas, InterviewAgents untuk perspektif langsung

Kode sumber lengkap tersedia di github.com/666ghj/MiroFish.

tombol

Ingin mencoba MiroFish? Kunjungi demo langsung untuk melihat simulasi acara hotspot beraksi.

Mengembangkan API dengan Apidog

Apidog adalah alat pengembangan API yang membantu Anda mengembangkan API dengan lebih mudah dan efisien.