news 2026/5/3 3:55:55

DeepSeek-R1-Distill-Qwen-1.5B实操手册:API封装为FastAPI服务供其他系统调用

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
DeepSeek-R1-Distill-Qwen-1.5B实操手册:API封装为FastAPI服务供其他系统调用

DeepSeek-R1-Distill-Qwen-1.5B实操手册:API封装为FastAPI服务供其他系统调用

1. 为什么要把Streamlit聊天应用改造成FastAPI服务?

你已经跑通了那个清爽好用的Streamlit本地对话界面——输入问题,气泡弹出思考链+答案,侧边栏一点就清空显存,整个过程丝滑又安心。但很快你会发现:Streamlit是给“人”用的,不是给“系统”用的。

比如,你想把DeepSeek-R1-Distill-Qwen-1.5B的能力嵌入到公司内部的知识库搜索页里,用户搜“报销流程”,后端自动调用模型生成结构化解读;或者集成进客服工单系统,当坐席遇到复杂技术问题时,一键触发模型推理并返回参考话术;再比如,想用Python脚本批量测试不同提示词对数学题求解效果的影响……这些场景,都绕不开一个核心需求:稳定、无状态、可编程调用的HTTP接口

而Streamlit本质是个前端驱动的交互式应用框架,它不暴露标准REST API,无法被curl、requests或Postman直接调用,也不支持并发请求管理、身份认证、请求限流等生产级能力。这时候,把它“抽离逻辑、封装接口、交由FastAPI托管”,就成了从“玩具”走向“工具”的关键一步。

本手册不讲大道理,只做一件事:手把手带你把已有的Streamlit项目中那套成熟可用的推理逻辑,完整剥离出来,封装成一个轻量、健壮、开箱即用的FastAPI服务。全程基于你已部署好的/root/ds_1.5b模型路径,零模型重下载、零参数重调优、零环境重配置——你只需要改代码、加路由、启服务。

1.1 你将获得什么(不是概念,是具体能力)

  • 一个独立运行的FastAPI服务,监听http://localhost:8000,提供标准/v1/chat/completions兼容接口
  • 完全复用原Streamlit项目的全部推理能力:原生聊天模板拼接、思维链格式化、temperature=0.6/top_p=0.95精准采样、max_new_tokens=2048长推理支持
  • 支持标准OpenAI-style JSON请求体(含messages,model,stream等字段),下游系统无需改造即可接入
  • 自动处理GPU/CPU设备识别、显存清理、无梯度推理,和原来一样省资源
  • 内置健康检查端点/health和模型信息端点/models,方便监控与集成
  • 提供完整可运行代码、启动命令、请求示例,复制粘贴就能跑

没有抽象架构图,没有理论推导,只有你能立刻执行、立刻验证、立刻集成的实操路径。

2. 核心改造思路:三步剥离,四层封装

我们不做“重写”,只做“迁移”。整个过程围绕一个原则:保留所有已验证的推理逻辑,只替换掉Streamlit的UI层和交互层,用FastAPI的标准组件重新组织

