コンテンツにスキップ

Workers AIとStripeを使用してeコマースサイトで製品を推奨する

Last reviewed: 3 months ago

Developer Spotlight community contribution

Written by: 岡本秀隆

Profile: LinkedIn

eコマースおよびメディアサイトは、収益性を向上させるために平均取引額を増やすことに取り組んでいます。平均取引額を増やすための戦略の一つは「クロスセル」であり、関連製品を推奨することを含みます。Cloudflareは、ユーザーが閲覧またはリクエストしている製品に関連するデータを取得するためのメカニズムを構築するために設計されたさまざまな製品を提供しています。このチュートリアルでは、関連製品の検索および製品推奨のためのAPIを作成することによって、クロスセルに必要な機能を開発する体験をします。

目標

このワークショップでは、3つのREST APIを開発します。

  1. 特定の製品に高度に関連する情報を検索するAPI。
  2. ユーザーの問い合わせに応じて製品を提案するAPI。
  3. 外部eコマースアプリケーションと製品情報を同期するWebhook API。

これらのAPIを開発することで、eコマースサイトのクロスセルおよび推奨機能を構築するために必要なリソースについて学びます。

次のCloudflare製品の使用方法も学びます:

Before you start

All of the tutorials assume you have already completed the Get started guide, which gets you set up with a Cloudflare Workers account, C3, and Wrangler.

  1. Sign up for a Cloudflare account.
  2. Install npm.
  3. Install Node.js.

Node.js version manager

Use a Node version manager like Volta or nvm to avoid permission issues and change Node.js versions. Wrangler, discussed later in this guide, requires a Node version of 16.17.0 or later.

前提条件

このチュートリアルでは、いくつかのCloudflare製品を使用します。これらの製品の中には無料プランがあるものもあれば、最小限の料金が発生するものもあります。以下の請求情報を注意深く確認してください。

1. 新しいWorkerプロジェクトを作成する

まず、Cloudflare Workersプロジェクトを作成しましょう。

C3 (create-cloudflare-cli) is a command-line tool designed to help you set up and deploy new applications to Cloudflare. In addition to speed, it leverages officially developed templates for Workers and framework-specific setup guides to ensure each new application that you set up follows Cloudflare and any third-party best practices for deployment on the Cloudflare network.

複数のAPIを効率的に作成および管理するために、Honoを使用します。Honoは、Cloudflareの開発者アドボケートによってリリースされたオープンソースのアプリケーションフレームワークです。軽量で、複数のAPIパスを作成し、リクエストとレスポンスの処理を効率的に行うことができます。 コマンドラインインターフェース(CLI)を開き、次のコマンドを実行します:

Terminal window
npm create cloudflare@latest cross-sell-api -- --framework hono

これが初めてC3コマンドを実行する場合、インストールするかどうか尋ねられます。インストールするパッケージ名がcreate-cloudflareであることを確認し、yと答えてください。

Terminal window
次のパッケージをインストールする必要があります:
create-cloudflare@latest
続行してもよろしいですか? (y)

セットアップ中に、プロジェクトのソースコードをGitで管理するかどうか尋ねられます。作業を記録し、変更を元に戻すのに役立つため、Yesと答えることをお勧めします。Noを選択することもできますが、チュートリアルの進行には影響しません。

Terminal window
Gitでバージョン管理を使用しますか?
  Yes / No

最後に、アプリケーションをCloudflareアカウントにデプロイするかどうか尋ねられます。今のところ、Noを選択してローカルで開発を開始します。

Terminal window
Cloudflareでデプロイ ステップ 3/3
アプリケーションをデプロイしますか?
  Yes / No

以下のようなメッセージが表示されれば、プロジェクトのセットアップは完了です。お好みのIDEでcross-sell-apiディレクトリを開いて開発を開始できます。

Terminal window
アプリケーションが作成されました npm run deployでアプリケーションをデプロイ
新しいディレクトリに移動 cd cross-sell-api
開発サーバーを実行 npm run dev
アプリケーションをデプロイ npm run deploy
ドキュメントを読む https://developers.cloudflare.com/workers
行き詰まりましたか? https://discord.cloudflare.comに参加してください
またお会いしましょう!

Cloudflare Workersアプリケーションは、ローカル環境で開発およびテストできます。CLIで新しく作成したWorkersにディレクトリを変更し、npx wrangler devを実行してアプリケーションを開始します。Wranglerを使用すると、アプリケーションが開始され、localhostで始まるURLが表示されます。

