いいえ、ありがとう、そしてさようなら。
さて、まず最初に、クリックベイト風のタイトルについてお詫びします。デモのアイデアを思いついたとき、それがうまくいかないことがあります。しかし、楽しく興味深い方法でうまくいかないこともあります。とにかくそれを共有するのが良い考えだと思います。 (また、私が何か間違ったことをしたためにうまくいかなかった可能性は常に高いです!) 今日のデモはその完璧な例だと思います。
今年の初めに、私は Alpine.js と非常に優れたDeck of Cards APIを使用してブラックジャック ゲームを構築しました。昨日、私はこのゲームについて考えていて、ゲームをプレイするときに生成 AI を使用して助けを求めたらどうなるだろうかと考えました。たとえば、私は自分のゲームに行って、次のようなものを見ました。
Google の PaLM Makersuite アプリで、私は次のように書きました。
ブラックジャックをプレイしているのですが、ディーラーがダイヤの 6 を示しています。私はクラブのジャックとハートのジャックを持っています。打つべきか、それとも留まるべきか?
そして、次のような応答が得られました。
あなたは 20 を持っています、これは良いハンドです。ディーラーの保有台数は 16 台で、平均を下回っています。ヒットすると、破産するカードを手に入れる危険があります。したがって、ディーラーが倒産することを祈りながら、ここに留まる方が良いでしょう。
それは明らかです。 2 つのジャックでヒットすることはありません。しかし、私はゲームの原則を強化する説明的な応答が気に入りました。私は考えました - GenAI に助けを求めることができるボタンをゲームに追加したらどうなるでしょうか?私がやった方法は次のとおりです。
フロントエンドでは、UI の変更を最小限に抑えました。まずは簡単なボタンを追加することから始めました。
これをクリックすると、2 つの重要な作業を行う必要があります。まず、ゲームの状態をプロンプトに「変換」する必要があります。次に、これをgen AI呼び出しを処理するサービスに渡す必要があります。これを処理する新しい関数askForHelp
を作成しました。
async askForHelp() { this.aiHelp = '<i>Asking our AI friend...</i>'; // first, "translate" the cards to English let q = `I'm playing blackjack and the dealer currently has ${this.pcCards[1].value} of ${this.pcCards[1].suit.toLowerCase()}. I've got a ${this.playerCards[0].value} of ${this.playerCards[0].suit.toLowerCase()} and a ${this.playerCards[1].value} of ${this.playerCards[1].suit.toLowerCase()}. Should I hit or should I stay?`; console.log(q); let resp = await fetch(`https://eo8dkukowewth66.m.pipedream.net?text=${encodeURIComponent(q)}`); let help = await resp.json(); /* note that help has line breaks and stuff in and in theory we should map to br or something, but im fine ignoring. */ this.aiHelp = ''; alert(help); },
プレーヤーと PC のカードを、彼らの手札を表すカードの配列を含む 2 つの変数に格納します。各カードには「値」があり、ジャックのように数字または名前のいずれかになります。各カードにはスーツもあります。理論的には、それはおそらく重要な情報ではありませんが、害はないと考えました。ゲームによって生成されるプロンプトの例を次に示します。
I'm playing blackjack and the dealer currently has 8 of clubs. I've got a 7 of spades and a QUEEN of hearts. Should I hit or should I stay?
これはバックエンド サービスに渡され (これについては後ほど説明します)、結果は JavaScript アラートに表示されます。これは 100% ひどい UX ですが、これは実験だったので、これで十分だと思いました。
興味があるなら、これは次のような応答でした。
滞在する。あなたは合計 17 を持っています。これは良いハンドです。ディーラーの 8 は高いカードではないので、残っても負ける可能性は低いです。
バックエンド用に、簡単なPipedreamワークフローを作成しました。私のワークフローは文字通りたった 3 つのステップで構成されていました。
1 つ目はトリガーである HTTP エンドポイントです。上記のフロントエンド コードで URL を確認できます。
2 番目のステップは、組み込みの Google PaLM API アクションです。私がしなければならなかったのは、プロンプトに何を使用するかを指示することだけでした: {{steps.trigger.event.query.text}}
私の最後のステップでは、単純に結果が返されました。
export default defineComponent({ async run({ steps, $ }) { await $.respond({ status: 200, headers: { "Content-Type":"application/json" }, body: JSON.stringify(steps.generate_text.$return_value[0].candidates[0].output), }) }, })
それで...ええ。最初のいくつかのテストはうまくいきましたが、その後... 物事が狂ってしまいました。ここに例を示します。ディーラーはハートの 7 を持っていました。スペードのクイーンとダイヤモンドの8を持っていました。助けを求めたところ、次のような答えが得られました。
ディーラーのアップカードは 7 で、これは「ハード」17 です。つまり、ディーラーはあなたが何をしてもスタンドしなければなりません。あなたは「ソフト」19 を持っています。これは、1 または 11 として数えられるエースを持っていることを意味します。あなたは留まるべきです。
わかりました、確かに、私はここに留まるべきですが、実際のところはどうですか?私は19個持っていますか?ディーラーは17本持ってますか?そしてそれは良くなります。かつて、18 件の上映を行ったとき、次のような結果が得られました。
打つことをお勧めします。合計が 18 なので、ブラックジャックの勝ちハンドである 21 まであとわずか 1 ポイントです。ディーラーのハートの 7 は合計 17 を与え、これは 21 のしきい値を下回ります。したがって、ディーラーがヒットした場合は破産する可能性が高く、そうすれば勝ちになります。しかし、このままだとハンドを改善することができず、ディーラーがバストすることを願うしかなくなります。
私はプロのギャンブラーではありませんが、それは間違ったアドバイスだと確信しています。
これにより、プロンプトについてもう少し考えるようになりました。 AI を混乱させるのではないかと心配したため、スーツを小文字にするなど、いくつかの調整を行いました。 Makersuite のいくつかのオプションを繰り返し試しましたが、一貫して、基本的な数学で時々苦労しているように見えました。私がはっきりと述べたときでさえ、
私はクラブのジャック (10 個相当) とハートの 8 (8 個相当) を持っています。
まだ 19 あると思っていましたが、これで運が良くなりました。
私はクラブのジャック (10 個相当) とハートの 8 (8 個相当) を持っています。私の手札の合計は18です。
そして、私もこれを試してみました:
私はクラブのエースとハートの 8 を持っています。私の手札の合計は 9 または 19 です。
そして、それは一貫して正しく機能しているように見えました。そこで、それを念頭に置いて、プロンプト世代に戻り、そこでロジックを更新しました。更新されたバージョンは次のとおりです。
async askForHelp() { this.aiHelp = '<i>Asking our AI friend...</i>'; let totalOb = this.getCount(this.playerCards); let totalStr = 'My hand has a total of '; if(totalOb.lowCount === totalOb.highCount) { totalStr += totalOb.lowCount; } else { totalStr += `${totalOb.lowCount} or ${totalOb.highCount}`; } // first, "translate" the cards to English let q = `I'm playing blackjack and the dealer currently has ${this.pcCards[1].value} of ${this.pcCards[1].suit.toLowerCase()}. I've got a ${this.playerCards[0].value} of ${this.playerCards[0].suit.toLowerCase()} and a ${this.playerCards[1].value} of ${this.playerCards[1].suit.toLowerCase()}. ${totalStr}. Should I hit or should I stay?`; console.log(q); let resp = await fetch(`https://eo8dkukowewth66.m.pipedream.net?text=${encodeURIComponent(q)}`); let help = await resp.json(); console.log('RESP', help); /* note that help has line breaks and stuff in and in theory we should map to br or something, but im fine ignoring. */ this.aiHelp = ''; alert(help); },
私のブラックジャック ゲームには、 getCount
ユーティリティ関数があり、この関数は 2 つの値lowCount
(エースは 1 つとして扱われます) とhighCount
(エースは最大 1 つで 11 とみなされます) を含むオブジェクトを返します。これで、合計に関するプロンプト情報を追加します。
これはかなり役に立ちましたが、それでも時々妄想的な応答が返されることがありました。
それで...ギャンブルのテーブルで酔った友人がアドバイスを提供していると考えてください?
このバージョンのデモを公開して共有したいと思っています。もちろん、コードを共有することもできますが、料金が発生しないように Pipedream ワークフローを無効にしています。ここでフロントエンド コードを参照してください。
https://codepen.io/cfjedimaster/pen/QWYpQjY/905e31ed6d9e3539c041044b308f5f06?editors=1011
いつものように、あなたの意見をお聞かせください!
ここでも公開されています。