高校生がchat-gptを使ったでデバッグツールを作る！

<h1>自己紹介</h1>

こんにちは！ニューヨークでプログラミングを勉強している高校2年生です！　qiitaでは学ぶのに苦戦した事や作った物などを日記代わりに書いていきます！僕が作った物が誰かの役に立ったら良いなと思っています！ですが、しょっちゅう間違えた理解をしているのでここに書く事は全部信じないでください！笑

<h2>作ったもの：</h2>
僕が今回作ったものは "DEBUG-GPT" です！
DEBUG-GPTは自動的にエラーメッセージ、コードの全貌、それにプロジェクトのフォルダー構造を理解した上で効果的なエラーの改善方法を提供してくれます！


僕はpythonでコードを書いていてエラーが起こるとchatgptに聞くことがよくあります。ですがエラーメッセージだけ提供しても効果的な解決方法が得られることは非常に少ないです！なのでエラーが起こると自動的に色んな情報をChatGPTに送信し、それらを理解した上で効果的なエラーの改善方法を提供してくれたら良いのになと思って作りました！


<h2>作った過程</h2>

まずopenai apiを使うのに三日ほど苦戦しました笑
なぜかずっと使えなかったのですが"python3 ファイル名"をコマンドで実行すると出来ました笑

そしてまずver1はこんな感じです

``` debug_gpt.py
import os
import subprocess
import openai
import api_key

openai.api_key = api_key.api_key


# Content_list and activate_file is only thing you are suppposed to change
content_list = ['test.py', 'ask.py']
activate_file = "test.py"
# Content_list and activate_file is only thing you are suppposed to change



code_dict = {}
messages = [
    {"role": "system", "content": "You are an assistant that helps with Python debugging."}
]

# Folder tree structure
tree_structure = subprocess.run(['tree', '-L', '3'], capture_output=True, text=True).stdout
messages.append({"role": "assistant", "content": f'Project file structure:\n\n{tree_structure}\n\n'})

# Read file contents
def read_code(file_name):
    with open(file_name, 'r') as file:
        code = file.read()
    return code

for file_name in content_list:
    with open(file_name, 'r') as file:
        code = read_code(file_name)
        code_dict[file_name] = code

messages.append({"role": "assistant", "content": f'Content of each file:\n\n{code_dict}\n\n'})

# Get error code
try:
    result = subprocess.run(['python3', '-c', activate_file], capture_output=True, text=True, check=True)
    stdout = result.stdout
    print(stdout)
except subprocess.CalledProcessError as e:
    stderr = e.stderr

messages.append({"role": "assistant", "content": f'Error code generated by executing {activate_file}:\n\n{stderr}\n\n'})
messages.append({"role": "user", "content": 'Please provide a really short summary of why the error occurred and a possible solution.'})

chat_completion = openai.ChatCompletion.create(
    model="gpt-3.5-turbo-16k",
    messages=messages
)

response = chat_completion["choices"][0]["message"]["content"]
print(response)
```

debug-gptを作るのにもChtaGPT使用しました。実現したい物を言うとそれにあった関数やmoduleを教えてくれるのですごく便利ですよね！2年前勉強してたときとは学習スピードが全然違います！！

まず
```python
content_list = ['test.py', 'ask.py']
activate_file = "test.py"
```
を指定します！
`content_list`で読み込みたいコード（エラーに関係してそうなファイル）を記述します
`activate_file`で実行したいコードを書きます


`tree_structure = subprocess.run(['tree', '-L', '3'], capture_output=True, text=True).stdout`

ではフォルダー構造を取得します！


```python
def read_code(file_name):
    with open(file_name, 'r') as file:
        code = file.read()
    return code

for file_name in content_list:
    with open(file_name, 'r') as file:
        code = read_code(file_name)
        code_dict[file_name] = code
```

ここでは`read_code`関数を指定しています。この関数は引数の`file_name`のコードの全貌を返します。
for文で`content_list`に指定されたコード全てを最初らへんに指定した`code_dict`に保存していきます。



```python
try:
    result = subprocess.run(['python3', '-c', activate_file], capture_output=True, text=True, check=True)
    stdout = result.stdout
    print(stdout)
except subprocess.CalledProcessError as e:
    stderr = e.stderr
```
これはsubprocess関数でactivate_fileに指定されたファイルを実行するものです。
try文で実行しエラーが起きるとexcept文にジャンプしエラーコードを取得します。取得したエラーコードはstderrに入ります。

そして全て`messeges`のdictに入れopenai apiに投げています。

<h2>ダメだったところ</h2>

- なぜか途中からsubprocessが動かなくなりました。よくよく調べてみると、subprocessで実行したファイルでエラーが起こった場合except文にジャンプしないそうです。なぜ最初は動いんたのかは結局わかりませんでした。笑

- 実行したいファイルを実行してエラーが起きたら自動的に解決策教えてくれる訳ではなくいわざわざ`debug.py`を実行しないと教えてくれないのが少し微妙

<h2>これらをもとにver2を作ってみた！</h2>
改善したコードがこちらです！

