OpenAIのAPIを使ってAPIを作る

2024/06/06に公開

OpenAIのAPIを使って架空の海洋生物を作ってくれるAPIを作りました。
https://platform.openai.com/docs/quickstart?context=node

ChatGPTのバージョンはgpt-4oを使用しました。

import OpenAI from "openai"

export const loader: LoaderFunction = async () => {
  const openai = new OpenAI({
    apiKey: ".env.localから引用してくる",
  })

async function main() {
  const completion = await openai.chat.completions.create({
    messages: [{ role: "system", content: "ここで命令をする" }],
    model: "gpt-4o",
  })

  console.log(completion.choices[0])
}

main()

上記のコードは公式に書いてあります。これを作ったファイルにコピペすれば命令に対する返答が得られます。

OpenAIのAPIを利用するにはOpenAIに登録してAPIキーを作成する必要がありますが、APIキーを入力したままGithubにあげてしまうとほかの方々にAPIキーを知られてしまいます。そのため、.env.localのファイルを作ってそこにAPIキーを保存しておくことでGithubに公開されないようにすることが大切です。

chatGPTにほしい結果を出力してもらうために、ほしい情報をJSON形式で応答してもらうように以下のように命令しました。

const message = `
  新種の海の生物を考案して以下の形式のJSONを日本語で応答してください。
  {
    名前: "string",
    分類: "string",
    外観: {
      色: "string",
      形状: "string",
      特徴: "string",
    },
    生息地: {
      海域: "string",
      深さ(m): "number",
    },
    習性: {
      食性: "string",
      行動: "string",
    },
    生態: {
      天敵: "string",
      繁殖: "string",
    },
  }

定数messageをcontentに代入し、以下を定数completionに付け加えると、命令した形どおりに返答が来ます。

response_format: { type: "json_object" }

以下をコードの最後に追加すると、JSON形式でChatGPTからのレスポンスが実際にページ上に返ってきます。

return new Response(JSON.stringify(json), {
  headers: { "Content-Type": "application/json" },
  })

その時に、プロパティは英語で帰ってきた方が後々使いやすくなるという事で、プロパティ名を英語に変換して、プロパティの中身だけ実際に帰ってきた日本語で表示されるようにします。

JSON.parse(content)

このコードはJSON形式の文字列をJavaScriptのオブジェクトに変換するものです。
contentは、プロパティが日本語のままで返ってくるJSON形式のChatGPTからの返答が入っているように定義しました。

 const json = {
    name: contentObj.名前,
    classification: contentObj.分類,
    appearance: {
      color: contentObj.外観.色,
      shape: contentObj.外観.形状,
      features: contentObj.外観.特徴,
    },
    habitat: {
      ocean_region: contentObj.生息地.食性,
      "depth(m)": contentObj.生息地.行動,
    },
    habits: {
      diet: contentObj.習性.食性,
      behavior: contentObj.習性.行動,
    },
    ecology: {
      predators: contentObj.生態.天敵,
      breed: contentObj.生態.繁殖,
    },
  }

オブジェクトのプロパティを英語名に変換して要素を正しい返答に書き換えたものが上記のコードです。

こういった返答がきます。

この時点でAPIはほとんど完成したように思われますが、稀にChatGPTからの返答でstringの指定があるのに違うものが返ってくることがあります。規定したものと同じものが毎回返答されないと、その返答をほかのものに利用したときに正しく動かなくなってしまうことがあります。それを避けるため、バリデーションをします。

バリデーションとは、返答が規約に沿って適切なものとなっているかを検証することです。

今回はvalibotを使用してバリデーションをします。
https://valibot.dev/

const contentObj = JSON.parse(content)

  const validationResult = safeParse(schema, contentObj)

  if (!validationResult.success) {
    return new Response(JSON.stringify(validationResult.issues), {
      status: 400,
      headers: { "Content-Type": "application/json" },
    })
  }

  const json = {
    name: validationResult.output.名前,
    classification: validationResult.output.分類,
    appearance: {
      color: validationResult.output.外観.色,
      shape: validationResult.output.外観.形状,
      features: validationResult.output.外観.特徴,
    },
    habitat: {
      ocean_region: validationResult.output.生息地.海域,
      depth: validationResult.output.生息地.深さ,
    },
    habits: {
      diet: validationResult.output.習性.食性,
      behavior: validationResult.output.習性.行動,
    },
    ecology: {
      predators: validationResult.output.生態.天敵,
      breed: validationResult.output.生態.繁殖,
    },
  }

  return new Response(JSON.stringify(json), {
    headers: { "Content-Type": "application/json" },
  })
}

このようにコードを書きました。上記のコードは返答が規約に従っているかを検証し、検証に失敗した場、合不具合を修正して正しいものを再びJSON形式で返答しています。

説明に必要だと感じた部分を自分なりに選択して書きましたが、足りない部分や認識が間違っていることがあればご教授願います。

Discussion