从零打通大模型:阿里云百炼 + FastAPI + Vue3 实现单轮与流式 AI 对话
本文以一个「AI 求职助手」为例完整演示如何调用通义千问大模型从阿里云百炼开通与配置到 Python 直连验证再到 FastAPI 封装接口、接口测试、最后用 Vue3 Element Plus 前端对接实现「单轮对话」与「流式输出」两种模式。所有代码均来自真实可运行项目。目录一、整体架构与最终效果二、阿里云百炼配置DASHSCOPE_API_KEY三、Python 直接调用大模型3.1 单轮对话case1.py3.2 流式输出case2.py四、FastAPI 封装 HTTP 接口4.1 请求体 Schema4.2 路由单轮 SSE 流式4.3 在 main.py 注册路由五、接口测试curl / Postman / Swagger六、前端对接Vue3 Element Plus6.1 接口封装 llm.js6.2 Vite 代理配置6.3 豆包风格聊天面板组件6.4 页面与路由七、常见问题与踩坑八、总结一、整体架构与最终效果我们要做的是一个模仿「豆包」的 AI 对话页面包含两种模式模式传输方式后端接口体验单轮对话普通POST等待完整响应后一次性渲染POST /llm-day01/case1发问 → 等几秒 → 整段出现流式输出POST SSEtext/event-stream逐段推送POST /llm-day01/case2发问 → 文字逐字「打字机」式出现整体链路如下浏览器(Vue3) │ /api/llm-day01/case1 (axios, 单轮) │ /api/llm-day01/case2 (fetchSSE, 流式) ▼ Vite 代理 (/api → http://127.0.0.1:8000) ▼ FastAPI 后端 (case1_api.py) ▼ 阿里云百炼 DashScope (通义千问 qwen-plus, OpenAI 兼容)二、阿里云百炼配置DASHSCOPE_API_KEY本项目通过阿里云百炼原 DashScope提供的OpenAI 兼容接口调用通义千问底层库使用官方openaiSDK。2.1 开通与获取 API Key登录 阿里云百炼控制台。开通「模型服务」并进入API-KEY 管理点击「创建 API-KEY」。复制生成的 Key格式形如sk-xxxxxxxxxxxxxxxx。2.2 配置环境变量推荐强烈建议用环境变量不要硬编码 Key 到代码里。# Linux / macOS export DASHSCOPE_API_KEYsk-你的key ​ # Windows (PowerShell) $env:DASHSCOPE_API_KEYsk-你的key ​ # 或写入 .env 文件不要提交到 git echo DASHSCOPE_API_KEYsk-你的key .env代码中直接读取import os api_key os.getenv(DASHSCOPE_API_KEY)2.3 base_url 与模型OpenAI 兼容模式下百炼的base_url为https://dashscope.aliyuncs.com/compatible-mode/v1注本文项目代码中使用的是专属 MaaS endpointhttps://ws-xxxxx.cn-beijing.maas.aliyuncs.com/compatible-mode/v1功能等价可按你自己的专属地址替换。常用模型通过model参数指定qwen-plus通义千问 plus性价比高本项目使用qwen-max效果更强qwen-turbo速度最快、最便宜三、Python 直接调用大模型先不碰 Web 框架用纯 Python 验证大模型调用是否跑通。项目把这两段放在llm/目录下。3.1 单轮对话case1.py# llm/case1.py import os from openai import OpenAI ​ client OpenAI( api_keyos.getenv(DASHSCOPE_API_KEY), base_urlhttps://dashscope.aliyuncs.com/compatible-mode/v1, ) ​ completion client.chat.completions.create( modelqwen-plus, messages[ {role: system, content: You are a helpful assistant.}, {role: user, content: 人为什么要睡觉?}, ], temperature0.75, max_completion_tokens100, ) ​ print(completion.choices[0].message.content)运行pip install openai python llm/case1.py3.2 流式输出case2.py关键参数是streamTrue此时返回的是一个可迭代的生成器每收到一个 chunk 就 yield 一次。# llm/case2.py import os from openai import OpenAI ​ client OpenAI( api_keyos.getenv(DASHSCOPE_API_KEY), base_urlhttps://dashscope.aliyuncs.com/compatible-mode/v1, ) ​ completion client.chat.completions.create( modelqwen-plus, messages[ {role: system, content: You are a helpful assistant.}, {role: user, content: 请介绍一下自己}, ], streamTrue, stream_options{include_usage: True}, # 返回 token 用量 ) ​ content_parts [] print(AI: , end, flushTrue) ​ for chunk in completion: if chunk.choices: content chunk.choices[0].delta.content or print(content, end, flushTrue) content_parts.append(content) elif chunk.usage: print(\n--- 请求用量 ---) print(f输入 Tokens: {chunk.usage.prompt_tokens}) print(f输出 Tokens: {chunk.usage.completion_tokens}) print(f总计 Tokens: {chunk.usage.total_tokens}) ​ print(f\n--- 完整回复 ---\n{.join(content_parts)})流式模式下内容通过chunk.choices[0].delta.content逐段获取用它来实现「打字机」效果是标准做法。四、FastAPI 封装 HTTP 接口验证通了之后把调用逻辑封装成 Web 接口供前端调用。4.1 请求体 Schema使用 Pydantic 定义请求体只接收一个question字段# app/schemas/llm_case1.py from pydantic import BaseModel, Field ​ ​ class LLMCase1(BaseModel): question: str Field(..., title问题, description用户问题)4.2 路由单轮 SSE 流式# app/llm/case1_api.py import os from fastapi import APIRouter from openai import OpenAI from starlette.responses import StreamingResponse from app.schemas.llm_case1 import LLMCase1 BASE_URL https://dashscope.aliyuncs.com/compatible-mode/v1 router APIRouter(prefix/llm-day01, tags[LLM-DAY01]) # 单轮对话等待完整回复后返回 JSON router.post(/case1, summary单轮对话) async def case1_api(body: LLMCase1): client OpenAI( api_keyos.getenv(DASHSCOPE_API_KEY), base_urlBASE_URL, ) completion client.chat.completions.create( modelqwen-plus, messages[ {role: system, content: 你是一个智能助手}, {role: user, content: body.question}, ], temperature0.75, ) ai_reply completion.choices[0].message.content return { code: 1, message: 请求成功, data: {ai_reply: ai_reply}, } # 流式对话SSE 逐段推送 def stream_chunk(user_question: str): client OpenAI( api_keyos.environ[DASHSCOPE_API_KEY], base_urlBASE_URL, ) completion client.chat.completions.create( modelqwen-plus, messages[ {role: system, content: You are a helpful assistant.}, {role: user, content: user_question}, ], streamTrue, stream_options{include_usage: True}, ) for chunk in completion: if chunk.choices: delta chunk.choices[0].delta if delta.content: yield fdata: {delta.content}\n\n # SSE 格式data: 内容 两个换行 yield data: [done]\n\n # 自定义结束标志 router.post(/case2, summary流式对话(SSE)) async def case2_api(body: LLMCase1): return StreamingResponse( contentstream_chunk(body.question), media_typetext/event-stream, )SSE 格式要点每个事件以data: 内容\n\ndata: 内容 两个换行分隔结束用data: [done]\n\n。前端据此切分。4.3 在 main.py 注册路由# main.py节选 from app.llm.case1_api import llm_day01_router app.include_router(llm_day01_router) # 别忘了跨域方便前端本地联调 from starlette.middleware.cors import CORSMiddleware app.add_middleware( CORSMiddleware, allow_origins[*], allow_methods[*], allow_headers[*], allow_credentialsTrue, )启动后端pip install fastapi uvicorn uvicorn main:app --host 127.0.0.1 --port 8000 --reload五、接口测试curl / Postman / Swagger5.1 curl 测试单轮curl -X POST http://127.0.0.1:8000/llm-day01/case1 \ -H Content-Type: application/json \ -d {question:你好}返回{ code: 1, message: 请求成功, data: { ai_reply: 你好很高兴见到你 有什么可以帮你的吗 } }5.2 curl 测试流式curl -N -X POST http://127.0.0.1:8000/llm-day01/case2 \ -H Content-Type: application/json \ -d {question:介绍一下你自己}你会看到内容被逐行推出来最后一行是data: [done]。5.3 Swagger 在线文档FastAPI 自带交互式文档浏览器打开即可填参数直接测试http://127.0.0.1:8000/docs如果接口文档里测试通过、但前端报错问题几乎一定在前端超时 / 代理 / SSE 解析见第七节。六、前端对接Vue3 Element Plus前端是标准的 Vue3 Vite Element Plus 项目。核心难点是流式接口是 POST不能用浏览器的原生EventSource它只支持 GET因此要手写fetch ReadableStream解析 SSE。6.1 接口封装 llm.js// src/api/llm.js import request from /utils/request // 单轮对话 export function askOnce(question) { return request({ url: /llm-day01/case1, method: post, data: { question }, timeout: 120000, // 大模型单轮完整生成可能较慢务必放宽超时 }) } // 流式对话POST SSE不能用 EventSource export async function askStream(question, { onChunk, onDone, onError } {}) { try { const token localStorage.getItem(candidateToken) const resp await fetch(/api/llm-day01/case2, { method: POST, headers: { Content-Type: application/json, ...(token ? { Authorization: Bearer ${token} } : {}), }, body: JSON.stringify({ question }), }) if (!resp.ok || !resp.body) { const text await resp.text().catch(() ) throw new Error(text || 请求失败状态码 ${resp.status}) } const reader resp.body.getReader() const decoder new TextDecoder(utf-8) let buffer while (true) { const { done, value } await reader.read() if (done) break buffer decoder.decode(value, { stream: true }) // SSE 以空行(\n\n)分隔事件 let sep while ((sep buffer.indexOf(\n\n)) ! -1) { const event buffer.slice(0, sep) buffer buffer.slice(sep 2) const dataLine event .split(\n) .find((line) line.startsWith(data:)) if (!dataLine) continue const data dataLine.slice(5).trim() if (data [DONE] || data [done]) { onDone onDone() return } onChunk onChunk(data) } } onDone onDone() } catch (err) { console.error([askStream] 流式请求异常:, err) onError onError(err) } }6.2 Vite 代理配置把/api代理到后端 8000并去掉/api前缀// vite.config.js export default defineConfig({ server: { port: 3003, proxy: { /api: { target: http://127.0.0.1:8000, changeOrigin: true, rewrite: (path) path.replace(/^\/api/, ), }, }, }, })这样前端访问/api/llm-day01/case1实际打到http://127.0.0.1:8000/llm-day01/case1跨域问题交给代理解决。6.3 豆包风格聊天面板组件核心是一个可复用组件AiChatPanel.vue通过mode区分单轮 / 流式!-- src/components/ai/AiChatPanel.vue核心逻辑节选 -- script setup import { ref, reactive } from vue import { askOnce, askStream } from /api/llm const props defineProps({ mode: { type: String, default: single } }) const messages ref([]) const inputText ref() const loading ref(false) const sendSingle async (question) { loading.value true const aiMsg reactive({ role: ai, content: , streaming: true }) messages.value.push(aiMsg) try { const res await askOnce(question) aiMsg.content res?.data?.ai_reply || 暂无回复 } catch (e) { console.error([sendSingle] 单轮请求失败:, e) aiMsg.content 请求失败请稍后重试。 } finally { aiMsg.streaming false loading.value false } } const sendStream (question) { loading.value true const aiMsg reactive({ role: ai, content: , streaming: true }) messages.value.push(aiMsg) askStream(question, { onChunk: (chunk) { aiMsg.content chunk }, // 逐字追加 onDone: () { aiMsg.streaming false; loading.value false }, onError: (err) { aiMsg.streaming false aiMsg.content aiMsg.content || 请求失败请稍后重试。 loading.value false }, }) } /script6.4 页面与路由两个页面分别复用面板再注册路由// router/index.js import AiSingleChat from /pages/ai/AiSingleChat.vue import AiStreamChat from /pages/ai/AiStreamChat.vue // 在 /candidate 子路由 children 中增加 { path: ai-single, name: AiSingleChat, component: AiSingleChat }, { path: ai-stream, name: AiStreamChat, component: AiStreamChat },!-- src/pages/ai/AiSingleChat.vue -- template AiChatPanel modesingle / /template script setup import AiChatPanel from /components/ai/AiChatPanel.vue /script !-- src/pages/ai/AiStreamChat.vue -- template AiChatPanel modestream / /template script setup import AiChatPanel from /components/ai/AiChatPanel.vue /script启动前端npm install npm run dev # 默认 http://127.0.0.1:3003七、常见问题与踩坑❌ 坑 1单轮对话「请求超时」axios 默认timeout: 1500015 秒。大模型在并发高或首字延迟大时单轮完整响应很容易超过 15 秒触发超时。解决在askOnce中单独设置timeout: 1200002 分钟见 6.1。流式模式不会超时因为首字节很快返回连接一直「活跃」。这也解释了为什么「流式正常、单轮超时」。❌ 坑 2流式接口是 POSTEventSource 用不了new EventSource(url)只能发 GET。我们的流式接口是POST /llm-day01/case2必须带 body。解决用fetchresponse.body.getReader()手动读取流按\n\n切分 SSE 事件见 6.1 的askStream。❌ 坑 3SSE 结束标志要自己约定标准 SSE 用data: [DONE]结束。本项目后端约定data: [done]小写。前端解析时两者都兼容即可。❌ 坑 4跨域 / 代理本地开发若前端直接请求http://127.0.0.1:8000会触发 CORS。统一走 Vite 的/api代理见 6.2即可无需在前端写完整域名。❌ 坑 5本地联调 405用浏览器或 curl 以GET方式访问/llm-day01/case1会返回405 Method Not Allowed——这是正常的因为接口只接受 POST。测试请用curl -X POST或 Swagger。八、总结本文从配置到落地打通了「阿里云百炼 → FastAPI → Vue3」的完整链路配置在百炼控制台拿到DASHSCOPE_API_KEY用环境变量注入base_url 走 OpenAI 兼容模式。Python 直连streamTrue实现流式delta.content取片段。FastAPI 封装单轮返回 JSON流式用StreamingResponse SSEdata: 片段/data: [done]。测试curl、Postman、Swagger/docs三件套验证接口。前端 axios 单轮注意超时fetch ReadableStream解析 POST 流式Vite 代理解决跨域。踩坑超时、EventSource 不支持 POST、SSE 结束标志、代理与 405。照着这套你也能快速给自己的系统加上一个「会打字」的 AI 助手。环境依赖# 后端 pip install fastapi uvicorn openai ​ # 前端 npm install axios element-plus完整项目结构节选fastApiProject4/ ├── llm/ │ ├── case1.py # Python 单轮直连示例 │ └── case2.py # Python 流式直连示例 ├── app/ │ ├── llm/case1_api.py # FastAPI 单轮 SSE 流式接口 │ ├── schemas/llm_case1.py │ └── main.py # 注册路由 CORS └── main.py ​ new_boss_vue-main/boss-candidate-ui/ ├── src/api/llm.js # 接口封装单轮 流式 ├── src/components/ai/AiChatPanel.vue ├── src/pages/ai/AiSingleChat.vue ├── src/pages/ai/AiStreamChat.vue ├── src/router/index.js └── vite.config.js # /api 代理到 8000

相关新闻

2025届学术党必备的十大AI科研方案解析与推荐

2025届学术党必备的十大AI科研方案解析与推荐

Ai论文网站排名(开题报告、文献综述、降aigc率、降重综合对比) TOP1. 千笔AI TOP2. aipasspaper TOP3. 清北论文 TOP4. 豆包 TOP5. kimi TOP6. deepseek 伴随人工智能写作工具得以普及, 大量的内容当中带有显著的AI生成出来的痕迹, 怎么样去降低A…

2026/10/17 0:54:36 阅读更多 →
Android NDK版本大全:构建自动化维护与实战应用指南

Android NDK版本大全:构建自动化维护与实战应用指南

1. 项目概述:为什么需要一个NDK版本地址大全? 如果你是一名Android平台的C/C开发者,或者你的项目需要集成一些高性能的本地库,那么Android NDK(Native Development Kit)绝对是你绕不开的工具。它让你能在An…

2026/10/15 13:50:58 阅读更多 →
MyCat 实现 MySQL 主从读写分离

MyCat 实现 MySQL 主从读写分离

目录 一、MySQL读写分离概述 1.1 工作原理 1.2 为什么要读写分离 1.3 什么是MyCat 二、MyCat安装与配置 2.1 解压 2.2 创建用户并修改权限 2.3 Java环境要求 2.4 MyCat启动和配置 三、配置数据库 3.1 配置主库(master) 3.2 配置两个从库&…

2026/10/17 22:36:01 阅读更多 →

最新新闻

15-内部类

15-内部类

内部类 什么是内部类为什么有内部类内部类的分类成员内部类 编写成员内部类的注意事项如何创建成员内部类的对象成员内部类如何获取外部类的成员变量 静态内部类 创建静态内部类的对象的格式调用静态内部类中非静态方法的格式调用静态内部类中静态方法的格式示例2 局部内部类 示…

2026/10/18 3:36:18 阅读更多 →
离散数学期末真题高效刷题法:从试卷结构到考前冲刺

离散数学期末真题高效刷题法:从试卷结构到考前冲刺

简介:这份《离散数学》期末考试真题试卷(doc格式)面向高校计算机、软件工程等专业的本科生及考研复习者,用于考前自测、模拟训练或教师组卷参考。试卷系统覆盖集合与关系、图论、逻辑与证明、组合数学、离散概率论等核心考点&…

2026/10/18 3:36:18 阅读更多 →
Cursor中C++插件报错与函数跳转失效:把settings.json改到TaoToken统一通道的排查大纲

Cursor中C++插件报错与函数跳转失效:把settings.json改到TaoToken统一通道的排查大纲

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/18 3:36:18 阅读更多 →
SoapUI 5.8.0 接口测试实践:从断言到数据驱动与自动化回归

SoapUI 5.8.0 接口测试实践:从断言到数据驱动与自动化回归

简介:SoapUI 5.8.0 是一套专为接口测试场景设计的完整工具发行包,采用 zip 压缩格式,内部共包含 316 个文件、约 122MB,主要面向后端开发、测试工程师以及接口自动化学习者。通过这套包,可完成 SOAP/REST 接口的请求构…

2026/10/18 3:35:18 阅读更多 →
Java智能CAI系统核心业务逻辑落地:掌握度评估、出题策略与复习推送

Java智能CAI系统核心业务逻辑落地:掌握度评估、出题策略与复习推送

做后端这几年,我见过不少挂着“智能”头衔的练习系统,本质就是个带筛选条件的题库。这种情况,项目验收的时候大概率会被问同一个问题:你到底智能在哪?回答不上来,就只能反复强调“能自动判分”“能生成报告…

2026/10/18 3:35:18 阅读更多 →
前端导出PDF实战:jsPDF+html2canvas原理、分页与避坑指南

前端导出PDF实战:jsPDF+html2canvas原理、分页与避坑指南

做前端这些年,"导出 PDF"这四个字,基本是后台管理系统里的钉子户需求。无论是财务报表、订单凭证还是审批单,产品经理都会在某一天突然提出:加个导出按钮吧。第一次我天真地以为调用浏览器打印就够了,结果分…

2026/10/18 3:35:18 阅读更多 →

日新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型(GMM)的语音识别系统,很多人第一反应就是一堆数学公式,觉得门槛很高。但如果你真的在MATLAB里把它落地跑通,会发现核心其实就是三件事:把声音变成特征向量,用GMM给每个类别…

2026/10/18 0:00:55 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人,但落到具体操作上,无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程:对图像做反白、改调色板、彩色转灰度,最后把像素数据导出成txt文件。单看每一…

2026/10/18 0:00:55 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词,你会发现一个很有意思的现象:每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型,有人指的是运动控制系统,还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/18 0:00:55 阅读更多 →

周新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型(GMM)的语音识别系统,很多人第一反应就是一堆数学公式,觉得门槛很高。但如果你真的在MATLAB里把它落地跑通,会发现核心其实就是三件事:把声音变成特征向量,用GMM给每个类别…

2026/10/18 0:00:55 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人,但落到具体操作上,无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程:对图像做反白、改调色板、彩色转灰度,最后把像素数据导出成txt文件。单看每一…

2026/10/18 0:00:55 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词,你会发现一个很有意思的现象:每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型,有人指的是运动控制系统,还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/18 0:00:55 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/18 0:55:02 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/17 17:22:31 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/16 14:23:36 阅读更多 →