大模型:阿里云百炼 + FastAPI + Vue3 实现单轮与流式 AI 对话
2026/8/4 5:21:58 网站建设 项目流程

1. 引言

从零打通大模型:阿里云百炼 + FastAPI + Vue3 实现单轮与流式 AI 对话(附 CSDN 实战)

摘要:本文是一份完整的全栈开发实战指南,详细介绍了如何从零开始构建一个支持单轮与流式对话的 AI 应用。文章以阿里云百炼(Bailian)作为大模型服务底座,使用 FastAPI 构建高效后端 API,并采用 Vue3 开发现代化前端界面。内容涵盖技术栈准备、项目结构初始化、后端服务集成、前端界面实现等完整开发流程,提供了可直接运行的代码示例和详细的部署指南,适合前端、后端及全栈开发者参考实践。

在人工智能浪潮席卷全球的今天,大语言模型(LLM)已成为开发者手中最炙手可热的工具。然而,如何将强大的模型能力快速、稳定地集成到自己的应用中,构建出体验流畅的 AI 对话功能,是许多开发者面临的挑战。

本文将带你从零开始,手把手搭建一个完整的 AI 对话应用。我们将以阿里云百炼(Bailian)作为大模型服务底座,使用FastAPI构建高效、易用的后端 API,并采用Vue3开发现代化的前端交互界面。你将学习到:

  • 单轮对话的实现:如何发送请求并接收完整的模型回复。
  • 流式对话(Streaming)的实现:如何实现打字机式的逐字输出效果,极大提升用户体验。
  • 前后端分离架构的实践:清晰的分层与通信方式。
  • 完整的项目部署与实战:提供可直接运行的代码示例和 CSDN 博客同步指南。

无论你是前端、后端还是全栈开发者,本文都将为你提供一条清晰、可行的技术路径。

2. 技术栈与工具准备

在开始编码之前,请确保你的开发环境已就绪。

2.1 后端技术栈 (FastAPI)

  • Python 3.8+
  • FastAPI: 现代、快速(高性能)的 Web 框架,用于构建 API。
  • Uvicorn: ASGI 服务器,用于运行 FastAPI 应用。
  • 阿里云百炼 SDK: 官方 Python SDK,用于调用百炼模型。
  • Pydantic: 用于数据验证和设置管理。
  • python-dotenv: 管理环境变量。

2.2 前端技术栈 (Vue3)

  • Node.js 18+
  • Vue 3Composition API
  • Vite: 下一代前端构建工具,启动极快。
  • Axios: HTTP 客户端,用于调用后端 API。
  • Element Plus(可选): 基于 Vue 3 的组件库,用于快速搭建 UI。

2.3 阿里云百炼准备

  1. 开通服务:访问 阿里云百炼控制台,开通百炼服务。
  2. 获取密钥:在控制台创建 AccessKey(AccessKey ID 和 AccessKey Secret),并妥善保管。
  3. 选择模型:百炼提供了多种模型(如 Qwen、Baichuan 等),本文以qwen-max为例,你也可以根据需求选择其他模型。

2.4 初始化项目结构

创建项目根目录ai-chat-demo,并初始化如下结构:

ai-chat-demo/ ├── backend/ # FastAPI 后端项目 │ ├── app/ │ │ ├── __init__.py │ │ ├── main.py # FastAPI 应用入口 │ │ ├── api/ # 路由模块 │ │ ├── core/ # 核心配置 │ │ └── services/ # 业务逻辑(如调用百炼) │ ├── requirements.txt │ └── .env.example └── frontend/ # Vue3 前端项目 ├── src/ │ ├── App.vue │ ├── main.js │ └── views/ # 页面组件 ├── package.json └── vite.config.js

3. 后端开发:FastAPI 集成阿里云百炼

3.1 安装依赖

进入backend目录,创建requirements.txt

fastapi==0.104.1 uvicorn[standard]==0.24.0 alibabacloud_bailian20231229==1.0.1 pydantic==2.5.0 pydantic-settings==2.1.0 python-dotenv==1.0.0 cors==1.0.1

运行pip install -r requirements.txt安装。

3.2 配置管理

创建app/core/config.py,使用 Pydantic 管理配置,从环境变量读取百炼密钥。

frompydantic_settingsimportBaseSettingsclassSettings(BaseSettings):# 阿里云百炼配置BAILIAN_ACCESS_KEY_ID:strBAILIAN_ACCESS_KEY_SECRET:strBAILIAN_AGENT_KEY:str=""# 代理密钥,非必填BAILIAN_MODEL_ID:str="qwen-max"# 默认使用 qwen-maxBAILIAN_ENDPOINT:str="bailian.cn-beijing.aliyuncs.com"# 应用配置APP_HOST:str="0.0.0.0"APP_PORT:int=8000DEBUG:bool=FalseclassConfig:env_file=".env"settings=Settings()

创建.env文件(参考.env.example)并填入你的密钥:

BAILIAN_ACCESS_KEY_ID=your_access_key_id BAILIAN_ACCESS_KEY_SECRET=your_access_key_secret # BAILIAN_AGENT_KEY=your_agent_key (可选)

3.3 创建百炼服务

创建app/services/bailian_service.py,封装调用逻辑。

importjsonfromtypingimportAsyncGeneratorfromalibabacloud_bailian20231229importmodelsasbailian_modelsfromalibabacloud_tea_openapiimportmodelsasopen_api_modelsfromalibabacloud_bailian20231229.clientimportClientasBailianClientfromapp.core.configimportsettingsclassBailianService:def__init__(self):# 初始化百炼客户端config=open_api_models.Config(access_key_id=settings.BAILIAN_ACCESS_KEY_ID,access_key_secret=settings.BAILIAN_ACCESS_KEY_SECRET,endpoint=settings.BAILIAN_ENDPOINT,)self.client=BailianClient(config)self.model_id=settings.BAILIAN_MODEL_IDasyncdefcreate_completion(self,prompt:str,stream:bool=False):"""创建单轮对话(非流式)"""request=bailian_models.CreateCompletionRequest(model_id=self.model_id,prompt=prompt,stream=False,# 单轮关闭流式parameters={"result_format":"text","max_tokens":2000,})try:resp=self.client.create_completion(request)returnresp.body.data.textexceptExceptionase:raiseException(f"百炼 API 调用失败:{e}")asyncdefcreate_completion_stream(self,prompt:str)->AsyncGenerator[str,None]:"""创建流式对话"""request=bailian_models.CreateCompletionRequest(model_id=self.model_id,prompt=prompt,stream=True,# 开启流式parameters={"result_format":"text","max_tokens":2000,})try:# 注意:SDK 的流式响应可能需要特殊处理,这里为示例逻辑resp=self.client.create_completion_with_options(request,runtime=None)# 假设 resp 是一个可迭代的流式响应体forchunkinresp:ifhasattr(chunk,'data')andchunk.data:yieldchunk.data.textexceptExceptionase:yieldf"[流式输出错误:{e}]"bailian_service=BailianService()

3.4 创建 API 路由

创建app/api/endpoints/chat.py,定义对话接口。

fromfastapiimportAPIRouter,HTTPExceptionfromfastapi.responsesimportStreamingResponsefrompydanticimportBaseModelfromapp.services.bailian_serviceimportbailian_serviceimportasyncio router=APIRouter()classChatRequest(BaseModel):message:strstream:bool=False# 是否使用流式输出@router.post("/chat")asyncdefchat_completion(request:ChatRequest):"""处理聊天请求,支持单轮和流式"""ifnotrequest.message.strip():raiseHTTPException(status_code=400,detail="消息不能为空")ifrequest.stream:# 流式响应asyncdefstream_generator():asyncforchunkinbailian_service.create_completion_stream(request.message):yieldf"data:{chunk}\n\n"yield"data: [DONE]\n\n"returnStreamingResponse(stream_generator(),media_type="text/event-stream",headers={"Cache-Control":"no-cache","Connection":"keep-alive",})else:# 单轮响应try:response_text=awaitbailian_service.create_completion(request.message)return{"response":response_text}exceptExceptionase:raiseHTTPException(status_code=500,detail=str(e))

3.5 主应用与 CORS 配置

app/main.py中创建 FastAPI 应用并挂载路由。

fromfastapiimportFastAPIfromfastapi.middleware.corsimportCORSMiddlewarefromapp.api.endpointsimportchat app=FastAPI(title="AI Chat API",version="1.0.0")# 配置 CORS,允许前端访问app.add_middleware(CORSMiddleware,allow_origins=["http://localhost:5173"],# Vite 默认前端地址allow_credentials=True,allow_methods=["*"],allow_headers=["*"],)# 挂载路由app.include_router(chat.router,prefix="/api/v1",tags=["chat"])@app.get("/")asyncdefroot():return{"message":"AI Chat API is running!"}

3.6 启动后端服务

backend目录下运行:

uvicorn app.main:app--reload--host0.0.0.0--port8000

访问http://localhost:8000/docs即可查看自动生成的 API 文档并进行测试。

4. 前端开发:Vue3 实现对话界面

4.1 初始化 Vue 项目

使用 Vite 快速创建 Vue 项目:

# 在项目根目录下npmcreate vue@latest frontend# 按照提示选择 Vue 3, TypeScript, Router 等(按需)cdfrontendnpminstallnpminstallaxios element-plus# 安装所需依赖

4.2 创建聊天组件

创建src/views/ChatView.vue,实现核心聊天界面。

