Skip to content

非公式本サイトは非公式の日本語ドキュメントであり、Cloudflare 公式サイトではありません。最新情報はdevelopers.cloudflare.comをご確認ください。

AI でデータを分析する

最終更新 Markdown で表示Agent セットアップ

CSV のアップロードを受け付け、Claude で Python の分析コードを生成し、サンドボックスで実行し、可視化を返す、AI を使ったデータ分析システムを作ります。

所要時間: 25 分

前提条件

  1. Cloudflare アカウント に登録します。
  2. Node.js をインストールします。

Node.js のバージョンマネージャー

権限の問題を避け、Node.js のバージョンを切り替えられるよう、Voltanvm などの Node バージョンマネージャーを使います。このガイドの後半で説明する Wrangler には、Node バージョン 16.17.0 以降が必要です。

次のものも必要です。

1. プロジェクトを作成する

新しい Sandbox SDK プロジェクトを作成します。

npm create cloudflare@latest -- analyze-data --template=cloudflare/sandbox-sdk/examples/minimal
cd analyze-data

2. 依存関係をインストールする

npm i @anthropic-ai/sdk

3. 分析ハンドラーを実装する

src/index.ts を置き換えます。

import { getSandbox, proxyToSandbox, type Sandbox } from "@cloudflare/sandbox";
import Anthropic from "@anthropic-ai/sdk";

export { Sandbox } from "@cloudflare/sandbox";

interface Env {
	Sandbox: DurableObjectNamespace<Sandbox>;
	ANTHROPIC_API_KEY: string;
}

export default {
	async fetch(request: Request, env: Env): Promise<Response> {
		const proxyResponse = await proxyToSandbox(request, env);
		if (proxyResponse) return proxyResponse;

		if (request.method !== "POST") {
			return Response.json(
				{ error: "POST CSV file and question" },
				{ status: 405 },
			);
		}

		try {
			const formData = await request.formData();
			const csvFile = formData.get("file") as File;
			const question = formData.get("question") as string;

			if (!csvFile || !question) {
				return Response.json(
					{ error: "Missing file or question" },
					{ status: 400 },
				);
			}

			// Upload CSV to sandbox
			const sandbox = getSandbox(env.Sandbox, `analysis-${Date.now()}`);
			const csvPath = "/workspace/data.csv";
			await sandbox.writeFile(csvPath, await csvFile.text());

			// Analyze CSV structure
			const structure = await sandbox.exec(
				`python3 -c "import pandas as pd; df = pd.read_csv('${csvPath}'); print(f'Rows: {len(df)}'); print(f'Columns: {list(df.columns)[:5]}')"`,
			);

			if (!structure.success) {
				return Response.json(
					{ error: "Failed to read CSV", details: structure.stderr },
					{ status: 400 },
				);
			}

			// Generate analysis code with Claude
			const code = await generateAnalysisCode(
				env.ANTHROPIC_API_KEY,
				csvPath,
				question,
				structure.stdout,
			);

			// Write and execute the analysis code
			await sandbox.writeFile("/workspace/analyze.py", code);
			const result = await sandbox.exec("python /workspace/analyze.py");

			if (!result.success) {
				return Response.json(
					{ error: "Analysis failed", details: result.stderr },
					{ status: 500 },
				);
			}

			async function streamToBase64(stream) {
			  const blob = await new Response(stream).blob();
			  const buffer = await blob.arrayBuffer();
			  const bytes = new Uint8Array(buffer);

			  // Convert to base64
			  let binary = '';
			  for (let i = 0; i < bytes.length; i++) {
			    binary += String.fromCharCode(bytes[i]);
			  }
			  return btoa(binary);
			}

			// Check for generated chart
			let chart = null;
			try {
				const { content, mimeType } = await sandbox.readFile("/workspace/chart.png", {
					encoding: "none"
				});
				chart = `data:${mimeType};base64,${await streamToBase64(content)}`;
			} catch {
				// No chart generated
			}

			await sandbox.destroy();

			return Response.json({
				success: true,
				output: result.stdout,
				chart,
				code,
			});
		} catch (error: any) {
			return Response.json({ error: error.message }, { status: 500 });
		}
	},
};

async function generateAnalysisCode(
	apiKey: string,
	csvPath: string,
	question: string,
	csvStructure: string,
): Promise<string> {
	const anthropic = new Anthropic({ apiKey });

	const response = await anthropic.messages.create({
		model: "claude-sonnet-4-5",
		max_tokens: 2048,
		messages: [
			{
				role: "user",
				content: `CSV at ${csvPath}:
${csvStructure}

Question: "${question}"

Generate Python code that:
- Reads CSV with pandas
- Answers the question
- Saves charts to /workspace/chart.png if helpful
- Prints findings to stdout

Use pandas, numpy, matplotlib.`,
			},
		],
		tools: [
			{
				name: "generate_python_code",
				description: "Generate Python code for data analysis",
				input_schema: {
					type: "object",
					properties: {
						code: { type: "string", description: "Complete Python code" },
					},
					required: ["code"],
				},
			},
		],
	});

	for (const block of response.content) {
		if (block.type === "tool_use" && block.name === "generate_python_code") {
			return (block.input as { code: string }).code;
		}
	}

	throw new Error("Failed to generate code");
}

4. ローカルの環境変数を設定する

ローカル開発用に、プロジェクトルートに .dev.vars ファイルを作成します。

echo "ANTHROPIC_API_KEY=your_api_key_here\nSANDBOX_TRANSPORT=rpc" > .dev.vars

your_api_key_here を、Anthropic Console の実際の API キーに置き換えます。

新しいファイルストリーミング API を使うには、SANDBOX_TRANSPORT が必要です。

5. ローカルでテストする

サンプル CSV を用意します。

# Create a test CSV
echo "year,rating,title
2020,8.5,Movie A
2021,7.2,Movie B
2022,9.1,Movie C" > test.csv

開発サーバーを起動します。

npm run dev

curl でテストします。

curl -X POST http://localhost:8787 \
  -F "[email protected]" \
  -F "question=What is the average rating by year?"

応答:

{
	"success": true,
	"output": "Average ratings by year:\n2020: 8.5\n2021: 7.2\n2022: 9.1",
	"chart": "data:image/png;base64,...",
	"code": "import pandas as pd\nimport matplotlib.pyplot as plt\n..."
}

6. デプロイする

Worker をデプロイします。

npx wrangler deploy

次に、Anthropic API キーを本番のシークレットとして設定します。

npx wrangler secret put ANTHROPIC_API_KEY

プロンプトが表示されたら、Anthropic Console の API キーを貼り付けます。

作成したもの

次の機能を持つ AI データ分析システムです。

  • CSV ファイルをサンドボックスへアップロードする
  • Claude のツール呼び出しで分析コードを生成する
  • pandas と matplotlib で Python を実行する
  • テキスト出力と可視化を返す

次のステップ

役に立ちましたか?