私はFlash開発者としての日々以来、マルチプレイヤーRPGウェブプラットフォームを作成することを考えてきました. オンラインTTRPGオプションがたくさんあります(多くは素晴らしいです!) しかし、どれも私がどのように遊びたいかで十分に正しいと感じませんでした。 見たか Where Deborah Ann Woll shows Jon Bernthal how to play D&D? (it has 2M views!) 動画 https://www.youtube.com/watch?v=JpVJZrabMQE&embedable=true こんな感じだったのかな?こんな感じで作れるのかな? アイデア 物語を駆動する、マルチプレイヤーオンラインRPGプラットフォーム、ルールに軽いが、共有ストーリーに高く、結果はD20のロールによって決定される。 I've Always Loved アート. エキサイティングな紹介. 詳細な出会いとエピックなストーリーは、プレイヤーが彼らのキャラクターが気象的な決勝へと進むように発見するための計画です。 D&Dモジュール どんなRPGジャンルでも、人々が自分のアドベンチャーモジュールを作る方法を作り出すことができれば、プレイヤーがそのアドベンチャーを通してキャラクターを走らせることができますか? 誰がゲームを実行するだろうか? ゲームマスターは見つけるのが難しいだろうか? ゲームマスターになるためにAIを訓練できるならどうだろうか? 実際には、プレイヤーにとって楽しい(人間によって設計された!)よく構造化された冒険プランを実行するために、ゲームマスターだけではない。 ? スロップ 始めること I've been having fun building a lot of things with AI (見る) ) 私は作るために私の好みのステックを使用します。 , a Next.js web app with Tailwind for UI (need it even be said?) powered by the 双子座とA Vercel に移行しました。 プロジェクトスタート D20アドベンチャー.com SDKとは Convexデータベース そして私はそれをオープンに構築し、コードを公開することにしました。 . GitHub プロトタイプ プロトタイプに関しては、私は文字通りPodcastで描かれたシナリオから始めています、夜、森の中を歩くレンジャーは遠くで裂け声を聞き、それはクマであることが判明します。 私の目標は、短い一発の冒険を構築し、AI DMを訓練して、この冒険のための小さなシリーズの回転と出会いを実際に実行できるかどうかを確認することです。 深夜のサムーン 古代のドラウイドの友人の神秘的な呼びかけは、ヴァルカールの森の荒野に避難したレンジャーを引きずり込む。 深夜のサムーン 古代のドラウイドの友人の神秘的な呼びかけは、ヴァルカールの森の荒野に避難したレンジャーを引きずり込む。 多くの試行と間違いの後、私はようやく完全にプレイし、それをYouTubeに投稿することができました。 https://youtu.be/9wzNVmhVMlg?si=qaNEJ8wcF4bzZJK9&embedable=true どのように機能する Landing Page ランディングページはビッグヒーロー画像(Midjourneyで「The Power of the D20」のプロンプトで生成)です。私は新しいCSS @starting-styleルールを使用してアニメーションでいくつかのシンプルなフードを追加しました。 .fade-in { @apply opacity-100 transition-opacity duration-1000 ease-in-out; @starting-style { opacity: 0; } } Authentication クイックスタートをプレイするには、ユーザーアカウントが必要です. これは、匿名の人々やボットが私のAPIを使用しているため、多くのお金が請求されるのを避けるためです。 ユーザー管理を簡単に追加し、すべてのプロジェクトで使用します。 クリエイター さらに、私はトークン使用の追跡を行っており、トークンシステムで使用を制限し、デモのプレイを1回行うのに十分なトークンで開始すると、あなたが進むにつれてより多くを購入することができます。 The First Turn ユーザーがアドベンチャーページに着陸すると、最初に起こることは、単純なJSONファイルであるデモアドベンチャー用のデータをロードすることです。 ) on S3. A adventure module or plan in my system is made up of a series of encounters, which are linked to each other with instructions for the LLM: 私のシステムのアドベンチャーモジュールまたはプランは、LLMのための指示で相互に関連付けられている一連の出会いから構成されています。 こんな感じ "encounters": [ { "id": "broken-silence", "title": "Broken Silence", "intro": "Thalbern, a solitary ranger of the Valkarr woods, has always trusted the silence of the wilds more than the promises of men. Orphaned by border raiders and raised by the elves of the Valkrarr Forest, he has spent years living on the edge of Kordavos, guiding travelers, hunting for his own survival, and keeping his distance from the tangled politics of the city.\n\nYet on this night, a message delivered by a red squirrel bearing the unmistakable script of Wollandora, a trusted elven friend and druid, has drawn him from his hidden home. The note was simple and urgent: Meet me at the Old Standing Stones at midnight. The balance of the forest could depend on it.\n\nNow, as midnight approaches, Thalbern moves quietly through the dense undergrowth, guided by memory and instinct. It is dark with almost no moonlight coming through the forest canopy.\n\nSuddenly, the hush of the night is broken by a sharp crack. Something large has just stepped on a branch somewhere off in the distance.", "instructions": "A perception check is appropriate if Thalbern investigates (low difficulty with a plus 3 modifier). If successful, he will determine it is a large creature that is approaching quickly. With a high roll (18+), he will determine it is an Owlbear. If combat ensues and Thalbern is below 25% health, Wollandora will intervene. If Thalbern avoids or defeats the Owlbear, or if Wollandora saves him, he proceeds to the Old Standing Stones.", "image": "images/settings/realm-of-myr/the-midnight-summons/broken-silence-2.png", "transitions": [ { "condition": "If Thalbern successfully uses stealth to evade and proceeds cautiously towards the Standing Stones, go to meeting-at-stones.", "encounter": "meeting-at-stones" }, { "condition": "If Thalbern fails a perception check, advance to owlbear-confrontation.", "encounter": "owlbear-confrontation" }, { "condition": "If Thalbern fails any dice roll (including stealth, perception, or any other check), advance to owlbear-confrontation.", "encounter": "owlbear-confrontation" }, { "condition": "If Thalbern does NOT successfully use stealth to evade, go to owlbear-confrontation.", "encounter": "owlbear-confrontation" }, { "condition": "If Thalbern does nothing or takes no action, go to owlbear-confrontation.", "encounter": "owlbear-confrontation" }, { "condition": "If Thalbern has a healthPercent of less than 50%, go to wollandora-intervention.", "encounter": "wollandora-intervention" } ] }, { "id": "owlbear-confrontation", "title": "Owlbear Confrontation", "intro": "From the direction of the sound, a little bit of eye shine glints in the shadows of the tree line. A hulking fifteen foot tall monster with the body of a giant bear and the head of an owl. As it crashes out from the undergrowth, it lets out a guttural squawk, clearly agitated and territorial.", "instructions": "The Owlbear will attack. If Thalbern attempts an animal handling check (high difficulty) and succeeds, he can move past the Owlbear. If Thalbern wins initiative and attempts to hide, he can move past the Owlbear if he passes a medium difficulty stealth check. If Thalbern's health drops to a critical level, Wollandora appears and drives off the Owlbear, transitioning to 'wollandora-intervention'. If Thalbern defeats the Owlbear, describe his victory and transition to 'meeting-at-stones'.", "image": "images/settings/realm-of-myr/the-midnight-summons/owlbear-confrontation.png", "npc": [ { "id": "owlbear", "behavior": "Aggressively attacks any perceived threat. Will fight until heavily wounded or driven off.", "initialInitiative": 1 } ], "transitions": [ { "condition": "Thalbern defeats the Owlbear, manages to evade it, successfully uses Animal Handling to pacify and move past it, or successfully rolls any other way to move past it.", "encounter": "meeting-at-stones" }, { "condition": "Thalbern is reduced to critical health by the Owlbear.", "encounter": "timely-rescue" } ] }, ... ] The First Reply 私たちはデモモモードにいるので、バックエンドでまだ作成された実際の冒険はありません。これはプレイヤーが最初の回答をするときに起こります。 AIを使用して答えを評価し、文法的に正しいことを確認し、第三者で答えを評価し、文学的物語のスタイルでうまく動作させるために対話やその他のプロサを追加します。 formatNarrativeAction フォーマットが適用された後、返信はサーバーアクションに送信されます。これはデモであるため、この最初の返信が受信されたときにデータベースで冒険を作成しています。 Processing Player Responses THE 関数は Convex から現在のターン、S3 の冒険データをロードし、特定の出会いとアクションを実行するキャラクターを識別します。 processTurnReply この文脈では、AIを使用して、アクションが信頼できるかどうかを決定します(私の息子はプレーテスト時にレンジャーがオウルベアに核を打ち上げた)そして、そうであれば、デスロールが機械的に必要かどうかを決定します(例えば、「攻撃ロール」または「ステールスチェック」)、ロールの種類とその難度を含む。 我々はこれを機能呼び出しで行うことができます、そこで我々は我々が構造化されたデータを返すことを望むAIに指定することができます。 : rollRequirementSchema import { z } from "zod"; export const rollRequirementSchema = z.union([ z.object({ rollType: z.string().describe("The type of roll required, e.g. 'Stealth Check'"), difficulty: z.number().describe("The difficulty class (DC) for the roll"), modifier: z.number().optional().describe("Bonus or penalty to the roll, e.g. +2 or -1"), }), z.null() ]); export type RollRequirement = z.infer<typeof rollRequirementSchema>; 次に、詳細なプロンプトとスケジュールを送信する関数があります。 : generateObject export async function getRollRequirementForAction(action: string) { const prompt = ` Given the following player or NPC action, determine if a dice roll is required for the character to attempt the action. If a roll is required, return a JSON object with "rollType" (choose the most appropriate from the list below) and "difficulty" (a number between 5 and 25). If no roll is required, return the JSON value null (not a string). Possible roll types: - Perception Check - Investigation Check - Insight Check - Stealth Check ... Examples: Action: "Try to sneak past the guards." Result: { "rollType": "Stealth Check", "difficulty": 15 } Action: "Attack the goblin." Result: { "rollType": "Attack Roll", "difficulty": 12 } Action: "Try to determine what the sound is." Result: { "rollType": "Perception Check", "difficulty": 10 } Action: "Say hello." Result: null Now, given the following action, determine the roll requirement. Action: "${action}" `; try { const result = await generateObject({ schema: rollRequirementSchema, prompt, }); if ( result.object && typeof result.object === "object" && "rollType" in result.object && (result.object.rollType === "null" || result.object.rollType === "none" || result.object.rollType === "") ) { return null; } return result.object ?? null; } catch (error) { throw error; } } ターンストーリーにプレイヤーの反応を付加した後、デッキロールが必要な場合は、コンヴェックスターンデータのキャラクターの状態を更新し、彼らが回答したが、彼らのターンが完了していないことを反映します。 ユーザがロールすると、私たちは server action that updates the narrative with a visual display of the result and writes prose describing the outcome. We also have another AI function call to update health and status for all of the characters in turn. サーバーアクションは、結果の視覚的な表示で物語を更新し、結果を説明するプロセスを書きます。 resolvePlayerRollResult NPC Actions NPCが返事を返すとき、私たちはプレイヤーとして似たパターンに従うが、この場合、AIは答えを書く。それぞれの出会いには、NPCの動機に関する情報が含まれており、これまでの冒険物語の文脈と組み合わせると、おそらくAIは良い返事を生成し、それから独自のコストロールと結果のアップデートを生成することができる。 Training AI to Run RPGs あなたがチャットでAIを使用してゲームセッションを試みたことがあるなら、それはどれほど速く走ることができるかを知っています。 適切な文脈、特定のプロンプトの指示、および構造化されたデータ機能の呼び出しを提供することで、私たちは楽しい経験を得ることができます。 確かに試行錯誤、コンソールログアップ、スンプトチューニングはたくさんあります。たとえば、なぜAIが私のテストの1つで遭遇指示に従わなかったのかを尋ねるとき、これはCursorのチャットが私に言ったことです。 要するに、LLMは、そのような失敗の明確な証拠と、その特定のシナリオのための過渡ルールを提供しているにもかかわらず、キャラクターが感覚チェックに失敗したときに、過渡への明確な指示に従わなかった。 要するに、LLMは、そのような失敗の明確な証拠と、その特定のシナリオのための過渡ルールを提供しているにもかかわらず、キャラクターが感覚チェックに失敗したときに、過渡への明確な指示に従わなかった。 I guess the randomness of the AI not doing exactly what is expected could be part of the fun. 私は、AIが正確に期待していることをしないという偶然性は、楽しみの一部かもしれません。 Here is an example of . 演奏セッションの一つ あなたはこのプロジェクトの完全なソースコードを見ることができます github.com/d20adventures.com トップページ