動機
巷で話題のChatGPTで利用されているモデルについて、OpenAIのAPIから叩けることを知りました。
LINEでやり取りできるようにする記事を読んで自分でもなにかやってみたいなと思ったので、なずなさんおなじみのDiscordBotへ、機能追加の形で取り組むことにしました。
作業していたブランチとそのPRは以下です。
https://github.com/na2na-p/2na2-Discord/pull/157
準備
- OpenAIのAPI Key発行
割愛します。
作業
Slash Commandの登録
const command: commandSetType = {
name: 'gpt3',
description: 'おしゃべりAIです。予告なく使えなくなる場合があります。',
options: [
{
type: 3,
name: 'prompt',
description: 'AIに入力する文章です。',
required: true,
},
],
};
// 別ファイルでcommandSetTypeを定義しています。下記のとおりです。
// export type commandSetType = Omit<ChatInputApplicationCommandData, 'name'> & {
// name: Lowercase<string>;
// };
Slash Command検知した際の処理
private async interactionHook(interaction: Readonly<ChatInputCommandInteraction>): Promise<boolean> {
if (interaction.commandName === 'gpt3') {
// 3秒以上かかるので必要
interaction.deferReply();
const prompt = interaction.options.getString('prompt', true);
// fetchGpt3{prompt: string}を受け取って文字列を返す関数
const completions = await fetchGpt3({ prompt });
console.log(completions);
interaction.editReply(completions);
return true;
} else {
return false;
}
}
fetchする関数
import ENDPOINTS from '@/constants/ENDPOINTS.js';
// configはAPIキー等を持っています。
import { config } from '@/config/index.js';
import GPT3_MODELS from '@/constants/GPT3_MODELS.js';
// https://api.openai.com/v1/completions に飛ばします
const COMPLETION_ENDPOINT = `${ENDPOINTS.OPEN_AI}/completions`;
// 0~1
const TEMPERATURE = 0.9;
// 2048 or 4000
const MAX_TOKENS = 4000;
/**
* @see https://beta.openai.com/docs/api-reference/completions/create
*/
async function fetchGpt3({ prompt }: {prompt: string}): Promise<string> {
const options = {
model: GPT3_MODELS.davinci,
max_tokens: MAX_TOKENS,
temperature: TEMPERATURE,
prompt,
};
const response = await fetch(COMPLETION_ENDPOINT, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${config.openAiApiKey}`,
},
body: JSON.stringify(options),
});
const results = await response.json();
try {
// results.choicesの中からランダムに返す
// エラー(tokensが長過ぎる等)の場合にresults.choicesは帰ってこないのでtry-catchしてます
const randomIndex = Math.floor(Math.random() * results.choices.length);
return results.choices[randomIndex].text;
} catch (e) {
return results.message;
}
}
export default fetchGpt3;
まとめ
かなーり雑ですが、フェッチする箇所だけ取り出して使いまわしが効くような書き方をしたつもりです。おもちゃとしての性能が非常に高く、作業時間は少しでしたが満足です。