見出し画像

ChatGPTのAPIを使って、APIを学びながらオリジナルのChatGPTアプリを開発するまとめ

ChatGPTをはじめ、現代のシステム開発では「API」を上手に使えると、高度な機能を簡単に実装できます。今回は、APIってなんだ?というところから、自分のシステム(React+Next.js)にAPIを実装する手順までを一度に学べるまとめをつくってみました。

最終的に、こんなアプリを作ります

開発部分については、この記事のところまで環境構築ができている(create-next-appでreactアプリを構築)ことを前提にしています。



そもそもAPIとは?

APIとは、簡単に理解すると、システムの機能だけを外部システムに提供したものです。普通、システムには操作画面がありますよね。例えば「ChatGPT」と言えば、たいていの人はChatGPTの画面を思い浮かべます。これはChatGPTを利用するユーザーの立場ですね。

一方、サービスを開発する側の立場の人は「自分のシステムにもChatGPTの機能を組み込めたらいいのに」と考えるはずです。
そこでChatGPTには、ユーザが簡単に使えるブラウザ画面のいわゆる「ChatGPT」のほかに、開発者向けにJSON(ジェイソン)というテキストの形式でChatGPTと会話できるシステムを提供しています。これが「ChatGPTのAPI」です。

ちなみにChatGPTのAPIは有償なので、開発元のOpenAIとしては、このAPIを使った「ChatGPTを組み込んだシステム」が作られれば作られるほど売り上げが上がる、ということになります。

とりあえずAPIを使ってみる

あとでChatGPTのAPIも動かしてみたいと思いますが、お金払わないと動かせないということもあり、とりあえず無料で簡単に動かせるものを使ってAPIを体験してみます。

郵便番号検索APIという有名なAPIがあります。

サイトに例として書いてある、このURLをブラウザのアドレスバーに打ち込んでアクセスしてみてください。

https://zipcloud.ibsnet.co.jp/api/search?zipcode=7830060

高知県の住所が表示されます。多くのAPIは、僕らが普段Webページを見るときに使うURLの形式で起動できるようになっていて、その結果がJSONというテキスト形式で戻ってきます。

JSONの形式は、定義を知るよりも、じっくり観察すれば分かってくると思います。「項目名: 値」という形式の最小パーツがあって、項目の大きな単位を{ }でくくっています。[ ] という括弧は、同種の項目が複数並ぶときに使われる「配列」の記号です。

さて、APIを扱う上で大事なのは、呼び出し方の方です。
郵便番号検索APIを呼び出したURLをもう一度見てみましょう。

https://zipcloud.ibsnet.co.jp/api/search?zipcode=7830060

このURLは、「?」の前後でこのような意味を持っています。

  • https://zipcloud.ibsnet.co.jp/api/search →動かす処理を表す部分

  • zipcode=7830060 →「zipcode」というパラメータ(変数)に「7830060」という値を入れる、という指示

つまり、「7830060」が検索したい郵便番号なので、これを変えれば他の場所の住所を調べられます。

APIに送るzipcodeの値を変えると…

このように、「?」の後ろにAPIに入力する値を指示することが出来ます。どんな情報を送り込めるのかはAPIによります。郵便番号検索APIの場合、公式サイトを見ると、zipcode、callback、limitの3項目を送れると書いてありますね。ちなみに、複数の項目を送りたい場合は、項目を「&」でつなげばよいです。

https://zipcloud.ibsnet.co.jp/api/search?zipcode=0040000&limit=1

limitの値を変えて動かしてみてください。

Postmanを使う

郵便番号検索APIのように、URLの後ろに「?」をつけてパラメータを送り込むスタイルを「GET形式」といいます。ただしGETは配列など複雑な構造のデータを送りにくかったり、URLの長さの制限があったりと不便なので、別の方法でパラメータを送る「POST形式」というものがあります。どちらの形式を使うのかは、使いたいAPIのドキュメントに必ず書いてあるので、それを見て判断します。

