2026/3/7 6:16:38
网站建设
项目流程
PHP做网站案例教程,烟台网站建设设计开发,辽宁建设工程质量监督站网站,医学ppt模板免费下载 素材一、引言大模型的里里外外我们都进行了很多细节的讲解#xff0c;但大模型的部署引用还没有涉及太多#xff0c;今天我们重点讲一下模型的接口发布#xff0c;以及利用Postman工具的鉴权调试#xff0c;Postman 是一款轻量、易用的 API 调试与测试工具#xff0c;无需编写…一、引言大模型的里里外外我们都进行了很多细节的讲解但大模型的部署引用还没有涉及太多今天我们重点讲一下模型的接口发布以及利用Postman工具的鉴权调试Postman 是一款轻量、易用的 API 调试与测试工具无需编写复杂代码即可快速验证接口的可用性是本地大模型 API 开发、调试阶段的首选工具。相比于浏览器的自动生成文档页面Postman 更贴近实际业务中客户端调用 API的真实场景如后端服务、移动端调用能直观验证鉴权逻辑、参数传递、返回结果是否符合预期。今天我们将针对本地大模型 API 的两种鉴权方式API Key和极简 JWT详细讲解 Postman 的调用步骤覆盖从请求配置到完整调用的全流程让我们能快速上手并实际应用。二、基础知识1. 核心概念大模型本地 API 服务把本地电脑或服务器上的大模型变成一个 可随时呼叫的工具人。不用连云端本地就能通过指令调用它干活数据全程不泄露。FastAPI 封装给 工具人 装一个 标准化服务窗口。让不同程序比如 APP、网页都能按统一规则跟大模型沟通而且这个窗口响应快、还会自动生成使用说明。接口鉴权给 服务窗口 装一把 安全锁。只允许有钥匙的人调用防止陌生人随便用、滥用大模型资源。2. 基础了解2.1 本地 API 服务的优势解决云端痛点云端大模型有隐私泄露风险、要付调用费、没网用不了本地 API 服务完美规避这些问题。适配多场景医疗、金融等敏感行业或无网络环境都需要本地处理数据不依赖外部服务。降低使用门槛封装后不懂大模型底层的开发者也能通过简单指令调用大模型能力。2.2 FastAPI 是什么轻量高效的 Python 框架专门用来搭建 API 服务比传统框架快很多支持并发请求。自带接口文档自动生成接口文档访问 /docs 就能看不用手动写说明语法简单跟写普通 Python 函数差不多。核心作用把大模型的 推理功能比如文本生成、问答包装成 可网络调用的接口。2.3 接口鉴权的核心目的身份验证确认调用者是 自己人不是恶意攻击者。权限控制防止大模型被滥用比如频繁调用耗光资源。常见鉴权方式API Key最常用的 钥匙一串唯一字符串调用时带上即可。基本认证用 用户名 密码 做凭证简单但安全性一般需配合 HTTPS 使用。JWT 令牌更灵活的 临时钥匙有过期时间适合多场景权限管理。三、核心原理1. 大模型本地运行原理前提本地设备电脑/服务器已安装大模型如 Llama 2、Qwen 等开源模型并能通过 Python 脚本运行。核心模型加载到本地内存通过代码调用其推理接口生成结果比如输入 写一段文案模型返回文案内容。流程流程说明1. 服务启动FastAPI应用开始运行2. 状态检查判断模型是否已经加载到内存中3. 模型加载已加载直接复用现有模型实例避免重复加载未加载按需加载模型包含配置加载、分词器初始化、权重加载等步骤4. 单例模式确保整个应用中只存在一个模型实例减少内存占用2. FastAPI 封装原理加载大模型启动服务时把大模型加载到内存避免每次调用都重新加载提速。定义接口函数写一个 Python 函数接收用户请求比如提示词调用大模型生成结果再返回给用户。启动服务用 UvicornASGI 服务器运行应用大模型就变成了可网络访问的 API 服务。3. 接口鉴权原理3.1 调用 API Key 鉴权的接口本质验证 调用者凭证 的有效性。通用流程1. 服务器给合法用户发 凭证如 API Key。2. 用户调用 API 时在请求中携带该凭证。3. 服务器接收请求后先验证凭证是否正确比如 API Key 是否存在、未过期验证通过才让大模型处理请求。核心流程1. 客户端请求客户端发起API请求必须在HTTP请求头中包含X-API-Key字段2. 服务端接收FastAPI服务接收请求获取请求头中的API Key3. 密钥比对从环境变量.env文件读取合法的API Key与客户端提供的Key进行比对4. 认证决策认证失败返回401未授权状态码请求终止认证成功继续处理请求参数调用大模型进行推理3.2 调用 JWT 鉴权的接口核心是“令牌申领-携带验证”两步走令牌携带需严格遵循“Bearer 空格 令牌”格式否则直接鉴权失败验证失败仅返回401错误需重新申请令牌后再尝试调用。核心流程1. 客户端申请令牌客户端向服务器的 /get-token 端点发送请求通常需要提供身份凭证如用户名和密码。2. 服务器生成JWT令牌服务器验证客户端提供的凭证如果有效则生成一个JWT令牌并将其返回给客户端。3. 客户端携带令牌调用接口客户端在后续的请求中将得到的JWT令牌放在HTTP请求头的Authorization字段中格式为Bearer 令牌然后访问服务器上的业务接口。4. 服务器验证令牌有效性服务器收到请求后从Authorization头中提取令牌并验证其签名和有效期。5. 验证结果处理如果令牌有效服务器执行业务逻辑例如使用大模型生成文本。如果令牌无效如签名错误、已过期等则返回401错误。6. 返回业务结果或错误信息服务器将业务逻辑处理的结果返回给客户端或者在验证失败时返回错误信息。四、JWT鉴权原理1. JWT 的核心概念JWTJSON Web TokenJSON 格式的网络令牌可以理解为服务器给客户端发的一串加密的“临时身份证”客户端后续调用接口时带着这张“身份证”服务器验证“身份证”的真伪和有效期就能确认身份。2. JWT 的核心优势无状态对比session鉴权需要存会话服务器不用存令牌信息验证时只靠加密算法适合本地 API 这种轻量部署场景过期可控避免 API Key 一旦泄露就被永久滥用大模型本地 API 的内存资源不会被无限占用轻量化令牌是字符串传输快适配大模型 API 的低延迟需求。3. JWT 的结构拆解JWT 令牌由3 部分组成用英文句号.分隔比如实际的令牌eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJsb2NhbF9sbG1fdXNlciIsImV4cCI6MTcxNzc0MjQwMH0.8Z7k1X8s7a9L0b8K7j6H5g4F3d2S1a0Z9Y8X7W6V5U4T3S2R1Q0P拆分成 3 部分Header头部eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9作用声明加密算法 令牌类型 JWT_ALGORITHM HS256Payload载荷eyJzdWIiOiJsb2NhbF9sbG1fdXNlciIsImV4cCI6MTcxNzc0MjQwMH0作用存储 “身份证信息”如用户标识、过期时间Signature签名8Z7k1X8s7a9L0b8K7j6H5g4F3d2S1a0Z9Y8X7W6V5U4T3S2R1Q0P作用加密校验防止头部 / 载荷被篡改详细介绍1. Header头部告诉服务器怎么验证本质JSON 字符串Base64 编码不是加密只是转格式内容为固定模板jwt.encode会自动生成{ alg: HS256, // 加密算法我们用的是对称加密HS256 typ: JWT // 令牌类型固定为JWT }2. Payload载荷存储身份信息本质JSON 字符串Base64 编码可解码不能存密码或密钥等敏感信息代码示例token_data { sub: local_llm_user, # 自定义的用户标识 exp: datetime.utcnow() timedelta(minutes30) # 过期时间 }3. Signature签名防止篡改的防伪码核心作用服务器用密钥 算法给 Header 和 Payload 加密生成唯一签名客户端篡改 Header/Payload 后签名会失效服务器能立刻发现。加密公式签名 HMACSHA256( Base64编码(Header) . Base64编码(Payload), 服务器密钥JWT_SECRET_KEY )代码里的密钥JWT_SECRET_KEY my_jwt_secret_1234. JWT 的核心原理4.1 对称加密验证服务器生成令牌和验证令牌用的是同一把密钥JWT_SECRET_KEY只要密钥不泄露客户端无法伪造令牌因为生成签名需要密钥注意不要把密钥写在前端或客户端只保留在服务器端我们的代码里存在.env 或直接写在代码里本地 API 没问题生产环境建议用环境变量。4.2 无状态特性服务器不用存“谁领了令牌”“令牌是否有效”只靠加密算法验证对比传统的“session 鉴权”服务器要存 sessionIDJWT 更轻量适合本地 API 这种简单部署场景。4.3 过期机制Payload 里的exp字段是时间戳服务器验证时会自动对比当前时间即使令牌泄露攻击者也只能在过期前使用风险可控我们代码里设为 30 分钟可按需调整。五、JWT 的完整工作流程以我们的“本地大模型 API 极简 JWT 鉴权”为例流程分 4 步每一步都对应代码里的接口函数1. 生成 JWT 令牌生成JWT令牌领临时身份证对应接口/get-token对应函数create_jwt_token()执行逻辑1. 客户端调用/get-token接口比如浏览器访问http://127.0.0.1:8080/docs点击调用2. 服务器执行create_jwt_token()拼 Payload用户标识 30 分钟过期时间用JWT_SECRET_KEY和 HS256 算法把 HeaderPayload 加密生成 Signature把 HeaderPayloadSignature 用.拼接生成完整 JWT 令牌3. 服务器返回令牌给客户端比如eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJsb2NhbF9sbG1fdXNlciIsImV4cCI6MTcxNzc0MjQwMH0.8Z7k1X8s7a9L0b8K7j6H5g4F3d2S1a0Z9Y8X7W6V5U4T3S2R1Q0P。代码核心片段def create_jwt_token(): token_data { sub: local_llm_user, # 身份标识 exp: datetime.utcnow() timedelta(minutes30) # 过期时间 } # 生成令牌自动处理HeaderPayloadSignature token jwt.encode(token_data, JWT_SECRET_KEY, algorithmJWT_ALGORITHM) return token2. 客户端携带令牌调用接口客户端携带令牌调用接口即出示身份证对应接口/generate-text-jwt执行逻辑1. 客户端把令牌放在请求头里格式固定为Authorization: Bearer 令牌字符串比如Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJsb2NhbF9sbG1fdXNlciIsImV4cCI6MTcxNzc0MjQwMH0.8Z7k1X8s7a9L0b8K7j6H5g4F3d2S1a0Z9Y8X7W6V5U4T3S2R1Q0P2. 客户端发送请求到/generate-text-jwt同时带上提示词如 “写一段秋天的文案”。3. 服务器验证令牌检查身份证真伪对应函数verify_jwt_token()执行逻辑1. 服务器从请求头里提取令牌credentials.credentials2. 服务器用JWT_SECRET_KEY和 HS256 算法做 3 件事1. 拆分令牌为 Header/Payload/Signature2. 重新计算签名用本地密钥对 HeaderPayload 再次加密生成 “新签名”3. 对比 “新签名” 和令牌里的 “旧签名”不匹配→令牌被篡改比如改了 Payload 里的过期时间返回 401 错误匹配→继续校验过期时间3. 校验 Payload 里的exp过期时间已过期→返回 “令牌过期”未过期→验证通过提取用户标识sub。代码核心片段def verify_jwt_token(credentials: HTTPAuthorizationCredentials Depends(bearer_scheme)): try: token credentials.credentials # 解码验证自动校验签名过期时间 payload jwt.decode(token, JWT_SECRET_KEY, algorithms[JWT_ALGORITHM]) # 兜底检查过期时间jwt库会自动校验此处仅做提示 expire payload.get(exp) if expire and datetime.utcfromtimestamp(expire) datetime.utcnow(): raise HTTPException(status_code401, detail令牌已过期) return payload.get(sub) # 返回用户标识 except JWTError: # 签名错误/篡改都会触发这个异常 raise HTTPException(status_code401, detail令牌无效)4. 处理请求验证结果验证通过→调用大模型生成文本返回结果给客户端验证失败令牌篡改 / 过期 / 无效→返回 401 错误提示 “重新获取令牌”。5. 示例运行过程5.1 基础库补充安装# Windows pip install -i https://pypi.tuna.tsinghua.edu.cn/simple python-jose # macOS/Linux pip3 install -i https://pypi.tuna.tsinghua.edu.cn/simple python-jose5.2 示例启动进入的文件所在目录执行python main.py启动项目出现以下界面表示项目启动成功5.3 获取 JWT 令牌1. 新建一个 POST 请求URL 填 http://127.0.0.1:8080/get-token2. 无需加请求头/请求体直接点击 Send3. 复制返回结果中的access_token示例eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJsb2NhbF9sbG1fdXNlciIsImV4cCI6MTc2NzQ0OTkyOX0.LlpxEytdkOwzNcNLi-SZwYwwEMg093sJ-oSNpFMNpfM。返回结果{ code: 200, message: 令牌生成成功有效期30分钟, access_token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJsb2NhbF9sbG1fdXNlciIsImV4cCI6MTc2NzQ0OTkyOX0.LlpxEytdkOwzNcNLi-SZwYwwEMg093sJ-oSNpFMNpfM, token_type: bearer }5.4 调用 JWT 鉴权的生成接口1. 新建 POST 请求URL 填 http://127.0.0.1:8080/generate-text-jwt2. 切换到【Headers】标签页新增 Authorization 请求头KeyAuthorizationValueBearer 复制的access_token注意Bearer 和令牌之间有一个空格3. 切换到【Body】标签页填写和 API Key 方式相同的 JSON 请求体4. 点击【Send】即可返回生成结果。返回结果{code: 200,message: 用户local_llm_user生成成功,data: {prompt: 写一段秋天的文案温馨风格,result: 秋天是一首诗意盎然的诗篇它以金黄的稻谷、红艳的枫叶和丰收的果实为大地披上了一层金色的纱衣。在这静谧的季节里每一处都充满了生活的韵味。\n\n秋风轻轻吹过带来了凉爽的气息仿佛在诉说着岁月的故事。树叶开始变色从绿色逐渐过渡到黄色、橙色和红色宛如一幅色彩斑斓的油画。每一片叶子都在空中翩翩起舞像是在向人们展示着它们的生命力和活力。这种景象让人感到宁静和平和仿佛所有的烦恼和压力都被这美丽的景色所带走。\n\n秋天的天空也是另一番风景。蓝天如洗云朵洁白无瑕像棉花糖一样柔软而甜美。偶尔有几只小鸟飞过欢快地鸣叫着给这个安静的季节增添了生机与活力。阳光透过}}当我用一个错误的令牌测试会直接提示“签名错误解决检查令牌是否正确或重新获取5.5 接口请求记录每一次失败或成功的接口请求都会有记录存在六、总结JWT鉴权接口是本地大模型API中安全性更高的调用方式核心依托“临时令牌”实现身份验证适配多端访问、临时授权等场景调用过程中需关注三大关键问题一是令牌有效期过期后需重新调用/get-token获取新令牌二是令牌安全性不可泄露给第三方密钥需妥善保管三是访问基础要求需使用http://127.0.0.1:8080作为基础URL确保服务正常启动且端口未被占用。相较于API Key鉴权JWT鉴权通过过期机制降低凭证泄露风险更适合非固定场景的访问需求。掌握其调用逻辑既能快速验证API功能完整性也能为后续业务系统对接提供安全的鉴权参考是本地大模型API落地应用的重要基础。附录完整的示例代码# model_path D:\\modelscope\\hub\\qwen\\Qwen1___5-1___8B-Chat # 导入需要的库 from fastapi import FastAPI, Header, HTTPException, Depends from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials from pydantic import BaseModel, Field from transformers import AutoTokenizer, AutoModelForCausalLM import torch from dotenv import load_dotenv from jose import jwt, JWTError from datetime import datetime, timedelta import os import uvicorn # 第一步加载配置API KeyJWT配置 try: load_dotenv() # API Key配置 VALID_API_KEY os.getenv(VALID_API_KEY, default_key_123) # 无配置时用默认值 # JWT极简配置新手不用改理解即可 JWT_SECRET_KEY os.getenv(JWT_SECRET_KEY, my_jwt_secret_123) # 加密密钥 JWT_ALGORITHM HS256 # 加密算法固定 JWT_EXPIRE_MINUTES 30 # 令牌过期时间30分钟 print(✅ 配置加载成功) except Exception as e: print(f❌ 配置加载失败{str(e)}) exit(1) # 第二步单例加载模型修复路径内存优化 class LocalLLM: _instance None _initialized False def __new__(cls): if cls._instance is None: cls._instance super().__new__(cls) return cls._instance def __init__(self): if not LocalLLM._initialized: # 修复1用超轻量化模型DistilGPT2避免内存不足 # 先运行download_small_model.py下载模型到./model文件夹 model_path D:\\modelscope\\hub\\qwen\\Qwen1___5-1___8B-Chat # 原始字符串避免反斜杠问题 try: print(正在加载本地大模型...) if not os.path.exists(model_path): raise FileNotFoundError( f模型文件夹不存在{model_path}\n解决1. 运行download_small_model.py下载小模型2. 确认路径正确) # 修复2添加low_cpu_mem_usage减少内存占用 self.tokenizer AutoTokenizer.from_pretrained(model_path, trust_remote_codeTrue) self.model AutoModelForCausalLM.from_pretrained( model_path, torch_dtypetorch.float32, device_mapcpu, trust_remote_codeTrue, low_cpu_mem_usageTrue # 减少CPU内存占用 ) self.model.eval() LocalLLM._initialized True print(✅ 模型加载完成) except FileNotFoundError as e: print(f❌ 模型加载失败{str(e)}) exit(1) except RuntimeError as e: if out of memory in str(e).lower(): print(f❌ 内存不足请关闭其他程序如浏览器/微信或换更小的模型) else: print(f❌ 模型运行错误{str(e)}) exit(1) except Exception as e: print(f❌ 模型加载未知错误{str(e)}) exit(1) def generate_text(self, prompt, max_length): try: if not prompt.strip(): raise ValueError(提示词不能为空) inputs self.tokenizer(prompt, return_tensorspt, truncationTrue, max_length512) if inputs.input_ids.shape[1] 512: raise ValueError(f提示词过长{inputs.input_ids.shape[1]}token最大支持512token) with torch.no_grad(): outputs self.model.generate(**inputs, max_lengthmax_length, temperature0.7, pad_token_idself.tokenizer.eos_token_id) result self.tokenizer.decode(outputs[0], skip_special_tokensTrue).replace(prompt, ).strip() if not result: raise ValueError(模型未生成内容请换提示词) return result except ValueError as e: raise HTTPException(status_code400, detailf参数错误{str(e)}) except Exception as e: raise HTTPException(status_code500, detailf推理错误{str(e)}) # 创建模型实例 try: llm LocalLLM() except Exception as e: print(f❌ 模型初始化失败{str(e)}) exit(1) # 第三步极简JWT鉴权核心函数 bearer_scheme HTTPBearer() def create_jwt_token(): 生成JWT令牌简化版无用户名密码直接生成 try: token_data { sub: local_llm_user, exp: datetime.utcnow() timedelta(minutesJWT_EXPIRE_MINUTES) } token jwt.encode(token_data, JWT_SECRET_KEY, algorithmJWT_ALGORITHM) return token except Exception as e: raise HTTPException(status_code500, detailf生成令牌失败{str(e)}) def verify_jwt_token(credentials: HTTPAuthorizationCredentials Depends(bearer_scheme)): 验证JWT令牌简化版只检查有效性和过期时间 try: token credentials.credentials payload jwt.decode(token, JWT_SECRET_KEY, algorithms[JWT_ALGORITHM]) expire payload.get(exp) if expire and datetime.utcfromtimestamp(expire) datetime.utcnow(): raise HTTPException(status_code401, detail❌ JWT令牌已过期\n解决重新调用/get-token获取新令牌) return payload.get(sub) except JWTError as e: raise HTTPException(status_code401, detailf❌ JWT令牌无效/签名错误\n解决检查令牌是否正确或重新获取) except Exception as e: raise HTTPException(status_code401, detailf❌ 令牌验证失败{str(e)}) # 第四步FastAPI接口API KeyJWT双鉴权 app FastAPI(title本地大模型APIAPI Key极简JWT鉴权) # 请求体模型保留 class LLMRequest(BaseModel): prompt: str Field(..., description提示词, max_length500) max_length: int Field(100, description生成长度, ge10, le1000) # 接口1获取JWT令牌 app.post(/get-token, summary获取JWT令牌无需密码直接领) def get_jwt_token(): token create_jwt_token() return { code: 200, message: 令牌生成成功有效期30分钟, access_token: token, token_type: bearer } # 接口2API Key鉴权的生成接口 app.post(/generate-text-apikey, summaryAPI Key鉴权-文本生成) def generate_by_apikey(request: LLMRequest, x_api_key: str Header(None)): try: if not x_api_key: raise HTTPException(status_code401, detail❌ 请传入X-API-Key) if x_api_key.strip() ! VALID_API_KEY: raise HTTPException(status_code401, detail❌ API Key错误) except HTTPException as e: raise e try: result llm.generate_text(request.prompt, request.max_length) return {code: 200, message: 生成成功, data: {prompt: request.prompt, result: result}} except HTTPException as e: raise e # 接口3JWT鉴权的生成接口 app.post(/generate-text-jwt, summaryJWT鉴权-文本生成) def generate_by_jwt( request: LLMRequest, username: str Depends(verify_jwt_token) ): try: result llm.generate_text(request.prompt, request.max_length) return { code: 200, message: f用户{username}生成成功, data: {prompt: request.prompt, result: result} } except HTTPException as e: raise e # 第五步启动服务修复端口日志 if __name__ __main__: try: # 修复3改端口为8080避免8000被占用添加更多日志 uvicorn.run( main:app, host0.0.0.0, port8080, # 改端口避免占用 reloadFalse, log_leveldebug, access_logTrue # 开启访问日志便于排查 ) except OSError as e: if address already in use in str(e).lower(): print(f❌ 服务启动失败端口8080被占用\n解决1. 改端口为80902. 结束占用8080的程序) else: print(f❌ 服务启动失败{str(e)}) exit(1) except Exception as e: print(f❌ 服务启动失败{str(e)}\n解决检查Python依赖是否安装完整) exit(1)