ChatGPTの新機能、関数呼び出し機能を使って自作ライブラリのチュートリアルを完全自動作成する。

## 動機

最近[next-pipe](https://github.com/Toshimichi0915/next-pipe)というライブラリを作ったのですが、どこにも記事を出していないためユーザーが全くいません。そこで、ChatGPTに自作ライブラリの使用方法について書かせてQiitaにアップロードしたら時間をかけずに良質かつ長文のチュートリアルを作れるのでは？と思ったので試してみました。

## ChatGPTの新機能、関数呼び出し機能とは

ChatGPTでは最近APIに更新があり、

1. 最大トークン長が16,000まで増えた（処理できる文字数が増える）
2. 関数呼び出し機能が追加され、ChatGPTに自作した関数を呼び出させることができるようになった。

ようです。特に関数呼び出し機能はかなり応用が効き、これを利用することで例えば「ChatGPTにウェブを検索させる」、「ChatGPTにスクリプトを実行させる」、「ファイルの読み込み・書き込みを行わせる」といったことができるようです。

今回はこれの機能を使用して、ChatGPTにライブラリのソースコードを読み込ませ、そこからチュートリアルを作成させることを目標とします。

## 実際に作る

### 1. OpenAI APIのラッパーを作成

OpenAIはChatGPTをPython経由で実行するためのライブラリを公開していて、これを使うことでChatGPTと会話を行うことができるのですが、残念ながらこれだけでは今回のユースケースには不十分です。

例えば「README.mdを読み込ませた後にtutorial.mdを作成する」といった処理が必要になる場合、

1. (コンピュータ):「README.mdを読み込んだ後にtutorial.mdを作れ」と指令を送る。
2. (ChatGPT): ファイル(README.md)を読み込む関数が呼び出されたことを通知する。
3. (コンピュータ): README.mdを読み込み、その内容をChatGPTに送信する。
4. (ChatGPT): README.mdの内容を読み込んだ後、ファイル(tutorial.md)を作成する関数が呼び出されたことを通知する。
5. (コンピュータ): tutorial.mdを作成する。

といった手順になります。この双方向でのやり取りを実現する方法をOpenAIのライブラリでは提供していないため、今回は自作する必要があります（もしかしたらあるのかもしれないし、間違いなくGitHubに上がってるライブラリを使えば一発だと思うのですが、あんまり詳しくないためｶﾞﾁｬｶﾞﾁｬ適当に自作します）。

ｶﾞﾁｬｶﾞﾁｬした結果が[こちらです](https://github.com/Toshimichi0915/tutorialgen/tree/739bc1813bd5f7dc128dd8cd34172b85452f5fba)。

この段階では、ChatGPTにファイルを読み込む関数のみ実装されています。ちなみに面倒くさくて色々端折った結果、ChatGPTに通常ユーザーで読み込める全てのファイルの読み取り権限があるという危険な代物になっています。

では関数ができたところで、実際にこの関数が実行できるか試してみましょう。実験に使うプロンプトは以下のとおりです。

```
Determine all the users inside the computer based on the directory structure, with get_file and list_dir functions.
```

```
(日本語訳)
コンピュータ上に存在する全てのユーザーを、get_fileとlist_dir関数を使って調べてください。
```

これを実行してみると、以下のような結果が帰ってきました。

```
executing function: get_file(path=/)
executing function: get_file(path=/Users)
assistant: Based on the directory structure, the users inside the computer are:

1. All Users
2. Default
3. Default User
4. main
5. Public
```

```
(日本語訳)
executing function: get_file(path=/)
executing function: get_file(path=/Users)
assistant: ディレクトリの構造によると、このコンピュータ上に存在するユーザーは以下のとおりです:

1. All Users
2. Default
3. Default User
4. main
5. Public
```

確かに関数が実行されていることが確認できました！実験成功です！

なおlist_dir関数は実際のソースコードから消えていますが、これはChatGPTの混乱を避けるために後から消しています（プロンプトだけ修正ミスでそのままになってしまっている）。

get_file関数とlist_dir関数の両方が存在する場合、ChatGPTは誤ってディレクトリに対しget_file関数を使ったり、ファイルに対してlist_dir関数を使ったりするケースが見受けられました。間違って関数を呼び出した場合にはその旨を伝えるようにしている（例えばlist_dirであれば「(This is a file.)」といった内容を返すようにしている）のですが、ChatGPTは混乱して「ファイルが存在しない」と勘違いするケースが多々見受けられたため、get_file関数とlist_dir関数をひとまとめにして、get_file関数でディレクトリもファイルも両方調べられるように改良しました。

### 2. ファイルの書き込み関数を作る。

さて実際に関数が呼び出せていることが確認できたため、次はファイルの書き込み関数を作って、実際にチュートリアルを作成していきましょう。

では作成していきます（ｶﾞﾁｬｶﾞﾁｬ）。

[できました](https://github.com/Toshimichi0915/tutorialgen/blob/08913d5abc4ec21c2254740a982d495648cb3a41/tutorialgen/main.py#L74-#L103)。

```python
...
            "write_file": {
                "description": "Write content to a file, or create a directory.",
                "properties": [
                    {
                        "name": "isdir",
                        "type": "boolean",
                        "description": "Whether the path is a directory.",
                        "required": True,
                    },
                    {
                        "name": "path",
                        "type": "string",
                        "description": "The path to the file or directory.",
                        "required": True,
                    },
                    {
                        "name": "content",
                        "type": "string",
                        "description": "The content to write to the file.",
                        "required": False,
                    },
                    {
                        "name": "append",
                        "type": "boolean",
                        "description": "Whether to append the content to the file.",
                        "required": False,
                    },
                ],
                "function": write_file,
            }
...
```
この関数では引数が4つあり、それぞれ

1. ディレクトリかどうか（ディレクトリならtrue）
2. ファイル・ディレクトリ名（str）
3. ファイルの中身（str）
4. 上書きするかどうか（上書きせずに追記するならtrue）

という風になっています。またwrite_fileでファイル・ディレクトリ作成の２つの機能が存在しますが、これは先程述べたようにChatGPTが混乱を防ぐためのトリックとなります。

ではファイルに書き込む関数ができたため、実際にチュートリアルをChatGPTが作成できるか試していこうと思います。

具体的にはライブラリが存在するディレクトリをChatGPTに教えてあげて、その中身をChatGPTが読み込むように誘導します。そしてその後にChatGPTがwrite_file関数を呼び出し、チュートリアルが含まれたファイルを作成するといった寸法です。

プロンプトは以下の通りとなります。

```
I'd like to create a step-by-step tutorial for my library, next-pipe.
The contents of the library exists in "C:/Users/main/Desktop/projects/next-pipe".
Please understand how the library works by reading the source code with \"get_file\".
After that, please create a step-by-step tutorial in the file \"tutorial.md\" using \"write_file\" command, inside the library repository.
```

```
(日本語訳)
自作ライブラリ、「next-pipe」についてのステップバイステップのチュートリアルを作成したいと思っています。
ライブラリの中身は"C:/Users/main/Desktop/projects/next-pipe"以下に存在します。
ライブラリがどのように動作しているか、get_file関数でソースコードを読み込んで理解してください。
それができたら、write_file関数でリポジトリ内のファイル、tutorial.mdにステップバイステップのチュートリアルを作成してください。
```

これを実行した結果は果たして！？

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/361412/821c45ed-a996-bc47-16ea-73d791e77a85.png)

お、できてる！？

と一瞬思いますが、よくよく見てみると全然違います。ライブラリの中身ガン無視です。関数呼び出しのログを見てみると

```
executing function: get_file(path=C:/Users/main/Desktop/projects/next-pipe)
executing function: write_file(isdir=False, path=C:/Users/main/Desktop/projects/next-pipe/tutorial.md, content=...)
```

となっています。つまりChatGPTはファイルを何も読み込まず、ディレクトリ内に存在するファイル一覧のみ読み込んでチュートリアルを作成していることになります。なぜそんなことをしたのか、謎ですね。

これだと使い物にならないため、なんとかファイルを読み込ませられないか試してみます。新しいプロンプトは以下のとおりです。

```
I'd like to create a step-by-step tutorial for my library, next-pipe.
The contents of the library exists in \"C:/Users/main/Desktop/projects/next-pipe\".
Please understand how the library works by reading README.md or any other source files with \"get_file\".
After that, please create a step-by-step tutorial in the file \"tutorial.md\" using \"write_file\" command, inside the library repository.
```

```
(日本語訳)
自作ライブラリ、「next-pipe」についてのステップバイステップのチュートリアルを作成したいと思っています。
ライブラリの中身は"C:/Users/main/Desktop/projects/next-pipe"以下に存在します。
ライブラリがどのように動作しているか、get_file関数でREADME.mdもしくはどんなソースファイルでも読み込んで理解してください。
それができたら、write_file関数でリポジトリ内のファイル、tutorial.mdにステップバイステップのチュートリアルを作成してください。
```

再度実行します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/361412/1de8346e-2c40-4e75-4645-cb17f9af6de3.png)

[できました！](https://gist.github.com/Toshimichi0915/1025c9bba9aa36aa10ce4e3971f43b9e)今回はちゃんとライブラリのコードを使ってくれています。ただ関数呼び出しのログを見てみると

```
executing function: get_file(path=C:/Users/main/Desktop/projects/next-pipe/README.md)
executing function: write_file(isdir=False, path=C:/Users/main/Desktop/projects/next-pipe/tutorial.md, content=...)
```

としかなっておらず、好きにソースコード読んで良いと言っているのにREADME.mdとしか読んでいないですね。

この後も色々試行錯誤を試したのですが、どうにもうまく行かずソースコード全体を読ませて全体を詳細に説明するようなチュートリアルを作成することはできなかったので、ここらで一旦締めくくらせていただきたいと思います。

## 困った点

ChatGPTの新しい関数呼び出し機能ですが、以下のような問題点が見られました。

1. 引数一覧を表現するJSONが偶にぶっ壊れておりパースできない。
2. 存在しない関数「python」をやたらと呼びたがる。

ChatGPTの関数呼び出し機能が存在する前に作られた類似ツール、「AutoGPT」でも似たような問題があったらしいので、LLMにJSONを作らせている限りは、どうしてもうまく行かない部分もあるんじゃないかなと思いました。

どうやらこういったエラーは複雑な指令を送ると発生しやすいみたいです。特にソースコード全体を読ませるような処理を行おうと試行錯誤していたときはかなりの確率で遭遇しました。現時点での対応策は、関数呼び出しではあまり複雑なことはさせず、単一のタスクに専念して実行させるのが良いようです。

今後のAIの発展で、これらの問題が解決されるといいですね。