ChatGPTはPOSTで動かすタイプのAPIです。
そして、POSTはブラウザのアドレスバーにURLを打ち込む方法では動かせず、自分でプログラムを書くか、ツールを利用する必要があります。プログラムは後で書くので、まずはツールで動かしてみましょう。

Postmanというツールを使います。無料で使えます。

まずはPostmanにユーザ登録してください。
サインインしたら、「Workspaces」のメニューからデフォルトで用意されている「My workspace」に進みます。

My Workspaceに入ったら、画面上部のタブのところにある「+」を押すと、APIを動かす画面になります。
ここに郵便番号検索APIのURLを入れて動かしてみましょう。「GET」の横にあるテキストボックスにURLを入れて「Send」ボタンを押下。

GETの場合、URLを入れると「Params」が勝手に増える。逆に、Paramsに入れると、勝手にURLにも書いてくれる

ブラウザのアドレスバーで動かしていた時と同じように応答が帰ってくることを確認します。

ChatGPTのAPIを動かす

ではいよいよPostmanを使ってChatGPTのAPIを動かします。
ChatGPTにクレジットカード登録して課金が必要です。

このページにアクセスして、ちょっと下にスクロールすると「Get Started」というボタンがあるので、それを押してChatGPTのAPI管理画面にサインインします。

サインインしたら、右上メニューの中にある「Manage account」を開き、Billingのページでクレジットカードを登録します。

※APIの利用料はこちらです。

カード登録が出来たら、API呼び出しのパスワードとなる「API Key」を取得します。
「API keys」の画面で「Create new secret key」を押して、キーを発行します。ポップアップでキーが表示されるので、それをコピーして書き留めておきます。

一度ポップアップを閉じると、キーを見ることはできません。キーは複数発行できます。

ではいよいよChatGPTのAPIを動かしましょう。
ドキュメントを参照して、動かし方を調べます。

画面右にあるこの部分がAPIの要求方法です。

curl https://api.openai.com/v1/chat/completions \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $OPENAI_API_KEY" \
  -d '{
    "model": "gpt-3.5-turbo",
    "messages": [{"role": "user", "content": "Hello!"}]
  }'

ここから、下記のことが読み取れます。

  • 要求形式はPOST(本文の方に書いてある)

  • URLは「https://api.openai.com/v1/chat/completions」

  • 「H(ヘッダー)」に、2つの項目を設定

    • Content-Type: application/json

    • Authorization: Bearer APIキー

  • 「d(ボディ)」に、JSON形式で2つの項目を設定

    • "model": "gpt-3.5-turbo"

    • "messages": [{"role": "user", "content": "Hello!"}]

これらの情報を、Postmanに打ち込んでいきます。
まずは、要求形式を「GET」→「POST」に変更して、URLを入力します。そして、「Headers」のタブを選択して、Content-TypeとAuthorizationの値を入力します。「Bearer」の後には、先ほどコピーしたAPIキーを入力します。

Postmanの設定によってはHeaderにもともといくつか項目が設定されているように表示されますが、それらは無視して2項目を追加すればよいです

次に、「Body」タブを表示→「raw」を選択すると、フリー入力のテキストボックスが表示されるので、ここにドキュメントサイトの-dの中身をコピペします。一番外側のクオーテーションは不要です。

ここまで正しく設定できていれば、「Send」でChatGPTのAPI応答が帰ってきます。

要求JSONの「Hello」の部分がChatGPTに聞いていることですね。ここを書き換えて遊んでみましょう。

これでChatGPTのAPIが動きました。ちなみに、URLにパラメーターをくっつけるGETに対して、POSTでは、JSON形式のテキストデータをAPIに送り込みます。

※正確には、JSON形式を採用しているAPIが多い、というだけで、他の形式のAPIもありますし、画像などテキストデータ以外を送るAPIもあります。そのあたりは、使いたいAPIのドキュメントを見て判断します。

