🌲

エルデンリングのナレッジグラフをLLMで作る

2024/08/18に公開

TL;DR;

  • エルデンリングのゲーム内テキストからナレッジグラフを構築した
  • グラフ構築にはLangChain、OpenAI API、Neo4jを使用した
  • oxigraphを使いブラウザ上でグラフを描画した
  • できたもの: https://seihmd.github.io/eldengraph/

エルデンリング

「エルデンリング」とは

エルデンリングはフロム・ソフトウェア開発、2022年に発売されたオープンワールド型のアクションRPGです。
多くの人がプレイした大ヒット作ですがそのストーリーや世界観はゲーム中ではっきりと明言されることはありません。

明言されない代わりに、さまざまな情報がアイテムの説明文やNPCとの会話に仄めかされています。プレイヤーはフレーバーテキストを読み、キーワードをつなぎ合わせ、世界の背景について想像を巡らせるわけです。

アイテムが多すぎる

エルデンリングは大ボリュームのゲームなだけあり、存在するアイテムの数も膨大です。
下記の記事によると装備類だけで600種弱あるそうです。

『エルデンリング』のボリュームは数字で見るとえぐい。「全ボス撃破スピードラン」でかかる時間は9時間47分

テキストデータは有志のプレイヤーによってまとめられたりしていますが、膨大なだけに人の頭で情報をまとめるのは難しいです。

https://github.com/EanNewton/Awesome-Elden-Ring-Resources

ナレッジグラフ

ナレッジグラフはデータとデータ間の関係性を人間が理解しやすい形にしたものです。
相関図やフローチャート、ER図、マインドマップなどもグラフの一形態と言えます。

そこでエルデンリングのテキストデータも、ナレッジグラフにできれば理解しやすくなるのでは、と考えました。

テキストデータを用意する

ゲームの全テキストデータを以下のリポジトリから取得し、"アイテム名":"説明文"の形式に加工しました。

https://github.com/AsteriskAmpersand/Carian-Archive

"緋琥珀のメダリオン": "緋色の琥珀が嵌めこまれたメダリオン HPの最大値を上昇させる. 琥珀とは、黄金樹の古い雫であり 最初のエルデの王、ゴッドフレイの時代に特別な宝石として扱われた. それは生命の原始的な力を宿している";

形式以外の加工はしていないため、世界観の説明に関係しないゲーム内効果(ステータスを上げる、など)も含まれたままです。そういった情報はグラフに含めないようプロンプトで指示することにしました(あまりうまくはいきませんでしたが)。

ナレッジグラフの構築

LangChainのドキュメントに従い実装しました。

https://js.langchain.com/v0.1/docs/use_cases/graph/construction/

ざっくり以下のような仕組みになっています。

LangChain.js

Pythonは普段書いていないためJavaScript版を使用しました。

LLM

コスト等の面で試しやすいOpenAI apiを利用しました。

モデルは実装中はgpt-4o-mini、最終的なデータ作成ではgpt-4oを使用しました。体感、グラフのクオリティにはコスト差ほどの違いはなかったように思います。

プロンプト
LangChainはデフォルトでナレッジグラフ構築指示のプロンプトをLLMに渡します。

今回はそのプロンプトをベースに渡すテキストの性質に基づいた指示を加えました。

  • エルデンリングのゲーム内のテキストであること
  • アイテムとその説明であること
  • ゲーム内効果は無視すること

https://github.com/seihmd/eldengraph/blob/main/apps/graph/src/prompt.ts

ノードのソースをグラフに含める

どのテキストからどのノードが抽出されたのかまでナレッジグラフに含めるために以下のようにしました。

  • テキストはアイテムひとつひとつに分けてLLMにリクエストする。
    • 各リクエストの大部分はプロンプトになるため、コスト的に非効率ではあります。
  • addGraphDocumentsメソッドのオプションで includeSource: true を指定する

https://github.com/seihmd/eldengraph/blob/main/apps/graph/src/create-graph.ts#L37-L52

これで(Document)-[MENTIONS]->(n)の形でノードのソースがグラフに含めることができました。

グラフの完成

できたグラフをNeo4jでクエリしてみます。

MATCH (n)-[r]-(n2)
WHERE n.id =~ '.*黄金樹.*' and type(r) <> 'MENTIONS'
RETURN n,r,n2
LIMIT 30;

悪くはなさそうです。

ブラウザで見られるようにしよう

せっかく作ったグラフなので公開したいですが、そのためにNeo4jを運用するほどではありません。
そこでwasmで動くグラフデータベースであるoxigraphを使うことにしました。

ナレッジグラフのエクスポート
Neo4jからノード、リレーションシップのデータをCSVへエクスポートします。

ノードの抽出
MATCH (n)
RETURN 
    id(n) as id,
    CASE labels(n) 
        WHEN ["Document"] THEN n.subject +":" + n.text 
        ELSE n.id END 
    AS value,
    apoc.text.join(labels(n), ',') AS labels;

https://github.com/seihmd/eldengraph/blob/main/apps/web/src/data/all-nodes.csv#L1-L5

リレーションシップの抽出
MATCH (n)-[r]->(n2)
RETURN id(n) AS from, type(r) AS rel, id(n2) AS to;

https://github.com/seihmd/eldengraph/blob/main/apps/web/src/data/all-rels.csv#L1-L5

グラフの描画

vite + reactのウェブアプリケーション上でグラフを描画します。
デザインの好みやカスタマイズ性を考慮してreact-force-graphを使用しました。

https://github.com/vasturiano/react-force-graph

oxigraph

Neo4jはwasm版がないため、oxigraphというグラフデータベースを使用しました。
SPARQLでグラフ構造を扱います。

https://github.com/oxigraph/oxigraph

使い方について詳しくはリポジトリをご覧ください。

あとは取得したグラフデータをreact-force-graphに渡せばグラフの描画ができました。

できたもの

https://seihmd.github.io/eldengraph/ にて公開しています。
性質上、データ量が多いためPC、wifi環境での閲覧をおすすめします。

Discussion