Terminal window
⛅️ wrangler 3.60.1
-------------------
ローカルサーバーを開始しています...
[wrangler:inf] http://localhost:8787で準備完了
╭───────────────────────────────────────────────────────────────────────────────────────────────────────────╮
[b] ブラウザを開く, [d] Devtoolsを開く, [l] ローカルモードをオフにする, [c] コンソールをクリア, [x] 終了する │
╰───────────────────────────────────────────────────────────────────────────────────────────────────────────╯

curlコマンドを使用してAPIにリクエストを送信できます。Hello Hono!というテキストが表示されれば、APIは正しく動作しています。

Terminal window
curl http://localhost:8787
Hello Hono!

これまでに、Cloudflare Workerプロジェクトの作成方法と、Cloudflareでの開発を効率化するためのC3コマンドやHonoフレームワークなどのツールとオープンソースプロジェクトを紹介しました。これらの機能を活用することで、Cloudflare Workers上でのアプリケーション開発がよりスムーズになります。

2. 製品情報をインポートするAPIを作成する

次に、クロスセルシステムで使用される3つのAPIの開発を開始します。まず、既存のeコマースアプリケーションと製品情報を同期するAPIを作成します。この例では、Stripeでの製品登録がクロスセルシステムと同期されるシステムを設定します。

このAPIは、Stripeのような外部サービスから送信された製品情報をWebhookイベントとして受信します。その後、検索目的のために必要な情報を抽出し、関連製品検索用のデータベースに保存します。ベクター検索が使用されるため、Cloudflare Workers AIが提供するEmbeddingモデルを使用して文字列をベクターデータに変換するプロセスも実装する必要があります。

プロセスの流れは以下のように示されます:

sequenceDiagram
    participant Stripe

    box Cloudflare
        participant CF_Workers
        participant CF_Workers_AI
        participant CF_Vectorize
    end

    Stripe->>CF_Workers: 製品登録イベントを送信
    CF_Workers->>CF_Workers_AI: 製品情報のベクタ化をリクエスト
    CF_Workers_AI->>CF_Workers: ベクターデータ結果を返す
    CF_Workers->>CF_Vectorize: ベクターデータを保存

ステップバイステップで実装を開始しましょう。

Workers AIとVectorizeをWorkerにバインドする

このAPIでは、Workers AIとVectorizeの使用が必要です。Workerからこれらのリソースを使用するには、まずリソースを作成し、次にそれらをWorkerにバインドする必要があります。まず、wrangler vectorize create {index_name} --dimensions={number_of_dimensions} --metric={similarity_metric}コマンドを使用して、WranglerでVectorizeインデックスを作成します。dimensionsmetricの値は、データのベクタ化に使用するテキスト埋め込みモデルの種類によって異なります。たとえば、bge-large-en-v1.5モデルを使用している場合、コマンドは次のようになります:

Terminal window
npx wrangler vectorize create stripe-products --dimensions=1024 --metric=cosine

このコマンドが正常に実行されると、以下のようなメッセージが表示されます。これは、VectorizeインデックスをWorkerアプリケーションにバインドするためにwrangler.tomlに追加する必要がある項目を提供します。[[vectorize]]で始まる3行をコピーしてください。

Terminal window
新しいVectorizeインデックス「stripe-products」を正常に作成しました
📋 Workerからクエリを開始するには、次のバインディング構成を「wrangler.toml」に追加してください:
[[vectorize]]
binding = "VECTORIZE_INDEX"
index_name = "stripe-products"

作成したVectorizeインデックスをWorkerから使用するために、バインディングを追加しましょう。wrangler.tomlを開き、コピーした行を追加します。

name = "cross-sell-api"
main = "src/index.ts"
compatibility_date = "2024-06-05"
[[vectorize]]
binding = "VECTORIZE_INDEX"
index_name = "stripe-products"

さらに、wrangler.tomlにWorkers AIを使用するための設定を追加します。

name = "cross-sell-api"
main = "src/index.ts"
compatibility_date = "2024-06-05"
[[vectorize]]
binding = "VECTORIZE_INDEX"
index_name = "stripe-products"
[ai]
binding = "AI" # Workerでenv.AIとして利用可能

