フロントエンド

Vue.jsとChatGPT APIでずんだもんに回答を喋ってもらう

reisuta

Webエンジニア | 20代中盤 | 大学時代はGmailすら知らないIT音痴でプログラミングとは無縁の生活を送る → 独学でプログラミングを学ぶ → Web系受託開発企業にエンジニアとして就職 → Web系自社サービス企業に転職 | 実務未経験の頃からVimを愛好しており、仕事でもプライベートでも開発はVimとTmuxを使っているので、VSCodeに疎いのが最近の悩み。何だかんだでやっぱりRubyが好き。

ChatGPTとは?

ChatGPTとは何でしょうか?
本人に聞いてみました。

おお、模範解答(?)ですね。

このように、ChatGPTとは、
AIの一種で、人間が質問したものに対して、
応答してくれます。

その精度や、回答が人間味があることで
話題になりましたね。

https://chat.openai.com

実際に色々試してみると、
面白いと思います。

「夏目漱石風に好きな人に告白してみて」とか、
「東京23区で一番勝ち組なのはどこ?」みたいな
アホみたいな質問にも回答してくれます。

「エッセイを作成して」とか、
しりとりの相手もしてくれます。

すごいですね笑

Vue.jsからChatGPT APIを叩いて話しかけた回答内容をずんだもんに喋ってもらう

※下記のコードは、Vue.jsの概要で実装したコードをもとに実装しているので、
まずは、こちらの記事でVue.jsのセットアップを済ませてからだと、
同じようにハンズオンできます。

まずは、Vue.jsのルーティングに、
ChatGPTにアクセスするページを追加します。

{
      path: '/chat',
      name: 'chat',
      component: () => import('../components/Chat.vue')
}

axiosをインストールします。

docker compose exec front yarn add axios

肝心のChat.vueのファイルの、
コードの全体像は下記のようになります。

<template>
  <v-row>
    <v-col cols="4">
      <v-card
          class="mx-auto"
          max-width="344"
          variant="outlined"
        >
        <v-card-item>
          <div class="text-overline mb-1">
            入力したものをずんだもんに話してもらう
            <v-text-field v-model="val"></v-text-field>
          </div>
        </v-card-item>

        <v-card-actions>
          <v-btn variant="outlined" v-on:click="buttonClicked">
            送信
          </v-btn>
        </v-card-actions>
      </v-card>
      <audio class="audio"></audio>
    </v-col>

    <v-col cols="4">
      <v-card
          class="mx-auto"
          max-width="344"
          variant="outlined"
        >
        <v-card-item>
          <div class="text-overline mb-1">
            入力したものをChatGPTに飛ばす
            <v-text-field v-model="chatgpt"></v-text-field>
          </div>
        </v-card-item>

        <v-card-actions>
          <v-btn variant="outlined" v-on:click="chatGptClick">
            ChatGPTへ送信
          </v-btn>
        </v-card-actions>
      </v-card>
      <audio class="audio"></audio>
    </v-col>

    <v-col cols="4">
      <v-card
          class="mx-auto"
          max-width="344"
          variant="outlined"
        >
        <v-card-item>
            ずんだもんに話しかける
        </v-card-item>

        <v-card-actions>
          <v-btn variant="outlined" v-on:click="start">
            開始
          </v-btn>
          <v-btn variant="outlined" v-on:click="end">
            終了
          </v-btn>
        </v-card-actions>
      </v-card>
      <audio class="audio"></audio>
    </v-col>
  </v-row>
  <v-row>
    <v-col cols="12">
      <div class="output"></div>
    </v-col>
  </v-row>
</template>

<script setup lang="ts">
import { ref } from 'vue';
import axios from 'axios'

const buttonClicked = async () => {
  await createAudio(val.value);
}

const chatGptClick = async () => {
  const responseText = await requestChatAPI(chatgpt.value);
  await createAudio(responseText)
  const output = document.querySelector(".output");
  output.textContent = responseText;
}

const val = ref('initial')
const chatgpt = ref('chatGPT initial')

async function createAudio(text) {
  const audio = document.querySelector(".audio");
  audio.src = await getAudioURL(text);
  audio.play();
}

async function getAudioURL(text) {
  const query = await getQuery(text);
  const response = await axios.post(
    "http://localhost:50021/synthesis?speaker=1",
    query,
    { responseType: "blob" }
  );
  return URL.createObjectURL(response.data);
}

async function getQuery(text) {
  const response = await axios.post(
    `http://localhost:50021/audio_query?speaker=1&text=${text}`
  );
  return response.data;
}

const api_key = "各自のキー";

