通义千问1.5-1.8B-Chat-GPTQ-Int4 WebUI项目实战:从Python入门到完成第一个AI应用集成

你是不是一直觉得AI模型很酷,但总感觉离自己很远?看着别人用代码调用模型,做出各种有趣的应用,自己却不知道从何下手?别担心,这篇教程就是为你准备的。

假设你只学过一些最基础的Python语法,比如知道什么是变量、循环和函数,但还没真正用Python做过一个完整的项目。今天,我们就从零开始,手把手带你完成一个超有成就感的任务:搭建一个属于你自己的、带网页界面的AI聊天助手。我们会用到通义千问的一个轻量级模型,它已经过优化,在普通电脑上也能跑起来。

整个过程就像搭积木:先学会怎么和模型的“服务窗口”(API)对话,再学怎么把对话内容整理好(处理JSON),然后给自己建一个好看的聊天窗口(用Flask做Web界面),最后把所有积木拼在一起。完成之后,你不仅能收获一个可用的AI应用,更能真正理解一个AI项目是如何从想法变成现实的。让我们开始吧。

1. 项目准备:理解我们要做什么

在写第一行代码之前,我们先花几分钟,看看这个项目的全貌,搞清楚每一步的目的。这样你学起来会更有方向感。

我们的最终目标是做出一个简单的网页。你在这个网页的输入框里打字提问,网页会把问题发送给后台的AI模型,拿到模型的回答后,再显示在网页上给你看。听起来很简单,对吧?为了实现它,我们需要三个核心部分:

  1. 模型服务:这是AI大脑本身。我们已经有了一个现成的、优化过的通义千问模型服务在运行。我们的程序需要能和它“说话”。
  2. 后台程序:这是用Python写的大脑指挥官。它负责两件事:一是接收你从网页发来的问题;二是把问题转发给模型服务,并把模型的回答整理好,送回网页。
  3. 网页界面:这是你看到和操作的聊天窗口。一个简单的输入框和一个显示对话的区域。

技术上说,后台程序是一个基于Flask框架的微型网站,模型服务通过HTTP API提供功能。我们的任务就是编写这个后台程序,让它能“粘合”网页和模型。

为了让环境干净,我们首先创建一个专属的项目文件夹,并安装必要的工具。

打开你的命令行终端(Windows上是CMD或PowerShell,Mac或Linux上是Terminal),依次执行以下命令:

# 创建一个新的项目文件夹,名字可以自己定,这里叫qwen_chatbot
mkdir qwen_chatbot
# 进入这个文件夹
cd qwen_chatbot

接下来,我们需要安装Python的几个库。请执行下面的安装命令:

pip install flask requests
  • flask:用来快速搭建我们网页后台的轻量级工具。
  • requests:一个非常流行、好用的库,专门用来让我们的Python程序能够轻松地访问网络,比如向模型服务发送请求。

如果安装过程比较慢,可以考虑临时使用国内的镜像源,例如在命令后加上 -i https://pypi.tuna.tsinghua.edu.cn/simple

安装成功后,准备工作就完成了。你可以用 pip list 命令看看 flaskrequests 是不是已经在列表里了。

2. 第一步:学会和AI模型“对话”(调用API)

现在,我们暂时先不管网页,专注于最核心的一步:如何用Python程序向那个已经存在的AI模型服务提问,并拿到回答。

你可以把模型服务想象成一个24小时在线的、非常专业的客服。它有一个固定的“电话号”(API地址)和一套标准的“提问流程”(请求格式)。我们的程序要按照这个流程打电话过去,才能得到有效的回答。

假设模型服务的“电话号”(API地址)是 http://localhost:8000/v1/chat/completions。我们通过发送一段特定格式的“话”(JSON数据)来提问。

下面,我们来写第一个Python脚本,体验一下这个过程。在你的项目文件夹 qwen_chatbot 里,创建一个新文件,命名为 test_api.py

# test_api.py - 测试如何调用模型API
import requests
import json

# 1. 定义模型服务的API地址
api_url = "http://localhost:8000/v1/chat/completions"

