从零打通大模型:阿里云百炼 + 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/8/4 5:48:21 阅读更多 →
Android NDK版本大全:构建自动化维护与实战应用指南

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

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

2026/8/4 5:48:20 阅读更多 →
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/8/4 5:47:20 阅读更多 →

最新新闻

ELN到底应该定制开发,还是选择标准产品?

ELN到底应该定制开发,还是选择标准产品?

很多企业在初期调研电子实验记录本(ELN)时,都会问供应商一个问题:“如果以后我们有一些必需的功能,现有系统又不支持,你们可以为我们做二次开发吗?”企业提出这个问题,是希望系统能够…

2026/8/4 7:25:00 阅读更多 →
MySQL 明明建了索引,为什么还是全表扫描?EXPLAIN 实战拆解 12 种“不走索引”场景

MySQL 明明建了索引,为什么还是全表扫描?EXPLAIN 实战拆解 12 种“不走索引”场景

MySQL 明明建了索引,为什么还是全表扫描?EXPLAIN 实战拆解 12 种“不走索引”场景 先说结论:“建了索引”不等于“本次查询一定使用索引”。MySQL 优化器会比较不同执行计划的成本;条件写法无法形成可搜索的索引范围、联合索引顺序…

2026/8/4 7:25:00 阅读更多 →
3步完成!Deepin Boot Maker:终极启动盘制作工具完整指南

3步完成!Deepin Boot Maker:终极启动盘制作工具完整指南

3步完成!Deepin Boot Maker:终极启动盘制作工具完整指南 【免费下载链接】deepin-boot-maker 项目地址: https://gitcode.com/gh_mirrors/de/deepin-boot-maker 还在为复杂的系统安装而烦恼吗?Deepin Boot Maker 是你的救星&#xff…

2026/8/4 7:25:00 阅读更多 →
DVWA SQL注入三级代码审计:从攻击视角到防御实战

DVWA SQL注入三级代码审计:从攻击视角到防御实战

1. 项目概述:一次从“矛”到“盾”的实战演练最近在带新人做安全演练,发现一个挺有意思的现象:很多刚入门的朋友在DVWA靶场里做SQL注入练习,通关Low、Medium、High三个级别后,往往只记住了“怎么注入成功”&#xff0c…

2026/8/4 7:25:00 阅读更多 →
抖音无水印下载终极指南:5分钟掌握批量下载的完整教程

抖音无水印下载终极指南:5分钟掌握批量下载的完整教程

抖音无水印下载终极指南:5分钟掌握批量下载的完整教程 【免费下载链接】douyin-downloader A practical Douyin downloader for both single-item and profile batch downloads, with progress display, retries, SQLite deduplication, and browser fallback suppo…

2026/8/4 7:25:00 阅读更多 →
MemPrivacy框架:端云协同智能体的隐私保护记忆系统设计与实践

MemPrivacy框架:端云协同智能体的隐私保护记忆系统设计与实践

1. 项目概述:当智能体拥有记忆,隐私如何安放?最近和几个做端侧大模型和智能体应用的朋友聊天,大家不约而同地提到了一个痛点:个性化记忆。我们都希望自己的智能助手能记住“我习惯下午三点喝咖啡”、“上周和客户A开会…

2026/8/4 7:23:59 阅读更多 →

日新闻

AI Agent白手起家26: 使用标准事件驱动大模型实践

AI Agent白手起家26: 使用标准事件驱动大模型实践

纲要 练习目标:掌握大模型标准事件的调用回顾 LangChain 中的核心标准事件 invokestreambatchastream_eventswith_structured_output 环境准备实战代码:多种事件调用对比 同步调用与流式输出批量处理异步事件流监听结构化输出 运行说明与预期结果总结与扩…

2026/8/4 0:00:40 阅读更多 →
dealsea是什么?跨境卖家必知的美国deal站入门指南

dealsea是什么?跨境卖家必知的美国deal站入门指南

说实话,第一次听说美国这个老牌折扣网站的跨境卖家,十个有八个会问同一个问题:这个平台到底是干嘛的?我见过一个做家居出口的朋友,他在亚马逊上月销二十万美金,却从来没用过它。我给他看了首页——一屏一屏…

2026/8/4 0:01:40 阅读更多 →
清华大学重磅EST:植物自导电闪蒸焦耳热600°C/2600°C两步法!稀土超积累植物秒级转化为CeO₂-石墨烯电催化剂!

清华大学重磅EST:植物自导电闪蒸焦耳热600°C/2600°C两步法!稀土超积累植物秒级转化为CeO₂-石墨烯电催化剂!

通讯作者:邓兵、刘建国通讯单位:清华大学DOI:https://doi.org/10.1021/acs.est.6c00603研究背景稀土元素(REEs)是清洁能源技术与电子器件不可或缺的核心原料,然而传统提取方式依赖能耗高、排放大的采矿与强…

2026/8/4 0:01:40 阅读更多 →

周新闻

最大流算法详解:从水管网络到Ford-Fulkerson与Dinic实战

最大流算法详解:从水管网络到Ford-Fulkerson与Dinic实战

1. 从水管网络到最大流:一个核心问题的诞生想象一下,你是一个城市供水系统的总工程师。你的城市有多个水源(水库),需要通过一个复杂的地下管道网络,将水输送到各个居民区。每条管道都有其最大通水能力&…

2026/8/3 4:58:13 阅读更多 →
基于Springboot的企业门户网站(源码+LW+调试文档+讲解)

基于Springboot的企业门户网站(源码+LW+调试文档+讲解)

温馨提示:本人主页置顶文章(点我)开头有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:本人主页置顶文章(点我)开头有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:本人主页置顶文章(点我)开头有 CSDN 平台…

2026/8/3 1:53:31 阅读更多 →
MATLAB xcorr函数详解:从互相关原理到四大实战应用

MATLAB xcorr函数详解:从互相关原理到四大实战应用

1. 从一次信号“找茬”说起:为什么我们需要互相关几年前,我在处理一组声学传感器数据时遇到了一个棘手的问题。我有两个麦克风记录了一段相同的音频信号,理论上它们接收到的声音波形应该非常相似,只是由于麦克风位置不同&#xff…

2026/8/4 5:26:40 阅读更多 →

月新闻

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南 【免费下载链接】BaiduNetdiskPlugin-macOS For macOS.百度网盘 破解SVIP、下载速度限制~ 项目地址: https://gitcode.com/gh_mirrors/ba/BaiduNetdiskPlugin-macOS 还在为百度网盘macOS版的龟速下…

2026/8/3 13:07:03 阅读更多 →
终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换

终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换

终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换 【免费下载链接】ncmdump 项目地址: https://gitcode.com/gh_mirrors/ncmd/ncmdump 还在为网易云音乐下载的NCM格式文件无法在其他播放器播放而烦恼吗?ncmdump解密工具帮你轻松解决这个困…

2026/8/3 5:19:38 阅读更多 →
HarmonyOS 应用开发《掌上英语》第81篇: 智能体卡片:为英语学习 App 打造桌面级学习助手

HarmonyOS 应用开发《掌上英语》第81篇: 智能体卡片:为英语学习 App 打造桌面级学习助手

AgentCard 智能体卡片:为英语学习 App 打造桌面级学习助手适用平台:HarmonyOS 7.0 (API 26 Beta)一、引言 HarmonyOS 7.0(API 26 Beta)新增了 AgentCard 智能体卡片能力,这是继 HMAF(鸿蒙智能体框架&#x…

2026/8/3 8:27:36 阅读更多 →