自分のアプリからAPIを呼び出す

それでは、ChatGPTを自分のシステムに組み込みます。ここでは、React & Next.jsを使います。
開発環境が無い方は、まずこちらの手順に従って、動作するアプリケーションを構築しておいてください。

src/app/page.tsx の全文を下記に書き換えます。APIキーの部分だけ、ご自身のAPIキーに書き換えてください。

コード全文(page.tsx)

"use client";

const sendMessage = async () => {
  //入力用テキストボックスと、出力用テキストボックスのオブジェクトを定義
  const inputValue = document.getElementById("inputText") as HTMLInputElement;
  const outputValue = document.getElementById("outputText") as HTMLTextAreaElement;

  //入力した内容を出力用テキストボックスに追記
  outputValue.value = outputValue.value + inputValue.value + '\n\n';
  
  //API呼び出し用の情報定義
  const url = 'https://api.openai.com/v1/chat/completions';
  const method = 'POST';
  const headers = {
    'Authorization': 'Bearer APIキー',
    'Content-Type': 'application/json'
  };
  let body = JSON.stringify(
    {
      "model": "gpt-3.5-turbo",
      "messages": [
        {
          "role": "user",
          "content": inputValue.value
        }
      ]
    }
  );

  //API呼び出し、結果をJSON形式に変換
  const resMessage = await fetch(url, {method, headers, body});
  const resMessageJson = await resMessage.json();

  //応答結果を出力用テキストボックスに表示
  outputValue.value = outputValue.value + resMessageJson.choices[0].message.content + '\n\n';
}


export default function Home() {
  return (
    <main className="flex min-h-screen flex-col items-center justify-between p-24">
      <div className=" w-full text-center place-items-center">
        <h2 className="text-3xl font-bold tracking-tight text-gray-900 mb-10">My ChatGPT</h2>

        {/* 入力用テキストボックス */}
        <div className="flex mb-10">
          <input id="inputText" type="text" className="flex-grow rounded-md border-0 px-3.5 py-2 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300"/>
          <button className="ml-2 px-5 py-2 rounded-md text-white bg-blue-500 hover:bg-blue-600" onClick={sendMessage}>送信</button>
        </div>

        {/* 出力用テキストボックス */}
        <textarea id="outputText" rows={20} className="block w-full rounded-md border-0 px-3.5 py-2 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300"></textarea>
      </div>
    </main>
  )
}

起動すると、このように動作します。なお、送信ボタンを押してから、ChatGPTのAPIの応答が帰ってくるまで10秒くらいかかります。

上のテキストボックスに入力して「送信」を押すと、下のテキストボックスにChatGPTの応答結果が表示される

これでChatGPTのAPIを自分のシステムに組み込むことが出来ました。

今回の開発方法だと、ブラウザの開発者ツールを使ってChatGPTのAPIキーを見ることができてしまいます。練習のため簡易なシステム構造にしているので、一般公開する場合には注意してください。(この問題の回避方法については別途まとめます。)

【参考①】少しだけコードの解説

コードの後半、HTMLのタグを書いている部分が、画面の見た目を定義している部分です。"inputText"というIDのinputタグが入力用のテキストボックス、"outputText"というIDのtextareaタグが結果出力用のテキストボックスです。
buttonタグの「onClick」属性に「sendMessage」を指定しているので、「送信」ボタンを押したときに、コード前半のsendMessage処理が動く、という仕組みになっています。

コード後半

export default function Home() {
  return (
    <main className="flex min-h-screen flex-col items-center justify-between p-24">
      <div className=" w-full text-center place-items-center">
        <h2 className="text-3xl font-bold tracking-tight text-gray-900 mb-10">My ChatGPT</h2>

        {/* 入力用テキストボックス */}
        <div className="flex mb-10">
          <input id="inputText" type="text" className="flex-grow rounded-md border-0 px-3.5 py-2 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300"/>
          <button className="ml-2 px-5 py-2 rounded-md text-white bg-blue-500 hover:bg-blue-600" onClick={sendMessage}>送信</button>
        </div>

        {/* 出力用テキストボックス */}
        <textarea id="outputText" rows={20} className="block w-full rounded-md border-0 px-3.5 py-2 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300"></textarea>
      </div>
    </main>
  )
}

