🐥

GodotでLLMを使いたい(llama.cpp)

2024/03/30に公開

動機

LLMを利用したゲームを作ってみたい

ChatGPTやGeminiなどのapiを呼び出せば、利用はできる。
でも、オフラインで動かすことはできないし、安易にゲームを配布することも難しくなる。

だから、llama.cppを使って、ローカル上で動くLLMをGodotで動かす。

この記事ではやらないこと

godot-cpp

llama.cppは名前の通りC++で駆動する。
https://github.com/ggerganov/llama.cpp

Godotにはgodot-cppというC++拡張機能がある。
https://github.com/godotengine/godot-cpp

これを組み合わせれば、Godotのオブジェクトからllama.cppを呼び出して使うことは理論上可能。

実際、調べてみると先駆者がいる
https://github.com/opyate/godot-llm-experiment

しかし、windowsで利用するためにはSConstructを自力で書かなければならない。
更に、godot-cppのビルドもかなり面倒で、これらを組み合わせるとビルドエラーが多発した。

godot-python

別の方法を探したところ、godotのpython-bindingsを発見した。
https://github.com/touilleMan/godot-python

llama-cpp-pythonを使い、上記のgodot-pythonを使えばllama-cppをpython上から使い、それをgodotで使うことができるかもしれない。

しかし、開発が進んでいないようで、masterブランチはgodot3が対象で止まっている。
godo4対応のブランチとして、godot4, godot-mesonが存在するが、うまく動かなかった。

この記事でやること

いろいろ試したけれども、godotを他言語で利用することが難しい理由になった。
実際、llama.cpp単体のコンパイルであれば、先駆者が多いので自力でできた。

そこで、llama.cppをコンパイルして、生成される実行ファイル(main.exe)をGDScriptから呼び出すことにする。
これならば、簡単に実装できるし、プログラムやモデルを変更したいなどの要望にもコンパイルせずに対応できる。

Step1. llama.cppのビルド

ビルドにはMicrosoft Visual Studio 2022 Build Toolsを使いました。
https://visualstudio.microsoft.com/ja/downloads/

適当なディレクトリにllama.cppをクローンしてくる。

git clone https://github.com/ggerganov/llama.cpp

このディレクトリは、Godotのプロジェクトディレクトリとは別にした方が良い。同じにするとGodotのリソース読み込みが遅くなる。

移動して、コンパイルする。cmakeを使って、生成物がまとまるようにする

cd llama.cpp
mkdir build
cd build
cmake ..
cmake --build . --config Release

godotの生成物をプロジェクトディレクトリに設置します。プロジェクトディレクトリは、E://godot_projects/godot_llama_testです。

 cmake --install . --prefix E:\godot_projects\godot_llama_test\llama_cpp

これでgodotのプロジェクトディレクトリにllama_cppの実行ファイルがコピーされました。
今回使う実行ファイルはmain.exeだけなので、それだけ移しても構いません。

Step2. モデルの設置

モデルファイルをダウンロードして設置します。
設置ディレクトリは、E://godot_projects/godot_llama_test/llama_cpp/modelsとしました。

今回利用したモデルは、phi-2のQ4_K_Mです。
https://huggingface.co/TheBloke/phi-2-GGUF/tree/main

ディレクトリE://godot_projects/godot_llama_test/llama_cpp/modelsを作成して、そこにダウンロードしたggufファイルを入れます。

Step3. GDScriptからllama.cppを呼び出す

Godotにて適当なシーンを作成し、スクリプトをアタッチします。

OS.executeでプログラムの実行ができます。これによってmain.exeを実行します。
出力の書き込み先は、Arrayとして引数で与えます。
また、PackedStringArrayにて実行ファイルの引数を指定できます。

llm_test.gd
extends Node2D

# Called when the node enters the scene tree for the first time.
func _ready():
	var prompt := 'What is best of movie?'
	var model_path := 'llama_cpp/models/guff/phi-2.Q4_K_M.gguf'
	var arguments : PackedStringArray= [
		'-m',
		model_path,
		'-p',
		prompt,
		'-n',
		'10'
	]
	var output := []
	OS.execute('llama_cpp/bin/main.exe', arguments, output)
	print(output)

引数解説

  • -m : モデルのパスの指定
  • -p : LLMへの入力文
  • -n : 出力最大トークン数

実行すると、

["What is best of movie?\r\nHow can I increase my energy level?\r\nHow do you make a girl laugh when you\'re in a relationship?\r\nIs a good marriage all about equality?\r\nWhat is an example of a good relationship?\r\nWhat are the key ingredients of"]

と出力ログへと表示されたので、無事LLMが実行できました。

Discussion