6
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

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

Last updated at Posted at 2023-07-07

自己紹介

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

作ったもの:

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

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

作った過程

まず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年前勉強してたときとは学習スピードが全然違います!!

まず

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

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

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に保存していきます。

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に投げています。

ダメだったところ

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

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

これらをもとにver2を作ってみた!

改善したコードがこちらです!
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)

こんな感じです!

変更点!

まず初めにtest.pyを実行してエラーが起きたら解決策を教えてほしいので「test.pyからsubprocessを使ってdebug.pyを呼び出せば良いじゃん!」と思って書いて実行すればターミナルが一生なんかをprintし始めました笑 subprocess.runがずーっとループしだしたので改善方法を考えました!ここからわかりにくくなります!笑
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.pytest.pyを実行する際、またdebug_file関数が呼び出されるのでループを防ぐ為にこんなアイデアを思いつきました!

  1. 新しくtxtファイルを作成し0と書いておきます。
  2. read_file関数でnum.txtを読み込みます
  3. もし0の場合(初めて呼び出される場合)はsubprocess.runmake_message.pyを実行します
  4. 実行する前にnum.txtの数字を1に変えておきます
  5. そして2回目にdebug_file関数が呼び出される時にはnum.txtが1になっているのでelseにジャンプします。
  6. write_file関数でnum.txtを0に戻します(次またデバッグする時に0になっていないとmake_messageが実行されないので)

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

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

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

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

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関数を使ってエラーコードを取得することにしました。そしてもしエラーが起きた場合は解決策を聞いてもしエラーが起きなかったら何もしないと言う機能付け足しました!

終わり!!

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

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

6
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?