コンテンツにスキップ

Retrieval Augmented Generation (RAG) AIの構築

Last reviewed: about 1 month ago

このガイドでは、Cloudflare AIを使用して最初のアプリケーションを設定し、デプロイする方法を説明します。Workers AI、Vectorize、D1、Cloudflare Workersなどのツールを使用して、完全な機能を備えたAI駆動のアプリケーションを構築します。

このチュートリアルの最後には、情報を保存し、大規模言語モデルを使用してそれをクエリすることができるAIツールを構築します。このパターンはRetrieval Augmented Generation(RAG)として知られており、CloudflareのAIツールキットの複数の側面を組み合わせて構築できる便利なプロジェクトです。AIツールを使用した経験は必要ありません。

  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.

Vectorizeへのアクセスも必要です。

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

C3(create-cloudflare-cli)は、WorkersをCloudflareにできるだけ早く設定し、デプロイするために設計されたコマンドラインツールです。

ターミナルウィンドウを開き、C3を実行してWorkerプロジェクトを作成します:

Terminal window
npm create cloudflare@latest -- rag-ai-tutorial

For setup, select the following options:

  • For What would you like to start with?, choose Hello Worldの例.
  • For Which template would you like to use?, choose Hello World Worker.
  • For Which language do you want to use?, choose JavaScript.
  • For Do you want to use git for version control?, choose Yes.
  • For Do you want to deploy your application?, choose No (we will be making some changes before deploying).

プロジェクトディレクトリ内で、C3がいくつかのファイルを生成しました。

C3が作成したファイルは何ですか?

  1. wrangler.toml: あなたのWrangler設定ファイル。
  2. worker.js/src内): ESモジュール構文で書かれた最小限の「‘Hello World!‘」Worker。
  3. package.json: 最小限のNode依存関係設定ファイル。
  4. package-lock.json: npmpackage-lock.jsonに関するドキュメントを参照してください。
  5. node_modules: npmnode_modulesに関するドキュメントを参照してください。

新しく作成したディレクトリに移動します:

Terminal window
cd rag-ai-tutorial

2. Wrangler CLIで開発する

Workersコマンドラインインターフェース、Wranglerを使用すると、作成テスト、およびデプロイができます。C3はデフォルトでプロジェクトにWranglerをインストールします。

最初のWorkerを作成した後、プロジェクトディレクトリでwrangler devコマンドを実行して、Workerの開発用ローカルサーバーを起動します。これにより、開発中にローカルでWorkerをテストできます。

Terminal window
npx wrangler dev --remote

これで、http://localhost:8787にアクセスして、Workerが実行されているのを見ることができます。コードに加えた変更は再ビルドをトリガーし、ページをリロードするとWorkerの最新の出力が表示されます。

3. AIバインディングの追加

CloudflareのAI製品を使用するには、wrangler.tomlaiブロックを追加します。これにより、プラットフォーム上の利用可能なAIモデルと対話するために使用できるCloudflareのAIモデルへのバインディングが設定されます。

この例では、テキストを生成する@cf/meta/llama-3-8b-instructモデルを使用しています。

[ai]
binding = "AI"

次に、src/index.jsファイルを見つけます。fetchハンドラー内で、AIバインディングをクエリできます:

export default {
async fetch(request, env, ctx) {
const answer = await env.AI.run("@cf/meta/llama-3-8b-instruct", {
messages: [{ role: "user", content: `9の平方根は何ですか?` }],
});
return new Response(JSON.stringify(answer));
},
};

AIバインディングを通じてLLMにクエリすることで、コード内でCloudflare AIの大規模言語モデルと直接対話できます。この例では、テキストを生成する@cf/meta/llama-3-8b-instructモデルを使用しています。

wranglerを使用してWorkerをデプロイできます:

Terminal window
npx wrangler deploy

Workerにリクエストを送信すると、LLMからテキスト応答が生成され、JSONオブジェクトとして返されます。

Terminal window
curl https://example.username.workers.dev
{"response":"答え: 9の平方根は3です。"}

4. Cloudflare D1とVectorizeを使用して埋め込みを追加する

埋め込みを使用すると、Cloudflare AIプロジェクトで使用できる言語モデルに追加機能を追加できます。これは、CloudflareのベクトルデータベースであるVectorizeを介して行われます。

Vectorizeを使用するには、wranglerを使用して新しい埋め込みインデックスを作成します。このインデックスは768次元のベクトルを保存し、コサイン類似度を使用して最も類似したベクトルを特定します:

Terminal window
npx wrangler vectorize create vector-index --dimensions=768 --metric=cosine

次に、新しいVectorizeインデックスの設定詳細をwrangler.tomlに追加します:

# ... 既存のwrangler設定
[[vectorize]]
binding = "VECTOR_INDEX"
index_name = "vector-index"

ベクトルインデックスを使用すると、データを表すために使用される浮動小数点数のコレクションを保存できます。ベクトルデータベースをクエリする際には、クエリを次元に変換することもできます。Vectorizeは、保存されたベクトルがクエリに最も類似しているかどうかを効率的に判断するように設計されています。

検索機能を実装するには、CloudflareからD1データベースを設定する必要があります。D1では、アプリのデータを保存できます。その後、このデータをベクトル形式に変換します。誰かが検索を行い、それがベクトルと一致する場合、一致するデータを表示できます。

wranglerを使用して新しいD1データベースを作成します:

Terminal window
npx wrangler d1 create database

次に、前のコマンドから出力された設定詳細をwrangler.tomlに貼り付けます:

# ... 既存のwrangler設定
[[d1_databases]]
binding = "DB" # env.DBでWorker内で利用可能
database_name = "database"
database_id = "abc-def-geh" # これを実際のdatabase_id(UUID)に置き換えてください

このアプリケーションでは、D1にnotesテーブルを作成し、メモを保存し、後でVectorizeで取得できるようにします。このテーブルを作成するには、wrangler d1 executeを使用してSQLコマンドを実行します:

Terminal window
npx wrangler d1 execute database --remote --command "CREATE TABLE IF NOT EXISTS notes (id INTEGER PRIMARY KEY, text TEXT NOT NULL)"

次に、wrangler d1 executeを使用してデータベースに新しいメモを追加できます:

Terminal window
npx wrangler d1 execute database --remote --command "INSERT INTO notes (text) VALUES ('最高のピザのトッピングはペパロニです')"

5. メモを作成し、Vectorizeに追加する

複数のルートを処理するためにWorkers関数を拡張するために、Workers用のルーティングライブラリであるhonoを追加します。これにより、データベースにメモを追加するための新しいルートを作成できます。npmを使用してhonoをインストールします:

Terminal window
npm install hono

次に、src/index.jsファイルにhonoをインポートします。また、fetchハンドラーをhonoを使用するように更新します:

import { Hono } from "hono";
const app = new Hono();
app.get("/", async (c) => {
const answer = await c.env.AI.run("@cf/meta/llama-3-8b-instruct", {
messages: [{ role: "user", content: `9の平方根は何ですか?` }],
});
return c.json(answer);
});
export default app;

これにより、ルートパス/に新しいルートが確立され、アプリケーションの以前のバージョンと機能的に同等になります。これで、データベースにメモを追加するための新しいルートを追加できます。

この例では、埋め込みを作成するために使用できる@cf/baai/bge-base-en-v1.5モデルを使用しています。埋め込みは、CloudflareのベクトルデータベースであるVectorize内に保存および取得されます。ユーザークエリも埋め込みに変換され、Vectorize内での検索に使用されます。

app.post("/notes", async (c) => {
const { text } = await c.req.json();
if (!text) {
return c.text("テキストがありません", 400);
}
const { results } = await c.env.DB.prepare(
"INSERT INTO notes (text) VALUES (?) RETURNING *",
)
.bind(text)
.run();
const record = results.length ? results[0] : null;
if (!record) {
return c.text("メモの作成に失敗しました", 500);
}
const { data } = await c.env.AI.run("@cf/baai/bge-base-en-v1.5", {
text: [text],
});
const values = data[0];
if (!values) {
return c.text("ベクトル埋め込みの生成に失敗しました", 500);
}
const { id } = record;
const inserted = await c.env.VECTOR_INDEX.upsert([
{
id: id.toString(),
values,
},
]);
return c.json({ id, text, inserted });
});