Next.jsでは標準でTailwind.cssというスタイルシートのフレームワークが組み込まれています。一つ一つのタグについている「className」の設定で、見た目を変化させることができます。
公式サイトに見た目のテンプレートがたくさんあるので、ここからコードをコピーして好きな見た目にしてください。

コード前半

const sendMessage = async () => {
  //入力用テキストボックスと、出力用テキストボックスのオブジェクトを定義
  const inputValue = document.getElementById("inputText") as HTMLInputElement;
  const outputValue = document.getElementById("outputText") as HTMLTextAreaElement;

  //入力した内容を出力用テキストボックスに追記
  outputValue.value = outputValue.value + inputValue.value + '\n\n';
  
  //API呼び出し用の情報定義
  const url = 'https://api.openai.com/v1/chat/completions';
  const method = 'POST';
  const headers = {
    'Authorization': 'Bearer APIキー',
    'Content-Type': 'application/json'
  };
  let body = JSON.stringify(
    {
      "model": "gpt-3.5-turbo",
      "messages": [
        {
          "role": "user",
          "content": inputValue.value
        }
      ]
    }
  );

  //API呼び出し、結果をJSON形式に変換
  const resMessage = await fetch(url, {method, headers, body});
  const resMessageJson = await resMessage.json();

  //応答結果を出力用テキストボックスに表示
  outputValue.value = outputValue.value + resMessageJson.choices[0].message.content + '\n\n';
}

「送信」ボタンを押したときに動作する処理です。上から順番に見ていきましょう。

//入力用テキストボックスと、出力用テキストボックスのオブジェクトを定義
const inputValue = document.getElementById("inputText") as HTMLInputElement;
const outputValue = document.getElementById("outputText") as HTMLTextAreaElement;

//入力した内容を出力用テキストボックスに追記
outputValue.value = outputValue.value + inputValue.value + '\n\n';

まずはdocument.getElementByIdという命令で、テキストボックスのオブジェクトを取得しています。TypeScriptでは「as ○○」と、なんのオブジェクトかを指示する必要があります。

オブジェクトを取得したら、「オブジェクト.value」でテキストボックスの値を取得し、「オブジェクト.value=○○」で値を書き換えます。
ここでは、出力テキストボックスの値(value)に、入力テキストボックスの値と改行(\n)2つを追記しています。

※Reactのもっと便利な画面項目操作「State」については別途まとめます。

  //API呼び出し用の情報定義
  const url = 'https://api.openai.com/v1/chat/completions';
  const method = 'POST';
  const headers = {
    'Authorization': 'Bearer APIキー',
    'Content-Type': 'application/json'
  };
  let body = JSON.stringify(
    {
      "model": "gpt-3.5-turbo",
      "messages": [
        {
          "role": "user",
          "content": inputValue.value
        }
      ]
    }
  );

  //API呼び出し、結果をJSON形式に変換
  const resMessage = await fetch(url, {method, headers, body});
  const resMessageJson = await resMessage.json();

ここがAPI呼び出し部分です。url、method、headers、body、いずれもPostmanで動かした設定をここに転記します。そしてfetchメソッドでAPIを呼び出し、最終的にresMessageJsonにAPIの応答がJSON形式で格納されます。

この処理は、url、method、headers、bodyの設定を変えれば他のAPI呼び出しにも応用できます。APIドキュメントを読む→Postmanで試す→上記のテンプレートプログラムに当てはめる、という手順を踏めば、大抵のAPIは動かせるようになります。


  //応答結果を出力用テキストボックスに表示
  outputValue.value = outputValue.value + resMessageJson.choices[0].message.content + '\n\n';

