5
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

概要

Markdown AIを使っておしゃべりサポーターを作ってみました。
単語を入れると、気の利いた話題3つと、AIによって生成された画像が表示されます。
話題で盛り上がるもよし、画像で盛り上がるもよしのおしゃべりサポーターです。

image.png

感想

大変お手軽でよかったです。こういう簡単にアイデアを形にできるツールと出会った時、結構頭を捻るんですが平凡なものしか出てこなくてサッと面白い発想ができる人間になりたいものです :thinking:

もっと非エンジニアでも使えるようなものに今後なっていくのだろうなと 今からワクワクしてます。

Markdown AIでできること

Markdown記法でWebサイトの見た目が記載できます。
AIモデルを簡単に作成、サイトに組み込めます。
サイトの公開をボタンポチポチポチで行えます。

どのように作ったのか?

まず、Create Modelからお題を提供してくれるAIモデルを作ります。

image.png

お題を提供してくれるモデルの作成

image.png

image.png

Model NameとPromptを入力します。
Knowledgeは試しに入力してみたりしたんですが何が変わったか分からず、下記見てもよく分からなかったので一旦空欄にしました。

Createできたら、Playgroundが活性になります。Playgroundでは作ったAIモデルのお試しが可能です :ok_woman:

Playground

image.png

こんなかんじでAIモデルのお試しができます。

image.png

なんかスクロールしてみたら 知らない履歴が出てきた... 他の人のお試し履歴だろうか... :thinking:

画像生成AIモデルを作る

先ほどはgpt-4o-miniで作成しましたが、今回はdall-e-3で作成します。

image.png

サイトを作る

実際にサイトを作っていきます。UIはこんな感じです。

image.png

H1でおしゃべりサポーター、H2で単語を入力すると〜と記載します。


# おしゃべりサポーター

## 単語を入力すると話題を提供してくれます

お題を作成してくれるAIモデルの追加

次に、先ほどのお題を作成してくれるAIモデルを入れるためにInsertをクリックします。
Scriptを選択すると、作成したAIモデルが出てくるのでクリックしてInsertします。

image.png

次のコードが追加されますので、見た目部分を書き換えます。

<div style="display: inline-block;">
-  <input class="input" type="text" id="text-1735658349" style="width: 300px;" value="Teach me about Markdown.">
-  <button class="button is-info" type="button" id="button-1735658349">Run AI</button>
+  <input class="input" type="text" id="text-1735653780" style="width: 300px;" value="例: 食べ物、猫">
+  <button class="button is-info" type="button" id="button-1735653780">教えて!</button>
</div>
<div id="answer-1735658349"></div>

<script>
(() => {
  const button = document.getElementById('button-1735658349');
  button.addEventListener('click', async event => {
    button.classList.add('is-loading');
    button.disabled = true;
    
    const serverAi = new ServerAI();
    const message = document.getElementById('text-1735658349').value;
    const answer = await serverAi.getAnswerText('qj57puSTRXh2jfti7hKhiF', '', message);
    
    document.getElementById('answer-1735658349').innerText = answer;
    button.classList.remove('is-loading');
    button.disabled = false;
  });
})();
</script>
画像生成AIモデルの追加

Insertをクリックします。Image Scriptを選択すると、作成したAIモデルが出てくるのでクリックしてInsertします。
image.png
image.png

次のコードが追加されます。

<div style="display: inline-block;">
  <input class="input" type="text" id="text-1735660769" style="width: 300px;" value="For example, cats." placeholder="Enter a description of the image you want to generate">
  <button class="button is-success" type="button" id="button-1735660769">Run AI</button>
</div>
<div id="image-1735660769"></div>

<script>
(() => {
  const button = document.getElementById('button-1735660769');
  const imageContainer = document.getElementById('image-1735660769');
  let dots = 0;
  let loadingInterval;

  const updateLoadingText = () => {
    dots = (dots + 1) % 4;
    const dotsText = '.'.repeat(dots);
    imageContainer.innerText = `Generating...${dotsText}`;
  };

  button.addEventListener('click', async event => {
    button.classList.add('is-loading');
    button.disabled = true;
    imageContainer.innerText = 'Generating...';
    
    loadingInterval = setInterval(updateLoadingText, 500);
    
    try {
      const serverAi = new ServerAI();
      const prompt = document.getElementById('text-1735660769').value;
      const answer = await serverAi.getAnswer('7ic7nG9Z7vFEZA5THkApzb', {
        prompt: prompt,
        size: '1024x1024',
        n: 1,
        type: 'image'
      });

      clearInterval(loadingInterval);
      imageContainer.innerHTML = `<img src="${answer.imageUrl}" alt="${prompt}">`;
    } catch (error) {
      clearInterval(loadingInterval);
      console.error('An error has occurred.:', error);
      alert(error.message || 'An error has occurred.');
      imageContainer.innerText = 'An error has occurred.';
    } finally {
      button.classList.remove('is-loading');
      button.disabled = false;
    }
  });
})();
</script>

ですがこれだと、単語を2回入れなきゃいけなくて面倒なのでちょっと書き換えます。

image.png

最終形

最終的に テキストはこんな感じです。

# おしゃべりサポーター

## 単語を入力すると話題を提供してくれます

<div style="display: inline-block;">
  <input class="input" type="text" id="text-1735653780" style="width: 300px;" value="例: 食べ物、猫">
  <button class="button is-info" type="button" id="button-1735653780">教えて!</button>
</div>
<div id="answer-1735653780"></div>
<div id="image-1735660769" style="width: 512px;"></div>