アプリケーションからバインドされたリソースを扱う際には、TypeScriptの型定義を生成して、より安全に開発できます。npm run cf-typegenコマンドを実行します。このコマンドはworker-configuration.d.tsファイルを更新し、VectorizeとWorkers AIの両方を型安全に使用できるようにします。

Terminal window
npm run cf-typegen
> cf-typegen
> wrangler types --env-interface CloudflareBindings
⛅️ wrangler 3.60.1
-------------------
interface CloudflareBindings {
VECTORIZE_INDEX: VectorizeIndex;
AI: Ai;
}

これらの変更を保存すると、関連するリソースとAPIがWorkersアプリケーションで使用可能になります。これらのプロパティにはenvからアクセスできます。この例では、次のように使用できます:

app.get("/", (c) => {
c.env.AI; // Workers AI SDK
c.env.VECTORIZE_INDEX; // Vectorize SDK
return c.text("Hello Hono!");
});

最後に、--remoteオプションを付けてnpx wrangler devコマンドを再実行します。これは、Vectorizeインデックスがローカルモードではサポートされていないため必要です。Vectorize bindings are not currently supported in local mode. Please use --remote if you are working with them.というメッセージが表示された場合は、--remoteオプションを追加してコマンドを再実行してください。

Terminal window
npx wrangler dev --remote

製品登録イベントを処理するWebhook APIを作成する

POSTリクエストを使用して製品登録および情報に関する通知を受け取ることができます。POSTリクエストを受け入れるAPIを作成しましょう。src/index.tsファイルを開き、次のコードを追加します:

app.post("/webhook", async (c) => {
const body = await c.req.json();
if (body.type === "product.created") {
const product = body.data.object;
console.log(JSON.stringify(product, null, 2));
}
return c.text("ok", 200);
});

このコードは、/webhookエンドポイントへのPOSTリクエストを処理するAPIを実装します。StripeのWebhookイベントによって送信されるデータは、リクエストボディにJSON形式で含まれています。したがって、c.req.json()を使用してデータを抽出します。Stripeが送信できるWebhookイベントの種類は複数あるため、typeが新しい製品が追加されたことを示す場合のみ処理するように条件を追加しました。

プロジェクトにStripeのAPIキーを追加する

Webhook APIを開発する際には、未承認のソースからのリクエストを拒否することを確認する必要があります。未承認のAPIリクエストが意図しない動作や運用上の混乱を引き起こさないように、APIリクエストのソースを検証するメカニズムが必要です。Stripeと統合する際には、Webhook検証に使用される署名シークレットを生成することでAPIを保護できます。

  1. Stripeのドキュメントを参照して、テスト環境用のシークレットAPIキーを取得します。
  2. 取得したAPIキーを.dev.varsファイルに保存します。
STRIPE_SECRET_API_KEY=sk_test_XXXX
  1. ガイドに従ってStripe CLIをインストールします。
  2. 次のStripe CLIコマンドを使用して、StripeからローカルアプリケーションにWebhookイベントを転送します。
Terminal window
stripe listen --forward-to http://localhost:8787/webhook --events product.created
  1. Stripe CLIコマンドの出力から、whsec_で始まる署名シークレットをコピーします。
> 準備完了!Stripe APIバージョン[2024-06-10]を使用しています。Webhook署名シークレットは
whsec_xxxxxx (^Cで終了)
  1. 取得した署名シークレットを.dev.varsファイルに保存します。
STRIPE_WEBHOOK_SECRET=whsec_xxxxxx
  1. npm run cf-typegenを実行して、worker-configuration.d.tsの型定義を更新します。
  2. npm install stripeを実行して、Stripe SDKをアプリケーションに追加します。
  3. APIキーをアプリケーションにインポートするために、npm run dev -- --remoteコマンドを再起動します。

最後に、Webhook APIがあなたのStripeアカウント以外のソースから使用されないようにするために、src/index.tsのソースコードを次のように修正します。

