チュートリアル

Twelve Labsを用いたビデオコンテンツクイズジェネレーターの構築

リシケシュ・ヤダフ

このチュートリアルでは、Twelve Labs Pegasusモデルを使用してインデックス化されたビデオから多肢選択式質問(MCQ)を自動生成する「ビデオコンテンツMCQジェネレーター」の構築手順を解説します。このジェネレーターは、ビデオのアップロード、質問への回答、および採点結果の表示を行うためのStreamlitインターフェースを備えています。

このチュートリアルでは、Twelve Labs Pegasusモデルを使用してインデックス化されたビデオから多肢選択式質問(MCQ)を自動生成する「ビデオコンテンツMCQジェネレーター」の構築手順を解説します。このジェネレーターは、ビデオのアップロード、質問への回答、および採点結果の表示を行うためのStreamlitインターフェースを備えています。

この記事の内容

No headings found on page

ニュースレターに登録する

ニュースレターに登録する

ビデオ理解に関する最新の技術進歩、チュートリアル、業界の動向をお届けします

ビデオ理解に関する最新の技術進歩、チュートリアル、業界の動向をお届けします

AIを活用してビデオを検索、分析、探索します。

2024/10/04

13分

記事へのリンクをコピー

すべての動画がインタラクティブなクイズに変わり、受動的な視聴を能動的な学習へと簡単に変化させる世界を想像してみてください 🎓

このチュートリアルでは、動画コンテンツに関連する学習体験に革命を起こすために設計されたアプリケーション「Video Content Quiz Generator」について説明します。Twelve Labsの技術を活用したこの動画ベースのツールは、動画コンテンツから多肢選択式クイズ(MCQ)を自動的に生成し、学習と評価の双方を向上させます。

迅速な評価方法を求めている教育関係者の方でも、エンゲージメントの向上を目指すコンテンツクリエイターの方でも、Video Content MCQ Generatorは能動的な学習を促進するための強力な味方になります。

アプリケーションのデモはこちらからお試しいただけます:Video Content MCQ Generator。また、こちらのReplitテンプレートから実際に使ってみることができます。

前提条件

  • Twelve Labs Playgroundにサインアップして、APIキーを生成してください。

  • ノートブックとこのアプリケーションのリポジトリはGitHubで公開されています。

アプリケーションの仕組み

このセクションでは、Twelve Labsを使用して動画コンテンツクイズ(MCQ)ジェネレーターを開発するためのアプリケーションの処理フローの概要を説明します。

プロセスは、ユーザーが動画や教育用コンテンツをアップロードすることから始まり、それが能動的なクイズ評価へと変換されます。アップロードされた動画は、セットアップ中に作成された特定のインデックスにインデックス登録されます。インデックス登録にはMarengo 2.6(エンベディングエンジン)が使用されます。インデックス登録が完了すると、Pegasus 1.1(生成エンジン)のオープンエンドプロンプト機能を使用して、動画コンテンツが多肢選択式クイズ(MCQ)に変換されます。

プロンプトは、教育用動画アナライザーの役割を割り当て、クイズの問題と正解を作成します。これらは、インデックス登録された動画に基づいて、指定された指示フォーマットに従ってユーザーの回答を評価するために使用されます。ユーザーが回答を送信すると、アプリケーションはスコアと正解を表示します。リセットボタンを押すことで、ユーザーは最初からやり直すことができます。

準備手順

インデックスを作成する方法は2つあります。Twelve Labs Playgroundを使用する方法と、提供されているコードスニペットを使用する方法です。両方の方法について解説します。PlaygroundからインデックスIDを準備するには、以下の手順に従ってください。

  • Twelve Labs Playgroundに登録し、インデックスを作成します。

  • インデックスに対して以下の動画理解(Video Understanding)エンジンを有効にします(これらのエンジンは、高度な動画理解の強固な基盤を提供します)。

Twelvelabs_API=your_api_key_here
API_URL=your_api_url_here

コードベースのアプローチをお好みの場合は、以下の手順に従ってください。

  • Twelve Labs PlaygroundからAPIキーを取得し、環境変数を準備します。

  • Twelve Labs SDKと環境変数をインポートします。環境変数から取得したTwelve Labs APIキーを使用して、SDKクライアントを初期化します。

from twelvelabs import TwelveLabs
from dotenv import load_dotenv

load_dotenv()

API_KEY = os.getenv("API_KEY")
client = TwelveLabs(api_key=API_KEY)
  • 生成タスクに使用するエンジンを指定します:

engines = [
        {
          "name": "marengo2.6",
          "options": ["visual", "conversation", "text_in_video", "logo"]
        },
        {
            "name": "pegasus1.1",
            "options": ["visual", "conversation"]
        }
    ]
  • インデックス名とエンジン構成パラメータを指定して client.index.create を呼び出し、新しいインデックスを作成します。インデックスには、一意で識別しやすい名前を使用してください。

index = client.index.create(
    name="<YOUR_INDEX_NAME>",
    engines=engines
)
print(f"A new index has been created: Index id={index.id} name={index.name} engines={index.engines}")

index.id フィールドは、新しいインデックスの一意の識別子を表します。この識別子は、動画を正しい場所にインデックス登録するために不可欠です。

これらの手順が完了したら、アプリケーションを開発する準備は万端です!

Video Content Quiz Generatorの構成解説

このチュートリアルでは、最小限のフロントエンドを持つStreamlitアプリケーションを構築します。以下のディレクトリ構造に従って作成してください:

.
├── app.py
├── requirements.txt
├── utils.py
├── .env
└── .gitignore

前の手順を終えたら、いよいよStreamlitアプリケーションを構築します。このアプリは、動画をアップロードして、それを選択式のクイズ評価に変換するシンプルな方法を提供します。アプリケーションは主に以下の2つのファイルで構成されます:

  • app.py:最小限のページアウトラインを持つアプリケーションフローを含みます

  • utils.py:アプリケーションの動作に不可欠なすべてのユーティリティ関数を含みます

1 - 動作に必要なユーティリティ関数の設定

このセクションでは、Twelve Labs SDKを使用して、動画コンテンツをインタラクティブな学習体験に変換するためのユーティリティ関数をセットアップする方法を探ります。このAI搭載システムにより、教育者やコンテンツクリエイターは、自動化された動画分析とクイズ生成を用いることで、視覚的コンテンツに基づいたエンゲージングな評価問題を作成できます。このコードは、動画のインデックス作成とカスタムプロンプトエンジニアリングを組み合わせることで、アップロードされたあらゆる動画から関連性の高い選択式クイズを自動生成し、インタラクティブな教材を作成するための汎用的なツールを提供します。

# Import the necessary libraries
import json
import tempfile
import os
from twelvelabs import TwelveLabs
from twelvelabs.models.task import Task
import re
from dotenv import load_dotenv


load_dotenv()

# Get the API Key from the Dashboard - https://playground.twelvelabs.io/dashboard/api-key
API_KEY = os.getenv("API_KEY")

# Create the INDEX ID as specified in the README.md and get the INDEX_ID
INDEX_ID = os.getenv("INDEX_ID")

# Initialize the Twelve Labs client
client = TwelveLabs(api_key=API_KEY)

# Create a temporary directory for uploaded files
UPLOAD_DIR = tempfile.mkdtemp()

def create_task(file_path):

    # Create a new task for video indexing
    return client.task.create(
        index_id=INDEX_ID,
        file=file_path,
    )

def on_task_update(task: Task):
    # Callback function to print task status updates
    print(f" Status={task.status}")

def wait_for_task(task):

    # Wait for the indexing task to complete
    task.wait_for_done(sleep_interval=5, callback=on_task_update)
    if task.status != "ready":
        raise RuntimeError(f"Indexing failed with status {task.status}")
    return task.video_id