# 2. 准备我们要发送的“话”,这必须是一个符合API要求的字典
# 通常,这类聊天API都要求我们提供“消息”列表,每条消息有“角色”和“内容”
payload = {
    "model": "Qwen1.5-1.8B-Chat-GPTQ-Int4", # 指定要使用的模型名称
    "messages": [
        {
            "role": "user", # 角色是“用户”,代表是我们提的问题
            "content": "你好,请介绍一下你自己。" # 这是我们提的问题内容
        }
    ],
    "stream": False # 我们先不用流式输出,一次性拿到完整回复
}

# 3. 设置请求头,告诉服务器我们发送的是JSON格式的数据
headers = {
    "Content-Type": "application/json"
}

# 4. 正式“打电话”:发送POST请求
try:
    print("正在向模型发送请求...")
    response = requests.post(url=api_url, headers=headers, data=json.dumps(payload))
    print(f"状态码: {response.status_code}") # 打印HTTP状态码,200表示成功

    # 5. 处理回复
    if response.status_code == 200:
        # 请求成功,解析返回的JSON数据
        response_data = response.json()
        # 从复杂的返回结构中,提取出我们需要的AI回复文本
        # 通常回复在 ['choices'][0]['message']['content'] 这个路径下
        ai_reply = response_data['choices'][0]['message']['content']
        print("模型回复:")
        print("-" * 20)
        print(ai_reply)
        print("-" * 20)
    else:
        # 请求失败,打印错误信息
        print(f"请求失败!状态码:{response.status_code}")
        print(f"错误信息:{response.text}")
except requests.exceptions.ConnectionError:
    print("连接错误!请确认模型服务是否已经启动,并且API地址是否正确。")
except Exception as e:
    print(f"发生未知错误:{e}")

代码解读与运行:

  1. 导入工具requests 用来发请求,json 用来处理数据格式。
  2. 准备数据payload 字典就是我们要说的“话”。messages 列表里可以放多轮对话,这里我们只放了一轮用户提问。
  3. 发送请求requests.post 方法发送一个POST请求到指定的URL。我们把 payloadjson.dumps() 转换成JSON字符串传过去。
  4. 处理结果:如果服务器返回状态码200(成功),我们就用 .json() 方法把返回的文本解析成Python字典,然后像剥洋葱一样,一层层找到最终的回复内容 ai_reply
  5. 错误处理:我们用 try...except 捕获可能出现的网络连接错误或其他异常,让程序更健壮。

现在,先别运行! 因为我们的模型服务(localhost:8000)还没启动呢。这一步的代码是为了让你理解原理。你可以先通读一遍,想象一下它工作的流程。等我们后续把模型服务跑起来,再回头运行这个测试脚本。

3. 第二步:搭建聊天网页的“后台”(Flask应用)

学会了怎么和模型对话,我们接下来给自己建一个“接待处”和“传达室”,也就是网页的后台。这个后台需要做两件事:

  • 当用户访问网站时,给用户展示一个聊天网页(接待处)。
  • 当用户在网页上发送消息时,接收这个消息,调用我们刚学的API去问模型,然后把答案送回网页(传达室)。

我们用Flask来搭建这个后台,它非常轻巧,适合我们这样的小项目。

在项目根目录下,创建一个新的Python文件,命名为 app.py。这将是我们的主程序文件。

# app.py - Flask网页应用主程序
from flask import Flask, render_template, request, jsonify
import requests
import json

# 初始化Flask应用
app = Flask(__name__)

# 配置
API_URL = "http://localhost:8000/v1/chat/completions"  # 模型API地址
HEADERS = {"Content-Type": "application/json"}        # 请求头

# 1. “接待处”:处理用户访问网站根目录的请求,返回聊天页面
@app.route('/')
def home():
    # render_template会去寻找并渲染一个叫‘index.html’的模板文件
    # 我们先返回一个简单的字符串,下一节再做漂亮的页面
    return """
    <h1>我的AI聊天助手</h1>
    <p>聊天界面正在建设中...请访问 <a href='/chat'>/chat</a> 使用简易版。</p>
    """

