ウェブサイト検索

Claude 3.5 Sonnet と React を使ってチュートリアルジェネレーターを作成する


紹介

何か新しいことを学ぶとき、一番最初に従うべきことは何ですか?料理であれ、プログラミングであれ。チュートリアルですよね?

あなたは一人ではありません—インターネットユーザーの25.6%がチュートリアルを利用しています。また、インターネット上で見るコンテンツの57%はAIによって生成されています。AIをアシスタントとして活用すれば、今まで以上に速くタスクを達成できると言えます。

このチュートリアルでは、DigitalOcean GenAIプラットフォームと最新のAnthropicモデルサポートを使用して、トピックを入力するとMarkdown形式の技術ガイドを生成できるアプリケーションを構築する方法を学びます。ここにあなたが構築するもののデモがあります:

前提条件

  • DigitalOceanアカウント - このデモでは、アプリケーションをホストするためにDigitalOceanのApp Platformと、AI機能を構築するためにGenAI Platformを使用します。

  • React、TypeScript、TailwindCSSの実務知識とAPIの使用方法。

DigitalOcean GenAIプラットフォームとは何ですか?

GenAIプラットフォームは、AIエージェントの構築を支援し、APIを使用してあらゆる種類のアプリケーションを作成できるオールインワンソリューションです。DeepSeek、Anthropic、Meta、Mistral AIのモデルをサポートしており、情報検索を強化した生成(RAG)、機能ルーティング、エージェントのカスタマイズ、セキュリティの高い文脈に応じたアプリケーションを作成するためのガードレールなどの高度な機能も備えています。

サポートされているモデルのリスト

GenAIプラットフォームは、AI駆動のアプリケーションを構築および展開するために使用できる複数のモデルをサポートしています。ここにGenAIプラットフォームがサポートするモデルの完全なリストがあります。最近、Anthropic Claude 3.5(ソネット&俳句)、Claude 3 Opus、およびDeepSeek R1-distill-llama-70Bを発表しました。これらを使用して、望むAIアプリケーションを作成できます。

アプリケーションを構築する

GenAIプラットフォームについて知ったので、それを使ってチュートリアルジェネレーターをどのように構築できるか見てみましょう。

アプリケーションはシンプルなフローに従い、DigitalOceanのApp Platformを使用してアプリケーションをデプロイし、GenAI Platformを使用してAI機能を追加します。

アプリケーションはこのように見えます:

ステップ1 - フロントエンドの構築

このアプリケーションは、履歴管理とユーザーインターフェースの2つの主要な部分で構成されています。

まずは、主要なブロックをどのように構築するかを理解することから始めましょう:

チュートリアルの履歴管理

まず、私たちのチュートリアルの状態管理を見てみましょう。

チュートリアルのリストと現在選択されているチュートリアルを管理するために、ReactのuseStateフックを使用します:

interface Tutorial {
  topic: string;
  timestamp: string;
  content?: string;
  isLoading?: boolean;
  error?: string;
}

function App() {
  const [tutorials, setTutorials] = useState<Tutorial[]>([]);
  const [selectedTutorial, setSelectedTutorial] = useState<Tutorial | null>(null);
  const [isSidebarOpen, setIsSidebarOpen] = useState(false);
}

これが管理します:

  • チュートリアルのリスト
  • 現在選択されているチュートリアル
  • サイドバーの開閉状態
  • 読み込み状態

チュートリアル生成ロジック

次に、チュートリアル生成ロジックがどのように処理されるかを理解します。handleSubmit関数は次の目的で使用されます:

  • 新しいチュートリアルオブジェクトを作成します。
  • 読み込み状態を表示するようにUIを更新します。
  • 成功とエラーのケースを処理する。
  • 結果でチュートリアルリストを更新してください。
const handleSubmit = async (e: React.FormEvent) => {
  e.preventDefault();
  if (!topic.trim()) return;

  setIsLoading(true);
  const newTutorial: Tutorial = {
    topic,
    timestamp: new Date().toLocaleString(),
    isLoading: true
  };

  setTutorials([newTutorial, ...tutorials]);
  
  try {
    const content = await generateTutorial(topic);
    const completedTutorial = { ...newTutorial, content, isLoading: false };
    setTutorials(prevTutorials => [
      completedTutorial,
      ...prevTutorials.slice(1)
    ]);
    setSelectedTutorial(completedTutorial);
  } catch (error) {
    const failedTutorial = { 
      ...newTutorial, 
      error: (error as Error).message, 
      isLoading: false 
    };
    setTutorials(prevTutorials => [
      failedTutorial,
      ...prevTutorials.slice(1)
    ]);
    setSelectedTutorial(failedTutorial);
  }
};

スライドアウトサイドバーの実装

以前に生成した記事にアクセスできるようにしたい場合は、以下のコードを使用してチュートリアルの履歴を表示するサイドバーを追加する必要があります:

{/* Sidebar Toggle Button */}
<button
  onClick={() => setIsSidebarOpen(true)}
  className="fixed top-4 left-4 p-2 bg-white rounded-lg shadow-md hover:bg-gray-50 z-50"
>
  <Menu className="w-6 h-6 text-gray-700" />
</button>

{/* Sidebar Component */}
<div
  className={`fixed inset-y-0 left-0 w-80 bg-white shadow-2xl transform transition-transform duration-300 ease-in-out z-50 ${
    isSidebarOpen ? 'translate-x-0' : '-translate-x-full'
  }`}
>
  <div className="p-4">
    <div className="flex justify-between items-center mb-6">
      <h2 className="text-xl font-semibold text-gray-900">Tutorial History</h2>
      <button
        onClick={() => setIsSidebarOpen(false)}
        className="p-2 hover:bg-gray-100 rounded-lg"
      >
        <X className="w-5 h-5 text-gray-600" />
      </button>
    </div>
    {/* Tutorial List */}
    <div className="space-y-3">
      {tutorials.map((tutorial, index) => (
        <button
          key={index}
          onClick={() => {
            setSelectedTutorial(tutorial);
            setIsSidebarOpen(false);
          }}
          className={`w-full text-left p-3 rounded-lg transition-colors ${
            selectedTutorial === tutorial
              ? 'bg-indigo-50 text-indigo-700'
              : 'hover:bg-gray-50'
          }`}
        >
          <h3 className="font-medium truncate">{tutorial.topic}</h3>
          <p className="text-sm text-gray-500">{tutorial.timestamp}</p>
        </button>
      ))}
    </div>
  </div>
</div>

チュートリアルコンテンツの表示

最後の部分では、選択したチュートリアルをMarkdownレンダリングで表示するコンテンツエリアが必要です。

これはコードの見た目です:

{selectedTutorial ? (
  <div className="bg-white rounded-xl shadow-sm border border-gray-100 p-6">
    <div className="flex items-start gap-4">
      <div className="p-2 bg-indigo-100 rounded-lg">
        <BookOpen className="w-6 h-6 text-indigo-600" />
      </div>
      <div className="flex-1">
        <h3 className="text-xl font-semibold text-gray-900 mb-2">
          {selectedTutorial.topic}
        </h3>
        <p className="text-sm text-gray-500">
          Generated on {selectedTutorial.timestamp}
        </p>
        {selectedTutorial.isLoading ? (
          <div className="mt-4 p-4 bg-gray-50 rounded-lg">
            {/* Loading skeleton */}
            <div className="h-4 bg-gray-200 rounded w-3/4 animate-pulse" />
            <div className="h-4 bg-gray-200 rounded w-1/2 mt-2 animate-pulse" />
          </div>
        ) : (
          <div 
            className="mt-4 p-4 bg-gray-50 rounded-lg prose prose-indigo max-w-none"
            dangerouslySetInnerHTML={{ 
              __html: marked(selectedTutorial.content || '') 
            }}
          />
        )}
      </div>
    </div>
  </div>
) : (
  <div className="text-center py-12">
    <div className="text-gray-400 mb-4">
      <BookOpen className="w-12 h-12 mx-auto" />
    </div>
    <h3 className="text-lg font-medium text-gray-900">
      No tutorial selected
    </h3>
    <p className="text-gray-500">
      Generate a new tutorial or select one from the history
    </p>
  </div>
)}

ステップ2 - GenAIエージェントの作成

DigitalOceanアカウントにログインし、GenAIプラットフォームに移動します。

エージェントの作成をクリックして、エージェントに名前を付けてください。

エージェントは、あなたがチュートリアルを作成するのを手伝いますので、あなたのタスクは、提供された入力に基づいて詳細なチュートリアルを生成することです。シンプルな言葉を使い、記事をマークダウン形式で提供してください。のような指示を与えてください。タスクを実行するために任意のモデルを選択できます。この例ではAnthropic Claude 3.5 Sonnetを使用します。

注意:Anthropicモデルを使用するには、こちらで無料で生成できるAPIキーが必要です。

もし望むなら、知識ベースを追加することができます。この例では、それをスキップしてエージェントの作成を進めることができます。

エージェントが展開されたら、Playgroundに移動して、エージェントがあなたの期待通りに機能したかどうかをテストしてください。

