Retrieval Augmented Generation (RAG) AIの構築
このガイドでは、Cloudflare AIを使用して最初のアプリケーションを設定し、デプロイする方法を説明します。Workers AI、Vectorize、D1、Cloudflare Workersなどのツールを使用して、完全な機能を備えたAI駆動のアプリケーションを構築します。
このチュートリアルの最後には、情報を保存し、大規模言語モデルを使用してそれをクエリすることができるAIツールを構築します。このパターンはRetrieval Augmented Generation(RAG)として知られており、CloudflareのAIツールキットの複数の側面を組み合わせて構築できる便利なプロジェクトです。AIツールを使用した経験は必要ありません。
- Sign up for a Cloudflare account ↗.
- Install
npm↗. - 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へのアクセスも必要です。
C3(create-cloudflare-cli)は、WorkersをCloudflareにできるだけ早く設定し、デプロイするために設計されたコマンドラインツールです。
ターミナルウィンドウを開き、C3を実行してWorkerプロジェクトを作成します:
npm create cloudflare@latest -- rag-ai-tutorialyarn create cloudflare@latest rag-ai-tutorialpnpm create cloudflare@latest rag-ai-tutorialFor 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が作成したファイルは何ですか?
wrangler.toml: あなたのWrangler設定ファイル。worker.js(/src内): ESモジュール構文で書かれた最小限の「‘Hello World!‘」Worker。package.json: 最小限のNode依存関係設定ファイル。package-lock.json:npmのpackage-lock.jsonに関するドキュメント ↗を参照してください。node_modules:npmのnode_modulesに関するドキュメント ↗を参照してください。
新しく作成したディレクトリに移動します:
cd rag-ai-tutorialWorkersコマンドラインインターフェース、Wranglerを使用すると、作成、テスト、およびデプロイができます。C3はデフォルトでプロジェクトにWranglerをインストールします。
最初のWorkerを作成した後、プロジェクトディレクトリでwrangler devコマンドを実行して、Workerの開発用ローカルサーバーを起動します。これにより、開発中にローカルでWorkerをテストできます。
npx wrangler dev --remoteこれで、http://localhost:8787 ↗にアクセスして、Workerが実行されているのを見ることができます。コードに加えた変更は再ビルドをトリガーし、ページをリロードするとWorkerの最新の出力が表示されます。
CloudflareのAI製品を使用するには、wrangler.tomlにaiブロックを追加します。これにより、プラットフォーム上の利用可能な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をデプロイできます:
npx wrangler deployWorkerにリクエストを送信すると、LLMからテキスト応答が生成され、JSONオブジェクトとして返されます。
curl https://example.username.workers.dev{"response":"答え: 9の平方根は3です。"}埋め込みを使用すると、Cloudflare AIプロジェクトで使用できる言語モデルに追加機能を追加できます。これは、CloudflareのベクトルデータベースであるVectorizeを介して行われます。
Vectorizeを使用するには、wranglerを使用して新しい埋め込みインデックスを作成します。このインデックスは768次元のベクトルを保存し、コサイン類似度を使用して最も類似したベクトルを特定します:
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データベースを作成します:
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コマンドを実行します:
npx wrangler d1 execute database --remote --command "CREATE TABLE IF NOT EXISTS notes (id INTEGER PRIMARY KEY, text TEXT NOT NULL)"次に、wrangler d1 executeを使用してデータベースに新しいメモを追加できます:
npx wrangler d1 execute database --remote --command "INSERT INTO notes (text) VALUES ('最高のピザのトッピングはペパロニです')"複数のルートを処理するためにWorkers関数を拡張するために、Workers用のルーティングライブラリであるhonoを追加します。これにより、データベースにメモを追加するための新しいルートを作成できます。npmを使用してhonoをインストールします:
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 });});この関数は以下のことを行います:
- リクエストのJSONボディを解析して
textフィールドを取得します。 - D1の
notesテーブルに新しい行を挿入し、新しい行のidを取得します。 textをLLMバインディングのembeddingsモデルを使用してベクトルに変換します。vector-indexインデックスにidとvectorsをアップサートします。- 新しいメモの
idとtextをJSONとして返します。
これにより、メモの新しいベクトル表現が作成され、後でメモを取得するために使用できます。
コードを完成させるために、ルートパス(/)を更新して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;メモが不要になった場合、データベースから削除できます。メモを削除するたびに、対応するベクトルも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)})ステップ1でWorkerをデプロイしていない場合は、Wranglerを介してWorkerを*.workers.devサブドメインまたは設定済みのカスタムドメインにデプロイします。サブドメインやドメインを設定していない場合、Wranglerは公開プロセス中に設定を促します。
npx wrangler deploy<YOUR_WORKER>.<YOUR_SUBDOMAIN>.workers.devでWorkerをプレビューします。
このコードベースの完全版はGitHubで入手できます。クエリ、追加、削除のためのフロントエンドUIと、データベースおよびベクトルインデックスと対話するためのバックエンドAPIが含まれています。こちらで見つけることができます:github.com/kristianfreeman/cloudflare-retrieval-augmented-generation-example ↗。
さらに行うには: