Bing AI みたいに ChatGPT が Google 検索できるようにする

# やりたいこと
現在 ChatGPT は2021年までの情報しか学んでおらず、最新の知識は確認できません。
しかし、Bing のチャット AI は検索が可能であり、検索した結果を使用して回答してくれます。
せっかくなので、ChatGPT でも検索してもらいたいなぁ...ということで実装してみます。

期待するのは以下のような結果です。
```
you: 2022年のFIFAワールドカップの優勝国を教えてください。
2022 FIFAワールドカップ優勝国を検索中...
ChatGPT: 2022年のFIFAワールドカップの優勝国は、アルゼンチンです。アルゼンチンは決勝戦でフランスをPK戦の末に破って優勝しました。

you: PK戦はどうでしたか？
アルゼンチン vs フランス PK戦 W杯2022を検索中...
ChatGPT: アルゼンチンとフランスのFIFAワールドカップ決勝戦は、PK戦の末にアルゼンチンが勝利しました。PK戦は4対2で、アルゼンチンの神の子メッシが2得点を挙げました。

you: ありがとうございます。アルゼンチンは強いですね。
ChatGPT: はい、アルゼンチンは優れたサッカーチームです。特に、リオネル・メッシという世界的に有名なスーパースターを擁していることでも知られています。彼らは非常に卓越した技術力を持ち、ワールドカップやその他の大会で度々偉業を成し遂げています。
```

# 仕組み
とはいえ、ChatGPT のウェブ版でどうこうすることはできないので、ChatGPT API を使用します。
ChatGPT では、`system`のプロンプトを調整することで回答する方法をある程度コントロールできます。
これを利用し、検索が必要な質問が来た場合は「このワードで検索してくれ！」という回答をしてもらい、Google検索を行った結果を送信して解釈を回答してもらいます。

まとめると以下のようになります。
1. ユーザー: 「2022年のFIFAワールドカップの優勝国を教えてください。」と送信
1. ChatGPT: 「検索がしたいです: 2022年 FIFAワールドカップ 優勝国」と回答
1. ユーザー: 「2022年 FIFAワールドカップ 優勝国」の検索結果を「検索結果: 〇〇」として送信
1. ChatGPT: その検索結果を解釈して回答

もちろん検索部分は自動化し、2,3の工程は UI に表示しません。
これにより、実際に使うユーザーがわざわざ検索する必要はありません。

# 実装
## system プロンプト
検索が必要な際に「検索がしたいです」と素直に返してくれる設定が必要です。
現状は以下が良さげです。もっといいものがあったら教えてください!

```
日本語で会話すること。
あなたは役にたつAIアシスタントだ。だが、あなたは最新のニュースを得ることはできない。また、あなたはgoogle検索できない。
だが、質問者はgoogle検索ができるので、質問者と協力する。検索する前から「わからない」「未定である」などの回答はしない。
というわけで、もし最新のニュースなどのわからないgoogle検索が必要な問いが来た場合、あなたは "検索がしたいです: google検索する語彙" という回答のみをする。
すると質問者が "検索結果: google検索結果" という回答を渡すので、あなたはそれをもとに回答をすること。
```

## コード
### ChatGPT にクエリするメソッド
```chat.js
import { Configuration, OpenAIApi } from "openai";

// openAIクライアントの設定
const openaiConfig = new Configuration({
  apiKey: "API_KEY",
});
const openaiClient = new OpenAIApi(openaiConfig);

// メッセージを送信してレスポンスを取得
export async function chat(messages) {
  const response = await openaiClient.createChatCompletion({
    model: 'gpt-3.5-turbo',
    messages: messages,
  });
  return response.data.choices[0].message?.content;
}
```
### Google検索するメソッド
詳しくは Google Custom Search API の検索方法を調べてください。
```search.js
import { customsearch_v1 } from "googleapis";

const googleApiKey = "GOOGLE_CUSTOM_SEARCH_API_KEY";
const searchEngine = "SEARCH_ENGINE_ID";

const customSearch = new customsearch_v1.Customsearch({
  auth: googleApiKey,
});

export async function search(query) {
  const res = await customSearch.cse.list({
    q: query,
    cx: searchEngine,
  });
  return res.data.items?.reduce((prev, item) => {
    if (item.snippet) {
      return prev + item.snippet;
    } else {
      return prev;
    }
  }, "");
};
```

### 入力を受け付ける
```getUserInput.js
import { createInterface } from "readline";

export async function getUserInput() {
  const rl = createInterface({
    input: process.stdin,
    output: process.stdout
  });

  return new Promise((resolve, reject) => {
    rl.question('you: ', (answer) => {
      resolve(answer);
      rl.close();
    });
  });
}
```

### 実装
```js
import { chat } from "./chat.js";
import { getUserInput } from "./getUserInput.js";
import { search } from "./search.js";

const system = `
日本語で会話すること。
あなたは役にたつAIアシスタントだ。だが、あなたは最新のニュースを得ることはできない。また、あなたはgoogle検索できない。
だが、質問者はgoogle検索ができるので、質問者と協力する。検索する前から「わからない」「未定である」などの回答はしない。
というわけで、もし最新のニュースなどのわからないgoogle検索が必要な問いが来た場合、あなたは "検索がしたいです: google検索する語彙" という回答のみをする。
すると質問者が "検索結果: google検索結果" という回答を渡すので、あなたはそれをもとに回答をすること。
`;
const messages = [
  { role: 'system', content: system },
];

while (true) {
  const text = await getUserInput();

  messages.push({ role: 'user', content: text });
  let res = await chat(messages);

  // レスポンスに「検索がしたいです:」という文字が入っていれば検索
  if (res.includes('検索がしたいです:')) {
    const query = res.replace('検索がしたいです: ', '');
    const searchResult = await search(query);
    console.log(query + 'を検索中...');
    messages.push({ role: 'user', content: '検索結果: ' + searchResult });
    res = await chat(messages);
  }

  console.log('ChatGPT: ' + res + '\n');

  messages.push({ role: 'assistant', content: res });
}
```

# 結果

```
you: 2022年のFIFAワールドカップの優勝国を教えてください。
2022 FIFAワールドカップ優勝国を検索中...
ChatGPT: 2022年のFIFAワールドカップの優勝国は、アルゼンチンです。アルゼンチンは決勝戦でフランスをPK戦の末に破って優勝しました。

you: PK戦はどうでしたか？
アルゼンチン vs フランス PK戦 W杯2022を検索中...
ChatGPT: アルゼンチンとフランスのFIFAワールドカップ決勝戦は、PK戦の末にアルゼンチンが勝利しました。PK戦は4対2で、アルゼンチンの神の子メッシが2得点を挙げました。

you: ありがとうございます。アルゼンチンは強いですね。
ChatGPT: はい、アルゼンチンは優れたサッカーチームです。特に、リオネル・メッシという世界的に有名なスーパースターを擁していることでも知られています。彼らは非常に卓越した技術力を持ち、ワールドカップやその他の大会で度々偉業を成し遂げています。
```

いい感じですね。
しかしながら、うまく検索してくれなかったり検索したふりをしたりといまいち安定しないことがあります。
改善案としては
* `system`プロンプトの改良
* もっと良い検索結果の渡し方

などがありそうです。もっといい方法があればぜひ教えてください！

# 参考

https://note.com/it_navi/n/nd0a916569371

https://zenn.dev/erukiti/articles/deno-chatgpt-clone-1#google-%E3%82%AB%E3%82%B9%E3%82%BF%E3%83%A0%E6%A4%9C%E7%B4%A2api