基于多个大模型自己建造一个AI智能助手

基于多个大模型自己建造一个AI智能助手

一、概述

前面在博文 "如何自己建造一个AI智能助手" https://blog.csdn.net/cnds123/article/details/155134935 是基于通义千问(Qwen)API(免费额度可用)构建的AI智能助手。

你只需要一个通义千问的DashScope API Key https://dashscope.console.aliyun.com/apiKey (获取 API Key。阿里云账号免费注册,新用户送额度)。需要用支付宝对账号账号进行实名认证。

下面介绍,基于多个大模型,建造一个AI智能助手。

基于多个大模型自己建造一个AI **智能助手,**需要获取多个大模型的API Key。这里基于DashScope API Key 和DeepSeek 的API为例演示。

获取 DeepSeek API Key: https://platform.deepseek.com/sign_in

注意:DeepSeek调用 API 服务需要充值。实名认证后再充值,支付金额最少10元。【充值金额仅用于调用 API 服务,网页版及 App 对话免费使用,无需充值。】

需要有一个邮箱验证登录。创建成功后,系统会生成一个以 sk- 开头的 API Key

立即复制并妥善保存这个 Key,因为关闭对话框后通常无法再次查看完整 Key。

编程能力,这里,具体采用

  • 前端:纯 HTML + JavaScript

  • 后端:Python FastAPI

  • AI 模型 :调用DashScope API Key 和DeepSeek 的API

  • 输入框改进行:用多行文本域(<textarea>),不仅能输入多行内容,还能保留换行、缩进等格式,特别适合粘贴 Python 代码、JSON、Markdown 等结构化文本。支持 Enter 发送(避免换行冲突,需按 Ctrl+Enter发送)

注意:在运行这个应用时,需要安装这些第三方库。

安装命令:

pip install fastapi uvicorn httpx pydantic dashscope

解释一下这些第三方库的作用:

FastAPI: 构建API的主要框架。其中,FastAPI创建 Web API 应用的主要类;HTTPException用于抛出 HTTP 错误异常;CORSMiddleware:处理跨域资源共享(CORS)的中间件。

pydantic: 通过Python类型注解来进行数据验证和设置管理。其中BaseModel 用于定义数据模型。

httpx: 支持异步的HTTP客户端。用于向 DeepSeek API 发送请求。

uvicorn: 用于运行FastAPI应用的ASGI服务器。运行 FastAPI 应用。

Dashscope:是阿里云推出的 模型即服务(MaaS)平台,提供包括通义千问(Qwen)在内的多种大模型 API,支持文本生成、图像生成、语音识别等。

先看运行效果截图:

​​

二、源码

项目结构

D:/ai-assistant- multiple/

├── backend/

│ └── main.py

└── frontend/

└── index.html

后端:backend/main.py 文件源码:

python 复制代码
# backend/main.py
from fastapi import FastAPI, HTTPException
from fastapi.middleware.cors import CORSMiddleware
from pydantic import BaseModel
import httpx
import json
from enum import Enum
from typing import Optional
import os  # 

# 模型配置
class ModelType(str, Enum):
    DEEPSEEK = "deepseek"
    TONGYI = "tongyi"
    OPENAI = "openai"

# API 配置
API_CONFIG = {
    ModelType.DEEPSEEK: {
        "url": "https://api.deepseek.com/v1/chat/completions",
        #"api_key": "你的 DeepSeek API Key",  # 你的 DeepSeek API Key,从环境变量读取更安全
        "api_key": os.getenv("DEEPSEEK_API_KEY"),  # ← 这行
        "model_name": "deepseek-chat"
    },
    ModelType.TONGYI: {
        "url": "https://dashscope.aliyuncs.com/api/v1/services/aigc/text-generation/generation",
        #"api_key": "你的 Tongyi API Key", # 你的 Tongyi API Key,从环境变量读取更安全
        "api_key": os.getenv("DASHSCOPE_API_KEY"),  # ← 这行
        "model_name": "qwen-plus"
    }
}

app = FastAPI()

# 允许前端跨域访问
app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

class ChatRequest(BaseModel):
    message: str
    model: ModelType = ModelType.DEEPSEEK  # 默认模型

class ModelInfo(BaseModel):
    name: str
    display_name: str
    provider: str

@app.get("/models")
async def get_available_models():
    """获取可用的模型列表"""
    models = [
        ModelInfo(
            name=ModelType.DEEPSEEK,
            display_name="DeepSeek Chat",
            provider="DeepSeek"
        ),
        ModelInfo(
            name=ModelType.TONGYI,
            display_name="通义千问",
            provider="阿里云"
        )
    ]
    return {"models": models}

@app.post("/chat")
async def chat(request: ChatRequest):
    try:
        config = API_CONFIG.get(request.model)
        if not config:
            raise HTTPException(status_code=400, detail="不支持的模型")
        
        if request.model == ModelType.TONGYI:
            return await call_tongyi_api(request.message, config)
        else:
            return await call_openai_compatible_api(request.message, config, request.model)
            
    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))

async def call_openai_compatible_api(message: str, config: dict, model_type: ModelType):
    """调用 OpenAI 兼容的 API(DeepSeek、OpenAI)"""
    headers = {
        "Content-Type": "application/json",
        "Authorization": f"Bearer {config['api_key']}"
    }
    
    payload = {
        "model": config["model_name"],
        "messages": [
            {
                "role": "user",
                "content": message
            }
        ],
        "stream": False
    }
    
    async with httpx.AsyncClient() as client:
        response = await client.post(
            config["url"],
            headers=headers,
            json=payload,
            timeout=30.0
        )
        
        if response.status_code == 200:
            data = response.json()
            return {"reply": data["choices"][0]["message"]["content"]}
        else:
            error_detail = f"{model_type.value} API 错误: {response.status_code} - {response.text}"
            raise HTTPException(status_code=500, detail=error_detail)

async def call_tongyi_api(message: str, config: dict):
    """调用通义千问 API"""
    import dashscope
    from dashscope import Generation
    
    dashscope.api_key = config["api_key"]
    
    response = Generation.call(
        model=config["model_name"],
        prompt=message
    )
    
    if response.status_code == 200:
        return {"reply": response.output.text}
    else:
        raise HTTPException(status_code=500, detail=f"通义千问 API 错误: {response.status_code}")

@app.get("/")
async def root():
    return {"message": "多模型 AI 助手后端服务运行中"}

前端:frontend/index.html 文件源码

html 复制代码
<!DOCTYPE html>
<html lang="zh">
<head>
  <meta charset="UTF-8" />
  <title>多模型 AI 助手</title>
  <link rel="stylesheet"
        href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/github-dark.min.css"
        crossorigin="anonymous">
  <style>
    body {
      font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
      max-width: 800px;
      margin: 40px auto;
      padding: 20px;
      background: #f9f9f9;
      line-height: 1.6;
    }
    #chat-box {
      height: 400px;
      border: 1px solid #ddd;
      padding: 15px;
      overflow-y: auto;
      background: white;
      margin-bottom: 10px;
      border-radius: 6px;
      box-shadow: 0 1px 3px rgba(0,0,0,0.1);
    }
    .controls {
      display: flex;
      gap: 10px;
      margin-bottom: 10px;
      align-items: flex-start; /* 适配 textarea 高度 */
    }
    #model-select {
      padding: 8px 12px;
      border: 1px solid #ccc;
      border-radius: 4px;
      background: white;
      flex-shrink: 0;
    }
    /* 多行输入框 */
    #user-input {
      flex: 1;
      padding: 12px;
      border: 1px solid #ccc;
      border-radius: 6px;
      resize: vertical; /* 允许手动拉高 */
      min-height: 60px;
      max-height: 200px;
      font-family: Consolas, Monaco, 'Courier New', monospace; /* 等宽字体,适合代码 */
      font-size: 14px;
      line-height: 1.4;
    }
    #send-btn {
      padding: 12px 20px;
      background: #007bff;
      color: white;
      border: none;
      border-radius: 6px;
      cursor: pointer;
      flex-shrink: 0;
    }
    #send-btn:hover { background: #0069d9; }
    #send-btn:disabled { background: #6c757d; cursor: not-allowed; }
    .message { margin-bottom: 16px; word-wrap: break-word; }
    .user { color: #007bff; }
    .ai { color: #28a745; }
    .ai-content { margin-top: 4px; white-space: pre-wrap; }
    #chat-box pre {
      background: #2d2d2d !important;
      border-radius: 6px;
      padding: 12px !important;
      margin: 8px 0;
      overflow-x: auto;
      font-size: 14px;
    }
    #chat-box code {
      font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, monospace;
      font-size: 13px;
    }

    /* 提示:按 Ctrl+Enter 发送 */
    .hint {
      font-size: 12px;
      color: #666;
      margin-top: 4px;
      margin-left: 8px;
    }
  </style>
</head>
<body>
  <h1>🤖 多模型 AI 助手</h1>
  <div id="chat-box"></div>
  <div class="controls">
    <select id="model-select">
      <option value="tongyi">通义千问</option>
      <option value="deepseek">DeepSeek Chat</option>
    </select>
    <textarea id="user-input" placeholder="请输入你的问题(支持多行,如 Python 代码)。按 Ctrl+Enter直接发送..."></textarea>
    <button id="send-btn">发送</button>
  </div>
  <div class="hint">💡 提示:按 <kbd>Ctrl+Enter</kbd>(Windows/Linux)或 <kbd>Cmd+Enter</kbd>(Mac)快速发送</div>

  <!-- 第三方库 -->
  <script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/dompurify/3.0.5/purify.min.js"></script>

  <script>
    marked.setOptions({
      highlight: function(code, lang) {
        const language = hljs.getLanguage(lang) ? lang : 'plaintext';
        return hljs.highlight(code, { language }).value;
      },
      langPrefix: 'hljs language-'
    });

    const chatBox = document.getElementById('chat-box');
    const modelSelect = document.getElementById('model-select');
    const input = document.getElementById('user-input');
    const sendBtn = document.getElementById('send-btn');

    // 自动调整 textarea 高度
    input.addEventListener('input', function() {
      this.style.height = 'auto';
      this.style.height = Math.min(this.scrollHeight, 200) + 'px';
    });

    async function sendMessage() {
      const msg = input.value.trim();
      if (!msg) return;

      const model = modelSelect.value;

      // 显示用户消息(保留原始格式)
      const userDiv = document.createElement('div');
      userDiv.className = 'message user';
      // 使用 <pre> 保留换行和空格,但用 DOMPurify 防 XSS
      userDiv.innerHTML = `<strong>你:</strong><pre style="margin:8px 0;background:#f0f8ff;padding:10px;border-radius:4px;overflow:auto;">${DOMPurify.sanitize(msg)}</pre>`;
      chatBox.appendChild(userDiv);
      chatBox.scrollTop = chatBox.scrollHeight;

      input.value = '';
      input.style.height = 'auto'; // 重置高度
      sendBtn.disabled = true;
      sendBtn.textContent = '发送中...';

      // 显示"思考中"
      const thinkingDiv = document.createElement('div');
      thinkingDiv.className = 'message ai';
      thinkingDiv.innerHTML = '<strong>AI:</strong> <i>正在思考...</i>';
      chatBox.appendChild(thinkingDiv);
      chatBox.scrollTop = chatBox.scrollHeight;

      try {
        const res = await fetch('http://localhost:8000/chat', {
          method: 'POST',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify({ message: msg, model: model })
        });

        const data = await res.json();
        if (res.ok) {
          const cleanHtml = DOMPurify.sanitize(marked.parse(data.reply));
          thinkingDiv.innerHTML = `<strong>AI:</strong> ${cleanHtml}`;
        } else {
          thinkingDiv.innerHTML = `<strong>AI:</strong> ❌ 出错了:${data.detail || '未知错误'}`;
        }
      } catch (err) {
        thinkingDiv.innerHTML = `<strong>AI:</strong> ⚠️ 无法连接到后端,请确保它正在运行。`;
      } finally {
        sendBtn.disabled = false;
        sendBtn.textContent = '发送';
      }
    }

    sendBtn.addEventListener('click', sendMessage);

    // 支持快捷键发送:Ctrl+Enter / Cmd+Enter
    input.addEventListener('keydown', (e) => {
      if ((e.ctrlKey || e.metaKey) && e.key === 'Enter') {
        e.preventDefault();
        sendMessage();
      }
    });
  </script>