エージェントの応答のタイプに改善が必要な場合は、以下のように設定タブから指示を更新し、温度を調整してみてください。

ステップ3 - アプリケーション内でエージェントを活用する

エージェントが作成され、期待通りに動作するようになったので、フロントエンドと統合する必要があります。そのために、以下の手順に従ってください:

下記の画像のように、エンドポイントURLをコピーし、エンドポイントアクセスキーを生成してください。

注意: 次のステップで必要になるため、アクセストークンをどこかに保存してください。

では、今書いたフロントエンドのコードに戻り、それをGitHubリポジトリにプッシュしてください(このGitHubリポジトリで行われたことと同様に)。その後、DigitalOcean App Platformに行き、アプリケーションをデプロイします。

このチュートリアルは、カスタムドメインを使用してDigitalOcean App Platform上でアプリケーションをビルドおよびデプロイする方法を教えます。

アプリケーションが展開されたら、設定タブに移動します。

次に、アプリレベル環境変数セクションまでスクロールし、編集をクリックして、次の変数を追加します:

AGENT_BASE_URL: <Your agent endpoint URL>
SECURE_AGENT_KEY: <The endpoint access key that you saved in the first step>

ステップ4 - アプリケーション内でOpenAI SDKを統合する

最終ステップでは、OpenAI SDKをアプリケーションに統合します。以下のコードを使用します。

import OpenAI from "openai";
const SECURE_AGENT_KEY = import.meta.env.VITE_SECURE_AGENT_KEY;
const AGENT_BASE_URL = import.meta.env.VITE_AGENT_BASE_URL;
const AGENT_ENDPOINT = `${AGENT_BASE_URL}/api/v1/`;

if (!SECURE_AGENT_KEY || !AGENT_BASE_URL) {
  throw new Error("Missing agent access key or base URL! Ensure VITE_SECURE_AGENT_KEY and VITE_AGENT_BASE_URL are set in .env");
}
const client = new OpenAI({
  apiKey: SECURE_AGENT_KEY,
  baseURL: AGENT_ENDPOINT, 
  dangerouslyAllowBrowser: true
});
export async function generateTutorial(topic: string): Promise<string> {
  try {
    console.log("Starting tutorial generation for topic:", topic);

    const prompt = `Your task is to generate a nice in-depth tutorial based on the input provided. Use simple language and provide the article in markdown format.\n\nTopic: ${topic}\n\nPlease create a comprehensive tutorial that includes:\n1. Introduction\n2. Prerequisites (if any)\n3. Step-by-step instructions\n4. Code examples (if applicable)\n5. Best practices\n6. Common pitfalls\n7. Conclusion`;

    const response = await client.chat.completions.create({
      model: "Anthropic Claude 3.5 Sonnet",
      messages: [
        { role: "system", content: "You are an expert technical writer who creates clear, concise, and comprehensive tutorials." },
        { role: "user", content: prompt }
      ],
      temperature: 0.7,
      max_tokens: 2000
    });

    console.log("OpenAI response received:", response);    
    const content = response.choices[0]?.message?.content || "No response from AI";
    
    return content;
    
  } catch (error) {
    console.error("Error generating tutorial:", error);
  
    let errorMessage = "Unknown error occurred.";
  
  if (error instanceof OpenAI.RateLimitError) {
      errorMessage = "Rate limit exceeded. Please try again later.";
    } else if (error instanceof Error) {
      errorMessage = error.message; 
    }  
    throw new Error(`OpenAI SDK Error: ${errorMessage}`);
  }
} 

これで終了です!このアプリケーションを使って、アイデアを出したりチュートリアルを書くことができます。

すべてのステップに従った後、あなたのアプリケーションはこのようになります:

展開されたチュートリアルジェネレーターのライブアプリも試すことができます。

結論

このチュートリアルでは、GenAIプラットフォームを使用して、あなたのユースケースに基づいてAI駆動のアプリケーションを構築する方法と、サードパーティAPIとの統合方法について学びました。

その間に、GenAIプラットフォームについてもっと知りたい場合は、こちらに役立つリソースがあります。

  1. アプリケーションコードのあるGitHubリポジトリ。
  2. ウェブサイトにGenAIエージェントを統合する:ステップバイステップガイド。
  3. DigitalOceanのGenAIプラットフォームを使ったカスタムリアルタイムチャット体験の構築
  4. デジタルオーシャンの12日間 (9日目) - デジタルオーシャンのGenAIエージェントを使用した領収書の自動解析

質問がある場合は、Discordでお問い合わせいただけます。

関連記事: