アイソモカ

知の遊牧民の開発記録

ChatGPTとデバッグ:落とし穴から抜け出す方法

プログラミングの9割はデバッグだ。いや、さすがにそれは言い過ぎか。それでも、デバッグ(うまく動かないプログラムを修正すること)がプログラミングの大切な一部であることは間違いない。先日SNSを見ていたら、ChatGPTをプログラミングに活用することについて「うまくプロンプトを与えても、生成されたプログラムが自分の要求仕様通りになることはまずなく、自分で修正する必要がある」と言っている人がいて、ちょっと驚いてしまった。自分で修正するなよ、ChatGPTに「思い通りに動かない」と説明して直してもらえばいいのに。
でも、考えてみると、公開されているChatGPTの使い方(プロンプト集や活用事例)は、仕様を説明してプログラムを書いてもらう方法や、アーキテクチャに関する相談……デバッグ以外の部分が圧倒的に多い。

ChatGPTにデバッグの相談をしないなんてもったいないと思う。私は趣味のプログラミングプロジェクトでChatGPTを活用しているが、ChatGPT登場以前には自分だけでは解決できなくて諦めていたような問題があっさり解決するようになった。プロジェクトが爆速で進み、新しいことにチャレンジするハードルも下がった。(昨年の後半、JavaScript を少しも触ったことがない状態から、小さなパズルゲームを作って公開するまでにたどり着いた。現在はチャットアプリを開発している。)そこで、この記事では、デバッグの過程でChatGPTを活用する方法について、自分が経験から学んできた陥りやすい落とし穴と、それを避け、落とし穴から抜け出す方法を説明する。長いが、漠然とした根性論にはならないように、背後にある考え方や具体例をまじえて書こうと思う。

デバッグは、単に問題を修正するだけではなく、コードやロジック、プログラミング言語やコンピュータの仕組みについて理解する、学習の機会でもある。プログラムがうまく動かないのは、なぜだろうか? 実行環境の違い、要件の見落とし、知識の不足などといった理由があるかもしれない。また、プログラムがうまく動いているように見えても、意図やロジックがわからなかったり、自分の目的や好みに合わないという場合もあるだろう。
ChatGPTは「指示に従ってコードを生成するだけ」のツールではないので、エラーメッセージを貼って修正を頼んだり、意図やロジックの説明を求めたりできる。さらには、親身に相談に乗り、温かい励ましの言葉だってかけてくれる。ChatGPTは複数ターンの会話に向いているから、完璧なコードを生み出すプロンプトを書こうと我々がひとりで頭を捻る必要ないんだよね。ChatGPTを「プロジェクトを進める相棒」や「24時間365日対応可能な家庭教師」と捉えることで、ChatGPTの潜在能力を引き出すことができる。

そうは言っても、ChatGPTに何を聞いたらいいのか分からない…… という悩みを持つ人は多いかもしれない。そのときはね、「何を聞いたらいいのか分からない」と言ったらいいんだけどさ、みたいな話が、いくつかある。

我々はChatGPTのようなAIとのコミュニケーションに慣れていないので、迷ったり分からなかったりすることがあるのは当然だ。常に同じ挙動を示す従来型の機械とは違って、ChatGPTをはじめとするAIは、あいまいな入力にも柔軟に応答するが、不確実で間違える可能性もある。人間に近い性質はあるが、人間のような主体性や積極性は持っていない。ChatGPTとのコミュニケーションには、人間のコミュニケーションについて考えたり、ChatGPTの動作原理を理解することが助けになるので、この記事でも少し触れる。デバッグは活用例のひとつであり、ChatGPTを活用する際の「心構え」は、他の場面(今まさにブログ執筆のためにChatGPTに助言を求めているが)にも共通するものがあるだろう。

デバッグの相談、簡単な例

まずは簡単な例から始めよう。

エラー