async function requestChatAPI(text) {
const headers = {
  "Content-Type": "application/json",
  Authorization: `Bearer ${api_key}`,
};

const messages = [
  {
    role: "user",
    content: text,
  },
];

const payload = {
  model: "gpt-3.5-turbo",
  max_tokens: 128,
  messages: messages,
};

const response = await axios.post(
  "https://api.openai.com/v1/chat/completions",
  payload,
  {
    headers: headers,
  }
);
console.log('ChatGPT送信')
return response.data.choices[0].message.content;
}


const recognition = new webkitSpeechRecognition();
const language = "ja";

recognition.lang = language;
recognition.interimResults = true;
recognition.continuous = true;
recognition.onresult = handleResult;

async function handleResult(event) {
  let finalTranscript = '';
  
  for (let i = event.resultIndex; i < event.results.length; i++) {
    const transcript = event.results[i][0].transcript;
    console.log(transcript);

    if (event.results[i].isFinal) {
      finalTranscript += transcript;
      const responseText = await requestChatAPI(finalTranscript);
      await createAudio(responseText);
      const output = document.querySelector(".output");
      output.textContent = responseText;
    }
  }
}

const start = () => {
  recognition.start();
};
const stop = () => {
  recognition.stop();
};
</script>

こんな感じになります。

https://platform.openai.com

上記のコードは、音声認識をして、
その内容をChatGPTに渡して、
回答結果をずんだもんに話してもらうという流れになっています。

一つずつ解説します。

音声認識をしている部分

まず、音声認識の部分ですが、
下記のコードです。

const recognition = new webkitSpeechRecognition();
const language = "ja";

recognition.lang = language;
recognition.interimResults = true;
recognition.continuous = true;
recognition.onresult = handleResult;

async function handleResult(event) {
  let finalTranscript = '';
  
  for (let i = event.resultIndex; i < event.results.length; i++) {
    const transcript = event.results[i][0].transcript;
    console.log(transcript);

    if (event.results[i].isFinal) {
      finalTranscript += transcript;
      const responseText = await requestChatAPI(finalTranscript);
      await createAudio(responseText);
      const output = document.querySelector(".output");
      output.textContent = responseText;
    }
  }
}

このコードは、Web Speech API を使用して音声認識を行い、認識されたテキストを使用してチャットボットとの対話を実現するためのものです。

最初に webkitSpeechRecognition() を使用して、新しい SpeechRecognition オブジェクトを作成します。

language 変数を定義し、認識される言語を指定します。この場合は、"ja" (日本語) が指定されています。

recognition.lang を使用して、認識される言語を設定します。

recognition.interimResults が true に設定されており、認識中に中間結果を取得することができます。

recognition.continuous が true に設定されており、ユーザーが話し続けている間に音声認識が継続します。

recognition.onresult には、認識結果が利用可能になったときに呼び出されるコールバック関数 handleResult が設定されています。

handleResult 関数は、event オブジェクトを引数に受け取り、認識されたテキストを使用してチャットボットとの対話を実現します。

音声認識の結果は event.results 配列に格納されており、for ループを使用してその中から認識されたテキストを取得しています。

event.results[i].isFinal が true の場合、認識が終了したことを示し、finalTranscript 変数に認識されたテキストが追加されます。

requestChatAPI() 関数を使用して、finalTranscript のテキストをチャットボット API に送信し、応答を受信します。

createAudio() 関数を使用して、チャットボットの応答を音声合成し、ユーザーに再生します。

最後に、output 変数を使用して、チャットボットの応答を表示します。

ChatGPTさん

せっかくなので、ChatGPTに処理の内容について説明してもらいました。

ずんだもんと連携している部分

ずんだもんは、VOICEVOXというアプリで使用できるキャラクターで、
可愛い声が特徴的です。

https://voicevox.hiroshiba.jp/

https://github.com/VOICEVOX/voicevox_engine

アプリをインストールして、
起動しておけば、localhost:50021にリッスンして、
リクエストを送れるようになります。

該当コードは、

async function createAudio(text) {
  const audio = document.querySelector(".audio");
  audio.src = await getAudioURL(text);
  audio.play();
}

async function getAudioURL(text) {
  const query = await getQuery(text);
  const response = await axios.post(
    "http://localhost:50021/synthesis?speaker=1",
    query,
    { responseType: "blob" }
  );
  return URL.createObjectURL(response.data);
}

async function getQuery(text) {
  const response = await axios.post(
    `http://localhost:50021/audio_query?speaker=1&text=${text}`
  );
  return response.data;
}

このコードは、与えられたテキストを使用して音声合成して再生する関数 createAudio と、テキストを使用して音声データの URL を取得する関数 getAudioURL 、テキストを使用してチャットボット API に送信するためのクエリを作成する関数 getQuery から構成されています。