# 2. “传达室”核心:处理网页发来的聊天消息
@app.route('/chat', methods=['GET', 'POST'])
def chat():
    if request.method == 'GET':
        # 如果是GET请求,就返回一个最简单的聊天输入表单
        return '''
        <!DOCTYPE html>
        <html>
        <head><title>简易聊天</title></head>
        <body>
            <h2>和AI聊天</h2>
            <div id="chat-box" style="border:1px solid #ccc; height:300px; overflow-y: scroll; padding:10px; margin-bottom:10px;">
                <p>AI: 你好!我是你的助手。有什么可以帮你的?</p>
            </div>
            <form method="POST">
                <input type="text" name="user_message" placeholder="输入你的消息..." style="width:70%; padding:8px;">
                <button type="submit">发送</button>
            </form>
        </body>
        </html>
        '''
    else:
        # 如果是POST请求,说明用户提交了消息
        user_message = request.form.get('user_message', '').strip()
        if not user_message:
            return jsonify({"error": "消息不能为空"}), 400

        # 准备发送给模型API的数据,和之前test_api.py里类似
        payload = {
            "model": "Qwen1.5-1.8B-Chat-GPTQ-Int4",
            "messages": [{"role": "user", "content": user_message}],
            "stream": False
        }

        try:
            # 调用模型API
            response = requests.post(API_URL, headers=HEADERS, data=json.dumps(payload))
            if response.status_code == 200:
                response_data = response.json()
                ai_reply = response_data['choices'][0]['message']['content']
                # 返回AI的回复
                return jsonify({"reply": ai_reply})
            else:
                return jsonify({"error": f"模型服务异常: {response.status_code}"}), 500
        except requests.exceptions.ConnectionError:
            return jsonify({"error": "无法连接到模型服务,请检查是否启动。"}), 503
        except Exception as e:
            return jsonify({"error": f"内部错误: {str(e)}"}), 500

# 运行应用
if __name__ == '__main__':
    # debug=True 表示开启调试模式,代码修改后会自动重启,方便开发
    # host='0.0.0.0' 表示让服务器监听所有公开的IP,这样在同一网络下的其他设备也能访问
    # port=5000 是Flask默认端口,如果被占用可以改成其他,如5001
    app.run(debug=True, host='0.0.0.0', port=5000)

代码解读:

  1. 路由@app.route(‘/’)@app.route(‘/chat’, methods=[‘GET’, ‘POST’]) 是Flask的核心概念,它们把特定的URL地址和我们的Python函数绑定在一起。
    • 访问 http://localhost:5000/ 会触发 home() 函数。
    • 访问 http://localhost:5000/chat 会触发 chat() 函数。这个函数能处理两种请求:GET(获取页面)和 POST(发送消息)。
  2. 处理请求:在 chat() 函数里,我们用 request.method 判断请求类型。GET 时返回一个内嵌了简单HTML的表单页面;POST 时,通过 request.form.get(‘user_message’) 拿到用户输入。
  3. 调用API:拿到用户输入后,逻辑就和 test_api.py 几乎一模一样了:构造数据、发送请求、解析回复。
  4. 返回响应:对于API调用结果,我们使用 jsonify() 将其转换为JSON格式返回给网页。这样网页端的JavaScript就能方便地处理了。

现在可以试运行了!

在终端里,确保你在 qwen_chatbot 目录下,然后运行:

python app.py

你会看到类似这样的输出:

* Serving Flask app 'app'
* Debug mode: on
* Running on all addresses (0.0.0.0)
* Running on http://127.0.0.1:5000
* Running on http://你的局域网IP:5000

打开你的浏览器,访问 http://127.0.0.1:5000/chat。你应该能看到一个非常简陋的聊天界面。试着输入一句话并点击发送。

你会遇到错误! 因为我们的模型服务还没启动,所以后台在调用API时会失败,页面上会显示连接错误。这完全正常,说明我们的后台逻辑(发现错误并报告)是通的。接下来,我们就来解决模型服务这个“最后一块积木”。