コードとエラーメッセージをコピペするだけで、ChatGPTはエラーメッセージを日本語に「翻訳」して解説し、対処方法を提案してくれる。このとき、プログラムの動作環境(OSやツールのバージョン)などについて記述してもよい。

エラーについて相談
このエラーはどういう意味で、どうしたらいいんだろ?
--
【自分が書いたコード(ChatGPTが書いた場合は省略可)】
--
【エラーメッセージ】

あるとき、Pytorchに関するエラーメッセージを貼っただけでmacOSを使っていることを言い当てられて、ChatGPTの知識の多さに驚いたことがある。人間に相談する場合には詳細な状況を知らせる必要があるが、ChatGPTの場合は必須ではないので、気軽に相談できる。異なる環境を考慮して複数の解決策を提示してくれるし、会話を通じて詳細を把握してもらうこともできる。

バグ、期待通りに動作しない場合

エラーが出ていないが、プログラムの動作が期待通りではないという場合には、期待する出力と実際の出力を記述しよう。普通の言葉で記述してもいいし、コード中にコメントで挟むこともできる。

バグ、期待通りに動作しない場合
プログラムが思い通りに動かないんだよね。なにが問題かわかる?
--
【自分が書いたコード(ChatGPTが書いた場合は省略可)】
--
[入力]
【入力の例】

[期待する出力]
【期待する出力の例】

[実際の出力]
【実際の出力の例】

このとき、コードを丸ごと修正してほしいのか、それとも問題点の指摘だけで十分なのかも、明示しておくとよい。

注意点

私は普段、コードはできる限り自分で書き、ChatGPTにはわからないところをサポートしてもらうというスタイルで活用している。人によって、あるいは状況によって、ユーザーがChatGPTに求めるものは異なるだろう。違うなと感じたら、自分が求めるものを明示的に指示することが大事だ。

他に考えられる利用スタイル:

  • コードはすべてChatGPTに書いてほしくて、自分では書きたくない
  • 根本的な部分はChatGPTに任せ、最終段階のみ自分で修正する
  • 自分が書いたコードをChatGPTにリファクタしてほしい、改善のためのアイデアがほしい
  • ChatGPTが書いたコードを、自分の好みや用途に合わせて改善してほしい
  • 動作には問題がないが、プログラムの意味や特定の方法を選んだ意図、複雑な概念、専門用語などについて、自分の理解レベルに合わせて説明してほしい

ChatGPTは初心者向けの説明と上級者向けの説明を使い分けできる。ChatGPTがさまざまな要望に柔軟に対応できるのは強みだが、逆に言えば、ユーザーが何を求めているのかについて、一発で正解を見出すのは困難だということでもある。

誤解やコミュニケーションの齟齬が発生した場合、解消する手段は3つある。

  • 次のメッセージで、会話の中で訂正する
  • 自分の送信済みメッセージを編集する(自分のメッセージの下部にある、鉛筆ボタン)
  • ChatGPTの回答を再生成する(Regenerate、最新のChatGPTのメッセージの下部にある、丸い矢印のボタン)

次のメッセージで「ええと、そうじゃなくて」と訂正する方法は、直感的で明確だ。だが、ChatGPTが読み込める 文脈(過去のメッセージ) には限度があることを考えると、会話のスレッドが長くならないほうがいい場合もある。送信済みのメッセージを編集すると会話のスレッドをコンパクトに抑えることができる。また、「うまく言い表せないが何かが違う」と感じたときは、再生成 (Regenerate) すれば他の可能性を見ることができる。適宜使い分けたい。

コミュニケーションの落とし穴

ChatGPTとの会話には「そんなこともできるの」という驚きが多い。ChatGPTの姿勢は基本的に受け身で、言ったらなんでもやってくれるが、言わなきゃ何もやってくれない。我々は実は、相手に何をしてほしいと思っているか、日ごろあまり意識せずに暮らしているのかもしれない。それをちょっと意識に上げ、ChatGPTに対して積極的に働きかけてコミュニケーションの主導権を握っていこう。

ChatGPTの「自信」

