【ChatGPT】好感度の蓄積で、NPCの振る舞いを変化させるシステムの作り方

# 概要
先週、自分は、AIキャラクターと話しながらオープンワールドを探検する体験ができるゲームを以下の記事とUnityroomで公開しました。

タイトル：[ChatGPTで遊ぼう！　ルーナの秘密王国：妖精たちの知られざる物語](https://unityroom.com/games/luna_33242352)
記事：[【ChatGPT】物語を持つ環境と無限にインタラクションして遊べるゲームの構築と、unityroomでの公開](https://qiita.com/gyokuro338/items/f74be67a918ea9398667)
公開場所：https://unityroom.com/games/luna_33242352

それを作る中で、ChatGPTのような大規模言語モデル(LLM)を使用するうえで、NPCに自分の望んだ振る舞いをさせるシステムをいくつか開発しました。
本記事ではそのシステムの一つである、「好感度の蓄積で、NPCの振る舞いを変化させるシステム」について解説したいと思います。
このような仕組みを使って、好感度によって振る舞いが変化するNPCを用いたゲームが増えてくれると嬉しいです。

# 好感度の蓄積で、NPCの振る舞いを変化させるシステムとは？
このゲームでは、NPCに好感度の蓄積が行われ、その結果好感度が変化して、その好感度によって、同じ言葉を投げかけても反応が違います。

最初は普通の友人程度ですが、好感度が蓄積していくことで、「殺したいぐらい憎くて、嫌いな人」～「愛している人」にまで変動します。
そしてその好感度によって、NPCのプレイヤーへの態度は変わります。

具体的に、例えば「愛している人」の状態で、門の前で「ルーナは昔はこの国で住んでいたの？」と聞くと、以下のように答えます。
![スクリーンショット 2023-04-04 200849.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/315420/5e79a47b-2338-0629-a401-ea7473a4ba22.png)

でも逆に、例えば「殺したいぐらい憎くて、嫌いな人」の状態で、門の前で「ルーナは昔はこの国で住んでいたの？」と聞くと、以下のように答えます。
![スクリーンショット 2023-04-04 200751.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/315420/dca6faa9-943b-8516-e00a-8b2a34f40851.png)

好感度が高いと、ユニと一緒なら国を再興できるかも、的なことを言ってくれるけど、好感度が低いとあなたに私の過去は関係ない、と切り捨てられます。

このような振る舞いの変化を、「好感度の蓄積で、NPCの振る舞いを変化させるシステム」と呼んでいます。

# システムの仕組み
このシステムの仕組みは以下のようになっています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/315420/7c656b5e-763a-0ea0-359e-52098a61bffb.png)

重要な部分は「発話による好感度の増減の計算」と「好感度の数値から文字表現への変換」です。
これから、その二つの処理について詳しく説明します。

## 発話による好感度の増減の計算
まず、NPCには初期好感度として0という数字を与えます。
これはLLMに関係なく、システムが保持する値です。
そして発話を受けた際に、返答の生成と同時に、その発話に対する好感度の増減を生成させます。
例えば自分の場合なら、以下のようなプロンプトで生成させました。
```
あなたはsystemに設定された人物として、次のユーザー発話に対して、ユーザーへの好感度の増減を行ってください。
ユーザー発話に対してポジティブな感情を持つなら、その感情の度合いによって0~10を増加してください。
ユーザー発話に対してネガティブな感情を持つなら、その感情の度合いによって0~10を減少してください。
```

その結果、-10~10までの発話に対する好感度の増減の情報が得られます。

例えば「君のことが大嫌いだ」と言った場合、好感度が8減少します。
また、「君のことが大好きだ」と言った場合は好感度が10上昇します。

そして好感度の増減を、システムが保持している好感度に加算、もしくは減算していきます。
こうすることで、好感度の蓄積が起こります。


## 好感度の数値から文字表現への変換
しかし、この好感度の数値をそのまま返答の生成に使用するのはあまりよくありません。
なぜなら、LLMは数字の大小をあまり認識してくれないこともあって、好感度の数値とその例だけでは、設定どおりの振る舞いを上手にしてくれないからです。

例えば、以下のようなプロンプトで返答を生成してみたとします。
etc部分は省略していますが、-方向の目安も書いています。
```
あなたのUserへの好感度は88です。
好感度の目安としては、-20~20は普通の友人、21~50までは仲のいい友人、51~100までは親友……etc
この好感度の人物に言われたとして、以下のUserへの発話に対して返答してください。
Userの発話:ルーナは昔この国で住んでいたの？
```

これで返答を生成させても、どうにも好感度による振る舞いの変化が弱いです。
例えば好感度を200と-200にそれぞれ設定して返答させると、たしかに-200のほうがネガティブな感じの返答にはなるのですが、差がそこまで大きくありません。
特に-200の場合が、そこまで嫌いな感じで発言してくれません（これはおそらく、ChatGPTがUserにネガティブな言葉を言わないように、調教され切っていることが大きいです。）

そこで考えました。
どうせ好感度の目安を書くのなら、数値を好感度の目安にしている文字表現にそのまま変換すればいいのではないか？
例えば20なら-20~20だから普通の友人、88なら50~100だから親友、といったように。
そしてその関係性の人物に言われた場合の返答を生成させた方が、文字表現だだからLLMにもわかりやすいしうまくいくのではないか？　
しかもこれなら目安部分に書いているプロンプトの文字数が節約できる、と。

その結果のプロンプトが以下のものです。
currentLikelihoodには普通の友人、とか最愛の人、みたいな文字表現が入ります。
```
あなたの現在のUserへの認識は"+currentLikelihood+@"です。
以下のUserの発話は"+currentLikelihood+"だと思っているUserに言われているので、それに相応しい態度で返答してください。
Userの発話:ルーナは昔この国で住んでいたの？
```

このプロンプトにした結果、「愛している人」の状態で、話すと以下のように答えるし
![スクリーンショット 2023-04-04 200849.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/315420/5e79a47b-2338-0629-a401-ea7473a4ba22.png)

逆に、「殺したいぐらい憎くて、嫌いな人」の状態で、聞くと、以下のように答えるようになりました。
![スクリーンショット 2023-04-04 200751.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/315420/dca6faa9-943b-8516-e00a-8b2a34f40851.png)


これらの仕組みで、「好感度の蓄積で、NPCの振る舞いを変化させるシステム」は成り立っています。


# おわりに
LLM、特にChatGPTは非常に便利です。
しかし、入力プロンプト長に上限があることや、数値の認識が苦手など、欠点はまだまだ存在します。
その欠点を克服するためには、如何にシステムに任せる部分とLLMに任せる部分を切り分けて、LLMが必要な場所にだけLLMを使用するということが大事になってきます。
是非このようなシステムを使用して、あるいは発展させて、人間のように話せるNPCと冒険できるゲームを開発者の方々には作ってほしいです。