## 模块 2: 构建你的第一个简易 AI 应用程序

让我们从最简单的 AI 应用程序开始。在这一步里，我们将调用 OpenAI 的 `gpt-4o` 模型，这是一种大语言模型（LLM）。它在被训练的时候，就已经了解了很多知识。关于如何将原始知识转换为大语言模型（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)

### 步骤 1. 运行简易 AI 应用程序的 Streamlit 服务

- 在 VS Code 终端中，使用 `streamlit` 命令行工具运行以下脚本来启动 Streamlit 服务。你可能注意到了，我们在 `streamlit` 前，加入了 `poetry run`。这让我们可以使用 poetry 来管理依赖，而非默认的 pip。

    ```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/images/2025/7/24/4e420f46b9d43485f5dc87d8bd3c8584d5ed0981.png)

### 步骤 2. 使用简易 AI 应用程序的演示网页

- 点击访问您的 [RAG 演示网页](http://${LAB:HOST_MONITOR1_PUBLIC_IP}:8501/)。

    ![90%](https://lab-static.pingcap.com/images/2025/7/24/9a4c1644a16628a8c63391ddad685953a6a6041f.png)

- 现在，你就可以在 `Enter your question:` 下的输入框内输入你的问题了，比如：

    ```txt
    What is TiDB?
    ```

- 随后，点击 `Send` 按钮。稍做等候，你就可以得到回答了！

    ![90%](https://lab-static.pingcap.com/images/2025/7/24/039351d0fec29f720e40a70a521533a8670863a4.png)

- 恭喜🎉！你已经完成了你的第一个 AI 应用程序。你可以继续问 AI 一些自己想知道的问题。甚至你可以尝试一下更困难的问题，来看看我们初步的 AI 应用程序的能力边界在哪里。
- 接下来，让我们看看代码内到底发生了什么？

### 步骤 3. 简易 AI 应用程序代码阅读

让我们回到 [VS Code](${LAB:HTTPS_REVERSE_PROXY_URL}?next=${LAB:"/vscode/?folder=/root/tidb-lab-unify-storage-openai"|urlencode} "VS Code") 并点击代码文件 `vanilla_ai_app.py`。

1. 脚本首先导入必要的库：`litellm` 用于与 LLM 交互，`streamlit` 用于构建网页界面。此外，它还从 `common` 模块导入 `LLM_RESPONSE_STYLE`，这是一个预定义的 CSS 样式，用于美化 LLM 的响应输出。

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

    from common import LLM_RESPONSE_STYLE
    ```

2. 定义了要使用的 LLM 模型。我们将模型设置为 `openai/gpt-4o`，`litellm` 使用的模型名称为两段式的，`/` 前被为 provider，是模型的提供者，在这个例子中，为 OpenAI，而后方是模型的名称，由 OpenAI 内部定义。

    ```python
    llm_model = "openai/gpt-4o"
    ```

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` 中提取，并使用 HTML `div` 标签和 `llm-response` 类进行包装，以便应用样式。

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

### 步骤 4. 简易 AI 应用程序的总结

我们可以看到，简易 AI 应用程序非常的简单。总的来说，就是单纯的调用了一下 OpenAI 的 API，获取了回答。而一切的知识储备，都是 LLM 在训练的时候就输入的。比如说上方的示例问题：

```text
What is TiDB?
```

之所以 LLM 可以回答这个问题，是因为 OpenAI 在训练这个模型的时候就有相关的文档或介绍被给到了 LLM。那我们不禁会想到一个问题：如果 LLM 在训练的时候没有这个数据怎么办？举个例子：

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

![90%](https://lab-static.pingcap.com/images/2025/7/24/eff979e800235873edadc40167678a6ea8e51936.png)

可以看到，LLM "编造"了一些信息出来。这是由于在 LLM 训练的时候，根本没有类似的信息输入。因此它无法正确的得知该员工和 PingCAP 之间的关系。只能"猜测"被询问的人为 CTO，因为 CTO 的名字和公司的联系会更加紧密，被询问的概率更大。

而且，这个答案是错误的，因为本实验的作者是 ***Cheese Wang***，他并不是 PingCAP 的 CTO。

这种 LLM 的行为，被称为 **幻觉 (Hallucination)**，这是我们在使用 LLM 的时候需要格外注意的一个现象。我们将在下一个模块，改进我们的 AI 应用程序，降低它的幻觉出现频率，同时给予它额外的知识储备，让他可以回答训练数据之外的数据，而无需更改模型本身。