import { Hono } from "hono";
import { env } from "hono/adapter";
import Stripe from "stripe";
type Bindings = {
[key in keyof CloudflareBindings]: CloudflareBindings[key];
};
const app = new Hono<{
Bindings: Bindings;
Variables: {
stripe: Stripe;
};
}>();
/**
* Stripe SDKクライアントを初期化します
* このSDKは、各APIルートで初期化することなく使用できます。
* 次の例で取得できます:
* ```
* const stripe = c.get('stripe')
* ```
*/
app.use("*", async (c, next) => {
const { STRIPE_SECRET_API_KEY } = env(c);
const stripe = new Stripe(STRIPE_SECRET_API_KEY);
c.set("stripe", stripe);
await next();
});
app.post("/webhook", async (c) => {
const { STRIPE_WEBHOOK_SECRET } = env(c);
const stripe = c.get("stripe");
const signature = c.req.header("stripe-signature");
if (!signature || !STRIPE_WEBHOOK_SECRET || !stripe) {
return c.text("", 400);
}
try {
const body = await c.req.text();
const event = await stripe.webhooks.constructEventAsync(
body,
signature,
STRIPE_WEBHOOK_SECRET,
);
if (event.type === "product.created") {
const product = event.data.object;
console.log(JSON.stringify(product, null, 2));
}
return c.text("", 200);
} catch (err) {
const errorMessage = `⚠️ Webhook署名の検証に失敗しました。${err instanceof Error ? err.message : "内部サーバーエラー"}`;
console.log(errorMessage);
return c.text(errorMessage, 400);
}
});
export default app;

これにより、Webhook APIが未承認のソースから直接呼び出された場合にHTTP 400エラーが返されることが保証されます。

Terminal window
curl -XPOST http://localhost:8787/webhook -I
HTTP/1.1 400 Bad Request
Content-Length: 0
Content-Type: text/plain; charset=UTF-8

Stripeからイベントを送信するテストを行うために、Stripe CLIコマンドを使用します。

Terminal window
stripe trigger product.created
製品のフィクスチャを設定中
製品のフィクスチャを実行中
トリガーが成功しました!イベントの詳細はダッシュボードで確認してください。

Stripe側で追加された製品情報は、npm run devが実行されているターミナル画面にログとして記録されます。

{
id: 'prod_QGw9VdIqVCNABH',
object: 'product',
active: true,
attributes: [],
created: 1718087602,
default_price: null,
description: '(created by Stripe CLI)',
features: [],
images: [],
livemode: false,
marketing_features: [],
metadata: {},
name: 'myproduct',
package_dimensions: null,
shippable: null,
statement_descriptor: null,
tax_code: null,
type: 'service',
unit_label: null,
updated: 1718087603,
url: null
}
[wrangler:inf] POST /webhook 201 Created (14ms)

3. Workers AIを使用してテキストをベクターデータに変換する

製品情報の取り込みの準備が整ったので、検索用のインデックスを作成するために必要な前処理の実装を始めましょう。Cloudflare Vectorizeを使用したベクトル検索では、テキストデータをインデックス化する前に数値データに変換する必要があります。データを数値シーケンスとして保存することで、これらのベクトルの類似性に基づいて検索できるようになり、高度に類似したデータを取得できます。

このステップでは、まず外部から送信されたデータをテキストデータに変換するプロセスを実装します。これは、ベクトルデータに変換する情報がテキスト形式であるため必要です。製品名、説明、およびメタデータを検索対象に含めたい場合は、以下の処理を追加します。

if (event.type === "product.created") {
const product = event.data.object;
const productData = [
`## ${product.name}`,
product.description,
"### metadata",
Object.entries(product.metadata)
.map(([key, value]) => `- ${key}: ${value}`)
.join("\n"),
].join("\n");
console.log(JSON.stringify(productData, null, 2));
}

この処理を追加することで、JSON形式の製品情報をシンプルなMarkdown形式の製品紹介テキストに変換します。

Terminal window
## product name
product description.
### metadata
- key: value

データをテキストに変換したので、次にそれをベクトルデータに変換しましょう。Workers AIのテキスト埋め込みモデルを使用することで、テキストを任意の次元のベクトルデータに変換できます。

const productData = [
`## ${product.name}`,
product.description,
"### metadata",
Object.entries(product.metadata)
.map(([key, value]) => `- ${key}: ${value}`)
.join("\n"),
].join("\n");
const embeddings = await c.env.AI.run("@cf/baai/bge-large-en-v1.5", {
text: productData,
});
console.log(JSON.stringify(embeddings, null, 2));

