【Markdown AI】AIが好みの映画をおすすめしてくれるサイトを作成してみた。

# 完成品

https://mdown.ai/content/c66e0e9e-46b7-4ddd-a7a7-9e3662605b4b

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3780099/d1004398-853a-85af-7467-b4e6cd583ad7.png)


## 本サイトの使い方

1.  観たい映画の希望を入力する
    - 制作国... 邦画/洋画/その他」
    - ジャンル...　アクション/コメディ/ヒューマンドラマ/ファンタジー/ホラー 
    - 上映時間...〜90分/90〜120分/121分〜
    - 制作年...~1990年代/2000年代/2010年代/2020年代
1. 上記条件に当てはまる映画を3つ、30文字前後のあらすじと共に出力してくれる。
（＊30秒ほど時間がかかります）

おわり。



# コードの構成

## 1. **HTML (構造)**

- **`<head>`** セクション: ページのメタ情報を記載。`<meta charset="UTF-8">`で文字エンコーディングを指定し、`<meta name="viewport" content="width=device-width, initial-scale=1.0">` でレスポンシブデザインに。
- **`<body>`** セクション: ページのメインコンテンツを含みます。`<div class="container">` 内に、映画のジャンル、国、上映時間、製作期間を選択するためのフォーム要素、推薦結果を表示するための要素。


<details><summary>実際のコードーーーーーーーーーーーーーーーーーーーーーーーー</summary>

```.HTML
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Movie Recommendation AI</title>
  <link> <!画像リンク>
  <style>
    /* CSS */
  </style>
</head>

<body>

<div class="container">
  <h1>Movie Recommendation AI</h1>

  <div class="input-group">
    <label for="genre">Genre:</label>
    <select id="genre">
      <option value="Action">Action</option>
      <option value="Comedy">Comedy</option>
      <option value="Drama">Drama</option>
      <option value="Fantasy">Fantasy</option>
      <option value="Horror">Horror</option>
    </select>

    <label for="country">Country:</label>
    <select id="country">
      <option value="Japanese">Japanese</option>
      <option value="Western">Western</option>
      <option value="Other">Other</option>
    </select>

    <label for="screenTime">Screen Time:</label>
    <select id="screenTime">
      <option value="Less than 90 min">Less than 90 min</option>
      <option value="91 to 120 min">91 to 120 min</option>
      <option value="More than 121 min">More than 121 min</option>
    </select>

    <label for="period">Period:</label>
    <select id="period">
      <option value="1990s">1990s</option>
      <option value="2000s">2000s</option>
      <option value="2010s">2010s</option>
      <option value="2020s">2020s</option>
    </select>

    <button id="ボタン名">Get Recommendation</button>
  </div>

  <div id="answer-キー"></div>
</div>

<script>
  // JavaScript
</script>

</body>
</html>
```

</details>


## 2. **CSS (デザイン)**

CSSは`<style>`タグ内に記述され、ページのスタイルを定義しています。

- bodyスタイル: ページ全体のフォント、背景画像、色、レイアウトを設定。背景画像は固定され、スクロールしても動かないように設定。
- .containerスタイル: 中央に配置されたコンテンツボックスのスタイルを設定。背景色、ボーダー、影、パディングなどが設定。
- h1スタイル: タイトルの色、フォントサイズ、マージンを設定。
- .input-groupスタイル: 入力フォームのスタイルを設定。背景色、ボーダー、影、パディングなどが設定。
- selectとbuttonスタイル: ドロップダウンメニューとボタンのスタイルを設定。ボタンにはホバー効果と無効化時のスタイルも設定。
- メディアクエリ: 画面幅が600px以下の場合のスタイル調整。


<details><summary>実際のコードーーーーーーーーーーーーーーーーーーーーーーーー</summary>

