見出し画像

GPT-4の脳内ChatGPTでユーザーの入力をインタラクティブにシミュレートする

今までのおさらい

このように今までインチキTypeScriptを用いてGPT-4の脳内にChatGPTインスタンスをでっちあげ、そこにプログラムから指示を与えることで目的の挙動を実現(?)してきましたが、今回はもう少しインタラクティブにシミュレーションを行うことができないか検討してみました。

今回のテーマ

例えばシェルスクリプトなどを実行している際には、適宜状況に応じてテキストの追加入力を求められる場合があります。そうすることで、起動時に大量のオプションを設定しなくとも、処理を進める上で必要になった物だけを入力するだけで済むので、ユーザーにとってもお手軽簡単です。
そこで、今回は脳内ChatGPTにシミュレーションをさせている途中に、ユーザーからの入力が発生するたびにこちらに聞き返してもらうようにすることを目標にコードをでっち上げて行きます。

仮説

またもやTypeScriptで進めて行きますが、Webで一番わかりやすくかつ雑にユーザーに入力を促す方法といえば、そう`prompt`関数ですよね。これは画面上にポップアップでダイアログを表示し、その中にユーザーがテキストを入力してOKボタンをクリックすることで、その値をプログラムから利用することができます。
このprompt関数のメリットは、なんと言ってもブロッキング処理であることです。つまり、非同期処理などの面倒なことを考えずにざっくり呼び出すことができ、かつその間コードの実行が停止されるというなかなかに潔い仕様となっています。
お世辞のもUXが良いとはいえず、またなかなか見た目もソリッドすぎるため敬遠されがちですが、今回はこの`prompt`関数の実行をトリガーにして、一旦シミュレーションを停止させ、こちらから内容を入力し、その入力を持ってシミュレーションを再開させることができるのではないかということを検証していきます

実験

せっかく何かインタラクティブなサンプルを作るので、目指せシンギュラリティということで、自分で自分のコードを書き換えてよりよくしてけるようなプログラムの実装を目指していきます。まずは適当に書いたインチキTypeScriptから。

プロンプト

```ts
interface CodePosition {
  lineNumber: number;
  offset: number;
}

interface CodeSuggestion {
  codeRange: { from: CodePosition, to: CodePosition };
  comment: string;
}

declare var ChatGPTCodeAnalyzer: {
  resolveDependenciesAndAnalyze: async (goal: string, entrypointFilePath: string, option: {
    fileResolver: (filepath: string) => string
  }) => Promise<CodeSuggestion[]>;
}
const start = async () => {
  console.log("こんにちは。私はスーパープログラミングChatGPTです。")
  const goal = prompt("これから私にどんなことをしてほしいですか?")!;
  const entrypointFilePath = prompt("わかりました。では最初にみるべきファイルのパスを教えてください")!;

  return await ChatGPTCodeAnalyzer.resolveDependenciesAndAnalyze(goal, entrypointFilePath, {
    fileResolver: (filepath: string) => prompt(`このファイルの中身を教えてください: ${filepath}`)!
  });
}

start();
```
上記のコードの実行をステップバイステップでシミュレートし、コンソールに出力される内容を
コードブロックで出力してください。実装が提供されていない箇所は、型定義に合わせて仮定をおいてシミュレーションを行なってください。私がユーザーの入力をシミュレートするので、prompt関数やalert関数、confirm関数の呼びだし時には、
仮定をおかずに出力を停止し、私に何を戻り値とすればよいか質問してください。
私の回答ををprompt関数、alert関数、confirm関数の戻り値として適切な値に変換した上で、
シミュレーションの実行を再開してください。

まずざっくりコードを読んでもらい、依存関係をできれば解決してもらいつつファイルの中身を読んで、そこからコードの改善点を列挙してはくれないだろうか、というお気持ちを込めてこのコードを書いてみました。

回答

スーパープログラミングChatGPTの登場

想定通りこちらに何をしてほしいか聞いてくれていますね。さすがスーパープログラミングChatGPT!戻り値を教えてくれというので教えて差し上げましょう。
 ユーザー入力1

ユーザーの指示に従ってよりよいコードに書き換える機能と、読みにくい部分を読みやすくする機能を実装したいです

回答2

ちゃんと順調に動いている

早速順調です。いい感じにコードが動いてくれています。もちろんこんなのは一発で成功している訳ではなく、頑張って諸々調整した上で作り上げたプロンプトを用いているのでこうなっています。が、コンディションにもよりますが再現性は半分以上あるはずなので、お手元のGPT-4でも制限の許す限りお試しいただけると思います。
とまぁ注釈はさておき、ファイルパスを教えてあげましょう。

ユーザー入力2

codeanalyzer.ts

回答3

素晴らしい!!それっぽくオプションで`fileResolver`というコールバック関数を書いておいただけにも関わらず、きっとファイルパスをもらったらこれ呼び出したら中身くれるんでしょと言わんばかりに`fileResolver`の呼び出しをシミュレートし、その中の`prompt`を呼び出してからの中断して質問!!!光の速さでレスしましょう。