def generate_mcq(video_id):
    # Prompt to generate the multiple choice questions based on video content
    prompt = """You're Educational Content Analyzer, and you are tasked to prepare the three Multiple Choice Questions based on the video content and the concept which is been discussed by the speaker or shown. The difficulty of the question should also gradually increase. 

    The response should be in the json format where there is Q1, Q2, and Q3. Each section would contain the question with options in question and the correct_answer"""
    

    # Twelve Labs SDK to generate text based on the video content
    gist_r = client.generate.text(
        video_id=video_id,
        prompt=prompt
    )
    return gist_r.data

def parse_json_with_regex(text):

    # Extract and parse JSON content from the response
    match = re.search(r'\{[\s\S]*\}', text)

    if match:
        json_str = match.group(0)
        try:
            return json.loads(json_str)
        except json.JSONDecodeError:

            # Return None if JSON parsing fails
            return None
    else:
        # Return None if no JSON like content is found
        return None

def save_uploaded_file(uploaded_file):

    # Save the uploaded file to the temporary directory
    file_path = os.path.join(UPLOAD_DIR, uploaded_file.name)
    with open(file_path, "wb") as f:
        f.write(uploaded_file.getbuffer())
    return file_path


# Calculate the user's score based on their answers
def calculate_score(user_answers, questions):
    return sum(answer == questions[q_num]["correct_answer"] for q_num, answer in user_answers.items())

まず、json、tempfile、os、および Twelve Labs SDK を含む必要なモジュールを、APIキーおよびインデックスIDの環境変数とともにインポートします。

  1. create_task(file_path) メソッドは、Twelve Labs SDKを利用して新しい動画インデックス登録タスクを作成します。これによりインデックス作成アプリケーション用のタスクオブジェクトが返されます。コールバック関数 on_task_update(task: Task) は、インデックス作成タスクのステータス更新を標準出力に表示します。ステータスが「ready」になり、video_id が返されると、generate_mcq(video_id) 関数がトリガーされます。

  2. generate_mcq(video_id) 関数は、Pegasus 1.1を使用して、インデックス登録された動画コンテンツに基づいて多肢選択式の質問を生成します。プロンプトは、モデルの役割、目的、および出力形式を定義します。その後、その結果は parse_json_with_regex(text) を使用してパースされます。

  3. calculate_score(user_answers, questions) 関数は、ユーザーの解答に基づいてスコアを算出し、次のセクションでユーザーが送信ボタンをクリックしたときに合計点数を返します。

2. Streamlitアプリケーションの処理フロー

2.1 クイズ評価の処理フロー

前述の通り、app.py には utils.py のユーティリティ関数を使用した Streamlit アプリケーションの適切な処理フローが含まれています。このセクションでは、クイズ評価と採点システムの機能およびフローに焦点を当てます。

# Import the necessary libraries and functions
import streamlit as st
from utils import create_task, wait_for_task, generate_mcq, parse_json_with_regex, save_uploaded_file, calculate_score

def upload_and_index():

    # Minimal custom HTML and CSS
    st.markdown("""
        <h1 style='text-align: center; color: #2c3e50; font-size: 36px; font-weight: bold; margin-bottom: 30px;'>
            📹 Video Content Quiz Generator
        <&sol;h1>
    """, unsafe_allow_html=True)

    st.write("Upload a video to start the quiz generation process and test yourself!")

    # # Check if a video has already been indexed
    if 'video_id' in st.session_state and st.session_state.video_id:
        st.info("Video already indexed. Next, Proceeding to quiz generation.")
        return generate_quiz()


    # File uploader for video files
    uploaded_file = st.file_uploader("Choose a video file", type=['mp4'])

    if uploaded_file is not None:

        # Save the uploaded file
        file_path = save_uploaded_file(uploaded_file)
        st.success("File uploaded successfully!")

        # To proceed witht the indexing of the video
        with st.spinner("Indexing video... Please wait"):
            task = create_task(file_path)
            video_id = wait_for_task(task)

        st.success("Video indexed successfully!")
        st.session_state.video_id = video_id
        
        return generate_quiz()

# Utility function to generate the quiz questions based on the indexed video
def generate_quiz():

    with st.spinner("Generating quiz questions..."):
        raw_response = generate_mcq(st.session_state.video_id)
        questions = parse_json_with_regex(raw_response)

    if questions:
        st.session_state.questions = questions
        st.session_state.page = "quiz"
        st.experimental_rerun()
    else:
        st.error("Failed to generate quiz questions. Please try again.")

def quiz():
    st.title("🎓 Video Content Quiz")
    st.write("Answer the following questions based on the video content -")

    questions = st.session_state.questions
    

    # # Initialize user answers if not already done
    if 'user_answers' not in st.session_state:
        st.session_state.user_answers = {q_num: None for q_num in questions}
    if 'submitted' not in st.session_state:
        st.session_state.submitted = False


    # Display the questions and answer options
    for q_num, q_data in questions.items():
        with st.container():
            st.subheader(f"Question {q_num[-1]}")
            st.write(q_data["question"])
            
            answer = st.radio(
                f"Select your answer for Question {q_num[-1]}:",
                options=q_data["options"],
                key=f"select_{q_num}",
                index=q_data["options"].index(st.session_state.user_answers[q_num]) if st.session_state.user_answers[q_num] else 0,
                on_change=update_answer,
                args=(q_num,)
            )
        st.markdown("---")

    # Submit quiz button
    col1, col2, col3 = st.columns([1, 2, 1])
    with col2:
        submit_button = st.button("Submit Quiz 📝", key="submit_quiz", use_container_width=True)
        if submit_button:
            submit_quiz()
            st.balloons()

    # Display results if quiz is submitted
    if st.session_state.submitted:
        display_results()

    start_over_button = st.button("Start Over 🔄", key="start_over", use_container_width=True)
    if start_over_button:
        reset_state()

def update_answer(q_num):
    # Session state, to update the user's answer
    st.session_state.user_answers[q_num] = st.session_state[f"select_{q_num}"]

def submit_quiz():
    # On the submission button, mark quiz as submitted and calculate score
    st.session_state.submitted = True
    st.session_state.score = calculate_score(st.session_state.user_answers, st.session_state.questions)

def display_results():
    # To display the user's score
    st.success(f"🏆 Your score: {st.session_state.score} out of {len(st.session_state.questions)}")

    # Display the correct results for each question
    for q_num, q_data in st.session_state.questions.items():
        with st.expander(f"Question {q_num[-1]} Details"):
            st.write(f"**Your answer:** {st.session_state.user_answers[q_num]}")
            st.write(f"**Correct answer:** {q_data['correct_answer']}")
            if st.session_state.user_answers[q_num] == q_data['correct_answer']:
                st.success("Correct! 🎉")
            else:
                st.error("Incorrect ❌")

def reset_state():
    # Clear all session state variables
    for key in list(st.session_state.keys()):
        del st.session_state[key]
    st.experimental_rerun()

Streamlit アプリケーションのこの部分は、動画コンテンツからのクイズ生成と、クイズ開始後のインタラクション管理を担当します。

upload_and_index() 関数は、Twelve Labs SDK を利用して動画のアップロードとインデックス作成を実行します。インデックス作成が完了すると、クイズ生成の処理が開始されます。

クイズ問題を自動生成するために、関数はインデックスされた動画の 'video_id' を使用します。そして、前述したように generate_mcq() を呼び出した後に parse_json_with_regex() を呼び出します。このプロセスによってクイズの問題が作成されます。生成が成功すると、クイズページが表示されます。

quiz() 関数は生成されたクイズを表示し、解答の選択やクイズの送信といったユーザーの操作を処理します。送信後には、**「最初からやり直す(Start Over)」** オプションが提供されます。クイズのやり取りを管理するために、いくつかのユーティリティ関数が用意されています:

  • update_answer() は、特定の質問に対するユーザーの回答を更新します。

  • submit_quiz() は、クイズ送信の完了処理とスコア計算を行います。

  • display_results() は、スコアや正解を含むクイズ結果画面を表示します。

  • reset_state() は、クイズを最初からやり直すために、すべてのセッション状態変数をクリアします。