最後に、出力用テキストボックスにChatGPTの応答結果を追記します。
JSONからデータを取り出す部分は、Postmanを見ながら設定します。Postmanで動かしたとき、応答はこんな形式になっていました。

{
    "id": "chatcmpl-7Fcyo3eStnHTw3DBKNSDroDDJ8Sqv",
    "object": "chat.completion",
    "created": 1683959114,
    "model": "gpt-3.5-turbo-0301",
    "usage": {
        "prompt_tokens": 14,
        "completion_tokens": 156,
        "total_tokens": 170
    },
    "choices": [
        {
            "message": {
                "role": "assistant",
                "content": "DXは「デジタルトランスフォーメーション(Digital Transformation)」の略で、企業がデジタル技術を活用し、ビジネスや業務を効率化・改善することを指します。具体的には、人工知能やビッグデータ、クラウド技術などを活用したデジタル化の取り組みが含まれます。DXは、企業の競争力を高めるために欠かせない重要な取り組みとなっています。"
            },
            "finish_reason": "stop",
            "index": 0
        }
    ]
}

いろいろな情報がありますが、画面に表示したいのは、"content"の部分ですね。つまり、choices配列の中の、0番目の項目の、messageの中の、content項目です。我々のプログラムでは、このJSONは「resMessageJson」という名前のオブジェクトなので、

resMessageJson.choices[0].message.content

と書けばcontentの値を取り出せます。

※choicesの中が [ ] 、つまり配列になっているところがちょっと難しいところですね。

この表記方法を応用すれば、他の項目の値も取り出して表示できますし、他のAPIを動かしたときも、Postmanでの検証結果と見比べながら欲しい項目を取得できます。

【参考②】プロンプトエンジニアリング

ChatGPTへの聞き方を工夫して意図通りの回答を得る技術は「プロンプトエンジニアリング」と呼ばれています。
自分のChatGPTアプリを作る場合、「素のChatGPT」ではなく、英語の先生としてとか、悩みに応えるカウンセラーとしてとか、何か特定の役割を担わせたいと考えることが多いはずです。このようなケースで、プロンプトエンジニアリングが重要になります。

ChatGPTのAPIでは、要求のときに「システムプロンプト」を組み込むことで、ChatGPTに会話の前提条件を与えることができます。
例えば、先ほどのプログラムでbodyをこう変えると、ChatGPTはユーザからの入力に対して「DXを教えるIT講師」として回答します。

  let body = JSON.stringify(
    {
      "model": "gpt-3.5-turbo",
      "messages": [
        {
          "role": "system",
          "content": "あなたはこれからuserにDXを教えるIT講師として振舞い、userからのDXに関する質問に回答します。ITに関係ないことを質問された場合は断ってください。"
        },
        {
          "role": "user",
          "content": inputValue.value
        }
      ]
    }
  );

同じようにして「英語の先生」にすることもできますし、「カウンセラー」にすることもできます。
ただし、この方法は100%ではないです。ユーザがITと全然関係ないことを聞いたら、指示に反して何となく回答したりします。このあたりはChatGPTのいいところでもあり、扱いが難しいところでもありますね。
最終的には、ユーザが意図通りの質問をするようなUI設計が大事です。

まとめ

これでChatGPTのAPIを活用したシステムを実装できました。限られた人数で利用するデモや社内サービスであれば、ここまでの実装でも十分に役立つはずです。ただし、一般向けのサービスとして公開するためには、もう少し改良が必要ですね。

  • ChatGPTのAPIを直接呼び出すと、APIキーを盗まれる可能性があるので、オリジナルのAPIを中継してキーを隠した方が良い

  • セリフの吹き出し表示など、複雑なUIを作りこむために「State」を使ってHTML(DOM)を操作する

これらは、今度別の記事でまとめてみます。


次です。オリジナルのAPIを作成します。


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