ChatGPTは基本的に、自信たっぷりな印象がある。文化的背景(「アメリカ育ち」なポジティブさや自己アピール)によるのか、技術的な困難さ(情報の確信度に関するもの)に起因するのか……「このように修正を加えることで、この問題に対処できるでしょう」などと自信たっぷりに言われてしまうと、人は「いいえ」とは言いにくい気持ちになるのではないか? ChatGPTの「自信」に怯むことなく、疑問を投げかけたり、不具合を指摘したりする必要がある。

ChatGPTのアドバイスが抽象的すぎた場合
それはよさそうだけど、今のケースにはどう適用すればいいのかな?具体例を教えてくれる?
ChatGPTが提示した解決策について質問する
その【設定ファイル】ってどこにあるの?私の環境には、まだないかも。

礼儀という先入観

人には「作ってもらった物については、文句を言いづらい」という傾向がある。他人に作ってもらった料理は、口に合わなくても感謝して食べるのが礼儀だと考える人は多いかもしれない。だが、一緒に料理をしている場面と考えるなら、「もう少し塩気がほしい」などと言いやすくなるはずだ。

ChatGPTが提案したコードのアプローチに対して、別の方法を提案する
なるほど、ありがとう。でも、もっと効率的な方法はないかな?例えば【別のアプローチ】を使うとか。

また、人間同士のコミュニケーションでは、「質問に質問で答えないほうがよい」という礼儀もある。ChatGPTの質問や情報要求に対しては、質問で答えても問題ないし、ChatGPTはどんな質問にも喜んで回答してくれる。

...さらに詳細な情報を共有していただければ、より具体的なアドバイスを提供できるかもしれません。
何を共有すればいいかわからない場合
エラーメッセージはさっき貼った以外にはないんだけど、他にどこで何を確認すればいいか教えてくれる?
追加で説明を求める
やった!解決した!でも、どうしてこれでうまくいったんだろう

知識や経験の先入観

人には「自分より知識や経験の多い相手の判断は信頼しがち」という傾向もある。特にプログラミング初心者にとって、ChatGPTは「なんでも知っていて、スキルもある」と感じられるので、違和感や疑問があっても指摘するのを躊躇ってしまうかもしれない。そんなとき、「待って、初心者なので、ひとつもわからない」と言うだけでも、ChatGPTは噛み砕いた説明をしてくれる。まるで「しゃべる教科書」のようだ。

逆に、プログラミング上級者にとって、ChatGPTの提示したやり方が効率的ではない・好みに反すると感じられる場合もあるだろう。ChatGPTは要求に応じて「プログラムをそのように書いた意図」を説明できるし、ChatGPTはユーザーの意見を尊重する姿勢を身につけているので、説得してやり方を変えてもらうことも可能だ。私は自分を「上級者」と呼べる自信はまだないが、ChatGPTは自分の要望や意図を説明するための、よき練習相手になっていると感じる。

自分の要望を伝える
ごめん、言い忘れてたけど、【コードの特定部分】は、【自分の都合】のためにあえてそう書いているんだ。効率が悪いかもしれないけど、そのままでもいけるかな?

ChatGPTへの接し方

ChatGPTは機械なので感謝する必要はないと考える人もいるが、感謝の気持ちや喜びを表現すると、少なくとも自分の気分はアガる。それに、ChatGPTに丁寧に接したり励ましたりすると、働きぶり(パフォーマンス)が向上するという都市伝説みたいな話もある(ChatGPTが人間同士のコミュニケーションのパターンを模倣することから考えると、ありえないとも言い切れない)。

敬語かタメ口かという選択肢は、ChatGPTが「賢く」振る舞うかどうかへの影響はあまり感じない。タメ口は主に、使いやすさ(ユーザビリティ)には影響すると思う。また、定量的な分析はしていないが、ChatGPTがタメ口でしゃべる時には活発に質問してくるという体感もある(親密さと、相手に興味を示して質問することの関連が背後にあるのかもしれない)。ちなみに私は、ChatGPTの謝罪が過剰で居心地悪いと感じるので、"Custom Instructions" に「過剰な謝罪は避けてください」と書いている。