ユーザー入力3

interface CodePosition {
  lineNumber: number;
  offset: number;
}

interface CodeSuggestion {
  codeRange: { from: CodePosition, to: CodePosition };
  comment: string;
}

declare var ChatGPTCodeAnalyzer: {
  resolveDependenciesAndAnalyze: async (goal: string, entrypointFilePath: string, option: {
    fileResolver: (filepath: string) => string
  }) => Promise<CodeSuggestion[]>;
}
const start = async () => {
  console.log("こんにちは。私はスーパープログラミングChatGPTです。")
  const goal = prompt("これから私にどんなことをしてほしいですか?")!;
  const entrypointFilePath = prompt("わかりました。では最初にみるべきファイルのパスを教えてください")!;

  return await ChatGPTCodeAnalyzer.resolveDependenciesAndAnalyze(goal, entrypointFilePath, {
    fileResolver: (filepath: string) => prompt(`このファイルの中身を教えてください: ${filepath}`)!
  });
}

start();

ここですかさず自分自身のコードを入力してあげます。これで意図にそって少しずつ賢くしていけるのであれば、理論上最強のスーパープログラミングChatGPTが出来上がるはずです。さて結果は、、、?

回答4

ここでまさかの仮定、、!!

なんということでしょう。ここまで意図通りにいい感じに動いていたのに、ここでまさか戻り値についてあんまり頭を使わずにでっち上げて終了とは情けない。いやはや期待しすぎたのでしょうか。プロンプトを追加してリテイクさせてみましょう。

 追加プロンプト

resolveDependenciesAndAnalyze関数の名称から挙動を推察し、
今までのユーザーからの入力を踏まえて、より正確にどのような戻り値が返されると思うか予測してください

回答4-2

まさかの直球のツッコミ
[
  {
    codeRange: {
      from: { lineNumber: 3, offset: 0 },
      to: { lineNumber: 11, offset: 0 },
    },
    comment: "ChatGPTCodeAnalyzerの実装が欠けています。ユーザーの指示に従ってよりよいコードに書き換える機能と、読みにくい部分を読みやすくする機能を実装してください。",
  },
  {
    codeRange: {
      from: { lineNumber: 20, offset: 0 },
      to: { lineNumber: 20, offset: 6 },
    },
    comment: "start関数の宣言が冗長です。アロー関数を使用してシンプルに書き換えることができます。",
  },
  {
    codeRange: {
      from: { lineNumber: 22, offset: 0 },
      to: { lineNumber: 22, offset: 6 },
    },
    comment: "start関数を直接呼び出すのではなく、必要に応じてエクスポートして使用することを検討してください。",
  },
]

全文を読めるようにスクショではなくコードブロックで貼っておきます。
なんという正論。`lineNumber`や`offset`はかなり雰囲気ものなので、ずれていますが遠からずと言ったところでしょうか。要約すれば「お前がちゃんと実装しろ」ということですね。残念。うまくいきませんでした。

が、そうではなくて、今回のテーマは`prompt`関数を使ってインタラクティブに作業をしてみるはずだった、、、つまり、、、この実験はッ、、成功!!!

参考

せっかくなのでこんなわけわからんことせずに普通に聞いてみました。

普通に聞いてみるプロンプト

```ts
interface CodePosition {
  lineNumber: number;
  offset: number;
}

interface CodeSuggestion {
  codeRange: { from: CodePosition, to: CodePosition };
  comment: string;
}

declare var ChatGPTCodeAnalyzer: {
  resolveDependenciesAndAnalyze: async (goal: string, entrypointFilePath: string, option: {
    fileResolver: (filepath: string) => string
  }) => Promise<CodeSuggestion[]>;
}
const start = async () => {
  console.log("こんにちは。私はスーパープログラミングChatGPTです。")
  const goal = prompt("これから私にどんなことをしてほしいですか?")!;
  const entrypointFilePath = prompt("わかりました。では最初にみるべきファイルのパスを教えてください")!;

  return await ChatGPTCodeAnalyzer.resolveDependenciesAndAnalyze(goal, entrypointFilePath, {
    fileResolver: (filepath: string) => prompt(`このファイルの中身を教えてください: ${filepath}`)!
  });
}

start();
```
上記のコードはTypeScriptによる擬似コードで表現されたChatGPTを用いたプログラミングAIです。上記のコードに対して、ユーザーの指示に従ってよりよいコードに書き換える機能と、読みにくい部分を読みやすくする機能を実装したいです。

回答

普通に聞いた方が早い

結論

  • 普通に聞いた方が早い

  • トリッキーなことして遊びたいのであれば、`prompt`関数は遊べる

  • プログラミング言語だけで雰囲気で制御する遊びと思ってみてね


この記事が気に入ったらサポートをしてみませんか?