<template> <div class="chat-container"> <h1>🤖 AI 对话助手 (阿里云百炼)</h1> <div class="chat-box"> <!-- 消息列表 --> <div class="message-list" ref="messageListRef"> <div v-for="(msg, index) in messages" :key="index" :class="['message', msg.role]"> <div class="avatar">{{ msg.role === 'user' ? '👤' : '🤖' }}</div> <div class="content"> <div v-if="msg.role === 'user'">{{ msg.content }}</div> <div v-else> <!-- 流式输出时,显示不断累积的内容 --> <span v-if="msg.isStreaming && msg.streamContent">{{ msg.streamContent }}</span> <span v-else>{{ msg.content }}</span> <span v-if="msg.isStreaming" class="streaming-cursor">▌</span> </div> </div> </div> </div> <!-- 输入区域 --> <div class="input-area"> <el-input v-model="inputMessage" type="textarea" :rows="3" placeholder="输入你的问题..." @keydown.enter.exact.prevent="handleSend" /> <div class="actions"> <el-checkbox v-model="useStream">启用流式输出</el-checkbox> <el-button type="primary" :loading="isLoading" @click="handleSend"> {{ isLoading ? '思考中...' : '发送' }} </el-button> <el-button @click="clearChat">清空对话</el-button> </div> </div> </div> </div> </template> <script setup> import { ref, computed, nextTick } from 'vue' import axios from 'axios' import { ElMessage } from 'element-plus' const API_BASE = 'http://localhost:8000/api/v1' const inputMessage = ref('') const useStream = ref(true) const isLoading = ref(false) const messages = ref([]) const messageListRef = ref(null) // 发送消息 const handleSend = async () => { const msg = inputMessage.value.trim() if (!msg || isLoading.value) return // 添加用户消息 messages.value.push({ role: 'user', content: msg, timestamp: new Date() }) inputMessage.value = '' // 添加一个空的 AI 消息占位 const aiMessageIndex = messages.value.length messages.value.push({ role: 'assistant', content: '', isStreaming: useStream.value, streamContent: '' }) isLoading.value = true scrollToBottom() try { if (useStream.value) { await handleStreamResponse(msg, aiMessageIndex) } else { await handleNormalResponse(msg, aiMessageIndex) } } catch (error) { console.error('请求失败:', error) ElMessage.error('请求失败:' + error.message) // 移除失败的占位消息 messages.value.splice(aiMessageIndex, 1) } finally { isLoading.value = false scrollToBottom() } } // 处理普通(单轮)响应 const handleNormalResponse = async (userMsg, aiIndex) => { const response = await axios.post(`${API_BASE}/chat`, { message: userMsg, stream: false }) messages.value[aiIndex].content = response.data.response messages.value[aiIndex].isStreaming = false } // 处理流式响应 const handleStreamResponse = async (userMsg, aiIndex) => { const eventSource = new EventSource(`${API_BASE}/chat?message=${encodeURIComponent(userMsg)}&stream=true`) messages.value[aiIndex].isStreaming = true messages.value[aiIndex].streamContent = '' eventSource.onmessage = (event) => { if (event.data === '[DONE]') { eventSource.close() messages.value[aiIndex].isStreaming = false // 将流式内容最终保存到 content messages.value[aiIndex].content = messages.value[aiIndex].streamContent return } // 累积流式内容 messages.value[aiIndex].streamContent += event.data scrollToBottom() } eventSource.onerror = (error) => { console.error('EventSource 错误:', error) eventSource.close() messages.value[aiIndex].isStreaming = false messages.value[aiIndex].content = '流式请求中断。' ElMessage.error('流式连接出错') } } // 清空对话 const clearChat = () => { messages.value = [] } // 滚动到底部 const scrollToBottom = () => { nextTick(() => { if (messageListRef.value) { messageListRef.value.scrollTop = messageListRef.value.scrollHeight } }) } </script> <style scoped> .chat-container { max-width: 800px; margin: 0 auto; padding: 20px; } .chat-box { border: 1px solid #dcdfe6; border-radius: 8px; overflow: hidden; } .message-list { height: 500px; overflow-y: auto; padding: 20px; background-color: #fafafa; } .message { display: flex; margin-bottom: 20px; } .message.user { flex-direction: row-reverse; } .message .avatar { width: 40px; height: 40px; border-radius: 50%; background: #409eff; color: white; display: flex; align-items: center; justify-content: center; margin: 0 10px; } .message.user .avatar { background: #67c23a; } .message .content { max-width: 70%; padding: 12px 16px; border-radius: 8px; background: white; box-shadow: 0 2px 8px rgba(0,0,0,0.1); } .message.user .content { background: #e1f3d8; } .input-area { padding: 20px; border-top: 1px solid #dcdfe6; } .actions { display: flex; justify-content: space-between; align-items: center; margin-top: 15px; } .streaming-cursor { animation: blink 1s infinite; } @keyframes blink { 0%, 100% { opacity: 1; } 50% { opacity: 0; } } </style>

4.3 配置路由与启动

src/router/index.js中配置路由,将ChatView设置为首页。

import{createRouter,createWebHistory}from'vue-router'importChatViewfrom'../views/ChatView.vue'constrouter=createRouter({history:createWebHistory(import.meta.env.BASE_URL),routes:[{path:'/',name:'chat',comp

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询