💫

[Minecraft × ChatGPT] マイクラで作りたいものを伝えると魔法みたいに実現してくれるコマンドを作る

2023/03/06に公開
1

息子と一緒に遊びたいなと、Minecraft と ChatGPT を繋げて、やりたいことを日本語で伝えるといい感じに実現してくれるコマンドを作りました

🪄 作ったもの

/py magic に続けてやりたいことを伝えると、いい感じに実現してくれます。

家をつくる例
/py magic 10マス先に豪華な家を作って。窓は広めで。

水流エレベーターをつくる例
/py magic 水流エレベーターを作って。周りは石ブロックで。

🛠️ 実装

事前に Minecraft で Python を実行できる環境を整えます。

https://files.minecraftforge.net/net/minecraftforge/forge/
https://github.com/arpruss/raspberryjammod

mcpi で Minecraft に接続できれば OK です。

次に必要なモジュールをインストールします。

pip install mistletoe openai mcpi

そして、mcpiディレクトリに以下コードで magic.py を作り保存します。

magic.py
import openai
import sys
from mcpi import minecraft
from mistletoe import Document
from mistletoe.block_token import CodeFence
import logging

logging.basicConfig(filename='magic_log.txt', level=logging.INFO)

mc = minecraft.Minecraft.create()


def get_code_blocks(text: str):
    doc = Document(text)

    code_blocks = []
    for token in doc.children:
        if isinstance(token, CodeFence):
            code_blocks.append(token.children[0].content)

    return "\n\n".join(code_blocks)


def think_code(prompt: str, extra_messages=[]): order = f"""
「{prompt}」という指示をMinecraft上で実現するためのPythonコードを教えてください。
MinecraftとPythonの連携には、mcpiというライブラリを使用します。
コードを出力する際には、以下のルールを守ってください。

- 可能な限り細部まで精巧な構築物を作るコードを出力する
- 完成したら構築物の全体を見通せる位置にプレイヤーを移動させる
- コードはMarkdownのコードブロックで囲む
- Pythonのexec関数で実行できる形式にする

"""

    openai.api_key = "YOUR API KEY"
    try:
        completion = openai.ChatCompletion.create(
            model="gpt-3.5-turbo",
            messages=[
                {"role": "system", "content": "あなたは優秀なPythonプログラマーです。"},
                {"role": "user", "content": order},
                *extra_messages,
            ]
        )
        return (completion.choices[0].message.content, False)
    except Exception:
        return ("", True)


def main():
    # 引数からpromptを取得
    prompt = sys.argv[1]
    mc.postToChat(f"Prompt: {prompt}")

    retry_count = 0
    max_retry_count = 8
    extra_messages = []

    # 一度でうまくいかない場合もあるので指定回数リトライする
    while retry_count < max_retry_count:
        # chat completion APIでコード生成
        generated_text, has_error = think_code(
            prompt=prompt,
            extra_messages=extra_messages
        )

        if has_error:
            retry_count += 1
            continue

        # APIの出力結果からコード部分をテキストで抽出
        code = get_code_blocks(generated_text)

        # コード部分が空の場合
        if code == "":
            retry_count += 1
            extra_messages.append({
                "role": "assistant",
                "content": generated_text
            })
            extra_messages.append({
                "role": "user",
                "content": "Minecraft上で実行できるPythonコードをコードブロックに書いてください。"
            })
            continue

        try:
            # 出力されたコードをeval
            exec(code, globals())
            mc.postToChat("成功しました!")
            break
        except Exception as e:
            retry_count += 1
            # evalでエラーが発生した場合はエラー内容をChatGPTのパラメーターに追加してリトライ
            extra_messages.append({
                "role": "assistant",
                "content": generated_text
            })
            extra_messages.append({
                "role": "user",
                "content": f"実行したところ {e} というエラーが発生しました。修正してください。"
            })
            continue

    if not retry_count < max_retry_count:
        mc.postToChat("Error: 実行に失敗しました。")

    logging.info(f"{prompt=}")
    logging.info(f"{generated_text=}")
    logging.info(f"{extra_messages=}")


main()

引数で受け取った prompt を Chat Completion API に渡して、その出力結果からコードを抽出して exec で実行しています。

あとは、Minecraft に入り /py magic でやりたことを伝えるだけです。

おわりに

まだ Prompt の詰めが甘いので、なかなかうまく建物を作ってくれないことが多いですが、息子が喜んでくれたのでヨシ!!

https://twitter.com/KawamataRyo/status/1632333351578390528

Discussion

ryo_kawamataryo_kawamata

もし、Pythonがないよ!てきなエラーが出る場合は、raspberryjammodの設定からPython Interpreterのパスを設定する
以下asdfのpythonを使う設定