## モジュール 2: 初めてのシンプルなAIアプリケーションの構築

最もシンプルなAIアプリケーションから始めましょう。このステップでは、大規模言語モデル（LLM）であるAWSのNovaモデルを呼び出します。このモデルはトレーニング中に多くの知識を学習済みです。生の知識がLLMに変換される方法については、以下の資料を参照してください。これらの資料は後で学習のために保存しておくことができます。それでは、ステップ1から始めましょう。

- Transformerの基礎論文：[Attention Is All You Need](https://arxiv.org/abs/1706.03762)
- YouTuber 3Blue1BrownによるTransformerの視覚的解説：[Transformers (how LLMs work) explained visually](https://www.youtube.com/watch?v=wjZofJX0v4M)

スペースの都合上、ここでは実装の詳細には深入りせず、LLMの使用に焦点を当てます。

### 手順 1. シンプルなAIアプリケーションのStreamlitサービスの実行

- VS Codeターミナルで、`streamlit` コマンドラインツールを使用して以下のスクリプトを実行し、Streamlitサービスを開始します。`streamlit` の前に `poetry run` を追加していることにお気づきかもしれません。これにより、デフォルトのpipではなく、poetryを使用して依存関係を管理できます。

    ```bash
    poetry run streamlit run vanilla_ai_app.py --server.port 8501
    ```

    ```output
    Collecting usage statistics. To deactivate, set browser.gatherUsageStats to false.

    You can now view your Streamlit app in your browser.

    Local URL: http://localhost:8501
    Network URL: http://172.18.0.7:8501
    External URL: http://${LAB:HOST_MONITOR1_PUBLIC_IP}:8501
    ```

    ![90%](https://lab-static.pingcap.com/quick-demo/demo-422/422-vanilla-ai-app-streamlit-start-0604.jpg)

### 手順 2. シンプルなAIアプリケーションのデモWebページの使用

- [RAGデモWebページ](http://${LAB:HOST_MONITOR1_PUBLIC_IP}:8501/)にアクセスしてください。

    ![90%](https://lab-static.pingcap.com/quick-demo/demo-422/422-vanilla-ai-app-frontend-0604.jpg)

- これで、`Enter your question:` の下の入力ボックスに質問を入力できます。例えば：

    ```txt
    What is TiDB?
    ```

- 次に、`Send` ボタンをクリックします。少し待つと、回答が表示されます！

    ![90%](https://lab-static.pingcap.com/quick-demo/demo-422/422-vanilla-ai-app-answer-0604.jpg)

- おめでとうございます🎉！初めてのAIアプリケーションが完成しました。AIに興味のある他の質問を続けてみてください。初期のAIアプリケーションの能力の限界を見るために、もっと難しい質問を試すこともできます。
- 次に、コードの内部で何が起こっているかを見てみましょう。

### 手順 3. シンプルなAIアプリケーションのコード読解

[VS Code](${LAB:HTTPS_REVERSE_PROXY_URL}?next=${LAB:"/vscode/?folder=/root/tidb-lab-unify-storage"|urlencode} "VS Code")に戻り、`vanilla_ai_app.py` というコードファイルをクリックしましょう。

1. スクリプトはまず必要なライブラリをインポートします：LLMと対話するための `litellm`、Webインターフェースを構築するための `streamlit` です。さらに、`common` モジュールから `LLM_RESPONSE_STYLE` をインポートします。これはLLMの応答出力を美しくするための定義済みCSSスタイルです。

    ```python
    from litellm import completion
    import streamlit as st

    from common import LLM_RESPONSE_STYLE
    ```

2. 使用するLLMが定義されます。モデルを `bedrock/us.amazon.nova-lite-v1:0` に設定します。`litellm` が使用するモデル名は2部構成です：`/` の前の部分はプロバイダーで、この場合はAmazon Bedrockです。後ろの部分はAmazon内部で定義されたモデル名です。

    ```python
    llm_model = "bedrock/us.amazon.nova-lite-v1:0"
    ```

3. Streamlitページのタイトルを "🔍 Vanilla AI Q&A Demo" に設定します。

    ```python
    st.title("🔍 Vanilla AI Q&A Demo")
    ```

4. ページに説明テキストを表示して、アプリケーションの機能をユーザーに伝えます：ユーザーが質問を入力すると、システムが組み込みの知識に基づいて回答します。

    ```python
    st.write(
        "Enter your question, and the system will answer it based on the built-in knowledge"
    )
    ```

5. `st.text_input` を使用して、ユーザーが質問を入力できるテキスト入力ボックスを作成します。入力された質問は `query` 変数に保存されます。

    ```python
    query = st.text_input("Enter your question:", "")
    ```

6. このコードブロックは、ユーザーが "Send" ボタンをクリックし、`query`（ユーザーが入力した質問）が空でない場合のロジックを処理します：
    - `with st.spinner("Processing...")` を使用して、システムがリクエストを処理中であることをユーザーに知らせるローディングアニメーションを表示します。
    - "Answer the question: " という単純なプロンプトにユーザーの質問 `query` を加えて構築します。

    ```python
    if st.button("Send") and query:
        with st.spinner("Processing..."):
            prompt = f"Answer the question: {query}"
    ```

7. `litellm.completion` 関数を呼び出して、LLMと対話し、回答を生成します。
    - `model` パラメータは使用するLLMを指定します（つまり `llm_model`）。
    - `messages` パラメータは、メッセージの内容（`prompt`）と役割（`user`）を定義する辞書を含むリストです。

    ```python
    # Call LLM to generate answer
    response = completion(
        model=llm_model,
        messages=[{"content": prompt, "role": "user"}],
    )
    ```

8. LLMのモデル名とロボットのアイコンをページに表示します。

    ```python
    st.markdown(f"### 🤖 {llm_model}")
    ```

9. 定義済みのCSSスタイル `LLM_RESPONSE_STYLE` を適用して、LLMの応答表示を美しくします。`unsafe_allow_html=True` パラメータにより、MarkdownにHTMLとCSSを埋め込むことができます。

    ```python
    st.markdown(LLM_RESPONSE_STYLE, unsafe_allow_html=True)
    ```

10. LLMによって生成された回答を表示します。回答は `response.choices[0].message.content` から抽出され、`llm-response` クラスを持つHTML `div` タグでラップされてスタイルが適用されます。

    ```python
    # show the response
    st.markdown(
        f'<div class="llm-response">{response.choices[0].message.content}</div>',
        unsafe_allow_html=True,
    )
    ```

### 手順 4. シンプルなAIアプリケーションのまとめ

シンプルなAIアプリケーションが非常に単純であることがわかります。要するに、Amazon Bedrock APIを呼び出して回答を得るだけです。すべての知識は、LLMがトレーニング中に供給されたものに基づいています。例えば、上記のサンプル質問：

```text
What is TiDB?
```

LLMがこの質問に答えられるのは、Amazonがこのモデルをトレーニングする際に関連するドキュメントや紹介を含んでいたためです。ここで疑問が浮かびます：もしLLMがトレーニング中にこのデータを持っていなかったらどうなるでしょうか？例えば：

```text
Who is Cheese Wang at PingCAP?
```

![90%](https://lab-static.pingcap.com/quick-demo/demo-422/422-vanilla-ai-app-hallucination-0604.jpg)

ご覧の通り、LLMはいくつかの情報を「捏造」しました。これは、LLMがトレーニングされた際に類似の情報が入力されなかったためです。したがって、この従業員とPingCAPとの関係を正しく知ることができません。CEOの名前は会社とより密接に関連しており、質問される可能性が高いため、質問された人物がCEOであると「推測」することしかできません。

そして、この回答は間違っています。なぜなら、この演習の作者は ***Cheese Wang*** であり、彼は PingCAP の CEO ではないからです。

LLMのこのような振る舞いは**ハルシネーション（幻覚）**と呼ばれ、LLMを使用する際には特に注意が必要な現象です。次のモジュールでは、AIアプリケーションを改善し、ハルシネーションの頻度を減らし、モデル自体を変更することなくトレーニングデータ以外の質問に答えられるように追加の知識を提供します。