Workers AIを使用する際は、c.env.AI.run()関数を実行します。使用したいモデルを最初の引数として指定し、2番目の引数にはテキスト埋め込みモデルを使用して変換したいテキストデータや生成された画像またはテキストの指示を入力します。Vectorizeを使用して変換されたベクトルデータを保存したい場合は、npx wrangler vectorize createコマンドで指定されたdimensionsの数に一致するモデルを選択してください。数が一致しない場合、変換されたベクトルデータを保存できない可能性があります。

Vectorizeにベクトルデータを保存する

最後に、作成したデータをVectorizeに保存しましょう。src/index.tsを編集して、VECTORIZE_INDEXバインディングを使用してインデックス処理を実装します。保存するデータはベクトルデータになるため、変換前のテキストデータをメタデータとして保存します。

if (event.type === "product.created") {
const product = event.data.object;
const productData = [
`## ${product.name}`,
product.description,
"### metadata",
Object.entries(product.metadata)
.map(([key, value]) => `- ${key}: ${value}`)
.join("\n"),
].join("\n");
console.log(JSON.stringify(productData, null, 2));
const embeddings = await c.env.AI.run("@cf/baai/bge-large-en-v1.5", {
text: productData,
});
await c.env.VECTORIZE_INDEX.insert([
{
id: product.id,
values: embeddings.data[0],
metadata: {
name: product.name,
description: product.description || "",
product_metadata: product.metadata,
},
},
]);
}

これで、製品データを推奨のためにデータベースと同期させるメカニズムが確立されました。Stripe CLIコマンドを使用して、いくつかの製品データを保存します。

Terminal window
stripe products create --name="Smartphone X" \
--description="Latest model with cutting-edge features" \
-d "default_price_data[currency]=usd" \
-d "default_price_data[unit_amount]=79900" \
-d "metadata[category]=electronics"
Terminal window
stripe products create --name="Ultra Notebook" \
--description="Lightweight and powerful notebook computer" \
-d "default_price_data[currency]=usd" \
-d "default_price_data[unit_amount]=129900" \
-d "metadata[category]=computers"
Terminal window
stripe products create --name="Wireless Earbuds Pro" \
--description="High quality sound with noise cancellation" \
-d "default_price_data[currency]=usd" \
-d "default_price_data[unit_amount]=19900" \
-d "metadata[category]=audio"
Terminal window
stripe products create --name="Smartwatch 2" \
--description="Stay connected with the latest smartwatch" \
-d "default_price_data[currency]=usd" \
-d "default_price_data[unit_amount]=29900" \
-d "metadata[category]=wearables"
Terminal window
stripe products create --name="Tablet Pro" \
--description="Versatile tablet for work and play" \
-d "default_price_data[currency]=usd" \
-d "default_price_data[unit_amount]=49900" \
-d "metadata[category]=computers"

保存が成功すると、stripe listenコマンドを実行している画面に[200] POSTのようなログが表示されます。

Terminal window
2024-06-11 16:41:42 --> product.created [evt_1PQPKsL8xlxrZ26gst0o1DK3]
2024-06-11 16:41:45 <-- [200] POST http://localhost:8787/webhook [evt_1PQPKsL8xlxrZ26gst0o1DK3]
2024-06-11 16:41:47 --> product.created [evt_1PQPKxL8xlxrZ26gGk90TkcK]
2024-06-11 16:41:49 <-- [200] POST http://localhost:8787/webhook [evt_1PQPKxL8xlxrZ26gGk90TkcK]

登録されたデータごとに1つのログエントリを確認できれば、保存プロセスは完了です。次に、関連製品検索のためのAPIを実装します。

4. Vectorizeを使用した関連製品検索APIの作成

検索用のインデックスが準備できたので、次のステップは関連製品を検索するAPIを実装することです。ベクトルインデックスを利用することで、データの類似性に基づいて検索を行うことができます。この方法を使用して、指定された製品IDに類似した製品データを検索するAPIを実装しましょう。

このAPIでは、製品IDがAPIパスの一部として受け取られます。受け取ったIDを使用して、c.env.VECTORIZE_INDEX.getByIds()を使用してVectorizeからベクトルデータを取得します。このプロセスの戻り値にはベクトルデータが含まれ、その後c.env.VECTORIZE_INDEX.query()に渡されて類似性検索が行われます。どの製品が推奨されるかを迅速に確認するために、returnMetadatatrueに設定して、保存されたメタデータ情報も取得します。topKパラメータは取得するデータ項目の数を指定します。2未満または4以上のデータ項目を取得したい場合は、この値を変更してください。

