01-Python调用大模型API-从第一次请求到可维护客户端

📅 2026/7/21 22:33:43
01-Python调用大模型API-从第一次请求到可维护客户端
Python 调用大模型 API从第一次请求到可维护客户端系列Python 大模型应用开发第 1 篇目标使用 Python 完成一次大模型调用并解决密钥、超时、异常和响应校验问题。1. 为什么不能只满足于“调用成功”很多入门示例只做三件事把 API Key 写进代码、发送请求、打印结果。这能验证接口却很难继续扩展成 RAG、Agent 或企业业务系统。一个可以继续迭代的最小模型客户端至少需要处理API Key接口密钥不能硬编码网络请求必须有超时401、429、500 等状态需要区分外部返回的 JSON 不能盲目信任服务地址和模型名称应当可配置用户输入需要进行基本校验。本文使用常见的兼容式/chat/completions接口演示。不同模型服务商的地址、模型名称和字段可能不同实际参数必须以对应服务商的官方文档为准。2. 从第一性原理理解模型 API大模型 API 本质上是一次 HTTP 网络通信用户问题 ↓ Python 组装 HTTP 请求 ↓ 模型服务接收并处理请求 ↓ 模型服务返回 HTTP 响应 ↓ Python 校验响应并提取答案一次常见请求由三部分组成URL请求地址Headers请求头用于鉴权和声明数据格式Body请求体包含模型名称、对话消息和生成参数。示例请求体{model:your-model-id,messages:[{role:system,content:你是一名严谨的 Python 助手。},{role:user,content:请解释 Python 列表和元组的区别。}],temperature:0.2}常见角色说明system描述模型的任务、规则和边界user用户输入assistant模型在历史对话中的回答。3. 创建项目项目结构llm_api_demo/ ├── config.py ├── llm_client.py ├── main.py └── requirements.txt创建虚拟环境并安装依赖python-m venv.venv.\.venv\Scripts\python.exe-m pip install--upgrade pip.\.venv\Scripts\python.exe-m pip installrequests2.31,3requirements.txtrequests2.31,34. 使用环境变量保护密钥不要这样写# 错误示例代码一旦被上传或分享真实密钥就可能泄露API_KEYsk-真实密钥在 Windows PowerShell 当前窗口中设置环境变量$env:LLM_API_KEY 替换为真实密钥$env:LLM_BASE_URL https://替换为模型服务地址/v1$env:LLM_MODEL 替换为真实模型标识注意示例地址不能直接使用。不同厂商的服务地址、模型标识和鉴权方法可能不同。5. 集中读取配置新建config.pyimportosfromdataclassesimportdataclassdataclass(frozenTrue)classSettings:保存模型服务配置。 frozenTrue 表示对象创建后字段不能被意外修改。 api_key:strbase_url:strmodel:strdefload_settings()-Settings:从环境变量读取配置并在程序启动阶段完成校验。# strip() 清除变量两侧可能存在的空格api_keyos.getenv(LLM_API_KEY,).strip()base_urlos.getenv(LLM_BASE_URL,).strip().rstrip(/)modelos.getenv(LLM_MODEL,).strip()# 收集所有缺少的变量一次性告诉使用者missing_variables[]ifnotapi_key:missing_variables.append(LLM_API_KEY)ifnotbase_url:missing_variables.append(LLM_BASE_URL)ifnotmodel:missing_variables.append(LLM_MODEL)ifmissing_variables:names, .join(missing_variables)raiseRuntimeError(f缺少环境变量{names})# 模型密钥会通过网络传输因此示例要求使用 HTTPSifnotbase_url.startswith(https://):raiseRuntimeError(LLM_BASE_URL 必须使用 https:// 地址)returnSettings(api_keyapi_key,base_urlbase_url,modelmodel,)把配置集中管理的价值在于程序可以在真正请求模型前发现配置问题而不是运行到一半才产生难以定位的错误。6. 封装模型客户端新建llm_client.pyfromtypingimportAnyimportrequestsfromconfigimportSettingsclassLLMError(RuntimeError):表示模型调用过程中可以预期的错误。classLLMClient:一个最小但相对完整的大模型 HTTP 客户端。def__init__(self,settings:Settings)-None:# 保存经过校验的配置self.settingssettings# Session 可以在多次请求之间复用底层 HTTP 连接self.sessionrequests.Session()defchat(self,user_message:str,system_message:str你是一名严谨、准确的 AI 助手。,temperature:float0.2,)-str:向模型发送一轮对话并返回模型文本。 Args: user_message: 用户问题。 system_message: 模型需要遵守的系统要求。 temperature: 常见的随机性参数本文示例限制在 0 到 2。 Returns: 模型返回的非空字符串。 Raises: ValueError: 输入参数不合法。 LLMError: 网络、鉴权、限流或响应结构异常。 # 用户输入来自程序外部使用前必须校验user_messageuser_message.strip()ifnotuser_message:raiseValueError(用户问题不能为空)# 本文为了演示设置 0 到 2 的范围真实范围以模型文档为准ifnot0temperature2:raiseValueError(temperature 必须位于 0 到 2 之间)# rstrip(/) 已在配置层处理避免地址中出现双斜杠request_urlf{self.settings.base_url}/chat/completions# Bearer 后面必须有一个空格request_headers{Authorization:fBearer{self.settings.api_key},Content-Type:application/json,}# requests 会通过 json 参数把 Python 字典序列化成 JSONrequest_body{model:self.settings.model,messages:[{role:system,content:system_message},{role:user,content:user_message},],temperature:temperature,}try:responseself.session.post(request_url,headersrequest_headers,jsonrequest_body,# 连接最多等待 5 秒读取响应最多等待 60 秒timeout(5,60),)exceptrequests.exceptions.Timeoutasexc:# 使用 from exc 保留原始异常链方便开发阶段定位问题raiseLLMError(模型请求超时请稍后重试)fromexcexceptrequests.exceptions.ConnectionErrorasexc:raiseLLMError(无法连接模型服务请检查网络和接口地址)fromexcexceptrequests.exceptions.RequestExceptionasexc:raiseLLMError(模型请求发生网络异常)fromexc# 不同状态码代表不同类型的问题不能统一当成“调用失败”ifresponse.status_code401:raiseLLMError(鉴权失败请检查 API Key)ifresponse.status_code429:raiseLLMError(请求过于频繁或额度不足请稍后重试)if400response.status_code500:raiseLLMError(f模型请求参数错误状态码{response.status_code})ifresponse.status_code500:raiseLLMError(f模型服务暂时异常状态码{response.status_code})try:# 把 JSON 响应转换成 Python 字典data:dict[str,Any]response.json()exceptrequests.exceptions.JSONDecodeErrorasexc:raiseLLMError(模型服务返回的内容不是有效 JSON)fromexctry:# 常见兼容式响应中的模型文本位于以下路径contentdata[choices][0][message][content]except(KeyError,IndexError,TypeError)asexc:# 请求成功不代表数据结构一定符合预期raiseLLMError(模型响应缺少预期字段)fromexc# 再次校验最终业务数据防止返回 None 或空字符串ifnotisinstance(content,str)ornotcontent.strip():raiseLLMError(模型返回了空内容)returncontent.strip()defclose(self)-None:释放 Session 持有的网络资源。self.session.close()7. 编写程序入口新建main.pyfromconfigimportload_settingsfromllm_clientimportLLMClient,LLMErrordefmain()-None:程序入口加载配置、读取问题、调用模型、展示结果。try:# 先加载配置配置错误时没有必要继续创建客户端settingsload_settings()clientLLMClient(settings)exceptRuntimeErrorasexc:print(f配置错误{exc})returntry:# input() 返回字符串具体输入校验由 chat() 统一负责questioninput(请输入你的问题)# 调用模型并取得最终文本answerclient.chat(user_messagequestion,system_message你是一名 Python 教师请用初学者能理解的方式回答。,temperature0.2,)print(\n模型回答)print(answer)exceptValueErrorasexc:print(f输入错误{exc})exceptLLMErrorasexc:# 只向终端展示可理解的信息不打印密钥和完整请求头print(f调用失败{exc})finally:# 无论调用成功还是失败都释放网络资源client.close()if__name____main__:main()运行.\.venv\Scripts\python.exe main.py8. 为什么要区分 HTTP 状态码状态码常见含义建议处理200请求成功解析并校验 JSON400请求参数错误检查请求体和模型名称401鉴权失败检查 API Key不应盲目重试429请求过多或额度受限限流、延迟重试、检查额度500—599服务端异常有限次数重试或执行降级自动重试并非越多越好。401 通常是配置问题重复请求不会自动恢复无限重试还会增加系统压力和调用成本。9. 对抗性审查这还不是生产系统当前代码已经适合作为后续学习的基础但上线前仍然需要解决接口差异并非所有厂商都支持同一种请求结构有限重试只对网络抖动、429 和部分 5xx 使用指数退避日志脱敏不能记录 API Key也不能默认记录客户隐私成本控制限制输入长度统计 Token 和用户额度事实校验请求成功不代表模型回答正确输入攻击恶意输入可能诱导模型忽略原有规则权限控制模型不应该因为用户的一句话就获得高风险操作权限。10. 常见问题排查返回 401检查密钥是否正确、是否失效、是否有模型权限以及鉴权格式是否符合服务商文档。返回 404检查服务地址和路径。网页首页地址通常不等于 API 地址不要靠猜测拼接接口。返回 429可能是请求频率受限也可能与账户额度有关具体含义需要结合服务商响应和官方文档判断。可以把密钥放到浏览器或小程序吗不应该。前端代码和网络请求可能被检查。通常应由后端保存密钥前端只调用自己的后端服务。11. 总结本文完成了一个可维护的大模型 API 客户端并建立了以下工程意识敏感配置与代码分离所有外部输入都需要校验网络请求必须设置超时不同错误应该采用不同处理策略接口成功与业务答案正确是两件事模型调用代码应该被封装避免散落在业务代码中。下一篇将使用 FastAPI 把模型客户端封装成 HTTP 服务让网页、小程序、企业微信侧边栏或其他后端系统都可以通过统一接口调用模型。12. 练习题输入空字符串观察程序如何拦截设置错误的 API Key观察 401 处理设置错误的服务地址观察连接异常修改temperature为 3观察参数校验思考哪些错误适合自动重试哪些错误不适合尝试为模型响应增加 Token 用量提取但必须先判断相应字段是否存在。