使用 Python + LangChain + Vue3 构建 LLM 聊天应用
从零搭建一个支持流式对话的全栈 LLM 应用:FastAPI 后端 + Vue3 前端 + DeepSeek API。
目录
- [使用 Python + LangChain + Vue3 构建 LLM 聊天应用](#使用 Python + LangChain + Vue3 构建 LLM 聊天应用)
- 目录
- 项目结构
- 技术栈
- 技术栈选择
- [AI Agent 主流框架](#AI Agent 主流框架)
- 环境配置
- [1. 安装 Python](#1. 安装 Python)
- [2. 配置 API 密钥](#2. 配置 API 密钥)
- [3. 编辑器](#3. 编辑器)
- 学习路径
- [1. Python 基础](#1. Python 基础)
- [2. FastAPI 入门](#2. FastAPI 入门)
- [3. LLM 与 OpenAI 接口](#3. LLM 与 OpenAI 接口)
- [4. LangChain 框架](#4. LangChain 框架)
- [5. 后端实现](#5. 后端实现)
- [SSE 流式协议](#SSE 流式协议)
- [API 接口](#API 接口)
- [6. 前端实现](#6. 前端实现)
- 运行与验证
- 请求链路
- 拓展阅读
项目结构
simple-llm-app/
├── test/ # 学习 & 实验代码
│ ├── test.py # 环境变量 + OpenAI 接口测试
│ ├── server.py # FastAPI 路由 / Pydantic / CORS 示例
│ ├── llm-app.py # LangChain 链式调用入门
│ ├── llm-app-message.py # LangChain 消息模板
│ └── prompt-template.py # PromptTemplate 示例
├── backend/ # 生产后端
│ ├── server.py # API 层:路由 + SSE + CORS + 启动入口
│ ├── llm_app.py # 业务层:LLM 封装与流式生成
│ ├── models.py # 数据层:Pydantic 模型
│ ├── test.py # SSE 流式响应实验脚本
│ └── requirements.txt # Python 依赖
└── frontend/ # 聊天窗口前端
└── src/
├── App.vue # 聊天页面
├── style.css # 全局样式
├── types/chat.ts # 类型定义
├── api/chat.ts # API 请求封装
└── composables/useChat.ts # 聊天逻辑 Hook
技术栈
| 层级 | 技术 | 说明 |
|---|---|---|
| 后端 | Python 3 · FastAPI · LangChain · Uvicorn | Web 框架 + LLM 编排 + ASGI 服务器 |
| 前端 | Vue 3 · TypeScript · Vite | 组件化 UI + 类型安全 + 构建工具 |
| 模型 | DeepSeek API | 兼容 OpenAI 接口格式 |
说明:后端也可替换为 Node.js/Java,但 AI 生态在 Python 侧仍具明显优势
技术栈选择
AI Agent 主流框架
| 框架 | 语言 | 特点 | 适合场景 |
|---|---|---|---|
| LangChain | Python/JS | 生态最全,组件多 | 快速原型、学习入门 |
| LlamaIndex | Python | 专注 RAG 和知识检索 | 知识库类应用 |
| AutoGen | Python | 微软出品,多 Agent 对话 | 多 Agent 协作 |
| CrewAI | Python | 角色化 Agent 团队 | 任务分工类场景 |
| LangGraph | Python | 状态机式 Agent 流程 | 复杂工作流 |
| Dify | Python/低代码 | 国产,可视化编排 | 快速交付、企业内部 |
| 阿里百炼 / 腾讯元器等 | 托管平台 | 国内合规,部署简单 | 国内商业落地 |
环境配置
1. 安装 Python
- 从 Python 官网 下载安装包
- 安装时勾选 Add Python to PATH
- 验证安装:
bash
python --version
pip --version
2. 配置 API 密钥
- 在 DeepSeek 控制台 申请 API Key
- 在项目根目录创建
.env文件:
bash
DEEPSEEK_API_KEY=sk-xxxxxx
- 安装依赖并验证读取:
bash
pip install python-dotenv
python test/test.py
完整示例见 test/test.py。
3. 编辑器
推荐 VS Code / Cursor,安装 Python 语言扩展。
学习路径
按以下顺序阅读代码,由浅入深:
环境变量 → FastAPI → OpenAI 接口 → LangChain → 后端分层 → 前端聊天页
1. Python 基础
Python 适合 AI 开发的核心原因:
| 特点 | 说明 |
|---|---|
| 简洁语法 | 接近自然语言,快速验证想法 |
| 丰富生态 | TensorFlow、PyTorch、LangChain、Hugging Face 等 |
| 动态类型 | 交互式开发,迭代效率高 |
| 胶水语言 | 底层 C/C++/CUDA 计算 + Python 高层编排 |
语法速览
python
name = "张三" # 无需声明关键字
AGE = 25 # 全大写表示常量(约定)
is_student = True
# 单行注释用 #,多行用 ''' 或 """
GIL 与并发(与 Web 服务相关)
- CPU 密集型:GIL 限制多线程并行 → 用多进程或 GPU
- I/O 密集型 (网络请求、LLM 调用):多线程 /
asyncio仍然有效 - FastAPI + Uvicorn 的异步模型适合 LLM 推理服务
2. FastAPI 入门
核心概念
| 组件 | 职责 |
|---|---|
| FastAPI | 路由、验证、序列化、依赖注入 |
| Uvicorn | HTTP 解析、并发处理、连接管理(ASGI 服务器) |
两者配合才能发挥 FastAPI 的异步性能。
快速示例
bash
pip install fastapi uvicorn
python test/server.py
访问 http://127.0.0.1:9528 查看响应,访问 /docs 查看自动生成的 API 文档。
完整代码见 test/server.py,包含:
- 基础路由与启动
- 路径参数 & 查询参数(
/items/{id}?q=xxx) - Pydantic 请求体 / 响应模型
- HTTP 中间件 & CORS 跨域配置
Pydantic 要点
- 基于类型注解的运行时数据验证
- FastAPI 自动生成 OpenAPI 文档(
/docs) - 典型场景:API 请求校验、配置管理、JSON 序列化
CORS
跨域指协议、域名、端口任一不同。后端需配置 CORSMiddleware 允许前端跨域访问,详见 test/server.py。
3. LLM 与 OpenAI 接口
DeepSeek 兼容 OpenAI API 格式,使用 openai 库即可对接。
bash
pip install openai
python test/test.py
核心代码
python
from openai import OpenAI
client = OpenAI(
api_key=os.getenv("DEEPSEEK_API_KEY"),
base_url="https://api.deepseek.com"
)
response = client.chat.completions.create(
model="deepseek-chat",
temperature=0.5,
messages=[{"role": "user", "content": "你是谁?"}]
)
print(response.choices[0].message.content)
关键概念
| 概念 | 说明 | 详见 |
|---|---|---|
choices 字段 |
模型返回的候选回复列表 | test/test.py 注释 |
流式响应 stream=True |
逐 token 输出,打字机效果 | test/test.py |
temperature |
控制随机性:低=确定性,高=创造性 | test/test.py 注释 |
| 消息角色 | system / user / assistant / tool |
见下表 |
消息角色
| 角色 | 用途 | 示例场景 |
|---|---|---|
system |
设定助手行为规则 | "你是专业翻译,只翻译中英文" |
user |
用户输入(必填) | "什么是机器学习?" |
assistant |
历史回复,保持多轮连贯 | 上一轮 AI 的回答 |
tool / function |
工具调用结果 | Function Calling 场景 |
4. LangChain 框架
Java 开发者可选 LangChain4j · 实战指南
LangChain 在 OpenAI 原生接口之上提供链式编排,简化 Prompt → LLM → Parser 流程。
安装 & 运行
bash
pip install langchain_openai langchain_core python-dotenv
python test/llm-app.py
LCEL 链式语法
python
chain = prompt | llm | parser # 输入 → 模板填充 → AI 处理 → 解析输出
result = chain.invoke({"question": "你是谁?"})
| 是 LangChain 重载的操作符(语法糖),等价于逐步 invoke。
相关文件
| 文件 | 内容 |
|---|---|
test/llm-app.py |
最简链式调用 |
test/llm-app-message.py |
消息模板系统 |
test/prompt-template.py |
PromptTemplate 用法 |
消息模板对照
| LangChain 类 | 角色 | 对应 OpenAI |
|---|---|---|
SystemMessagePromptTemplate |
system | system |
HumanMessagePromptTemplate |
human | user |
AssistantMessagePromptTemplate |
assistant | assistant |
AIMessagePromptTemplate |
ai | assistant |
ToolMessagePromptTemplate |
tool | tool |
5. 后端实现
采用 单一职责 分层架构:
#mermaid-svg-6dZh1Pb1ZqRemuzf{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-6dZh1Pb1ZqRemuzf .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-6dZh1Pb1ZqRemuzf .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-6dZh1Pb1ZqRemuzf .error-icon{fill:#552222;}#mermaid-svg-6dZh1Pb1ZqRemuzf .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-6dZh1Pb1ZqRemuzf .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-6dZh1Pb1ZqRemuzf .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-6dZh1Pb1ZqRemuzf .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-6dZh1Pb1ZqRemuzf .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-6dZh1Pb1ZqRemuzf .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-6dZh1Pb1ZqRemuzf .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-6dZh1Pb1ZqRemuzf .marker{fill:#333333;stroke:#333333;}#mermaid-svg-6dZh1Pb1ZqRemuzf .marker.cross{stroke:#333333;}#mermaid-svg-6dZh1Pb1ZqRemuzf svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-6dZh1Pb1ZqRemuzf p{margin:0;}#mermaid-svg-6dZh1Pb1ZqRemuzf .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-6dZh1Pb1ZqRemuzf .cluster-label text{fill:#333;}#mermaid-svg-6dZh1Pb1ZqRemuzf .cluster-label span{color:#333;}#mermaid-svg-6dZh1Pb1ZqRemuzf .cluster-label span p{background-color:transparent;}#mermaid-svg-6dZh1Pb1ZqRemuzf .label text,#mermaid-svg-6dZh1Pb1ZqRemuzf span{fill:#333;color:#333;}#mermaid-svg-6dZh1Pb1ZqRemuzf .node rect,#mermaid-svg-6dZh1Pb1ZqRemuzf .node circle,#mermaid-svg-6dZh1Pb1ZqRemuzf .node ellipse,#mermaid-svg-6dZh1Pb1ZqRemuzf .node polygon,#mermaid-svg-6dZh1Pb1ZqRemuzf .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-6dZh1Pb1ZqRemuzf .rough-node .label text,#mermaid-svg-6dZh1Pb1ZqRemuzf .node .label text,#mermaid-svg-6dZh1Pb1ZqRemuzf .image-shape .label,#mermaid-svg-6dZh1Pb1ZqRemuzf .icon-shape .label{text-anchor:middle;}#mermaid-svg-6dZh1Pb1ZqRemuzf .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-6dZh1Pb1ZqRemuzf .rough-node .label,#mermaid-svg-6dZh1Pb1ZqRemuzf .node .label,#mermaid-svg-6dZh1Pb1ZqRemuzf .image-shape .label,#mermaid-svg-6dZh1Pb1ZqRemuzf .icon-shape .label{text-align:center;}#mermaid-svg-6dZh1Pb1ZqRemuzf .node.clickable{cursor:pointer;}#mermaid-svg-6dZh1Pb1ZqRemuzf .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-6dZh1Pb1ZqRemuzf .arrowheadPath{fill:#333333;}#mermaid-svg-6dZh1Pb1ZqRemuzf .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-6dZh1Pb1ZqRemuzf .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-6dZh1Pb1ZqRemuzf .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-6dZh1Pb1ZqRemuzf .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-6dZh1Pb1ZqRemuzf .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-6dZh1Pb1ZqRemuzf .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-6dZh1Pb1ZqRemuzf .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-6dZh1Pb1ZqRemuzf .cluster text{fill:#333;}#mermaid-svg-6dZh1Pb1ZqRemuzf .cluster span{color:#333;}#mermaid-svg-6dZh1Pb1ZqRemuzf div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-6dZh1Pb1ZqRemuzf .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-6dZh1Pb1ZqRemuzf rect.text{fill:none;stroke-width:0;}#mermaid-svg-6dZh1Pb1ZqRemuzf .icon-shape,#mermaid-svg-6dZh1Pb1ZqRemuzf .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-6dZh1Pb1ZqRemuzf .icon-shape p,#mermaid-svg-6dZh1Pb1ZqRemuzf .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-6dZh1Pb1ZqRemuzf .icon-shape .label rect,#mermaid-svg-6dZh1Pb1ZqRemuzf .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-6dZh1Pb1ZqRemuzf .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-6dZh1Pb1ZqRemuzf .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-6dZh1Pb1ZqRemuzf :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} HTTP POST
前端 Vue3
API 层
backend/server.py
业务层
backend/llm_app.py
DeepSeek API
数据层
backend/models.py
| 层级 | 文件 | 职责 |
|---|---|---|
| API 层 | backend/server.py |
路由、SSE 流式响应、CORS、Uvicorn 启动 |
| 业务层 | backend/llm_app.py |
LLM 交互、对话历史、流式生成 |
| 数据层 | backend/models.py |
ChatRequest / ChatMessage 等 Pydantic 模型 |
| 实验脚本 | backend/test.py |
独立验证 SSE 流式逻辑(开发阶段原型) |
| 依赖 | backend/requirements.txt |
类似前端的 package.json |
SSE 流式协议
大模型逐字生成场景首选 Server-Sent Events (SSE):
| 特性 | SSE | WebSocket | 长轮询 |
|---|---|---|---|
| 协议 | HTTP | 自定义 TCP | HTTP |
| 方向 | 单向(服务器→客户端) | 双向 | 单向 |
| 复杂度 | 低 | 高 | 高 |
| 适用 | LLM 流式输出 | 实时游戏、协作 | 旧系统兼容 |
SSE 事件格式:data: {json}\n\n
backend/server.py 中 chat_stream 接口流程(原型见 backend/test.py):
- 发送
start事件 - 逐 token 发送
token事件 - 发送
end事件(含完整回复)
API 接口
| 接口 | 方法 | 说明 |
|---|---|---|
/api/chat/stream |
POST | SSE 流式对话 |
/api/health |
GET | 健康检查 |
6. 前端实现
创建项目
bash
npm create vite@latest frontend -- --template vue-ts
cd frontend && npm install
功能清单
- 消息列表(用户 / AI 气泡)
- 流式打字机效果
- Enter 发送 · Shift+Enter 换行
- 清空对话 · 自动滚动 · 在线状态
文件说明
| 文件 | 职责 |
|---|---|
frontend/src/types/chat.ts |
Message · ChatRequest · SSEEvent 类型 |
frontend/src/api/chat.ts |
fetch + ReadableStream 处理 SSE(POST 不支持原生 EventSource) |
frontend/src/composables/useChat.ts |
消息状态、发送、流式更新、取消请求 |
frontend/src/App.vue |
聊天 UI 组件 |
frontend/src/style.css |
全局样式 |
SSE 前端处理要点
typescript
// EventSource 不支持 POST,故用 fetch + ReadableStream
const reader = response.body?.getReader()
// 按 "\n\n" 分割,解析 "data: {...}" 行
// type: start | token | end | error
完整实现见 frontend/src/api/chat.ts。
运行与验证
后端
bash
cd backend
pip install -r requirements.txt
# 确保 backend/.env 中已配置 DEEPSEEK_API_KEY
python server.py
# → http://0.0.0.0:8000
前端
bash
cd frontend
npm install
npm run dev
浏览器打开 Vite 提示的本地地址,输入问题并发送即可。
请求链路
DeepSeek API LangChain FastAPI Vue3 前端 用户浏览器 DeepSeek API LangChain FastAPI Vue3 前端 用户浏览器 #mermaid-svg-qzlsXzVRZrnJZQQg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-qzlsXzVRZrnJZQQg .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-qzlsXzVRZrnJZQQg .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-qzlsXzVRZrnJZQQg .error-icon{fill:#552222;}#mermaid-svg-qzlsXzVRZrnJZQQg .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-qzlsXzVRZrnJZQQg .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-qzlsXzVRZrnJZQQg .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-qzlsXzVRZrnJZQQg .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-qzlsXzVRZrnJZQQg .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-qzlsXzVRZrnJZQQg .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-qzlsXzVRZrnJZQQg .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-qzlsXzVRZrnJZQQg .marker{fill:#333333;stroke:#333333;}#mermaid-svg-qzlsXzVRZrnJZQQg .marker.cross{stroke:#333333;}#mermaid-svg-qzlsXzVRZrnJZQQg svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-qzlsXzVRZrnJZQQg p{margin:0;}#mermaid-svg-qzlsXzVRZrnJZQQg .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-qzlsXzVRZrnJZQQg text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-qzlsXzVRZrnJZQQg .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-qzlsXzVRZrnJZQQg .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-qzlsXzVRZrnJZQQg .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-qzlsXzVRZrnJZQQg .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-qzlsXzVRZrnJZQQg #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-qzlsXzVRZrnJZQQg .sequenceNumber{fill:white;}#mermaid-svg-qzlsXzVRZrnJZQQg #sequencenumber{fill:#333;}#mermaid-svg-qzlsXzVRZrnJZQQg #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-qzlsXzVRZrnJZQQg .messageText{fill:#333;stroke:none;}#mermaid-svg-qzlsXzVRZrnJZQQg .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-qzlsXzVRZrnJZQQg .labelText,#mermaid-svg-qzlsXzVRZrnJZQQg .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-qzlsXzVRZrnJZQQg .loopText,#mermaid-svg-qzlsXzVRZrnJZQQg .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-qzlsXzVRZrnJZQQg .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-qzlsXzVRZrnJZQQg .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-qzlsXzVRZrnJZQQg .noteText,#mermaid-svg-qzlsXzVRZrnJZQQg .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-qzlsXzVRZrnJZQQg .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-qzlsXzVRZrnJZQQg .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-qzlsXzVRZrnJZQQg .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-qzlsXzVRZrnJZQQg .actorPopupMenu{position:absolute;}#mermaid-svg-qzlsXzVRZrnJZQQg .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-qzlsXzVRZrnJZQQg .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-qzlsXzVRZrnJZQQg .actor-man circle,#mermaid-svg-qzlsXzVRZrnJZQQg line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-qzlsXzVRZrnJZQQg :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} loop 逐 token 输入问题,点击发送 POST /api/chat/stream stream_chat(message, history) chat.completions (stream=True) chunk token SSE data: {type: token} 打字机效果更新 SSE data: {type: end} 显示完整回复
一句话:前端 → FastAPI → LangChain → DeepSeek API → 流式返回
拓展阅读
| 主题 | 链接 |
|---|---|
| CGI / WSGI / ASGI 对比 | 掘金文章 |
| FastAPI 官方文档 | fastapi.tiangolo.com/zh |
| FastAPI 源码 | github.com/fastapi/fastapi |
| Uvicorn 文档 | uvicorn.org |
| JS → Python 对照学习 | langshift.dev |