<script>
(() => {
  const button = document.getElementById('button-1735653780');
  const imageContainer = document.getElementById('image-1735660769');
  let dots = 0;
  let loadingInterval;

  const updateLoadingText = () => {
    dots = (dots + 1) % 4;
    const dotsText = '.'.repeat(dots);
    imageContainer.innerText = `Generating...${dotsText}`;
  };

  button.addEventListener('click', async event => {
    button.classList.add('is-loading');
    button.disabled = true;
    imageContainer.innerText = 'Generating...';
    loadingInterval = setInterval(updateLoadingText, 500);

    const serverAi = new ServerAI();
    try {
      const prompt = document.getElementById('text-1735653780').value;
      const answer = await serverAi.getAnswer('7ic7nG9Z7vFEZA5THkApzb', {
        prompt: prompt,
        size: '512x512',
        n: 1,
        type: 'image'
      });

      clearInterval(loadingInterval);
      imageContainer.innerHTML = `<img src="${answer.imageUrl}" alt="${prompt}">`;
    } catch (error) {
      clearInterval(loadingInterval);
      console.error('An error has occurred.:', error);
      alert(error.message || 'An error has occurred.');
      imageContainer.innerText = 'An error has occurred.';
    }
    const message = document.getElementById('text-1735653780').value;
    const answer = await serverAi.getAnswerText('qj57puSTRXh2jfti7hKhiF', '', message);
    
    document.getElementById('answer-1735653780').innerText = answer;
    button.classList.remove('is-loading');
    button.disabled = false;
  });
})();
</script>

Viewボタンを押すと実際の見た目を確認することができます。
動作を確認することも可能です。

image.png

公開してみる

実際出来上がったコードは次の通りです。

<div class="text-preview"><div><h1 class="is-size-1">おしゃべりサポーター</h1><p style="margin-bottom: 1.35rem"></p><h2 class="is-size-2">単語を入力すると話題を提供してくれます</h2><p style="margin-bottom: 1.35rem"></p><div style="display: inline-block;">
  <input class="input" type="text" id="text-1735653780" style="width: 300px;" value="例: 食べ物、猫">
  <button class="button is-info" type="button" id="button-1735653780">教えて!</button>
</div>
<div id="answer-1735653780"></div>
<div id="image-1735660769" style="width: 512px;"></div>

<script>
(上で貼ったのと同じなので省略)
</script>
</div></div>

<script src="/server-ai-script.js" data-nscript="lazyOnload"></script>

server-ai-script.jsもついでに確認しました。
見たとこでふむふむ...ていうだけなんですけどね :smile:

server-ai-script.js
class ServerAI {
  constructor() {
  }


  getUniqueId(strong = 1000) {
    return new Date().getTime().toString(16) + Math.floor(strong * Math.random()).toString(16)
  }


  /**
   * 
   * @param {*} modelId 
   * @param {*} historyId 
   * @param {*} question 
   * @returns {Promise<string>} text
   */
  async getAnswerText(modelId, historyId, question) {
    const res = await fetch(`/api/chat.get-answer-from-editor`, {
      method: 'POST',
      body: JSON.stringify({
        chatBotId: modelId,
        historyId: historyId,
        question: question,
      }),
      headers: {
        'Accept': 'application/json, */*',
        'Content-type': 'application/json',
        'X-UID': 'hogehoge',
      }
    });

    /**
     * @type {{
     * historyId?: string;
     * text: string;
     * noMemory: boolean;
     * character: 'bot' | 'user'; 
     * }}
     */
    const data = await res.json();
    return data.text;
  }





  /**
   * 
   * @param {string} method 関数名
   * @param {object} parameter 引数 
   */
  // setLocalStorage(method, parameter) {
  //   const newValue = JSON.stringify({
  //     method,
  //     args: parameter
  //   });

  //   localStorage.setItem('caller', newValue);
  //   window.dispatchEvent( new StorageEvent('storage', {
  //     key: 'caller',
  //     newValue: newValue,
  //   }))
  // }

  // getAnswer(modelId, question) {
  //   this.setLocalStorage('getAnswer', {
  //     modelId,
  //     question,
  //   })
  // }

  /**
   * 画像生成用のメソッド
   * @param {string} modelId モデルID
   * @param {object} options オプション
   * @param {string} options.prompt プロンプト
   * @param {string} options.size 画像サイズ
   * @param {number} options.n 生成数
   * @returns {Promise<{imageUrl: string}>}
   */
  async getAnswer(modelId, options) {
    const res = await fetch(`/api/get-image-answer`, {
      method: 'POST',
      body: JSON.stringify({
        modelId: modelId,
        prompt: options.prompt,
        size: options.size,
        n: options.n,
        source: 'playground'
      }),
      headers: {
        'Accept': 'application/json, */*',
        'Content-type': 'application/json',
        'X-UID': 'hogehoge',
      }
    });

    const data = await res.json();
    return data;
  }
}

Markdown AIでできなかったこと

私が最初に作ろうとしたのはWordwolfのサイトでした。

画面遷移が必要なサイトの作成は、今はまだ時期尚早なのかなと感じました。
きっと今後簡単にできるようになるんだと思います。

自分1人で最初から最後までコーディングするのは大変なので、ChatGPTにお願いして書き出して貰いました!

今後カバーされるところなのかなと思ってるんですが、いい感じの画面構成作りたい場合は今のところ外付けのAIに頼む感じです
もちろん、MarkdownAIではAIモデル簡単に作れるのでの中に閉じたい場合は作成したAIモデルに頼むのもありですね。

image.png

(...余談...個人的には画面項目作るならClaudeに頼むとプレビューが楽でいい感じです :hugging: )

最後に

ここまで読んでくださってありがとうございます!
今後もいろんな新しいサービスに触れていきたいです。

5
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?