```.CSS
  <style>
    /* 全体のスタイル設定 */
    body {
      font-family: 'Roboto', sans-serif; 
      background-color: #e0f7e9;
      color: #2f4f2f;
      display: flex;
      justify-content: center;
      align-items: center;
      height: 100vh;
      margin: 0;
      background-image: url(画像リンク); /* フリー画像のURLを使用 */
  background-size: cover;
  background-repeat: no-repeat;
  background-position: center;
  background-attachment: fixed; /* 背景を固定 */
  } 

    /* コンテナのスタイル設定 */
    .container {
      background-color: rgba(255, 255, 255, 0.9);
      border-radius: 15px;
      box-shadow: 0px 4px 15px rgba(0, 0, 0, 0.2);
      padding: 20px;
      width: 80%;
      max-width: 800px; 
      display: flex;
      flex-direction: column;
      align-items: center;
      }

    /* タイトルのスタイル設定 */
    h1 {
      color: #ffffff; 
      font-size: 1.2em; 
      margin-bottom: 12px;
      text-align: center;
      }

    /* 入力グループのスタイル設定 */
    .input-group {
      width: 100%;
      display: flex;
      flex-direction: column;
      align-items: center;
      background-color: #f0f0f0;
      border-radius: 10px;
      padding: 20px;
      box-shadow: 0px 2px 10px rgba(0, 0, 0, 0.1);
    }

    /* ラベルのスタイル設定 */
    label {
      margin-top: 10px;
      font-weight: bold;
      color: #2f4f2f; 
    }

    /* セレクトボックスとボタンのスタイル設定 */
    select, button {
      margin: 10px;
      padding: 10px;
      font-size: 1em;
      width: 90%; 
      max-width: 400px; 
      border-radius: 8px;
      border: 1px solid #8fbc8f;
    }

    /* ボタンのスタイル設定 */
    button {
      background-color: #228b22;
      color: #ffffff;
      cursor: pointer;
      transition: background-color 0.3s, transform 0.3s;
    }

    /* ボタンのホバー時のスタイル設定 */
    button:hover {
      background-color: #006400;
      transform: scale(1.05);
    }

    /* ボタンが無効化されたときのスタイル設定 */
    button:disabled {
      background-color: #cccccc;
      cursor: not-allowed;
    }

    /* 出力ボックスのスタイル設定 */
    #answer-1735790677 {
      margin-top: 20px;
      text-align: left;
      font-size: 1.2em;
      color: #333333;
      background: linear-gradient(135deg, #e0f7e9, #ffffff); 
      border-radius: 15px;
      padding: 20px;
      box-shadow: 0px 4px 15px rgba(0, 0, 0, 0.2);
      width: 100%; 
      max-width: 750px; 
      font-family: 'Roboto', sans-serif; 
      border: 2px solid #8fbc8f;
      position: relative;
    }

    /* メディアクエリでのスタイル調整 */
    @media (max-width: 600px) {
      .container {
        width: 95%;
      }

      select, button {
        font-size: 1rem;
      }
    }
  </style>
```
</details>


## 3. **JavaScript (機能)**

- 即時実行関数: スコープを汚染しないように、JavaScriptコードを即時実行関数で囲う。
- イベントリスナー: ボタンがクリックされたときに、選択された映画の条件を取得し、AIに問い合わせ。
- 非同期処理: async/awaitを使用して、AIからの応答を非同期で取得。
- エラーハンドリング: try-catchを使用して、AIからの応答取得に失敗した場合にエラーメッセージを表示。

<details><summary>実際のコードーーーーーーーーーーーーーーーーーーーーーーーー</summary>


```.JavaScript
(() => {
  const button = document.getElementById('ボタン名');
  
  button.addEventListener('click', async event => {
    button.disabled = true; // ボタンを無効化して連続クリックを防ぐ
    
    // 各選択肢の値を取得
    const genre = document.getElementById('genre').value;
    const country = document.getElementById('country').value;
    const screenTime = document.getElementById('screenTime').value;
    const period = document.getElementById('period').value;
    
    // プロンプトを作成
    const query = `You are a movie recommendation expert. Please suggest 3 movies that best match the conditions specified by the user. Based on the following criteria, please provide the recommended movies in plain text format:

Genre: ${genre}
Screen time: ${screenTime}
Production period: ${period}
Language: ${country}