createAudio 関数は、与えられたテキストを使用して音声合成して再生する関数です。まず、 document.querySelector を使用して、 HTML 上の audio タグを取得します。次に、 getAudioURL 関数を使用して、与えられたテキストを使用して音声データの URL を取得します。最後に、 audio.play() を呼び出して音声を再生します。

getAudioURL 関数は、与えられたテキストを使用して音声データの URL を取得する関数です。まず、 getQuery 関数を使用して、与えられたテキストを使用してチャットボット API に送信するためのクエリを取得します。次に、axios.post を使用して、API に対してクエリを送信し、Blob データを取得します。最後に、URL.createObjectURL を使用して Blob データから URL を作成し、URL を返します。

getQuery 関数は、与えられたテキストを使用して、チャットボット API に送信するためのクエリを作成する関数です。axios.post を使用して、API に対してテキストを送信し、応答を受信します。そして、API から受け取った応答データを返します。

このコードは、非同期処理を使用して、API からの応答を待ってから次の処理を実行することができます。また、 URL.createObjectURL を使用して Blob データから URL を作成することで、オーディオ再生のための URL を直接取得することができます。

ChatGPTさん

基本正しいのですが、
getQuery関数は、VOICEBOXのAPIに送信するためのクエリーです
(ChatGPTさん、チャットボットAPIとは何者でしょうか?)

ChatGPT APIを叩いている部分

さて、いよいよChatGPT APIを叩いている部分ですが、

async function requestChatAPI(text) {
const headers = {
  "Content-Type": "application/json",
  Authorization: `Bearer ${api_key}`,
};

const messages = [
  {
    role: "user",
    content: text,
  },
];

const payload = {
  model: "gpt-3.5-turbo",
  max_tokens: 128,
  messages: messages,
};

const response = await axios.post(
  "https://api.openai.com/v1/chat/completions",
  payload,
  {
    headers: headers,
  }
);
console.log('ChatGPT送信')
return response.data.choices[0].message.content;
}

このコードは、OpenAIのChat APIを使用して、テキストを受け取り、AIが生成したテキストを返すための関数です。

まず、Authorizationヘッダーを設定して、APIキーを含めます。次に、リクエストの本文として、ユーザーが入力したテキストを含むメッセージオブジェクトを作成します。そして、APIの使用モデル、最大トークン数、そしてメッセージオブジェクトを含むペイロードを定義します。

最後に、Axiosを使用して、OpenAIのAPIエンドポイントにPOSTリクエストを送信し、レスポンスを受け取ります。レスポンスには、AIによって生成されたテキストが含まれています。また、送信が成功したことを示すために、"ChatGPT送信"という文字列をコンソールに出力します。最後に、AIが生成したテキストを返します。

ChatGPTさん

ありがとうございます!ChatGPTさん!

  • この記事を書いた人
  • 最新記事

reisuta

Webエンジニア | 20代中盤 | 大学時代はGmailすら知らないIT音痴でプログラミングとは無縁の生活を送る → 独学でプログラミングを学ぶ → Web系受託開発企業にエンジニアとして就職 → Web系自社サービス企業に転職 | 実務未経験の頃からVimを愛好しており、仕事でもプライベートでも開発はVimとTmuxを使っているので、VSCodeに疎いのが最近の悩み。何だかんだでやっぱりRubyが好き。

おすすめ記事はこちら

Vim/Neovimプラグイン 1

プラグインをどれだけ入れるかは、その人の思想なども関係するので、一概にこれがいいというのはないかもしれません。 プラグインを全く入れない人もいれば、100個以上入れる人もいます。 ただそれでも、これだ ...

VimとNeovimの比較 2

本記事では、VimとNeovimの違いについて、解説します。 VimとNeovimの違いについては、普段頻繁にVimなどを使う方でなければ、正直、あまり気にしなくてもいいかなと思います。 ただ、Vim ...

Ruby変数やすべてがオブジェクトについて 3

本記事は、Rubyの基礎文法である、変数や真偽値、論理演算子に触れると同時に、「すべてがオブジェクト」というRubyの特徴的な思想についても解説します。 この思想は、Rubyの文法の根幹になっているの ...

4

エンジニアにおすすめの技術書 書籍学習は、エンジニアの嗜みみたいなところがありますが、 良書というものは、意外とそこまで多くもありません。 そこで本記事では「技術書マニアの筆者が厳選した技術書20選」 ...

5

エンジニアになるには? プログラミングは、専門性が高く自分一人で勉強するのが大変に感じることも多いですよね。 そこで本記事では「おすすめのプログラミングスクール5選」を特徴と、現役エンジニア目線で優れ ...

-フロントエンド