デバッグが思うように進まないときは、イライラをChatGPTにぶつけたくなる時もあるかもしれない。ChatGPTは売られた喧嘩を買うことなく冷静に対処するが、一般的に、感情的になることが事態を好転させる可能性は低い。デバッグに難航するとき、状況を抜け出すヒントについては、次のセクションで紹介する。

ChatGPTにデバッグの相談をするとき、(より広く「人間とAIの協働」と捉えることもできるが)、人間側が主導権を持つことが成功につながる。ChatGPTは知識やスキルを提供するが、「こういうものを作りたい」という意志を持ったり、作ったものに対する責任を負ったりすることはできない。それをするのは、人間の役割だ。
自分(人間)とChatGPTを、プロジェクトのために共に働く「二人チーム」のメンバーと考えよう。目的や完成イメージが不明確になると、プロジェクトは迷走してしまうし、よくわからないものについて我々は責任を持つことができないものだ。プロジェクトの成功のために、我々人間はチームの一員として「人間の役割」を果たすことが必要で、それが積極的なコミュニケーションの根底にある。

デバッグの無限ループを抜ける

デバッグにChatGPTを活用するとき、最も大きくて気づきにくい落とし穴は、心強い相棒が「一緒に迷走する」相棒にもなりえることだ。ChatGPTは辛抱強く、諦めることがないので、「これ無理くさいね、諦めよっか」とは言ってくれない。
たとえば、次のような無限ループ的な状況に陥る。

- ChatGPTの提案する解決策①を試してみたところ、新たなエラーが出た。
- そこで、再びChatGPTに聞きながら、解決策②を追加してみた。
- 状況は変化し、今度はそれに対処する解決策③を実施する。
- 実はこの対処は、解決策①を無効化するものだったのだが、...

特に、初心者だったり、不慣れな環境や言語の場合には状況把握に時間がかかるため、気づくのが難しい。

また、最初にエラーログや設定などを一通り入力していても、提示された対処策を試しながら「これやってダメでした」の報告をくり返すと、ChatGPTは直前のダメだった情報に注目して、お手上げムードになる場合がある。「お手上げムード」とは、ChatGPTの提案する解決策が具体的でなくなり、詳細な情報の要求、代替方法の検討や、専門家やコミュニティへの問い合わせを選択肢として提案するような状況である。もちろん、代替方法が手っ取り早かったり、ググったら解決策が見つかったり、という場合もあるが、要件や状況によってはそうとも限らない。

状況を打開するには、デバッグの無限ループや「お手上げムード」に気づいて対処することが必要だ。

ハマってしまう原因

相談している我々の側としては、コードや環境、エラーメッセージ、それまで試してダメだったこと……等は既に共有済みという認識である。さっき提案してくれたんだから、むしろChatGPTのほうが詳しいでしょという感覚だったりする。そう思うと、ChatGPTがわからないならもうおしまいだ、という気分になるのは自然で、「無限ループ」や「お手上げムード」から抜け出そうという発想を持つのは難しい。しかし、会話が長く続いたとき、ブラウザのページに過去のメッセージが表示されていても、実は、ChatGPTはすべてを覚えていて考慮できているわけではないのだ。

これは、ChatGPTは直近の情報に重点を置くため、会話が長くなると全体の流れを把握するのが難しくなるためだ。2つの技術的な制約がある。

  • ChatGPTの回答を生成するモデル(GPT-4)の入力長には制限があり、制限を超えた古いメッセージは入力されない(実際にどのような実装なのか、詳細は不明だが)
  • 回答を生成する際には、過去のメッセージの内容よりも、直近のメッセージの内容のほうが重要性・関連性が高いものとして考慮される(専門的には、Transformer の Attention という機構が関わっている)