2.2 Streamlitのセッション状態(Session State)とカスタムCSS

app.py パッケージの main 関数は、Streamlit ページの設定を行い、カスタムCSSを適用します。背景画像の設定、ボタンの装飾、そしてアプリケーション全体のビジュアルの外観を決定します。また、セッション状態の初期化も行います。

def main():
    # Set up the Streamlit page configuration
    st.set_page_config(page_title="QnA Generator", page_icon="🎥", layout="wide")

    # Custom CSS
    st.markdown("""
        <style>
        .stApp {
            background-color: #f0f2f6;
        }
        .stButton > button {
            background-color: #4CAF50;
            color: white;
            font-weight: bold;
            border-radius: 30px;
            padding: 15px 30px;
            font-size: 18px;
            transition: all 0.3s ease 0s;
            border: none;
            box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
        }
        .stButton > button:hover {
            background-color: #45a049;
            box-shadow: 0 6px 8px rgba(0, 0, 0, 0.15);
            transform: translateY(-2px);
        }
        .stButton > button:active {
            transform: translateY(0px);
            box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
        }
        .stRadio > label {
            background-color: #e1e5eb;
            padding: 12px;
            border-radius: 8px;
            margin-bottom: 12px;
            transition: all 0.2s ease 0s;
            cursor: pointer;
        }
        .stRadio > label:hover {
            background-color: #d0d4d9;
            transform: translateY(-2px);
            box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
        }
        .stExpander {
            background-color: #ffffff;
            border-radius: 15px;
            box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
            margin-bottom: 20px;
        }
        h1 {
            color: #2c3e50;
            font-size: 36px;
            font-weight: bold;
            margin-bottom: 20px;
        }
        h2, h3 {
            color: #34495e;
        }
        .stAlert {
            border-radius: 10px;
            font-weight: bold;
        }
        .stSpinner > div {
            border-color: #4CAF50 !important;
        }
        [data-testid="stAppViewContainer"] {
            background-image: url("https://img.freepik.com/free-photo/vivid-blurred-colorful-wallpaper-background_58702-3508.jpg?size=626&ext=jpg");
            background-size: cover;
        }
        [data-testid="stHeader"] {
            background-color: rgba(0,0,0,0);
        }
        [data-testid="stToolbar"] {
            right: 2rem;
            background-image: url("");
            background-size: cover;
        }
        <&sol;style>
    """, unsafe_allow_html=True)


    # Initialize the page state if not already set
    if 'page' not in st.session_state:
        st.session_state.page = "upload"


    # To show the appropriate page based on the current state
    if st.session_state.page == "upload":
        upload_and_index()
    elif st.session_state.page == "quiz":
        quiz()

if __name__ == "__main__":
    main()

Streamlit アプリケーションを起動するエントリポイントとして機能する main() 関数は、タイトル付きのページを設定し、スペースを有効活用するためにワイドレイアウトを使用します。カスタムCSSは、背景、ボタン、ラジオボタン、エクスパンダーのスタイルを整え、意図したデザインへと引き締めて視覚的な魅力を向上させます。

アプリの異なるファイル/セクション間のナビゲーションを管理するために、セッション状態変数 'page' が初期化されます。定義されていない場合、デフォルトで "upload" に設定されます。

アプリケーションは、条件分岐に従い、現在の状態に基づいて適切なページを描画します:

  • "upload" — 動画アップロードインターフェース用の upload_and_index() 関数を呼び出します。

  • "quiz" — 生成されたクイズを表示するために quiz() 関数を呼び出します。

以下は、PCA(主成分分析)に関する動画コンテンツをアップロードし、インデックスを作成した後に自動生成されたデモクイズの様子です:

Twelve Labsが提供するオープンエンドプロンプトと Pegasus 1.1 をさらに試して、探索してみてください。

このチュートリアルからさらに一歩進めるためのアイデア

アプリケーションの仕組みと開発プロセスを理解すれば、革新的なアイデアを形にし、ユーザーのニーズを満たすプロダクトを作成できるようになります。このチュートリアルブログと同様のユースケースを提案します:

📚️ マイペース学習(Self-Paced Learning):生徒が動画レッスンの理解度をテストするための問題を独自に作成できます。

🎓 教育評価(Educational Assessment):教師は講義動画や教材コンテンツに基づく学習クイズを素早く作成できます。

🏢️ 企業研修(Corporate Training):組織は、さまざまな重要ステップに対応する自動評価クイズを組み込むことで、動画ベースの研修プログラムを強化できます。

🎬 コンテンツへのエンゲージメント(Content Engagement):YouTuberなどの動画クリエイターは、動画に連動したクイズを提供することで、視聴者のエンゲージメントを高めることができます。

🧠 記憶定着(Memory Retention):情報動画から生成された質問に回答することで、学習した知識をしっかりと記憶に定着させることができます。

結論

このブログ記事では、Twelve Labsを使用した動画コンテンツクイズジェネレーターの動作手順と開発プロセスについて包括的に解説しました。チュートリアルにお付き合いいただきありがとうございます。ユーザー体験の向上、そして様々な課題を解決するような皆さんの魅力的なアイデアを楽しみにしています。

追加リソース

自動生成タスクに使用されるエンジンの詳細を理解するには、Marengo 2.6(エンベディングエンジン)およびPegasus 1.1(ジェネレーターエンジン)を参照してください。Twelve Labsをさらに深く知り、動画コンテンツ分析の理解を広げるには、以下の情報が役立ちます:

  • Discordコミュニティ:開発者や熱心なメンバーが集まる活発なコミュニティに参加して、アイデアを議論したり、質問をしたり、プロジェクトを共有したりしましょう。

  • サンプルアプリ一覧:様々なサンプルアプリケーションを調べて、次のプロジェクトのインスピレーションを得たり、新しい実装テクニックを学びましょう。

  • チュートリアルを調べる:包括的なチュートリアルを通じて、Twelve Labsで何ができるのか、その機能をさらに深く体験してください。

これらのリソースを積極的に活用し、知識を広げ、Twelve Labsの動画理解技術を活用した革新的なアプリケーションを構築してみてください。

すべての動画がインタラクティブなクイズに変わり、受動的な視聴を能動的な学習へと簡単に変化させる世界を想像してみてください 🎓

このチュートリアルでは、動画コンテンツに関連する学習体験に革命を起こすために設計されたアプリケーション「Video Content Quiz Generator」について説明します。Twelve Labsの技術を活用したこの動画ベースのツールは、動画コンテンツから多肢選択式クイズ(MCQ)を自動的に生成し、学習と評価の双方を向上させます。

迅速な評価方法を求めている教育関係者の方でも、エンゲージメントの向上を目指すコンテンツクリエイターの方でも、Video Content MCQ Generatorは能動的な学習を促進するための強力な味方になります。

アプリケーションのデモはこちらからお試しいただけます:Video Content MCQ Generator。また、こちらのReplitテンプレートから実際に使ってみることができます。

前提条件

  • Twelve Labs Playgroundにサインアップして、APIキーを生成してください。

  • ノートブックとこのアプリケーションのリポジトリはGitHubで公開されています。

アプリケーションの仕組み

このセクションでは、Twelve Labsを使用して動画コンテンツクイズ(MCQ)ジェネレーターを開発するためのアプリケーションの処理フローの概要を説明します。

プロセスは、ユーザーが動画や教育用コンテンツをアップロードすることから始まり、それが能動的なクイズ評価へと変換されます。アップロードされた動画は、セットアップ中に作成された特定のインデックスにインデックス登録されます。インデックス登録にはMarengo 2.6(エンベディングエンジン)が使用されます。インデックス登録が完了すると、Pegasus 1.1(生成エンジン)のオープンエンドプロンプト機能を使用して、動画コンテンツが多肢選択式クイズ(MCQ)に変換されます。