</body>
</html>

提示,可以把 marked.min.js、highlight.min.js、purify.min.js 下载到本地 static/ 目录,改用相对路径。

其中,CDN (Content Delivery Network,内容分发网络)加载 marked 和 highlight.js 用于前端的交互输出格式。让 AI 返回的 代码块、Markdown 格式 能正确显示,并支持 Python 等语言的语法高亮!若缺乏格式输出python代码时就不好用了。

引入purify.min.js,增强安全:purify.min.js 是 DOMPurify 库的压缩版,它的核心作用是: 安全地清理(Sanitize)HTML 字符串,防止 XSS(跨站脚本攻击)。

三、运行步骤

1.启动后端服务

在cmd中(下行若在Windows PowerShell中运行,去掉 /d):

cd /d D:/ai-assistant-multiple/backend

$env:DASHSCOPE_API_KEY="真实的DASHSCOPE_API_KEY "

$env:DEEPSEEK_API_KEY="真实的DEEPSEEK_API_KEY"

uvicorn main:app --port 8000

如果你嫌输入比较麻烦,可以使用批处理文件(.bat)

在D:\ai-assistant-multiple\backend目录中用记事本建立名称为start_backend.bat批处理文件,以后只需双击这个 .bat 文件就能运行后端,无需手动输入命令。内容如下:

bash 复制代码
@echo off
cd /d "D:\ai-assistant-multiple\backend"

:: Set API Key
set DASHSCOPE_API_KEY=真实的DASHSCOPE_API_KEY
set DEEPSEEK_API_KEY=真实的DEEPSEEK_API_KEY

echo Starting AI assistant backend...
echo Visit http://localhost:8000 to check status
echo Open frontend/index.html in your browser
echo.
uvicorn main:app --port 8000

pause

注意,在使用时不要关闭后端服务。

2.打开前端

直接双击 frontend/index.html 用浏览器打开

3.聊天

现在可以在前端网页中开始聊天!

可以在同一个界面中体验不同 AI 模型的回答风格和能力了!

输入框改用多行文本域(<textarea>),不仅能输入多行内容,还能保留换行、缩进等格式,特别适合粘贴 Python 代码、JSON、Markdown 等结构化文本。支持 Enter 发送(避免换行冲突,需按 Ctrl+Enter发送)

用户点击"发送" → 立刻看到"正在思考...",让用户在等待期间知道系统在工作,不会误以为卡死。

未来可以继续扩展高级功能,如:

· 保存聊天历史

· 部署到公网,7x24 小时可用,分享链接给任何人

相关推荐
中國龍在廣州23 分钟前
现在人工智能的研究路径可能走反了
人工智能·算法·搜索引擎·chatgpt·机器人
攻城狮7号32 分钟前
小米具身大模型 MiMo-Embodied 发布并全面开源:统一机器人与自动驾驶
人工智能·机器人·自动驾驶·开源大模型·mimo-embodied·小米具身大模型
搜移IT科技36 分钟前
【无标题】2025ARCE亚洲机器人大会暨展览会将带来哪些新技术与新体验?
人工智能
信也科技布道师FTE1 小时前
当AMIS遇见AI智能体:如何为低代码开发装上“智慧大脑”?
人工智能·低代码·llm
青瓷程序设计1 小时前
植物识别系统【最新版】Python+TensorFlow+Vue3+Django+人工智能+深度学习+卷积神经网络算法
人工智能·python·深度学习
AI即插即用1 小时前
即插即用系列 | CVPR 2025 WPFormer:用于表面缺陷检测的查询式Transformer
人工智能·深度学习·yolo·目标检测·cnn·视觉检测·transformer
唐兴通个人1 小时前
数字化AI大客户营销TOB营销客户开发专业销售技巧培训讲师培训师唐兴通老师分享AI销冠人工智能销售AI赋能销售医药金融工业品制造业
人工智能·金融
人机与认知实验室2 小时前
国内主流大语言模型之比较
人工智能·语言模型·自然语言处理
T0uken2 小时前
【Python】UV:境内的深度学习环境搭建
人工智能·深度学习·uv