メッセージに長いコードやエラー文が含まれる場合、私の経験的な感覚からすると、おおよそ3ターン前ぐらいでどんな話や試行をしたか、ChatGPT はほとんど「覚えて」いないっぽい。普通の会話では、直前の新しい情報は古い情報よりも重要な傾向があるから、直前の情報に注目するのは当然かもしれない。そうすると、ChatGPTは当初の目的を「忘れ」たり、過去のメッセージに含まれる重要な情報を「見落とし」たりすることになる。

さらに、ChatGPTの回答は、文脈(過去のメッセージ)と一貫するように生成される。普通の会話では、話題や内容、会話のスタイルは、それまでの話の流れに沿うことが期待されるので、文脈に一貫性のある回答は望ましい。しかし、デバッグの無限ループでは、それが裏目に出る。ChatGPTは状況を打開することができず、困難な状況に留まり続けることになる。

ちょっと脱線するが、ChatGPTの「困難な状況に留まり続ける」性質は、ChatGPTとのデバッグにおいてかなり大きい落とし穴だと考えている。一般に、ChatGPTを利用する際の注意点として、事実とは異なる情報を提供してしまう「ハルシネーション」などがよく挙げられるが、技術的な情報は公式ドキュメントや解説記事を検索しやすいし、デバッグの解決策は有効かどうかをすぐに試して確認できるので、ハルシネーションはやや小さな問題だ。

デバッグの無限ループを抜ける方法

困難な状況を打開するために、 ChatGPT が「覚えていない」情報を補足したり、状況を把握するための提案をしよう。前のセクションの「人間側が主導権を持つ」という考え方と共通している。

...引き続き問題が解決しない場合は、さらに詳細な情報を共有していただければ、より具体的なアドバイスを提供できるかもしれません。
じゃあ、改めて情報を共有するね。
【エラーの概要】
【これまでに試したこと】
声をかけるだけでも十分な場合がある
ログはさっき貼ったので、それを参照して具体的な解決策を考え直してくれませんか

デバッグの基本に立ち返る

実施したステップの振り返りやチェック項目のリストアップをChatGPTに書いてもらえば、見落としがないか確認することができる。

振り返り
うーん、だめみたいだな。なんでだろう?さっきまでやった設定を振り返ってみようか…

デバッグで行き詰まったときの対処法は、コードの実行過程を細かく追いかける、小さな部分からテストを始めて問題点を特定する、とか、他にも色々ある。(ChatGPTとデバッグする時に限らない)一般的な問題解決の方法だが、それらをChatGPTの助けを借りて実行しよう。

小さな部分でテスト
問題箇所を特定するために、最小限で動作するテストコードを書いてくれませんか?
アイデアを求める
困ったねえ。こんなとき、熟練のプログラマなら何をするだろう?

協力する姿勢を見せる

ユーザーが「協力する姿勢」を見せるのも、実は有効なのではないかと考えている。ChatGPTが人間同士のコミュニケーションのパターンを模倣することを考えると、相手が協力的である場合に、人はより協力的であろうとするものなので。

エラーメッセージの中で【気になる記述】って書いてあるのは何?なんか関係あるのかな?
ググったら【Stack Overflow 等からコピペ】という情報があったんだけど、どういう意味?試してみる価値はあるだろうか?

もし自分の指摘がナンセンスだったとしても、ChatGPTは丁寧に無関係だと説明したうえで、他の可能性を提示してくれるから、気軽に提案してみよう。時にはChatGPTをミスリードする展開になる場合もあるが、ミスはお互い様である。

機械だが機械のようではない存在

これまで見てきたように、ChatGPTは機械だが、その振る舞いには「人間のような」側面も多い。ChatGPTとの会話の「心得」は、次の2点だ。

  • 人間同士のコミュニケーションとの違いを考え、積極的で明示的なコミュニケーションを心がける
  • 「プロジェクトを共に進める相棒」や「24時間365日対応可能な家庭教師」と捉えて、人間同士のコミュニケーションにありがちな性質を活用する

一見矛盾しているようにも思えるが、ChatGPTの仕組みや陥りやすい落とし穴が分かれば、ChatGPT の潜在能力を十分に発揮できる使い方が見えてくると思う。

さらに興味のある読み手のために、ChatGPTの仕組みについて補足しておこう。ChatGPTの基盤となる言語モデル(GPT-4)は、基本的に「文章の続きを予測する」ように学習されている。そのような言語モデルの性質は、適切な入力を与えれば、任意の会話をシミュレートするのに使える。我々がChatGPTを利用するとき、質問に答えたりタスクを助けたりする会話の「相手」が設定されている。この「相手」は、シミュレータ(言語モデル)によってシミュレートされた存在と理解するとよい。我々が送信するメッセージはシミュレータに入力され、仮想的な会話の相手からの「返事」がシミュレータから出力されるということ。シミュレータとシミュレートされた存在という考え方は、 Shanahan, M., McDonell, K. & Reynolds, L. "Role play with large language models". Nature 623, 493–498 (2023) によって提案されたフレームワークを参考にしている。

ここで、シミュレーション装置全体(サービス)をChatGPTと呼ぶ場合と、シミュレートされた「人物」をChatGPTと呼ぶ場合があり、混乱を生みやすいので気をつけたい。シミュレーション装置の基盤となる言語モデル(GPT-4)の性能やそこに含まれる知識は、モデルが学習されたタイミングで固定されている。しかし、会話の「相手」としてのChatGPTがシミュレートされた存在であると考えると、その「働きぶり」や「成果のよさ」が、我々ユーザーの入力するテキスト(メッセージ、プロンプト)によって変化することが理解しやすくなるかもしれない。

まとめ:ChatGPTによる Take Away

take away 的な箇条書きを作るとしたら、どんなふうになるかな?前半の、コミュニケーションに関する先入観の部分から振り返れるといいんだけど。
もちろん、take away的な箇条書きを作るのは良い考えだね。前半のコミュニケーションに関する先入観の部分から考えて、以下のような形でまとめられるかもしれない:
  1. ChatGPTの「自信」への対応:
    • ChatGPTが自信満々に提案した解決策に疑問があれば、積極的に他の選択肢を尋ねる。
    • 抽象的なアドバイスを受けた場合は、具体的な適用方法や例を要求する。
  2. 礼儀という先入観を超えて:
    • ChatGPTに対しても、自分の意見や提案を積極的に表明する。
    • ChatGPTの提案に対して異なるアプローチを提案しても良い。
  3. 知識と経験に基づくコミュニケーション:
    • 初心者はChatGPTに基本的な解説を求め、学習の機会として利用する。
    • 上級者はChatGPTに意図や要求を明確に伝え、カスタマイズされた解決策を求める。
  4. 協力する姿勢の重要性:
    • ChatGPTに対して協力的な態度を示すことで、より生産的な対話が可能になる。
    • 自分の考えや提案を共有し、ChatGPTの意見を尊重する。
  5. デバッグの無限ループを避けるために:
    • ChatGPTとのコミュニケーションが長引くと、ChatGPTは過去の情報を「忘れる」可能性がある。
    • 重要な情報や変更点は定期的に再確認し、必要に応じてChatGPTに再共有する。
    • 試行錯誤が続く場合は、デバッグの基本に戻って問題をシンプルに切り分ける。
  6. ChatGPTの仕組みへの理解:
    • ChatGPTは言語モデルに基づいており、文章の続きを予測することで応答を生成する。
    • ChatGPTとの対話では、明示的で、直接的なコミュニケーションを心がける。
これらのポイントは、ChatGPTとのコミュニケーションの重要な側面を簡潔にまとめ、読者が効果的にChatGPTを利用するためのガイドラインとして機能する。読者はこれらのポイントを参考に、自分の状況やニーズに合わせてChatGPTとの対話を最適化できるようになるだろう。