プロンプトは、教育用動画アナライザーの役割を割り当て、クイズの問題と正解を作成します。これらは、インデックス登録された動画に基づいて、指定された指示フォーマットに従ってユーザーの回答を評価するために使用されます。ユーザーが回答を送信すると、アプリケーションはスコアと正解を表示します。リセットボタンを押すことで、ユーザーは最初からやり直すことができます。

準備手順

インデックスを作成する方法は2つあります。Twelve Labs Playgroundを使用する方法と、提供されているコードスニペットを使用する方法です。両方の方法について解説します。PlaygroundからインデックスIDを準備するには、以下の手順に従ってください。

  • Twelve Labs Playgroundに登録し、インデックスを作成します。

  • インデックスに対して以下の動画理解(Video Understanding)エンジンを有効にします(これらのエンジンは、高度な動画理解の強固な基盤を提供します)。

Twelvelabs_API=your_api_key_here
API_URL=your_api_url_here

コードベースのアプローチをお好みの場合は、以下の手順に従ってください。

  • Twelve Labs PlaygroundからAPIキーを取得し、環境変数を準備します。

  • Twelve Labs SDKと環境変数をインポートします。環境変数から取得したTwelve Labs APIキーを使用して、SDKクライアントを初期化します。

from twelvelabs import TwelveLabs
from dotenv import load_dotenv

load_dotenv()

API_KEY = os.getenv("API_KEY")
client = TwelveLabs(api_key=API_KEY)
  • 生成タスクに使用するエンジンを指定します:

engines = [
        {
          "name": "marengo2.6",
          "options": ["visual", "conversation", "text_in_video", "logo"]
        },
        {
            "name": "pegasus1.1",
            "options": ["visual", "conversation"]
        }
    ]
  • インデックス名とエンジン構成パラメータを指定して client.index.create を呼び出し、新しいインデックスを作成します。インデックスには、一意で識別しやすい名前を使用してください。

index = client.index.create(
    name="<YOUR_INDEX_NAME>",
    engines=engines
)
print(f"A new index has been created: Index id={index.id} name={index.name} engines={index.engines}")

index.id フィールドは、新しいインデックスの一意の識別子を表します。この識別子は、動画を正しい場所にインデックス登録するために不可欠です。

これらの手順が完了したら、アプリケーションを開発する準備は万端です!

Video Content Quiz Generatorの構成解説

このチュートリアルでは、最小限のフロントエンドを持つStreamlitアプリケーションを構築します。以下のディレクトリ構造に従って作成してください:

.
├── app.py
├── requirements.txt
├── utils.py
├── .env
└── .gitignore

前の手順を終えたら、いよいよStreamlitアプリケーションを構築します。このアプリは、動画をアップロードして、それを選択式のクイズ評価に変換するシンプルな方法を提供します。アプリケーションは主に以下の2つのファイルで構成されます:

  • app.py:最小限のページアウトラインを持つアプリケーションフローを含みます

  • utils.py:アプリケーションの動作に不可欠なすべてのユーティリティ関数を含みます

1 - 動作に必要なユーティリティ関数の設定

このセクションでは、Twelve Labs SDKを使用して、動画コンテンツをインタラクティブな学習体験に変換するためのユーティリティ関数をセットアップする方法を探ります。このAI搭載システムにより、教育者やコンテンツクリエイターは、自動化された動画分析とクイズ生成を用いることで、視覚的コンテンツに基づいたエンゲージングな評価問題を作成できます。このコードは、動画のインデックス作成とカスタムプロンプトエンジニアリングを組み合わせることで、アップロードされたあらゆる動画から関連性の高い選択式クイズを自動生成し、インタラクティブな教材を作成するための汎用的なツールを提供します。

# Import the necessary libraries
import json
import tempfile
import os
from twelvelabs import TwelveLabs
from twelvelabs.models.task import Task
import re
from dotenv import load_dotenv


load_dotenv()

# Get the API Key from the Dashboard - https://playground.twelvelabs.io/dashboard/api-key
API_KEY = os.getenv("API_KEY")

# Create the INDEX ID as specified in the README.md and get the INDEX_ID
INDEX_ID = os.getenv("INDEX_ID")

# Initialize the Twelve Labs client
client = TwelveLabs(api_key=API_KEY)

# Create a temporary directory for uploaded files
UPLOAD_DIR = tempfile.mkdtemp()

def create_task(file_path):

    # Create a new task for video indexing
    return client.task.create(
        index_id=INDEX_ID,
        file=file_path,
    )

def on_task_update(task: Task):
    # Callback function to print task status updates
    print(f" Status={task.status}")

def wait_for_task(task):

    # Wait for the indexing task to complete
    task.wait_for_done(sleep_interval=5, callback=on_task_update)
    if task.status != "ready":
        raise RuntimeError(f"Indexing failed with status {task.status}")
    return task.video_id

def generate_mcq(video_id):
    # Prompt to generate the multiple choice questions based on video content
    prompt = """You're Educational Content Analyzer, and you are tasked to prepare the three Multiple Choice Questions based on the video content and the concept which is been discussed by the speaker or shown. The difficulty of the question should also gradually increase. 

    The response should be in the json format where there is Q1, Q2, and Q3. Each section would contain the question with options in question and the correct_answer"""
    

    # Twelve Labs SDK to generate text based on the video content
    gist_r = client.generate.text(
        video_id=video_id,
        prompt=prompt
    )
    return gist_r.data

def parse_json_with_regex(text):

    # Extract and parse JSON content from the response
    match = re.search(r'\{[\s\S]*\}', text)

    if match:
        json_str = match.group(0)
        try:
            return json.loads(json_str)
        except json.JSONDecodeError:

            # Return None if JSON parsing fails
            return None
    else:
        # Return None if no JSON like content is found
        return None

def save_uploaded_file(uploaded_file):

    # Save the uploaded file to the temporary directory
    file_path = os.path.join(UPLOAD_DIR, uploaded_file.name)
    with open(file_path, "wb") as f:
        f.write(uploaded_file.getbuffer())
    return file_path


# Calculate the user's score based on their answers
def calculate_score(user_answers, questions):
    return sum(answer == questions[q_num]["correct_answer"] for q_num, answer in user_answers.items())

まず、json、tempfile、os、および Twelve Labs SDK を含む必要なモジュールを、APIキーおよびインデックスIDの環境変数とともにインポートします。

  1. create_task(file_path) メソッドは、Twelve Labs SDKを利用して新しい動画インデックス登録タスクを作成します。これによりインデックス作成アプリケーション用のタスクオブジェクトが返されます。コールバック関数 on_task_update(task: Task) は、インデックス作成タスクのステータス更新を標準出力に表示します。ステータスが「ready」になり、video_id が返されると、generate_mcq(video_id) 関数がトリガーされます。

  2. generate_mcq(video_id) 関数は、Pegasus 1.1を使用して、インデックス登録された動画コンテンツに基づいて多肢選択式の質問を生成します。プロンプトは、モデルの役割、目的、および出力形式を定義します。その後、その結果は parse_json_with_regex(text) を使用してパースされます。

  3. calculate_score(user_answers, questions) 関数は、ユーザーの解答に基づいてスコアを算出し、次のセクションでユーザーが送信ボタンをクリックしたときに合計点数を返します。

2. Streamlitアプリケーションの処理フロー

2.1 クイズ評価の処理フロー

前述の通り、app.py には utils.py のユーティリティ関数を使用した Streamlit アプリケーションの適切な処理フローが含まれています。このセクションでは、クイズ評価と採点システムの機能およびフローに焦点を当てます。

# Import the necessary libraries and functions
import streamlit as st
from utils import create_task, wait_for_task, generate_mcq, parse_json_with_regex, save_uploaded_file, calculate_score

def upload_and_index():

    # Minimal custom HTML and CSS
    st.markdown("""
        <h1 style='text-align: center; color: #2c3e50; font-size: 36px; font-weight: bold; margin-bottom: 30px;'>
            📹 Video Content Quiz Generator
        <&sol;h1>
    """, unsafe_allow_html=True)

    st.write("Upload a video to start the quiz generation process and test yourself!")

    # # Check if a video has already been indexed
    if 'video_id' in st.session_state and st.session_state.video_id:
        st.info("Video already indexed. Next, Proceeding to quiz generation.")
        return generate_quiz()


    # File uploader for video files
    uploaded_file = st.file_uploader("Choose a video file", type=['mp4'])

    if uploaded_file is not None:

        # Save the uploaded file
        file_path = save_uploaded_file(uploaded_file)
        st.success("File uploaded successfully!")

        # To proceed witht the indexing of the video
        with st.spinner("Indexing video... Please wait"):
            task = create_task(file_path)
            video_id = wait_for_task(task)

        st.success("Video indexed successfully!")
        st.session_state.video_id = video_id
        
        return generate_quiz()

# Utility function to generate the quiz questions based on the indexed video
def generate_quiz():

    with st.spinner("Generating quiz questions..."):
        raw_response = generate_mcq(st.session_state.video_id)
        questions = parse_json_with_regex(raw_response)

    if questions:
        st.session_state.questions = questions
        st.session_state.page = "quiz"
        st.experimental_rerun()
    else:
        st.error("Failed to generate quiz questions. Please try again.")

def quiz():
    st.title("🎓 Video Content Quiz")
    st.write("Answer the following questions based on the video content -")

    questions = st.session_state.questions
    

    # # Initialize user answers if not already done
    if 'user_answers' not in st.session_state:
        st.session_state.user_answers = {q_num: None for q_num in questions}
    if 'submitted' not in st.session_state:
        st.session_state.submitted = False


    # Display the questions and answer options
    for q_num, q_data in questions.items():
        with st.container():
            st.subheader(f"Question {q_num[-1]}")
            st.write(q_data["question"])
            
            answer = st.radio(
                f"Select your answer for Question {q_num[-1]}:",
                options=q_data["options"],
                key=f"select_{q_num}",
                index=q_data["options"].index(st.session_state.user_answers[q_num]) if st.session_state.user_answers[q_num] else 0,
                on_change=update_answer,
                args=(q_num,)
            )
        st.markdown("---")

    # Submit quiz button
    col1, col2, col3 = st.columns([1, 2, 1])
    with col2:
        submit_button = st.button("Submit Quiz 📝", key="submit_quiz", use_container_width=True)
        if submit_button:
            submit_quiz()
            st.balloons()

    # Display results if quiz is submitted
    if st.session_state.submitted:
        display_results()

    start_over_button = st.button("Start Over 🔄", key="start_over", use_container_width=True)
    if start_over_button:
        reset_state()

def update_answer(q_num):
    # Session state, to update the user's answer
    st.session_state.user_answers[q_num] = st.session_state[f"select_{q_num}"]

def submit_quiz():
    # On the submission button, mark quiz as submitted and calculate score
    st.session_state.submitted = True
    st.session_state.score = calculate_score(st.session_state.user_answers, st.session_state.questions)

def display_results():
    # To display the user's score
    st.success(f"🏆 Your score: {st.session_state.score} out of {len(st.session_state.questions)}")

    # Display the correct results for each question
    for q_num, q_data in st.session_state.questions.items():
        with st.expander(f"Question {q_num[-1]} Details"):
            st.write(f"**Your answer:** {st.session_state.user_answers[q_num]}")
            st.write(f"**Correct answer:** {q_data['correct_answer']}")
            if st.session_state.user_answers[q_num] == q_data['correct_answer']:
                st.success("Correct! 🎉")
            else:
                st.error("Incorrect ❌")

def reset_state():
    # Clear all session state variables
    for key in list(st.session_state.keys()):
        del st.session_state[key]
    st.experimental_rerun()

Streamlit アプリケーションのこの部分は、動画コンテンツからのクイズ生成と、クイズ開始後のインタラクション管理を担当します。

upload_and_index() 関数は、Twelve Labs SDK を利用して動画のアップロードとインデックス作成を実行します。インデックス作成が完了すると、クイズ生成の処理が開始されます。

クイズ問題を自動生成するために、関数はインデックスされた動画の 'video_id' を使用します。そして、前述したように generate_mcq() を呼び出した後に parse_json_with_regex() を呼び出します。このプロセスによってクイズの問題が作成されます。生成が成功すると、クイズページが表示されます。

quiz() 関数は生成されたクイズを表示し、解答の選択やクイズの送信といったユーザーの操作を処理します。送信後には、**「最初からやり直す(Start Over)」** オプションが提供されます。クイズのやり取りを管理するために、いくつかのユーティリティ関数が用意されています:

  • update_answer() は、特定の質問に対するユーザーの回答を更新します。

  • submit_quiz() は、クイズ送信の完了処理とスコア計算を行います。

  • display_results() は、スコアや正解を含むクイズ結果画面を表示します。

  • reset_state() は、クイズを最初からやり直すために、すべてのセッション状態変数をクリアします。

2.2 Streamlitのセッション状態(Session State)とカスタムCSS

app.py パッケージの main 関数は、Streamlit ページの設定を行い、カスタムCSSを適用します。背景画像の設定、ボタンの装飾、そしてアプリケーション全体のビジュアルの外観を決定します。また、セッション状態の初期化も行います。

def main():
    # Set up the Streamlit page configuration
    st.set_page_config(page_title="QnA Generator", page_icon="🎥", layout="wide")

    # Custom CSS
    st.markdown("""
        <style>
        .stApp {
            background-color: #f0f2f6;
        }
        .stButton > button {
            background-color: #4CAF50;
            color: white;
            font-weight: bold;
            border-radius: 30px;
            padding: 15px 30px;
            font-size: 18px;
            transition: all 0.3s ease 0s;
            border: none;
            box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
        }
        .stButton > button:hover {
            background-color: #45a049;
            box-shadow: 0 6px 8px rgba(0, 0, 0, 0.15);
            transform: translateY(-2px);
        }
        .stButton > button:active {
            transform: translateY(0px);
            box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
        }
        .stRadio > label {
            background-color: #e1e5eb;
            padding: 12px;
            border-radius: 8px;
            margin-bottom: 12px;
            transition: all 0.2s ease 0s;
            cursor: pointer;
        }
        .stRadio > label:hover {
            background-color: #d0d4d9;
            transform: translateY(-2px);
            box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
        }
        .stExpander {
            background-color: #ffffff;
            border-radius: 15px;
            box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
            margin-bottom: 20px;
        }
        h1 {
            color: #2c3e50;
            font-size: 36px;
            font-weight: bold;
            margin-bottom: 20px;
        }
        h2, h3 {
            color: #34495e;
        }
        .stAlert {
            border-radius: 10px;
            font-weight: bold;
        }
        .stSpinner > div {
            border-color: #4CAF50 !important;
        }
        [data-testid="stAppViewContainer"] {
            background-image: url("https://img.freepik.com/free-photo/vivid-blurred-colorful-wallpaper-background_58702-3508.jpg?size=626&ext=jpg");
            background-size: cover;
        }
        [data-testid="stHeader"] {
            background-color: rgba(0,0,0,0);
        }
        [data-testid="stToolbar"] {
            right: 2rem;
            background-image: url("");
            background-size: cover;
        }
        <&sol;style>
    """, unsafe_allow_html=True)


    # Initialize the page state if not already set
    if 'page' not in st.session_state:
        st.session_state.page = "upload"


    # To show the appropriate page based on the current state
    if st.session_state.page == "upload":
        upload_and_index()
    elif st.session_state.page == "quiz":
        quiz()

if __name__ == "__main__":
    main()

Streamlit アプリケーションを起動するエントリポイントとして機能する main() 関数は、タイトル付きのページを設定し、スペースを有効活用するためにワイドレイアウトを使用します。カスタムCSSは、背景、ボタン、ラジオボタン、エクスパンダーのスタイルを整え、意図したデザインへと引き締めて視覚的な魅力を向上させます。