app.get("/products/:product_id", async (c) => {
// APIパスパラメータから製品IDを取得
const productId = c.req.param("product_id");
// 製品IDによってインデックスデータを取得
const [product] = await c.env.VECTORIZE_INDEX.getByIds([productId]);
// 埋め込みデータを使用して類似製品を検索
const similarProducts = await c.env.VECTORIZE_INDEX.query(product.values, {
topK: 3,
returnMetadata: true,
});
return c.json({
product: {
...product.metadata,
},
similarProducts,
});
});

このAPIを実行してみましょう。prod_で始まる製品IDを使用します。これは、stripe products crateコマンドを実行するか、stripe products listコマンドの結果から取得できます。

Terminal window
curl http://localhost:8787/products/prod_xxxx

Vectorizeインデックスに存在する製品IDを使用してリクエストを送信すると、その製品と2つの関連製品のデータが以下のように返されます。

{
"product": {
"name": "Tablet Pro",
"description": "Versatile tablet for work and play",
"product_metadata": {
"category": "computers"
}
},
"similarProducts": {
"count": 3,
"matches": [
{
"id": "prod_QGxFoHEpIyxHHF",
"metadata": {
"name": "Tablet Pro",
"description": "Versatile tablet for work and play",
"product_metadata": {
"category": "computers"
}
},
"score": 1
},
{
"id": "prod_QGxFEgfmOmy5Ve",
"metadata": {
"name": "Ultra Notebook",
"description": "Lightweight and powerful notebook computer",
"product_metadata": {
"category": "computers"
}
},
"score": 0.724717327
},
{
"id": "prod_QGwkGYUcKU2UwH",
"metadata": {
"name": "demo product",
"description": "aaaa",
"product_metadata": {
"test": "hello"
}
},
"score": 0.635707003
}
]
}
}

similarProductsscoreを見てみると、score1のデータがあることがわかります。これは、検索に使用されたクエリと完全に同じであることを意味します。メタデータを見れば、リクエストで送信された製品IDと同じデータであることが明らかです。関連製品を検索したいので、同じ製品が検索結果に含まれないようにfilterを追加しましょう。ここでは、metadataの名前を使用して同じ製品名のデータを除外するフィルターを追加します。

app.get("/products/:product_id", async (c) => {
const productId = c.req.param("product_id");
const [product] = await c.env.VECTORIZE_INDEX.getByIds([productId]);
const similarProducts = await c.env.VECTORIZE_INDEX.query(product.values, {
topK: 3,
returnMetadata: true,
filter: {
name: {
$ne: product.metadata?.name.toString(),
},
},
});
return c.json({
product: {
...product.metadata,
},
similarProducts,
});
});

このプロセスを追加した後、APIを実行すると、score1のデータは存在しないことがわかります。

{
"product": {
"name": "Tablet Pro",
"description": "Versatile tablet for work and play",
"product_metadata": {
"category": "computers"
}
},
"similarProducts": {
"count": 3,
"matches": [
{
"id": "prod_QGxFEgfmOmy5Ve",
"metadata": {
"name": "Ultra Notebook",
"description": "Lightweight and powerful notebook computer",
"product_metadata": {
"category": "computers"
}
},
"score": 0.724717327
},
{
"id": "prod_QGwkGYUcKU2UwH",
"metadata": {
"name": "demo product",
"description": "aaaa",
"product_metadata": {
"test": "hello"
}
},
"score": 0.635707003
},
{
"id": "prod_QGxFEafrNDG88p",
"metadata": {
"name": "Smartphone X",
"description": "Latest model with cutting-edge features",
"product_metadata": {
"category": "electronics"
}
},
"score": 0.632409942
}
]
}
}

このようにして、Vectorizeを使用して関連製品情報を検索するシステムを実装できます。

5. ユーザーの質問に答える推薦APIの作成

推薦は関連製品を表示するだけでなく、ユーザーの質問や懸念に対処することもできます。最終的なAPIは、VectorizeとWorkers AIを使用してユーザーの質問に答えるプロセスを実装します。

このAPIは以下のプロセスを実装します。

  1. Workers AIのテキスト埋め込みモデルを使用してユーザーの質問をベクトル化します。
  2. Vectorizeを使用して、非常に関連性の高い製品を検索して取得します。
  3. 検索結果をMarkdown形式の文字列に変換します。
  4. Workers AIのテキスト生成モデルを利用して、検索結果に基づいて応答を生成します。