4. 第三步:启动AI模型服务

要让我们的聊天助手真正“智能”起来,必须启动通义千问模型服务。这个过程可能会因你的具体部署方式而略有不同,但核心思路是:运行一个服务程序,它加载好模型,并开放出我们在代码中一直使用的那个API地址(http://localhost:8000/v1/chat/completions)。

这里提供一种基于 Ollama 或类似 OpenAI兼容API框架 的通用思路。你需要先根据模型提供方的文档,将 Qwen1.5-1.8B-Chat-GPTQ-Int4 这个模型文件准备好。

假设你使用一个提供OpenAI兼容API的框架,启动命令通常类似于:

# 这是一个示例,具体命令请参考你所使用框架的文档
python -m vllm.entrypoints.openai.api_server \
    --model /你的/模型/路径/Qwen1.5-1.8B-Chat-GPTQ-Int4 \
    --served-model-name Qwen1.5-1.8B-Chat-GPTQ-Int4 \
    --api-key token-abc123 \
    --host 0.0.0.0 \
    --port 8000

或者,如果你使用的是已经封装好的Docker镜像,命令可能类似于:

docker run -d --gpus all -p 8000:8000 \
    -v /你的/模型/路径:/models \
    镜像名称 \
    --model /models/Qwen1.5-1.8B-Chat-GPTQ-Int4 \
    --host 0.0.0.0 --port 8000

关键点:

  • 端口:确保服务启动在 8000 端口,这样我们的 app.py 才能正确连接到 localhost:8000
  • API路径:确保服务提供的API端点确实是 /v1/chat/completions。大多数兼容OpenAI的框架都使用这个标准路径。
  • 模型名称:在 app.pypayload 里,我们指定的 model 参数值需要和服务端识别的名称一致。

启动成功后,你应该能在终端看到模型加载的日志,并最终提示服务已在指定端口运行。

现在,进行终极测试:

  1. 确保模型服务在运行(端口8000)。
  2. 确保Flask应用在运行(端口5000,之前运行的 app.py 如果退出了,就重新运行 python app.py)。
  3. 打开浏览器,再次访问 http://127.0.0.1:5000/chat
  4. 在输入框里问一个问题,比如“Python是什么?”
  5. 点击发送。

如果一切顺利,页面会刷新,并在聊天区域上方或下方显示AI模型的回复!恭喜你,最核心的流程已经跑通了!

5. 第四步:美化前端界面与功能完善

虽然功能实现了,但那个页面实在太简陋了,而且每次发送消息页面都会刷新,体验不好。现在我们来用一点点HTML、CSS和JavaScript,做一个不刷新页面、更像现代聊天工具的单页应用。

我们在项目根目录下创建一个 templates 文件夹,然后在里面创建一个 index.html 文件。Flask会自动在这个文件夹里寻找模板文件。

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>我的通义千问聊天助手</title>
    <style>
        * { box-sizing: border-box; font-family: 'Segoe UI', 'Microsoft YaHei', sans-serif; }
        body { background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%); margin: 0; padding: 20px; min-height: 100vh; }
        .container { max-width: 800px; margin: 0 auto; background: white; border-radius: 16px; box-shadow: 0 10px 30px rgba(0,0,0,0.1); overflow: hidden; }
        .header { background: linear-gradient(90deg, #4776E6, #8E54E9); color: white; padding: 20px 30px; text-align: center; }
        .chat-box { height: 450px; padding: 20px; overflow-y: auto; display: flex; flex-direction: column; gap: 15px; }
        .message { max-width: 80%; padding: 12px 18px; border-radius: 18px; line-height: 1.5; word-wrap: break-word; }
        .user-message { align-self: flex-end; background-color: #4776E6; color: white; border-bottom-right-radius: 4px; }
        .ai-message { align-self: flex-start; background-color: #f0f2f5; color: #333; border-bottom-left-radius: 4px; }
        .input-area { border-top: 1px solid #eee; padding: 20px 30px; display: flex; gap: 10px; }
        #userInput { flex: 1; padding: 15px; border: 2px solid #ddd; border-radius: 25px; font-size: 16px; outline: none; transition: border 0.3s; }
        #userInput:focus { border-color: #4776E6; }
        #sendButton { padding: 15px 30px; background: linear-gradient(90deg, #4776E6, #8E54E9); color: white; border: none; border-radius: 25px; font-size: 16px; font-weight: bold; cursor: pointer; transition: transform 0.2s; }
        #sendButton:hover { transform: scale(1.05); }
        #sendButton:disabled { background: #ccc; cursor: not-allowed; }
        .typing-indicator { align-self: flex-start; color: #888; font-style: italic; padding: 10px; display: none; }
    </style>
</head>
<body>
    <div class="container">
        <div class="header">
            <h1>🤖 我的AI聊天助手</h1>
            <p>基于 通义千问1.5-1.8B-Chat-GPTQ-Int4 模型</p>
        </div>
        <div id="chatBox" class="chat-box">
            <!-- 初始欢迎消息 -->
            <div class="message ai-message">
                你好!我是一个轻量级的AI助手。虽然我体积小,但也能帮你解答问题、进行对话。试试问我点什么吧!
            </div>
        </div>
        <div class="input-area">
            <input type="text" id="userInput" placeholder="输入你想聊的内容..." autocomplete="off">
            <button id="sendButton" onclick="sendMessage()">发送</button>
        </div>
        <div id="typingIndicator" class="typing-indicator">AI正在思考...</div>
    </div>

    <script>
        const chatBox = document.getElementById('chatBox');
        const userInput = document.getElementById('userInput');
        const sendButton = document.getElementById('sendButton');
        const typingIndicator = document.getElementById('typingIndicator');

        // 添加用户消息到聊天框
        function addUserMessage(text) {
            const msgDiv = document.createElement('div');
            msgDiv.className = 'message user-message';
            msgDiv.textContent = text;
            chatBox.appendChild(msgDiv);
            scrollToBottom();
        }

        // 添加AI消息到聊天框
        function addAiMessage(text) {
            const msgDiv = document.createElement('div');
            msgDiv.className = 'message ai-message';
            msgDiv.textContent = text;
            chatBox.appendChild(msgDiv);
            scrollToBottom();
        }

        // 滚动到底部
        function scrollToBottom() {
            chatBox.scrollTop = chatBox.scrollHeight;
        }

        // 发送消息到后端
        async function sendMessage() {
            const message = userInput.value.trim();
            if (!message) return;

            // 禁用输入和按钮,显示用户消息
            userInput.value = '';
            userInput.disabled = true;
            sendButton.disabled = true;
            addUserMessage(message);

            // 显示“正在思考”提示
            typingIndicator.style.display = 'block';
            scrollToBottom();

            try {
                // 使用FormData构造请求数据,与后端@app.route('/chat', methods=['POST'])对应
                const formData = new FormData();
                formData.append('user_message', message);

                const response = await fetch('/chat', {
                    method: 'POST',
                    body: formData
                });

                const result = await response.json();

                // 隐藏“正在思考”提示
                typingIndicator.style.display = 'none';

                if (response.ok && result.reply) {
                    addAiMessage(result.reply);
                } else {
                    addAiMessage(`抱歉,出错了: ${result.error || '未知错误'}`);
                }
            } catch (error) {
                typingIndicator.style.display = 'none';
                addAiMessage('网络请求失败,请检查后端服务是否运行。');
                console.error('请求失败:', error);
            } finally {
                // 重新启用输入和按钮
                userInput.disabled = false;
                sendButton.disabled = false;
                userInput.focus();
            }
        }

        // 支持按回车键发送
        userInput.addEventListener('keypress', function(event) {
            if (event.key === 'Enter' && !event.shiftKey) {
                event.preventDefault();
                sendMessage();
            }
        });

        // 页面加载后自动聚焦到输入框
        window.onload = function() {
            userInput.focus();
        };
    </script>
</body>
</html>

然后,我们需要修改 app.py 中的 home() 函数,让它返回这个漂亮的模板,并且简化 chat 函数,让它只处理POST请求(因为页面逻辑已经由前端JavaScript接管了)。

更新 app.py 文件:

# app.py - 更新后的版本
from flask import Flask, render_template, request, jsonify
import requests
import json
import time

app = Flask(__name__)

API_URL = "http://localhost:8000/v1/chat/completions"
HEADERS = {"Content-Type": "application/json"}

# 首页,返回我们制作好的漂亮界面
@app.route('/')
def home():
    return render_template('index.html')  # 这行代码会渲染 templates/index.html

# 聊天API端点,现在只处理POST请求
@app.route('/chat', methods=['POST'])
def chat():
    user_message = request.form.get('user_message', '').strip()
    if not user_message:
        return jsonify({"error": "消息不能为空"}), 400

    payload = {
        "model": "Qwen1.5-1.8B-Chat-GPTQ-Int4",
        "messages": [{"role": "user", "content": user_message}],
        "stream": False
    }

    try:
        # 可以加一个简单的超时设置
        response = requests.post(API_URL, headers=HEADERS, data=json.dumps(payload), timeout=30)
        if response.status_code == 200:
            response_data = response.json()
            ai_reply = response_data['choices'][0]['message']['content']
            return jsonify({"reply": ai_reply})
        else:
            app.logger.error(f"模型API错误: {response.status_code}, {response.text}")
            return jsonify({"error": f"模型服务返回错误: {response.status_code}"}), 500
    except requests.exceptions.Timeout:
        return jsonify({"error": "请求模型超时,请稍后再试。"}), 504
    except requests.exceptions.ConnectionError:
        return jsonify({"error": "无法连接到模型服务,请确认服务已启动。"}), 503
    except Exception as e:
        app.logger.error(f"处理请求时发生未知错误: {e}")
        return jsonify({"error": "服务器内部错误"}), 500

if __name__ == '__main__':
    app.run(debug=True, host='0.0.0.0', port=5000)

现在,重启你的Flask应用(先按Ctrl+C停止,再运行 python app.py),然后刷新浏览器访问 http://127.0.0.1:5000/

一个美观、交互流畅的聊天助手界面就出现了!输入消息,点击发送,消息会立刻出现在对话框里,然后AI的回复也会无刷新地显示出来。这就是一个完整的、前后端分离的迷你AI应用了。

6. 总结与后续探索

走完这个完整的项目,你应该已经不再是一个仅仅知道Python语法的初学者了。你实际动手完成了一次小型的全栈开发体验:从后端的API调用、Web服务器搭建,到前端的界面交互。

回顾一下我们搭建的积木:用 requests 库与AI模型通信,用 Flask 搭建应用骨架和API,用 HTML/CSS/JavaScript 构建用户界面,最后将它们无缝集成。这个模式具有很强的扩展性。比如,你可以轻松地更换另一个支持相同OpenAI API格式的模型,只需要修改 API_URLmodel 参数。

这个项目还有很多可以完善和探索的方向。例如,你可以为聊天记录加上持久化存储,这样关闭网页后对话不会丢失;可以增加多轮对话的记忆功能,让AI能联系上下文;或者尝试集成语音输入输出,让交互更自然。每一步都可以拆解成类似的小任务,用你在这个项目中学到的方法去攻克。

最重要的是,你证明了从想法到实现之间并没有不可逾越的鸿沟。带着这个成功的经验,你可以更有信心地去探索更复杂的AI应用,或者将AI能力融入到你的其他编程项目中去。编程和AI工具的学习,正是在这样一个个具体项目的实践中不断进步的。


获取更多AI镜像

想探索更多AI镜像和应用场景?访问 CSDN星图镜像广场,提供丰富的预置镜像,覆盖大模型推理、图像生成、视频生成、模型微调等多个领域,支持一键部署。

Logo

欢迎加入DeepSeek 技术社区。在这里,你可以找到志同道合的朋友,共同探索AI技术的奥秘。

更多推荐