アプリの異なるファイル/セクション間のナビゲーションを管理するために、セッション状態変数 'page' が初期化されます。定義されていない場合、デフォルトで "upload" に設定されます。

アプリケーションは、条件分岐に従い、現在の状態に基づいて適切なページを描画します:

  • "upload" — 動画アップロードインターフェース用の upload_and_index() 関数を呼び出します。

  • "quiz" — 生成されたクイズを表示するために quiz() 関数を呼び出します。

以下は、PCA(主成分分析)に関する動画コンテンツをアップロードし、インデックスを作成した後に自動生成されたデモクイズの様子です:

Twelve Labsが提供するオープンエンドプロンプトと Pegasus 1.1 をさらに試して、探索してみてください。

このチュートリアルからさらに一歩進めるためのアイデア

アプリケーションの仕組みと開発プロセスを理解すれば、革新的なアイデアを形にし、ユーザーのニーズを満たすプロダクトを作成できるようになります。このチュートリアルブログと同様のユースケースを提案します:

📚️ マイペース学習(Self-Paced Learning):生徒が動画レッスンの理解度をテストするための問題を独自に作成できます。

🎓 教育評価(Educational Assessment):教師は講義動画や教材コンテンツに基づく学習クイズを素早く作成できます。

🏢️ 企業研修(Corporate Training):組織は、さまざまな重要ステップに対応する自動評価クイズを組み込むことで、動画ベースの研修プログラムを強化できます。

🎬 コンテンツへのエンゲージメント(Content Engagement):YouTuberなどの動画クリエイターは、動画に連動したクイズを提供することで、視聴者のエンゲージメントを高めることができます。

🧠 記憶定着(Memory Retention):情報動画から生成された質問に回答することで、学習した知識をしっかりと記憶に定着させることができます。

結論

このブログ記事では、Twelve Labsを使用した動画コンテンツクイズジェネレーターの動作手順と開発プロセスについて包括的に解説しました。チュートリアルにお付き合いいただきありがとうございます。ユーザー体験の向上、そして様々な課題を解決するような皆さんの魅力的なアイデアを楽しみにしています。

追加リソース

自動生成タスクに使用されるエンジンの詳細を理解するには、Marengo 2.6(エンベディングエンジン)およびPegasus 1.1(ジェネレーターエンジン)を参照してください。Twelve Labsをさらに深く知り、動画コンテンツ分析の理解を広げるには、以下の情報が役立ちます:

  • Discordコミュニティ:開発者や熱心なメンバーが集まる活発なコミュニティに参加して、アイデアを議論したり、質問をしたり、プロジェクトを共有したりしましょう。

  • サンプルアプリ一覧:様々なサンプルアプリケーションを調べて、次のプロジェクトのインスピレーションを得たり、新しい実装テクニックを学びましょう。

  • チュートリアルを調べる:包括的なチュートリアルを通じて、Twelve Labsで何ができるのか、その機能をさらに深く体験してください。

これらのリソースを積極的に活用し、知識を広げ、Twelve Labsの動画理解技術を活用した革新的なアプリケーションを構築してみてください。

すべての動画がインタラクティブなクイズに変わり、受動的な視聴を能動的な学習へと簡単に変化させる世界を想像してみてください 🎓

このチュートリアルでは、動画コンテンツに関連する学習体験に革命を起こすために設計されたアプリケーション「Video Content Quiz Generator」について説明します。Twelve Labsの技術を活用したこの動画ベースのツールは、動画コンテンツから多肢選択式クイズ(MCQ)を自動的に生成し、学習と評価の双方を向上させます。

迅速な評価方法を求めている教育関係者の方でも、エンゲージメントの向上を目指すコンテンツクリエイターの方でも、Video Content MCQ Generatorは能動的な学習を促進するための強力な味方になります。

アプリケーションのデモはこちらからお試しいただけます:Video Content MCQ Generator。また、こちらのReplitテンプレートから実際に使ってみることができます。

前提条件

  • Twelve Labs Playgroundにサインアップして、APIキーを生成してください。

  • ノートブックとこのアプリケーションのリポジトリはGitHubで公開されています。

アプリケーションの仕組み

このセクションでは、Twelve Labsを使用して動画コンテンツクイズ(MCQ)ジェネレーターを開発するためのアプリケーションの処理フローの概要を説明します。

プロセスは、ユーザーが動画や教育用コンテンツをアップロードすることから始まり、それが能動的なクイズ評価へと変換されます。アップロードされた動画は、セットアップ中に作成された特定のインデックスにインデックス登録されます。インデックス登録にはMarengo 2.6(エンベディングエンジン)が使用されます。インデックス登録が完了すると、Pegasus 1.1(生成エンジン)のオープンエンドプロンプト機能を使用して、動画コンテンツが多肢選択式クイズ(MCQ)に変換されます。

プロンプトは、教育用動画アナライザーの役割を割り当て、クイズの問題と正解を作成します。これらは、インデックス登録された動画に基づいて、指定された指示フォーマットに従ってユーザーの回答を評価するために使用されます。ユーザーが回答を送信すると、アプリケーションはスコアと正解を表示します。リセットボタンを押すことで、ユーザーは最初からやり直すことができます。

準備手順

インデックスを作成する方法は2つあります。Twelve Labs Playgroundを使用する方法と、提供されているコードスニペットを使用する方法です。両方の方法について解説します。PlaygroundからインデックスIDを準備するには、以下の手順に従ってください。

  • Twelve Labs Playgroundに登録し、インデックスを作成します。

  • インデックスに対して以下の動画理解(Video Understanding)エンジンを有効にします(これらのエンジンは、高度な動画理解の強固な基盤を提供します)。

Twelvelabs_API=your_api_key_here
API_URL=your_api_url_here

コードベースのアプローチをお好みの場合は、以下の手順に従ってください。

  • Twelve Labs PlaygroundからAPIキーを取得し、環境変数を準備します。

  • Twelve Labs SDKと環境変数をインポートします。環境変数から取得したTwelve Labs APIキーを使用して、SDKクライアントを初期化します。

from twelvelabs import TwelveLabs
from dotenv import load_dotenv

load_dotenv()

API_KEY = os.getenv("API_KEY")
client = TwelveLabs(api_key=API_KEY)
  • 生成タスクに使用するエンジンを指定します:

engines = [
        {
          "name": "marengo2.6",
          "options": ["visual", "conversation", "text_in_video", "logo"]
        },
        {
            "name": "pegasus1.1",
            "options": ["visual", "conversation"]
        }
    ]
  • インデックス名とエンジン構成パラメータを指定して client.index.create を呼び出し、新しいインデックスを作成します。インデックスには、一意で識別しやすい名前を使用してください。

index = client.index.create(
    name="<YOUR_INDEX_NAME>",
    engines=engines
)
print(f"A new index has been created: Index id={index.id} name={index.name} engines={index.engines}")

index.id フィールドは、新しいインデックスの一意の識別子を表します。この識別子は、動画を正しい場所にインデックス登録するために不可欠です。

これらの手順が完了したら、アプリケーションを開発する準備は万端です!

Video Content Quiz Generatorの構成解説

このチュートリアルでは、最小限のフロントエンドを持つStreamlitアプリケーションを構築します。以下のディレクトリ構造に従って作成してください:

.
├── app.py
├── requirements.txt
├── utils.py
├── .env
└── .gitignore

前の手順を終えたら、いよいよStreamlitアプリケーションを構築します。このアプリは、動画をアップロードして、それを選択式のクイズ評価に変換するシンプルな方法を提供します。アプリケーションは主に以下の2つのファイルで構成されます:

  • app.py:最小限のページアウトラインを持つアプリケーションフローを含みます

  • utils.py:アプリケーションの動作に不可欠なすべてのユーティリティ関数を含みます

1 - 動作に必要なユーティリティ関数の設定