この方法は、Cloudflareを使用したRetrieval Augmented Generation (RAG)と呼ばれるテキスト生成メカニズムを実現します。バインディングやその他の準備はすでに完了しているので、APIを追加しましょう。

app.post("/ask", async (c) => {
const { question } = await c.req.json();
if (!question) {
return c.json({
message: "Please tell me your question.",
});
}
/**
* 質問をベクトルデータに変換
*/
const embeddedQuestion = await c.env.AI.run("@cf/baai/bge-large-en-v1.5", {
text: question,
});
/**
* Vectorizeインデックスから類似データをクエリ
*/
const similarProducts = await c.env.VECTORIZE_INDEX.query(
embeddedQuestion.data[0],
{
topK: 3,
returnMetadata: true,
},
);
/**
* JSONデータをMarkdownテキストに変換
**/
const contextData = similarProducts.matches.reduce((prev, current) => {
if (!current.metadata) return prev;
const productTexts = Object.entries(current.metadata).map(
([key, value]) => {
switch (key) {
case "name":
return `## ${value}`;
case "product_metadata":
return `- ${key}: ${JSON.stringify(value)}`;
default:
return `- ${key}: ${value}`;
}
},
);
const productTextData = productTexts.join("\n");
return `${prev}\n${productTextData}`;
}, "");
/**
* 答えを生成
*/
const response = await c.env.AI.run("@cf/meta/llama-3.1-8b-instruct", {
messages: [
{
role: "system",
content: `You are an assistant for question-answering tasks. Use the following pieces of retrieved context to answer the question. If you don't know the answer, just say that you don't know.\n#Context: \n${contextData} `,
},
{
role: "user",
content: question,
},
],
});
return c.json(response);
});

作成したAPIを使用して製品に関する相談を行いましょう。POSTリクエストのボディに質問を送信できます。たとえば、新しいPCを取得したい場合、次のコマンドを実行できます。

Terminal window
curl -X POST "http://localhost:8787/ask" -H "Content-Type: application/json" -d '{"question": "I want to get a new PC"}'

質問が送信されると、前述のように推薦テキストが生成されます。この例では、Ultra Notebook製品が推奨されました。これは、notebook computerの説明が含まれているため、Vectorize検索で比較的高いスコアを受け取ったことを意味します。

{
"response": "Exciting! You're looking to get a new PC! Based on the context I retrieved, I'd recommend considering the \"Ultra Notebook\" since it's described as a lightweight and powerful notebook computer, which fits the category of \"computers\". Would you like to know more about its specifications or features?"
}

テキスト生成モデルは、入力プロンプト(質問や製品検索結果)に基づいて毎回新しいテキストを生成します。したがって、同じリクエストをこのAPIに送信しても、応答テキストはわずかに異なる場合があります。製品向けに開発する際は、AI Gatewayのログやキャッシング機能を使用して、適切な制御とデバッグを設定してください。

6. アプリケーションのデプロイ

アプリケーションをデプロイする前に、Workerプロジェクトが以前に作成したStripe APIキーにアクセスできることを確認する必要があります。外部サービスのAPIキーは.dev.varsに定義されているため、この情報もWorkerプロジェクトに設定する必要があります。APIキーやシークレットを保存するには、npx wrangler secret put <KEY>コマンドを実行します。このチュートリアルでは、.dev.varsに設定された値を参照して、コマンドを2回実行します。

Terminal window
npx wrangler secret put STRIPE_SECRET_API_KEY
npx wrangler secret put STRIPE_WEBHOOK_SECRET

次に、npx wrangler deployを実行します。これにより、アプリケーションがCloudflareにデプロイされ、公開アクセス可能になります。

結論

ご覧のとおり、Cloudflare Workers、Workers AI、およびVectorizeを使用することで、関連製品や製品推薦APIを簡単に実装できます。製品データがStripeのような外部サービスで管理されていても、Webhook APIを追加することでそれらを組み込むことができます。また、このチュートリアルでは紹介していませんが、ユーザーの好みや興味のあるカテゴリなどの情報をWorkers KVやD1に保存することもできます。この保存された情報をテキスト生成プロンプトとして使用することで、より正確な推薦機能を提供できます。

このチュートリアルの経験を活かして、あなたのeコマースサイトを新しいアイデアで強化してください。