```make_message.py
from distutils.log import error
import os
import subprocess
import openai
import api_key

openai.api_key = api_key.api_key

# Content_list and activate_file is the only thing you are supposed to change
content_list = ['test.py', 'ask.py']
activate_file = "test.py"
# Content_list and activate_file is the only thing you are supposed to change

code_dict = {}
messages = [
    {"role": "system", "content": "You are an assistant that helps with Python debugging."}
]

stdout = ''
stderr = ''

# Folder tree structure
tree_structure = subprocess.run(['tree', '-L', '3'], capture_output=True, text=True).stdout
messages.append({"role": "assistant", "content": f'Project file structure:\n\n{tree_structure}\n\n'})

# Read file contents
def read_code(file_name):
    with open(file_name, 'r') as file:
        code = file.read()
    return code

for file_name in content_list:
    with open(file_name, 'r') as file:
        code = read_code(file_name)
        code_dict[file_name] = code
messages.append({"role": "assistant", "content": f'Content of each file:\n\n{code_dict}\n\n'})
error_occur = True
# Get error code
def outerr():
    process = subprocess.Popen(['python3', 'test.py'], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
    stdout, stderr = process.communicate()
    process.wait()
    print("Standard output:")
    print(stdout)

    if len(stderr) > 0:
        print("Standard error:")
        print(stderr)
        messages.append({"role": "assistant", "content": f'Error code generated by executing {activate_file}:\n\n{stderr}\n\n'})
        messages.append({"role": "user", "content": 'Please provide a brief summary of why the error occurred and a possible solution.'})
        error_occur = True
    else:
        print("No error occurred!")
        error_occur = False


outerr()
with open('final.txt', 'w') as filing:
    filing.write('1')


chat_completion = openai.ChatCompletion.create(
    model="gpt-3.5-turbo-16k",
    messages=messages
)

if error_occur == True:
    response = chat_completion["choices"][0]["message"]["content"]
    print(response)
else:
    pass
```
元々の`debug.py`はもうその名前にふさわしくなくなったので`make_message.py`に降格させてやりました
そして新しくファイルを作成しました。
```debug_gpt.py
import subprocess     
def read_file(file_name):
    with open(file_name,"r") as file:
        content = file.read()
        content = int(content)
        return content

def write_file(file_name):
    with open(file_name , 'w') as file:
        file.write('0')

def debug_file():
    content = read_file("num.txt")
    if content == 0:
        with open("num.txt", "w") as file:
            file.write("1")
        subprocess.run(["python3", "make_message.py"])
    else:
        write_file('num.txt')

def quit_code():
    stop_code = read_file("final.txt")
    if stop_code == 1:
        write_file('final.txt')
        quit()


debug_file()
quit_code()
```

```test.py
import debug_gpt


print('hello world! from test.py!!')
print(hi)

```
こんな感じです！

<h2>変更点！</h2>
まず初めにtest.pyを実行してエラーが起きたら解決策を教えてほしいので「test.pyからsubprocessを使ってdebug.pyを呼び出せば良いじゃん！」と思って書いて実行すればターミナルが一生なんかをprintし始めました笑
subprocess.runがずーっとループしだしたので改善方法を考えました！ここからわかりにくくなります！笑

```python
def debug_file():
    content = read_file("num.txt")
    if content == 0:
        with open("num.txt", "w") as file:
            file.write("1")
        subprocess.run(["python3", "make_message.py"])
    else:
        write_file('num.txt')
```
まず`debug_file()`関数を実行します。この関数は`make_message.py`を実行する関数です。ですが`make_message.py`で`test.py`を実行する際、また`debug_file`関数が呼び出されるのでループを防ぐ為にこんなアイデアを思いつきました！
1. 新しくtxtファイルを作成し0と書いておきます。
2. `read_file`関数で`num.txt`を読み込みます
3. もし0の場合（初めて呼び出される場合）は`subprocess.run`で`make_message.py`を実行します
4. 実行する前にnum.txtの数字を1に変えておきます
5. そして2回目に`debug_file`関数が呼び出される時には`num.txt`が1になっているのでelseにジャンプします。
6. `write_file`関数で`num.txt`を0に戻します（次またデバッグする時に0になっていないと`make_messag`eが実行されないので）

と言う感じです。ややこしいですよね笑
これ今書いていて気づいたんですがファイル増やす必要無かったんじゃ無いかなって思い始めました笑

ちなみに`test.py`から`debug_gpt.py`をimportした際に`debug_gpt`内に実行されるコードがある場合、それは `import debug_gpt` を行った時点で実行されるので、できるだけ`test.py`内をスッキリさせる為に`debug_gpt.py`内で実行することにしました。

これでループ問題とわざわざ`debug.py`を実行してデバッグをしないといけない問題が解決し、結果的には`debug_gpt`ファイルをimportするだけですみました！僕的にこれが理想だったのですごく満足してます！


あと変えたところで言うとここら辺ですね〜！

```python
def outerr():
    process = subprocess.Popen(['python3', 'test.py'], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
    stdout, stderr = process.communicate()
    process.wait()
    print("Standard output:")
    print(stdout)

    if len(stderr) > 0:
        print("Standard error:")
        print(stderr)
        messages.append({"role": "assistant", "content": f'Error code generated by executing {activate_file}:\n\n{stderr}\n\n'})
        messages.append({"role": "user", "content": 'Please provide a brief summary of why the error occurred and a possible solution.'})
        error_occur = True
    else:
        print("No error occurred!")
        error_occur = False
```
`try/except`文を使っていましたが、`subprocess.poppen`関数を使ってエラーコードを取得することにしました。そしてもしエラーが起きた場合は解決策を聞いてもしエラーが起きなかったら何もしないと言う機能付け足しました！

<h2>終わり！！</h2>

終わりです！最後に適当に感想でも述べとこうかなと思います！
なんかめっちゃ楽しかったです！笑
特にループするのをどうして解決しようかなと考えてる時がなんかダンジョン攻略してるみたいで面白かったです！笑
制作するのに一週間かかりましたがなんだか完成した後溜めてたアニメを全て見終わったみたいな感覚になりました笑
次は夏休み中にdjangoで口コミサイトを作ろうと思うのですが、このデバッグツールを駆使して頑張ろうと思います！

ここまで読んでくれた人はありがとうございました！！また読んでください！！🙇‍♂️