このセクションでは、Twelve Labs SDKを使用して、動画コンテンツをインタラクティブな学習体験に変換するためのユーティリティ関数をセットアップする方法を探ります。このAI搭載システムにより、教育者やコンテンツクリエイターは、自動化された動画分析とクイズ生成を用いることで、視覚的コンテンツに基づいたエンゲージングな評価問題を作成できます。このコードは、動画のインデックス作成とカスタムプロンプトエンジニアリングを組み合わせることで、アップロードされたあらゆる動画から関連性の高い選択式クイズを自動生成し、インタラクティブな教材を作成するための汎用的なツールを提供します。

# Import the necessary libraries
import json
import tempfile
import os
from twelvelabs import TwelveLabs
from twelvelabs.models.task import Task
import re
from dotenv import load_dotenv


load_dotenv()

# Get the API Key from the Dashboard - https://playground.twelvelabs.io/dashboard/api-key
API_KEY = os.getenv("API_KEY")

# Create the INDEX ID as specified in the README.md and get the INDEX_ID
INDEX_ID = os.getenv("INDEX_ID")

# Initialize the Twelve Labs client
client = TwelveLabs(api_key=API_KEY)

# Create a temporary directory for uploaded files
UPLOAD_DIR = tempfile.mkdtemp()

def create_task(file_path):

    # Create a new task for video indexing
    return client.task.create(
        index_id=INDEX_ID,
        file=file_path,
    )

def on_task_update(task: Task):
    # Callback function to print task status updates
    print(f" Status={task.status}")

def wait_for_task(task):

    # Wait for the indexing task to complete
    task.wait_for_done(sleep_interval=5, callback=on_task_update)
    if task.status != "ready":
        raise RuntimeError(f"Indexing failed with status {task.status}")
    return task.video_id

def generate_mcq(video_id):
    # Prompt to generate the multiple choice questions based on video content
    prompt = """You're Educational Content Analyzer, and you are tasked to prepare the three Multiple Choice Questions based on the video content and the concept which is been discussed by the speaker or shown. The difficulty of the question should also gradually increase. 

    The response should be in the json format where there is Q1, Q2, and Q3. Each section would contain the question with options in question and the correct_answer"""
    

    # Twelve Labs SDK to generate text based on the video content
    gist_r = client.generate.text(
        video_id=video_id,
        prompt=prompt
    )
    return gist_r.data

def parse_json_with_regex(text):

    # Extract and parse JSON content from the response
    match = re.search(r'\{[\s\S]*\}', text)

    if match:
        json_str = match.group(0)
        try:
            return json.loads(json_str)
        except json.JSONDecodeError:

            # Return None if JSON parsing fails
            return None
    else:
        # Return None if no JSON like content is found
        return None

def save_uploaded_file(uploaded_file):

    # Save the uploaded file to the temporary directory
    file_path = os.path.join(UPLOAD_DIR, uploaded_file.name)
    with open(file_path, "wb") as f:
        f.write(uploaded_file.getbuffer())
    return file_path


# Calculate the user's score based on their answers
def calculate_score(user_answers, questions):
    return sum(answer == questions[q_num]["correct_answer"] for q_num, answer in user_answers.items())

まず、json、tempfile、os、および Twelve Labs SDK を含む必要なモジュールを、APIキーおよびインデックスIDの環境変数とともにインポートします。

  1. create_task(file_path) メソッドは、Twelve Labs SDKを利用して新しい動画インデックス登録タスクを作成します。これによりインデックス作成アプリケーション用のタスクオブジェクトが返されます。コールバック関数 on_task_update(task: Task) は、インデックス作成タスクのステータス更新を標準出力に表示します。ステータスが「ready」になり、video_id が返されると、generate_mcq(video_id) 関数がトリガーされます。

  2. generate_mcq(video_id) 関数は、Pegasus 1.1を使用して、インデックス登録された動画コンテンツに基づいて多肢選択式の質問を生成します。プロンプトは、モデルの役割、目的、および出力形式を定義します。その後、その結果は parse_json_with_regex(text) を使用してパースされます。

  3. calculate_score(user_answers, questions) 関数は、ユーザーの解答に基づいてスコアを算出し、次のセクションでユーザーが送信ボタンをクリックしたときに合計点数を返します。

2. Streamlitアプリケーションの処理フロー

2.1 クイズ評価の処理フロー

前述の通り、app.py には utils.py のユーティリティ関数を使用した Streamlit アプリケーションの適切な処理フローが含まれています。このセクションでは、クイズ評価と採点システムの機能およびフローに焦点を当てます。

# Import the necessary libraries and functions
import streamlit as st
from utils import create_task, wait_for_task, generate_mcq, parse_json_with_regex, save_uploaded_file, calculate_score

def upload_and_index():

    # Minimal custom HTML and CSS
    st.markdown("""
        <h1 style='text-align: center; color: #2c3e50; font-size: 36px; font-weight: bold; margin-bottom: 30px;'>
            📹 Video Content Quiz Generator
        <&sol;h1>
    """, unsafe_allow_html=True)

    st.write("Upload a video to start the quiz generation process and test yourself!")

    # # Check if a video has already been indexed
    if 'video_id' in st.session_state and st.session_state.video_id:
        st.info("Video already indexed. Next, Proceeding to quiz generation.")
        return generate_quiz()


    # File uploader for video files
    uploaded_file = st.file_uploader("Choose a video file", type=['mp4'])

    if uploaded_file is not None:

        # Save the uploaded file
        file_path = save_uploaded_file(uploaded_file)
        st.success("File uploaded successfully!")

        # To proceed witht the indexing of the video
        with st.spinner("Indexing video... Please wait"):
            task = create_task(file_path)
            video_id = wait_for_task(task)

        st.success("Video indexed successfully!")
        st.session_state.video_id = video_id
        
        return generate_quiz()

# Utility function to generate the quiz questions based on the indexed video
def generate_quiz():

    with st.spinner("Generating quiz questions..."):
        raw_response = generate_mcq(st.session_state.video_id)
        questions = parse_json_with_regex(raw_response)

    if questions:
        st.session_state.questions = questions
        st.session_state.page = "quiz"
        st.experimental_rerun()
    else:
        st.error("Failed to generate quiz questions. Please try again.")

def quiz():
    st.title("🎓 Video Content Quiz")
    st.write("Answer the following questions based on the video content -")

    questions = st.session_state.questions
    

    # # Initialize user answers if not already done
    if 'user_answers' not in st.session_state:
        st.session_state.user_answers = {q_num: None for q_num in questions}
    if 'submitted' not in st.session_state:
        st.session_state.submitted = False


    # Display the questions and answer options
    for q_num, q_data in questions.items():
        with st.container():
            st.subheader(f"Question {q_num[-1]}")
            st.write(q_data["question"])
            
            answer = st.radio(
                f"Select your answer for Question {q_num[-1]}:",
                options=q_data["options"],
                key=f"select_{q_num}",
                index=q_data["options"].index(st.session_state.user_answers[q_num]) if st.session_state.user_answers[q_num] else 0,
                on_change=update_answer,
                args=(q_num,)
            )
        st.markdown("---")

    # Submit quiz button
    col1, col2, col3 = st.columns([1, 2, 1])
    with col2:
        submit_button = st.button("Submit Quiz 📝", key="submit_quiz", use_container_width=True)
        if submit_button:
            submit_quiz()
            st.balloons()

    # Display results if quiz is submitted
    if st.session_state.submitted:
        display_results()

    start_over_button = st.button("Start Over 🔄", key="start_over", use_container_width=True)
    if start_over_button:
        reset_state()

def update_answer(q_num):
    # Session state, to update the user's answer
    st.session_state.user_answers[q_num] = st.session_state[f"select_{q_num}"]

def submit_quiz():
    # On the submission button, mark quiz as submitted and calculate score
    st.session_state.submitted = True
    st.session_state.score = calculate_score(st.session_state.user_answers, st.session_state.questions)

