Uygulamayı aç
Moonborn — Developers

Bir öğleden sonrada etkileşimli kurgu (interactive fiction) prototipi kur

Üç NPC'li oynanabilir bir sahneyi terminal CLI'ı üzerinden ayağa kaldır — bir oyun motoruna bağlanmadan yazı yinelemesi (iteration) yap.

Yazılı bir kadronun "birlikte durup durmadığını" test etmek için bir oyun motoruna ihtiyacın yok. Bir terminal + Moonborn sohbet (chat) API'si yazı yinelemesi için yeterlidir. Hızlıca üç NPC alır, ensemble (çok karakterli) oturumda konuştururken sen oynarsın, drift skorlarını gözlersin, kadro çalışmıyorsa öğleden sonra refine (incelt) edersin.

Bu rehber minimum bir Node.js komut satırı aracı (CLI) kurar, kadroyu yükler, paylaşılan bir oturum açar ve speaker: (konuşmacı) ön ekiyle hangi karakterin konuşacağını seçtiğin bir döngü yazarsın.

Bu rehberi bitirdiğinde

  • Üç (veya daha fazla) NPC ile ensemble oturum açabileceksin.
  • Bir terminalden speaker: mesaj biçiminde oturumu sürebileceksin.
  • Yazı yinelemesi sırasında drift zarfını (envelope) yan yana izleyebileceksin.
  • Hafıza (memory) ve konuşma kaydı (transcript) ile geri çağrılı (callback) sahne yapabileceksin.
  • Motora geçtiğinde aynı oturum kimliğini (ID) taşıyabileceksin.

Ön koşul: Node.js + @moonborn/sdk + API anahtarı. En az üç persona (yoksa hızlıca İlk persona'yı baştan sona üret ile üret).

Minimum CLI

import Moonborn from '@moonborn/sdk';
import readline from 'node:readline';
 
const client = new Moonborn({ apiKey: process.env.MOONBORN_API_KEY });
 
// 1. Kadroyu yükle (önceden üretilmiş 3 persona).
const cast = {
  merchant: await client.personas.get({ id: 'per_merchant...' }),
  traveler: await client.personas.get({ id: 'per_traveler...' }),
  tavernKeeper: await client.personas.get({ id: 'per_tavern...' }),
};
 
// 2. Tek bir paylaşılan oturum aç.
const session = await client.chat.sessions.create({
  personaId: cast.tavernKeeper.id,
  ensemble: [cast.merchant.id, cast.traveler.id],
  metadata: { sceneId: 'tavern_prototype' },
});
 
// 3. CLI döngüsü.
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
 
function nameToPersona(name: string) {
  const lower = name.toLowerCase().trim();
  if (lower === 'merchant') return cast.merchant;
  if (lower === 'traveler') return cast.traveler;
  if (lower === 'tavern' || lower === 'keeper') return cast.tavernKeeper;
  return cast.tavernKeeper; // varsayılan
}
 
function prompt() {
  rl.question('You > ', async (text) => {
    if (text.trim() === ':quit') return rl.close();
 
    // "merchant: dışarlılara hizmet vermiyoruz" biçimi:
    //   speaker = merchant, content = "dışarlılara hizmet vermiyoruz"
    // Hiç ön ek yoksa anlatıcı (narrator) modu; varsayılan konuşmacı tavernKeeper.
    const [tag, ...rest] = text.split(':');
    const speaker = rest.length > 0 ? nameToPersona(tag) : cast.tavernKeeper;
    const content = rest.length > 0 ? rest.join(':').trim() : text;
 
    const reply = await client.chat.messages.create({
      sessionId: session.id,
      speaker: speaker.id,
      content,
    });
 
    console.log(`${speaker.surface.name.display} > ${reply.content}`);
    if (reply.driftAlert) {
      console.warn(`  (drift: ${reply.driftScore.toFixed(2)})`);
    }
    prompt();
  });
}
 
prompt();

Tipik bir oturumun nasıl göründüğü

You > Selam, kim var?
Tavern Keeper > Bir köşede oturmuş yabancı. Buralı değil.
 
You > traveler: tavern keeper'a bakıyor. Bir yer var mı?
Traveler > "Bir yatak ve bir tas çorba istiyorum. Param yok ama elimde
bir mektup var."
 
You > tavern: traveler'ın yüzüne bak.
Tavern Keeper > "Mektup mu? Buralarda mektup ya bir yargıç ya bir
mezar habercisidir. Hangisini taşıyorsun?"
 
You > traveler: cebinden buruşmuş bir kâğıt çıkarır.
Traveler > "Yargıç değil. Çocuk için."
 
You > merchant: kapıdan içeri girer, omzunda bir çuval.
Merchant > "Tavernci! Bana bir kase çorba, bir ekmek—"
   (drift: 0.32)

Ne kazanırsın

KazanımNeden önemli
Dakika ölçeğinde yinelemeBir karakteri değiştir, hemen sahneyi tekrar oyna
Drift skorları yan yanaSahne ısındıkça hangi karakterin kaymaya başladığını anında gör
Hafıza kalıcılığı (memory persistence)Önceki turdaki olaylar sonraki turda hatırlanır; "geçen seferki mektup neydi?" sorusu doğru yanıtlanır
Hızlı kadro değişimiBir NPC'yi kaldır, başkasını koy — oturum kapatmadan

Gerçek motora karşı ne kaybedersin

KayıpTelafi
Oyuncu arayüzü (player UI)CLI metni; motora geçtiğinde UI motorda kurulur
Dallı sahne durumu (branching scene state)Sen doğrusal (linear) betik yazarsın; motor bunu kendi durum makinesinde (state machine) modeller
Kayıt / yükleme (oturum kimliği dışında)Motor kayıt sistemine bağlanır; Moonborn oturum kimliği referans anahtarı (key) olur
Ses / animasyonCLI yalnızca metin; motor metin-konuşma (TTS) ve animasyon katmanı ekler

Motora "mezun olma"

Prototipleme aşaması bittiğinde:

  1. Oturum kimliklerini sakla — motor referans olarak kullanır.
  2. Persona kimlikleri kararlı (stable) kalır — motor bunlara aynen referans verir.
  3. Soyu (lineage) koruyarak motora aktar — prototipte ürettiğin varyantlar üretime (production) taşınır.
  4. CLI betiklerini motor sahne betiğine dönüştür — Twine, Ink, Unity diyalog betikleri tipik hedeflerdir.

Yineleme hızı için ipuçları

  • Sıcak yeniden yükleme (hot reload) kadrosunode --watch run-cli.ts ile dosya kaydedildikçe CLI yeniden başlar. Persona kimlikleri sabit kalır, kadro listesi anında güncellenir.
  • Konuşma kaydını dosyaya kaydetmetadata.sceneId'yi bir dosya adına eşle (logs/${sceneId}-${timestamp}.txt), her çalıştırmayı (run) arşivle. Yazar incelemesinde geri dönüş kolaylaşır.
  • Hızlı sıfırlama komutu:reset komutu yeni bir oturum açıp aynı kadroyla başlasın; sahneyi baştan denemek için.

İlgili

Çok karakterli bir sahne kur

Ensemble oturum + konuşmacı seçimi adım adım eğitimi.

Open →
NPC sahne orkestrasyonu

Prototipte ısındıktan sonra motor entegrasyonu desenleri.

Open →
Oyun NPC orkestrasyonu kullanım senaryosu

Oyun ekiplerinin Moonborn'u motorla nasıl kullandığına dair kullanım senaryosu.

Open →
İlk persona'yı baştan sona üret

Prototipe başlamadan kadro yaratımı için.

Open →