Please list the movie titles along with a brief recommendation point, 
in 30 words or less. Please answer all in Japanese.`; // プロンプト自由記述欄
    
    try {
      const serverAi = new ServerAI(); // ServerAIクラスのインスタンスを作成
      const answer = await serverAi.getAnswerText('APIキー', '', query); // AIからの応答を取得
      document.getElementById('キー').innerText = answer;
    } catch (error) {
      document.getElementById('キー').innerText = 'Error: Could not retrieve a response.'; // エラーメッセージを表示
    } finally {
      button.disabled = false; // ボタンを再度有効化
    }
  });
})();

```
</details>

## 4. Create Model (Claude)

以下をプロンプトに組み込んでいます。
Knowledgeには何も入力していません。

```
You are a movie recommendation expert. Please suggest 3 movies that best match the conditions specified by the user. 
Based on the following criteria, please provide the recommended movies in plain text format:

Genre: <Selected genre (e.g., Action, Drama, Horror, etc.)>
Screen time: <Selected time (e.g., 90-120 min, 120 minutes or more, etc.)>
Production period: <Selected production period (e.g., 1990s, 2000s, 2010s, 2020s)>
Language: <Japanese, Western, or Other>

Please list the movie titles along with a brief recommendation point, in 30 words or less. Please answer all in Japanese.

```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3780099/58e071f6-5f4b-ce10-359c-90f0bab2867b.png)


# 今回の学び

## 工夫した点
- **UI**
    - シンプルで直感的。
    - ユーザーが無駄な入力を考えずに済むように元からプロンプトの枠だけ作成済み。
- **デザイン**
    - 映画を選ぶ前のワクワク感をイメージ。
    - モバイルにも対応。
- **機能** 
    - AIと連携して、条件に基づく映画推薦を提供。
    - 映画タイトルと一緒に30字前後のあらすじも提供。
- **AI**
    - Claudeを使用。リスク観点と日本語の丁寧さから採用。
    - 余計な齟齬が出ないように、最後の出力のみ日本語。


## 難しかった点
- AIモデルの挿入
「Insert」機能で作成したモデルがコードとともに挿入されますが、そのままでは機能が限定的でした。
どのような仕組みで動作しているかを理解し、自分で編集する必要がありました。実際、本サイトではAPIキーのみを使用し、その他の部分はほとんど自作で対応しました。

- CSSの扱い
MarkdownはCSSに対応していますが、基本的な記述はその名の通りMarkdownです。ただ、おしゃれなデザインにするためにはCSSが必須です。
そしてMarkdownではHTML・CSS・JavaScriptを1つのファイルにまとめて記述する必要があり、ここが非常に難しかったです。
どこまでが反映されるのか試行錯誤しながら進めましたが、もしファイルを分けて記述できるようになれば、編集がもっと簡単になると感じました。
例えば、imgフォルダなどを設置できれば、ディレクトリ参照が簡単になりそうです。


## 所感・今後の活用
今回初めてMarkdown AIを使い、何ができるのかを試しながらサイトを作成してみました。
特に以下の点が初学者にとって大きなメリットだと感じました。

- 個人でサーバーやドメインをレンタルする必要がない
- CSSやHTMLの高度な知識が不要である

ただし、これだけではビジネスレベルでの実用にはなかなか到達しにくい印象を受けました。活用レベルを目指すためには、ある程度の知識が必要だと感じます。
そこで活用すべきはChatGPT等の生成AIですね。
私は「コーディング＝暗記する必要はないもの」と思っているため、より相性が良かったかもしれません。

個人的に最も魅力的だと思ったのは、
**「生成AI（LLM）をサイトへ簡単に挿入できること」**
です。

Markdown AIは現在も急速に進化しているようですので、近いうちにさらに使いやすくなることを期待しています。
最近追加された画像生成機能についても、このコンテストの間にぜひ試してみたいと思っています。
⇒　（後日追記）本サイトで機能を使用してみました。（下記参照）

https://qiita.com/official-events/52830497d1d63301da95

# Markdown AI について

以下参考にさせていただきました。

https://qiita.com/mdown_ai_jpn/items/58c58dfd16954c19aeaf





https://zenn.dev/markdownai



# 後日追記

画像生成機能を使用して、背景をおしゃれに変えました。
次の投稿に経緯含め、記載していますので、良ければ参照ください。

https://qiita.com/setowatson/items/5fd1dc70b091d8ed5485