def display_results():
    # To display the user's score
    st.success(f"🏆 Your score: {st.session_state.score} out of {len(st.session_state.questions)}")

    # Display the correct results for each question
    for q_num, q_data in st.session_state.questions.items():
        with st.expander(f"Question {q_num[-1]} Details"):
            st.write(f"**Your answer:** {st.session_state.user_answers[q_num]}")
            st.write(f"**Correct answer:** {q_data['correct_answer']}")
            if st.session_state.user_answers[q_num] == q_data['correct_answer']:
                st.success("Correct! 🎉")
            else:
                st.error("Incorrect ❌")

def reset_state():
    # Clear all session state variables
    for key in list(st.session_state.keys()):
        del st.session_state[key]
    st.experimental_rerun()

Streamlit アプリケーションのこの部分は、動画コンテンツからのクイズ生成と、クイズ開始後のインタラクション管理を担当します。

upload_and_index() 関数は、Twelve Labs SDK を利用して動画のアップロードとインデックス作成を実行します。インデックス作成が完了すると、クイズ生成の処理が開始されます。

クイズ問題を自動生成するために、関数はインデックスされた動画の 'video_id' を使用します。そして、前述したように generate_mcq() を呼び出した後に parse_json_with_regex() を呼び出します。このプロセスによってクイズの問題が作成されます。生成が成功すると、クイズページが表示されます。

quiz() 関数は生成されたクイズを表示し、解答の選択やクイズの送信といったユーザーの操作を処理します。送信後には、**「最初からやり直す(Start Over)」** オプションが提供されます。クイズのやり取りを管理するために、いくつかのユーティリティ関数が用意されています:

  • update_answer() は、特定の質問に対するユーザーの回答を更新します。

  • submit_quiz() は、クイズ送信の完了処理とスコア計算を行います。

  • display_results() は、スコアや正解を含むクイズ結果画面を表示します。

  • reset_state() は、クイズを最初からやり直すために、すべてのセッション状態変数をクリアします。

2.2 Streamlitのセッション状態(Session State)とカスタムCSS

app.py パッケージの main 関数は、Streamlit ページの設定を行い、カスタムCSSを適用します。背景画像の設定、ボタンの装飾、そしてアプリケーション全体のビジュアルの外観を決定します。また、セッション状態の初期化も行います。

def main():
    # Set up the Streamlit page configuration
    st.set_page_config(page_title="QnA Generator", page_icon="🎥", layout="wide")

    # Custom CSS
    st.markdown("""
        <style>
        .stApp {
            background-color: #f0f2f6;
        }
        .stButton > button {
            background-color: #4CAF50;
            color: white;
            font-weight: bold;
            border-radius: 30px;
            padding: 15px 30px;
            font-size: 18px;
            transition: all 0.3s ease 0s;
            border: none;
            box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
        }
        .stButton > button:hover {
            background-color: #45a049;
            box-shadow: 0 6px 8px rgba(0, 0, 0, 0.15);
            transform: translateY(-2px);
        }
        .stButton > button:active {
            transform: translateY(0px);
            box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
        }
        .stRadio > label {
            background-color: #e1e5eb;
            padding: 12px;
            border-radius: 8px;
            margin-bottom: 12px;
            transition: all 0.2s ease 0s;
            cursor: pointer;
        }
        .stRadio > label:hover {
            background-color: #d0d4d9;
            transform: translateY(-2px);
            box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
        }
        .stExpander {
            background-color: #ffffff;
            border-radius: 15px;
            box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
            margin-bottom: 20px;
        }
        h1 {
            color: #2c3e50;
            font-size: 36px;
            font-weight: bold;
            margin-bottom: 20px;
        }
        h2, h3 {
            color: #34495e;
        }
        .stAlert {
            border-radius: 10px;
            font-weight: bold;
        }
        .stSpinner > div {
            border-color: #4CAF50 !important;
        }
        [data-testid="stAppViewContainer"] {
            background-image: url("https://img.freepik.com/free-photo/vivid-blurred-colorful-wallpaper-background_58702-3508.jpg?size=626&ext=jpg");
            background-size: cover;
        }
        [data-testid="stHeader"] {
            background-color: rgba(0,0,0,0);
        }
        [data-testid="stToolbar"] {
            right: 2rem;
            background-image: url("");
            background-size: cover;
        }
        <&sol;style>
    """, unsafe_allow_html=True)


    # Initialize the page state if not already set
    if 'page' not in st.session_state:
        st.session_state.page = "upload"


    # To show the appropriate page based on the current state
    if st.session_state.page == "upload":
        upload_and_index()
    elif st.session_state.page == "quiz":
        quiz()

if __name__ == "__main__":
    main()

Streamlit アプリケーションを起動するエントリポイントとして機能する main() 関数は、タイトル付きのページを設定し、スペースを有効活用するためにワイドレイアウトを使用します。カスタムCSSは、背景、ボタン、ラジオボタン、エクスパンダーのスタイルを整え、意図したデザインへと引き締めて視覚的な魅力を向上させます。

アプリの異なるファイル/セクション間のナビゲーションを管理するために、セッション状態変数 'page' が初期化されます。定義されていない場合、デフォルトで "upload" に設定されます。

アプリケーションは、条件分岐に従い、現在の状態に基づいて適切なページを描画します:

  • "upload" — 動画アップロードインターフェース用の upload_and_index() 関数を呼び出します。

  • "quiz" — 生成されたクイズを表示するために quiz() 関数を呼び出します。

以下は、PCA(主成分分析)に関する動画コンテンツをアップロードし、インデックスを作成した後に自動生成されたデモクイズの様子です:

Twelve Labsが提供するオープンエンドプロンプトと Pegasus 1.1 をさらに試して、探索してみてください。

このチュートリアルからさらに一歩進めるためのアイデア

アプリケーションの仕組みと開発プロセスを理解すれば、革新的なアイデアを形にし、ユーザーのニーズを満たすプロダクトを作成できるようになります。このチュートリアルブログと同様のユースケースを提案します:

📚️ マイペース学習(Self-Paced Learning):生徒が動画レッスンの理解度をテストするための問題を独自に作成できます。

🎓 教育評価(Educational Assessment):教師は講義動画や教材コンテンツに基づく学習クイズを素早く作成できます。

🏢️ 企業研修(Corporate Training):組織は、さまざまな重要ステップに対応する自動評価クイズを組み込むことで、動画ベースの研修プログラムを強化できます。

🎬 コンテンツへのエンゲージメント(Content Engagement):YouTuberなどの動画クリエイターは、動画に連動したクイズを提供することで、視聴者のエンゲージメントを高めることができます。

🧠 記憶定着(Memory Retention):情報動画から生成された質問に回答することで、学習した知識をしっかりと記憶に定着させることができます。

結論

このブログ記事では、Twelve Labsを使用した動画コンテンツクイズジェネレーターの動作手順と開発プロセスについて包括的に解説しました。チュートリアルにお付き合いいただきありがとうございます。ユーザー体験の向上、そして様々な課題を解決するような皆さんの魅力的なアイデアを楽しみにしています。

追加リソース

自動生成タスクに使用されるエンジンの詳細を理解するには、Marengo 2.6(エンベディングエンジン)およびPegasus 1.1(ジェネレーターエンジン)を参照してください。Twelve Labsをさらに深く知り、動画コンテンツ分析の理解を広げるには、以下の情報が役立ちます:

  • Discordコミュニティ:開発者や熱心なメンバーが集まる活発なコミュニティに参加して、アイデアを議論したり、質問をしたり、プロジェクトを共有したりしましょう。

  • サンプルアプリ一覧:様々なサンプルアプリケーションを調べて、次のプロジェクトのインスピレーションを得たり、新しい実装テクニックを学びましょう。

  • チュートリアルを調べる:包括的なチュートリアルを通じて、Twelve Labsで何ができるのか、その機能をさらに深く体験してください。

これらのリソースを積極的に活用し、知識を広げ、Twelve Labsの動画理解技術を活用した革新的なアプリケーションを構築してみてください。