この関数は以下のことを行います:

  1. リクエストのJSONボディを解析してtextフィールドを取得します。
  2. D1のnotesテーブルに新しい行を挿入し、新しい行のidを取得します。
  3. textをLLMバインディングのembeddingsモデルを使用してベクトルに変換します。
  4. vector-indexインデックスにidvectorsをアップサートします。
  5. 新しいメモのidtextをJSONとして返します。

これにより、メモの新しいベクトル表現が作成され、後でメモを取得するために使用できます。

6. Vectorizeをクエリしてメモを取得する

コードを完成させるために、ルートパス(/)を更新してVectorizeをクエリできます。クエリをベクトルに変換し、vector-indexインデックスを使用して最も類似したベクトルを見つけます。

topKパラメータは、関数によって返されるベクトルの数を制限します。たとえば、topKを1に設定すると、クエリに基づいて最も類似したベクトルのみが返されます。topKを5に設定すると、最も類似した5つのベクトルが返されます。

類似したベクトルのリストが与えられた場合、それらのベクトルに保存されたレコードIDに一致するメモを取得できます。この場合、単一のメモのみを取得していますが、必要に応じてカスタマイズできます。

これらのメモのテキストをLLMバインディングのプロンプトにコンテキストとして挿入できます。これはRetrieval-Augmented Generation(RAG)の基礎であり、LLMによって生成されたテキストを強化するために、LLMの外部データからの追加コンテキストを提供します。

プロンプトを更新してコンテキストを含め、LLMに応答時にコンテキストを使用するように依頼します:

import { Hono } from "hono";
const app = new Hono();
// 既存のpostルート...
// app.post('/notes', async (c) => { ... })
app.get('/', async (c) => {
const question = c.req.query('text') || "9の平方根は何ですか?"
const embeddings = await c.env.AI.run('@cf/baai/bge-base-en-v1.5', { text: question })
const vectors = embeddings.data[0]
const vectorQuery = await c.env.VECTOR_INDEX.query(vectors, { topK: 1 });
const vecId = vectorQuery.matches[0].id
let notes = []
if (vecId) {
const query = `SELECT * FROM notes WHERE id = ?`
const { results } = await c.env.DB.prepare(query).bind(vecId).all()
if (results) notes = results.map(vec => vec.text)
}
const contextMessage = notes.length
? `コンテキスト:\n${notes.map(note => `- ${note}`).join("\n")}`
: ""
const systemPrompt = `質問に答える際は、提供されたコンテキストを使用してください。`
const { response: answer } = await c.env.AI.run(
'@cf/meta/llama-3-8b-instruct',
{
messages: [
...(notes.length ? [{ role: 'system', content: contextMessage }] : []),
{ role: 'system', content: systemPrompt },
{ role: 'user', content: question }
]
}
)
return c.text(answer);
});
app.onError((err, c) => {
return c.text(err);
});
export default app;

7. メモとベクトルの削除

メモが不要になった場合、データベースから削除できます。メモを削除するたびに、対応するベクトルもVectorizeから削除する必要があります。これを実装するには、src/index.jsファイルにDELETE /notes/:idルートを構築します:

app.delete('/notes/:id', async (c) => {
const { id } = c.req.param();
const query = `DELETE FROM notes WHERE id = ?`
await c.env.DATABASE.prepare(query).bind(id).run()
await c.env.VECTOR_INDEX.deleteByIds([id])
return c.status(204)
})

8. プロジェクトをデプロイする

ステップ1でWorkerをデプロイしていない場合は、Wranglerを介してWorkerを*.workers.devサブドメインまたは設定済みのカスタムドメインにデプロイします。サブドメインやドメインを設定していない場合、Wranglerは公開プロセス中に設定を促します。

Terminal window
npx wrangler deploy

<YOUR_WORKER>.<YOUR_SUBDOMAIN>.workers.devでWorkerをプレビューします。

関連リソース

このコードベースの完全版はGitHubで入手できます。クエリ、追加、削除のためのフロントエンドUIと、データベースおよびベクトルインデックスと対話するためのバックエンドAPIが含まれています。こちらで見つけることができます:github.com/kristianfreeman/cloudflare-retrieval-augmented-generation-example

さらに行うには: