このチュートリアルでは、OpenAI ↗ API と Cloudflare R2 を使い、ファインチューンモデル ↗ を作成します。
OpenAI API のこの機能では、独自の指示と回答例をもとに、OpenAI の各種大規模言語モデルからカスタムモデルを導出できます。指示と回答例はドキュメントに書き、これをファインチューンドキュメントと呼びます。このドキュメントは R2 に保存し、新しいファインチューンモデルを作るときに OpenAI API へ動的に渡します。
この機能を使うには、次の作業を行います。
- ファインチューンドキュメントを R2 にアップロードします。
- R2 のファイルを読み取り、OpenAI にアップロードします。
- そのドキュメントをもとに、新しいファインチューンモデルを作成します。
完成したアプリケーションのコードは、このチュートリアルの GitHub リポジトリ ↗ を参照してください。
始める前に、次を用意します。
- R2 を利用できる Cloudflare アカウント。Cloudflare アカウントがない場合は、先に 登録 ↗ してください。その後、Cloudflare ダッシュボードから R2 を購入します。
- OpenAI API キー。
- JSON Lines ↗ 形式のファインチューンドキュメント。ソースコード内の サンプルドキュメント ↗ を使えます。
まず、c3 CLI で新しい Cloudflare Workers プロジェクトを作成します。
npm create cloudflare@latest -- finetune-chatgpt-modelyarn create cloudflare finetune-chatgpt-modelpnpm create cloudflare@latest finetune-chatgpt-modelセットアップでは、次のオプションを選びます。
- What would you like to start with? では、
Hello World exampleを選びます。 - Which template would you like to use? では、
Worker onlyを選びます。 - Which language do you want to use? では、
TypeScriptを選びます。 - Do you want to use git for version control? では、
Yesを選びます。 - Do you want to deploy your application? では、
Noを選びます(デプロイ前にいくつか変更します)。
上記の選択肢で、"Hello World" の TypeScript プロジェクトが作成されます。
作成したディレクトリへ移動します。
cd finetune-chatgpt-model次に、ファインチューンドキュメントを R2 にアップロードします。R2 はキーバリューストアで、Workers アプリケーション内からファイルを保存・取得できます。新しい R2 バケットの作成には Wrangler を使います。
新しい R2 バケットを作成するには、wrangler r2 bucket create コマンドを使います。Cloudflare アカウントでログインしていることを確認してください。Wrangler でログインしていない場合は、wrangler login コマンドを使います。
npx wrangler r2 bucket create <BUCKET_NAME><BUCKET_NAME> を希望するバケット名に置き換えます。バケット名は小文字のみで、使える記号はハイフンだけです。
次に、wrangler r2 object put コマンドでファイルをアップロードします。
npx wrangler r2 object put <PATH> -f <FILE_NAME><PATH> は、アップロードするファイルのバケット名とファイルパスを合わせたものです。例: fine-tune-ai/finetune.jsonl(fine-tune-ai がバケット名)。<FILE_NAME> は、ローカルにあるファインチューンドキュメントのファイル名に置き換えます。
バインディングは、Worker が R2 バケットなどの外部リソースとやり取りする仕組みです。
R2 バケットを Worker にバインドするには、Wrangler ファイルに次を追加します。binding プロパティは、有効な JavaScript の変数識別子に更新します。<YOUR_BUCKET_NAME> は、手順 2 で作成したバケット名に置き換えます。
{
"r2_buckets": [
{
"binding": "MY_BUCKET", // <~ valid JavaScript variable name
"bucket_name": "<YOUR_BUCKET_NAME>"
}
]
}[[r2_buckets]]
binding = "MY_BUCKET"
bucket_name = "<YOUR_BUCKET_NAME>"Cloudflare Workers アプリケーション向けの軽量フレームワーク Hono ↗ を使います。Hono は、ルートとミドルウェア関数を定義するインターフェースを提供します。プロジェクトディレクトリ内で次のコマンドを実行し、Hono をインストールします。
npm i honoyarn add honopnpm add honobun add honoOpenAI Node API ライブラリ ↗ もインストールします。このライブラリを使うと、Node.js プロジェクトから OpenAI REST API にアクセスしやすくなります。次のコマンドでインストールします。
npm i openaiyarn add openaipnpm add openaibun add openai次に、src/index.ts を開き、デフォルトのコードを次のコードに置き換えます。<MY_BUCKET> は、Wrangler ファイルで設定したバインディング名に置き換えます。
import { Context, Hono } from "hono";
import OpenAI from "openai";
type Bindings = {
<MY_BUCKET>: R2Bucket
OPENAI_API_KEY: string
}
type Variables = {
openai: OpenAI
}
const app = new Hono<{ Bindings: Bindings, Variables: Variables }>()
app.use('*', async (c, next) => {
const openai = new OpenAI({
apiKey: c.env.OPENAI_API_KEY,
})
c.set("openai", openai)
await next()
})
app.onError((err, c) => {
return c.text(err.message, 500)
})
export default app;上のコードでは、まず必要なパッケージをインポートし、型を定義します。次に、app を新しい Hono インスタンスとして初期化します。use ミドルウェア関数で、すべてのルートのコンテキストに OpenAI API クライアントを追加します。このミドルウェアにより、どのルートハンドラーからでもクライアントにアクセスできます。onError() は、エラーをレスポンスとして返すエラーハンドラーを定義します。
この節では、ファイルアップロードを担当するルートと関数を定義します。
createFile では、Worker が R2 からファイルを読み取り、File オブジェクトに変換します。その後、OpenAI API でファイルをアップロードし、応答を返します。
GET /files ルートは、クエリパラメーター file 付きの GET リクエストを受け取ります。file は、R2 にアップロード済みのファインチューンドキュメントのファイル名です。関数は createFile を使ってアップロード処理を進めます。
<MY_BUCKET> は、Wrangler ファイルで設定したバインディング名に置き換えます。
// New import added at beginning of file
import { toFile } from 'openai/uploads'
const createFile = async (c: Context, r2Object: R2ObjectBody) => {
const openai: OpenAI = c.get("openai")
const blob = await r2Object.blob()
const file = await toFile(blob, r2Object.key)
const uploadedFile = await openai.files.create({
file,
purpose: "fine-tune",
})
return uploadedFile
}
app.get('/files', async c => {
const fileQueryParam = c.req.query("file")
if (!fileQueryParam) return c.text("Missing file query param", 400)
const file = await c.env.<MY_BUCKET>.get(fileQueryParam)
if (!file) return c.text("Couldn't find file", 400)
const uploadedFile = await createFile(c, file)
return c.json(uploadedFile)
})この節では、GET /models ルートと createModel 関数を追加します。createModel は詳細を指定し、OpenAI でファインチューン処理を開始します。ルートは、新しいファインチューンモデル作成のリクエストを処理します。
const createModel = async (c: Context, fileId: string) => {
const openai: OpenAI = c.get("openai");
const body = {
training_file: fileId,
model: "gpt-4o-mini",
};
return openai.fineTuning.jobs.create(body);
};
app.get("/models", async (c) => {
const fileId = c.req.query("file_id");
if (!fileId) return c.text("Missing file ID query param", 400);
const model = await createModel(c, fileId);
return c.json(model);
});この節では、GET /jobs ルートと対応する getJobs 関数を説明します。関数は OpenAI API とやり取りし、すべてのファインチューンジョブの一覧を取得します。ルートは、この情報を取り出すインターフェースを提供します。
const getJobs = async (c: Context) => {
const openai: OpenAI = c.get("openai");
const resp = await openai.fineTuning.jobs.list();
return resp.data;
};
app.get("/jobs", async (c) => {
const jobs = await getJobs(c);
return c.json(jobs);
});Worker アプリケーションを作成し、必要な関数を追加したら、アプリケーションをデプロイします。
デプロイ前に、アプリケーションの OPENAI_API_KEY シークレット を設定する必要があります。wrangler secret put コマンドで設定します。
npx wrangler secret put OPENAI_API_KEYWorker アプリケーションを Cloudflare のグローバルネットワークにデプロイするには、次の手順を行います。
- Worker プロジェクトのディレクトリにいることを確認し、
wrangler deployコマンドを実行します。
npx wrangler deploy-
Wrangler がコードをパッケージ化し、アップロードします。
-
デプロイが完了すると、Wrangler が Worker の URL を表示します。
アプリケーションを使うには、先にアップロードしたファイル名と一致する file クエリパラメーターを付けて /files にリクエストし、新しいファインチューンジョブを作成します。
curl https://your-worker-url.com/files?file=finetune.jsonlファイルのアップロード後、file_id クエリパラメーターを付けて /models にリクエストします。この値は、/files ルートが JSON で返す id と一致させてください。
curl https://your-worker-url.com/models?file_id=file-abc123最後に /jobs を開き、OpenAI 上のファインチューンジョブの状態を確認します。ジョブが完了すると fine_tuned_model の値が表示され、ファインチューンモデルが作成されたことがわかります。
ファインチューンモデルを使うには、OpenAI Playground ↗ を開きます。画面左上のドロップダウンから、ファインチューンモデルを選びます。
OpenAI の chat completions エンドポイントへの API リクエストでも使えます。次のコード例を参照してください。
openai.chat.completions.create({
messages: [{ role: "system", content: "You are a helpful assistant." }],
model: "ft:gpt-4o-mini:my-org:custom_suffix:id",
});Workers でさらに作るには、チュートリアル を参照してください。
質問がある場合、支援が必要な場合、プロジェクトを共有したい場合は、Discord ↗ の Cloudflare Developer コミュニティに参加して、ほかの開発者や Cloudflare チームとつながれます。