2.1 第一步:剥离模型加载与推理核心(inference.py

原Streamlit代码里,模型加载、分词器初始化、推理函数通常混在st.cache_resource装饰器下,和UI逻辑耦合。我们要做的,是把它们“提纯”出来,变成一个干净、无依赖、可独立测试的模块。

新建inference.py,内容如下:

# inference.py import torch from transformers import AutoTokenizer, AutoModelForCausalLM, pipeline from typing import List, Dict, Optional # 全局模型与分词器实例(单例,避免重复加载) _model = None _tokenizer = None def load_model_and_tokenizer(model_path: str = "/root/ds_1.5b") -> tuple: """加载模型与分词器,自动适配设备与精度""" global _model, _tokenizer if _model is not None and _tokenizer is not None: return _model, _tokenizer print(f" Loading model from {model_path}...") _tokenizer = AutoTokenizer.from_pretrained(model_path, trust_remote_code=True) _model = AutoModelForCausalLM.from_pretrained( model_path, device_map="auto", torch_dtype="auto", trust_remote_code=True ) print(" Model loaded successfully.") return _model, _tokenizer def format_thinking_output(text: str) -> str: """将模型原始输出中的<|think|>...</think>标签转为结构化格式""" # 原Streamlit中使用的相同逻辑 import re pattern = r"<\|think\|>(.*?)<\|answer\|>" match = re.search(pattern, text, re.DOTALL) if match: thinking = match.group(1).strip() answer = text.split("<|answer|>")[-1].strip() return f"「思考过程」\n{thinking}\n\n「最终回答」\n{answer}" return text.strip() def generate_response( messages: List[Dict[str, str]], max_new_tokens: int = 2048, temperature: float = 0.6, top_p: float = 0.95, do_sample: bool = True ) -> str: """执行一次完整推理:模板拼接 → 模型生成 → 格式化输出""" model, tokenizer = load_model_and_tokenizer() # 1. 使用官方聊天模板拼接上下文 input_text = tokenizer.apply_chat_template( messages, tokenize=False, add_generation_prompt=True ) # 2. 编码输入 inputs = tokenizer(input_text, return_tensors="pt").to(model.device) # 3. 无梯度推理(节省显存) with torch.no_grad(): outputs = model.generate( **inputs, max_new_tokens=max_new_tokens, temperature=temperature, top_p=top_p, do_sample=do_sample, pad_token_id=tokenizer.eos_token_id, eos_token_id=tokenizer.eos_token_id ) # 4. 解码并去除输入部分 full_output = tokenizer.decode(outputs[0], skip_special_tokens=False) response = full_output[len(input_text):].strip() # 5. 格式化思维链输出 return format_thinking_output(response)

这个文件就是你的新“引擎”。它完全复用了原项目的所有关键逻辑:apply_chat_templatedevice_map="auto"torch.no_grad()format_thinking_output——只是去掉了所有st.前缀和UI相关代码。你可以单独运行它测试:

# test_inference.py from inference import generate_response msgs = [ {"role": "system", "content": "你是一个严谨的数学助手"}, {"role": "user", "content": "解方程:2x + 3 = 7"} ] print(generate_response(msgs))

2.2 第二步:定义FastAPI路由与请求模型(main.py

现在,把引擎装进FastAPI的“车身”。新建main.py

# main.py from fastapi import FastAPI, HTTPException, status from pydantic import BaseModel from typing import List, Dict, Optional, Any from inference import generate_response import uvicorn import json app = FastAPI( title="DeepSeek-R1-Distill-Qwen-1.5B API", description="基于DeepSeek-R1-Distill-Qwen-1.5B蒸馏模型的轻量级本地推理服务", version="1.0.0" ) class ChatMessage(BaseModel): role: str content: str class ChatCompletionRequest(BaseModel): model: str = "deepseek-r1-distill-qwen-1.5b" messages: List[ChatMessage] max_tokens: Optional[int] = 2048 temperature: Optional[float] = 0.6 top_p: Optional[float] = 0.95 stream: Optional[bool] = False # 本版本暂不支持流式,保持兼容 class ChatCompletionResponse(BaseModel): id: str object: str = "chat.completion" created: int model: str choices: List[Dict[str, Any]] usage: Dict[str, int] @app.get("/health") def health_check(): """健康检查端点""" return {"status": "healthy", "model_loaded": True} @app.get("/models") def list_models(): """列出可用模型信息""" return { "data": [{ "id": "deepseek-r1-distill-qwen-1.5b", "object": "model", "owned_by": "local", "permission": [] }] } @app.post("/v1/chat/completions", response_model=ChatCompletionResponse) def chat_completions(request: ChatCompletionRequest): """标准OpenAI-style聊天补全接口""" try: # 调用核心推理函数 response_text = generate_response( messages=[m.dict() for m in request.messages], max_new_tokens=request.max_tokens, temperature=request.temperature, top_p=request.top_p ) # 构造标准响应(简化版,满足基本集成需求) import time import uuid return { "id": f"chatcmpl-{uuid.uuid4().hex}", "object": "chat.completion", "created": int(time.time()), "model": request.model, "choices": [{ "index": 0, "message": { "role": "assistant", "content": response_text }, "finish_reason": "stop" }], "usage": { "prompt_tokens": 0, # 实际可扩展为真实统计 "completion_tokens": 0, "total_tokens": 0 } } except Exception as e: raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=f"推理失败: {str(e)}" ) if __name__ == "__main__": uvicorn.run(app, host="0.0.0.0", port=8000, workers=1)

注意几个关键点:

  • 请求体ChatCompletionRequest严格遵循OpenAI API规范,messages字段支持system/user/assistant角色;
  • generate_response调用方式与你在Streamlit中完全一致,参数一一对应;
  • /v1/chat/completions返回结构也尽量贴近OpenAI,下游系统(如LangChain、LlamaIndex)可直接复用已有客户端;
  • 错误处理明确,异常直接转为标准HTTP错误码。

2.3 第三步:启动服务并验证(终端操作)

确保你已安装必要依赖:

pip install fastapi uvicorn transformers torch sentencepiece

然后,在项目根目录(与main.py同级)执行:

uvicorn main:app --host 0.0.0.0 --port 8000 --reload

注意:--reload仅用于开发调试,生产环境请去掉,并使用--workers 1(因模型加载耗显存,多进程需谨慎)。

服务启动后,你会看到类似日志:

INFO: Uvicorn running on http://0.0.0.0:8000 (Press CTRL+C to quit) INFO: Started reloader process [12345] INFO: Started server process [12346] INFO: Waiting for application startup. Loading model from /root/ds_1.5b... Model loaded successfully. INFO: Application startup complete.

此时,服务已就绪。打开新终端,用curl测试:

curl -X POST "http://localhost:8000/v1/chat/completions" \ -H "Content-Type: application/json" \ -d '{ "model": "deepseek-r1-distill-qwen-1.5b", "messages": [ {"role": "system", "content": "你是一个代码助手"}, {"role": "user", "content": "用Python写一个快速排序函数"} ] }' | python -m json.tool

你会看到结构清晰的JSON响应,其中choices[0].message.content就是带「思考过程」+「最终回答」的完整结果——和你在Streamlit界面上看到的一模一样。

3. 进阶实用技巧:让服务更稳、更快、更好集成

光能跑通还不够。在真实系统中,你需要应对并发、监控、日志、安全等现实问题。以下是几条经过验证的轻量级增强建议,全部基于现有代码微调,无需引入复杂中间件。

3.1 显存自动回收:避免长时间运行后OOM

原Streamlit的「🧹 清空」按钮在FastAPI里没了,但我们可以用atexit钩子,在服务退出时主动释放显存:

main.py顶部添加:

import atexit import torch def cleanup_gpu(): if torch.cuda.is_available(): torch.cuda.empty_cache() print(" GPU cache cleared on exit") atexit.register(cleanup_gpu)

更进一步,如果你希望在每次请求后都轻量清理(适合低频但长时服务),可在chat_completions函数末尾加一行:

torch.cuda.empty_cache() # 放在return之前

3.2 请求日志记录:排查问题有据可依

chat_completions函数开头加入简单日志:

import logging logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__) @app.post("/v1/chat/completions", response_model=ChatCompletionResponse) def chat_completions(request: ChatCompletionRequest): logger.info(f"Received request for model {request.model}, user message: {request.messages[-1]['content'][:50]}...") # ...后续逻辑

日志会输出到控制台,清晰显示谁在什么时候问了什么,极大缩短排障时间。

3.3 简单请求限流:防止单个客户端拖垮服务

对于内部系统调用,一般不需要复杂限流。用slowapi库加一行即可:

pip install slowapi

main.py中:

from slowapi import Limiter from slowapi.util import get_remote_address limiter = Limiter(key_func=get_remote_address) app.state.limiter = limiter @app.post("/v1/chat/completions", response_model=ChatCompletionResponse) @limiter.limit("5/minute") # 每分钟最多5次 def chat_completions(request: ChatCompletionRequest): # ...原有逻辑

超过限制会自动返回429 Too Many Requests,无需额外处理。

3.4 Docker一键封装(可选,但强烈推荐)

把服务打包成Docker镜像,彻底解决环境一致性问题。新建Dockerfile

FROM nvidia/cuda:12.1.1-base-ubuntu22.04 RUN apt-get update && apt-get install -y python3-pip python3-dev && rm -rf /var/lib/apt/lists/* COPY requirements.txt . RUN pip3 install --no-cache-dir -r requirements.txt COPY . /app WORKDIR /app CMD ["uvicorn", "main:app", "--host", "0.0.0.0:8000", "--port", "8000"]

requirements.txt内容:

fastapi==0.110.0 uvicorn==0.29.0 transformers==4.40.0 torch==2.2.0+cu121 sentencepiece==0.2.0

构建并运行:

docker build -t ds15b-api . docker run -p 8000:8000 --gpus all -v /root/ds_1.5b:/root/ds_1.5b ds15b-api

模型路径通过卷映射挂载,安全又灵活。

4. 与其他系统的集成示例:三行代码接入

服务跑起来后,怎么用?下面给出最常用的三种集成方式,每种都只需3-5行核心代码。

4.1 Python requests调用(最常用)

import requests url = "http://localhost:8000/v1/chat/completions" headers = {"Content-Type": "application/json"} data = { "model": "deepseek-r1-distill-qwen-1.5b", "messages": [ {"role": "user", "content": "解释下Transformer的自注意力机制"} ] } response = requests.post(url, headers=headers, json=data) result = response.json() print(result["choices"][0]["message"]["content"])

4.2 LangChain快速接入(已有LangChain项目)

from langchain.llms import OpenAI from langchain.chat_models import ChatOpenAI # 复用OpenAI客户端,只需改base_url llm = ChatOpenAI( openai_api_base="http://localhost:8000/v1", openai_api_key="not-needed", # 本服务无需key model_name="deepseek-r1-distill-qwen-1.5b" ) result = llm.invoke("用一句话总结量子计算的核心思想") print(result.content)

4.3 前端JavaScript调用(嵌入网页)

async function askDeepSeek(question) { const response = await fetch('http://localhost:8000/v1/chat/completions', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ model: 'deepseek-r1-distill-qwen-1.5b', messages: [{ role: 'user', content: question }] }) }); const data = await response.json(); return data.choices[0].message.content; } // 调用示例 askDeepSeek("如何给初学者解释递归?").then(console.log);

所有方式,都复用你已验证的同一套推理逻辑。你投入在Streamlit上的所有调优工作,此刻全部生效。

5. 总结:从界面到接口,能力真正流动起来

你刚刚完成的,不只是一个技术动作,而是一次能力升级:

  • 从“演示”到“可用”:Streamlit界面是展示,FastAPI服务是交付;
  • 从“单点”到“网络”:一个模型,不再只服务一个浏览器标签页,而是成为整个系统可调用的智能节点;
  • 从“实验”到“生产”:健康检查、日志、限流、Docker封装——每一步都在拉近与真实业务的距离。

整个过程没有魔改模型,没有重写推理,没有新增依赖。你只是把已有的、可靠的、经过验证的代码,用更合适的方式组织起来。这正是工程实践的精髓:不追求炫技,只关注价值落地

现在,你的DeepSeek-R1-Distill-Qwen-1.5B,已经准备好为知识库、客服系统、自动化脚本、内部工具……提供稳定、私有、高效的文本推理能力。下一步,就是把它接入你真正需要的地方。


获取更多AI镜像

想探索更多AI镜像和应用场景?访问 CSDN星图镜像广场,提供丰富的预置镜像,覆盖大模型推理、图像生成、视频生成、模型微调等多个领域,支持一键部署。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/1 11:15:31

Qwen2.5-1.5B惊艳效果:处理「请用苏格拉底式提问法探讨XX问题」指令

Qwen2.5-1.5B惊艳效果&#xff1a;处理「请用苏格拉底式提问法探讨XX问题」指令 1. 为什么这个小模型能“读懂”苏格拉底&#xff1f; 你有没有试过对一个AI说&#xff1a;“请用苏格拉底式提问法&#xff0c;帮我思考‘技术是否必然带来进步’这个问题&#xff1f;” 不是简…

作者头像 李华
网站建设 2026/5/1 6:11:57

Qwen3-1.7B开箱即用,LangChain调用超简单教程

Qwen3-1.7B开箱即用&#xff0c;LangChain调用超简单教程 1. 为什么你不需要再为“部署难”发愁 你是不是也经历过这些时刻&#xff1a; 看中一个新模型&#xff0c;结果卡在环境配置上两小时&#xff0c;连第一行代码都没跑通&#xff1b;想试试LangChain集成&#xff0c;却…

作者头像 李华
网站建设 2026/4/18 5:20:11

lychee-rerank-mm多语言排序案例:同一描述下不同语言图库匹配效果

lychee-rerank-mm多语言排序案例&#xff1a;同一描述下不同语言图库匹配效果 1. 为什么“同一句话”在中英文里&#xff0c;图库匹配结果会不一样&#xff1f; 你有没有试过这样操作&#xff1a;用中文写一句“穿汉服的女孩站在樱花树下”&#xff0c;上传一批图片&#xff…

作者头像 李华
网站建设 2026/5/2 7:53:27

无需复杂配置:yz-bijini-cosplay本地部署与使用全攻略

无需复杂配置&#xff1a;yz-bijini-cosplay本地部署与使用全攻略 1. 为什么这款Cosplay生成工具值得你立刻上手&#xff1f; 你是否试过用文生图模型生成Cosplay角色&#xff0c;却总卡在几个痛点上&#xff1a; 模型加载慢&#xff0c;换一个风格就要重载整个底座&#xf…

作者头像 李华
网站建设 2026/4/23 18:15:32

掌握哔哩下载姬DownKyi:从入门到精通的10个实用技巧

掌握哔哩下载姬DownKyi&#xff1a;从入门到精通的10个实用技巧 【免费下载链接】downkyi 哔哩下载姬downkyi&#xff0c;哔哩哔哩网站视频下载工具&#xff0c;支持批量下载&#xff0c;支持8K、HDR、杜比视界&#xff0c;提供工具箱&#xff08;音视频提取、去水印等&#xf…

作者头像 李华
网站建设 2026/5/1 6:11:35

开源免费还能商用?这款AI抠图工具太良心

开源免费还能商用&#xff1f;这款AI抠图工具太良心 1. 真的不用花钱&#xff0c;还允许商用&#xff1f; 你有没有遇到过这样的场景&#xff1a; 电商运营要连夜处理200张商品图&#xff0c;每张都要换白底设计师赶着交稿&#xff0c;客户临时要求把人像从复杂背景里干净抠…

作者头像 李华