大模型接口接入这件事,说难不难,说简单也真不简单。我见过太多团队在Demo阶段跑得飞起,一上生产就各种翻车:密钥硬编码进了前端、超时没设导致请求堆积、流式响应解析到一半断掉、限流来了直接雪崩。这些问题单拎出来都不复杂,但凑在一起就够喝一壶的。这篇内容我打算把大模型API接入的完整链路拆成7个步骤讲透,从密钥怎么管、请求怎么封装、错误怎么重试、流式怎么处理,一直到上线前的检查清单。适合刚接触大模型接口的开发者照着落地,也适合已经跑通Demo但还没上生产的团队查漏补缺。核心关键词就三个:密钥配置、请求封装、流式响应,全文会围绕它们展开,把每一步背后的取舍逻辑讲清楚,而不是只给一段能跑的代码就完事。
1. 整体接入设计与方案选型思路
1.1 为什么要把接入拆成7步而不是一把梭
很多人拿到API Key的第一反应是打开编辑器,写个HTTP请求,看到返回结果就认为接入完成了。这种"一把梭"的做法在个人玩具项目里没问题,但一旦要交付给团队、要上生产环境,问题就会成倍放大。我之所以把接入拆成密钥配置、请求封装、参数设计、错误重试、流式响应、日志监控、上线检查这7个步骤,是因为这7个环节各自对应一类独立的失败模式,混在一起写会导致排查困难、复用性差、安全边界模糊。
举个最典型的例子:密钥管理和请求逻辑耦合在一起。初期为了图快,把Key写在请求函数里,后面要换Key、要做多环境切换、要轮换密钥,就得满代码库找。再比如错误重试和流式响应,这两块如果不在封装层统一处理,每个业务调用点都要重复写一遍重试逻辑,代码会迅速腐化。拆步骤的本质是分离关注点,让每一层只负责一件事,出问题时能快速定位是哪一层的问题。
从工程角度看,这7步的依赖关系是线性的:密钥配置是地基,请求封装是骨架,参数设计决定行为,错误重试保证韧性,流式响应提升体验,日志监控提供可观测性,上线检查兜底。跳过任何一步,后面的步骤都会带着隐患往前走。我个人的经验是,宁可前期多花半天把这7步的骨架搭好,也不要后期花三天去填坑。
1.2 技术选型:HTTP客户端与SDK的取舍
接入大模型API,第一个要做的决策是用官方SDK还是自己封装HTTP请求。这两条路各有适用场景,不能一概而论。
官方SDK的优点是开箱即用,鉴权、重试、流式解析这些常见逻辑都内置了,版本升级也由官方维护。缺点是抽象层厚,出问题时排查链路长,而且不同语言的SDK成熟度参差不齐,某些边缘参数可能没有暴露出来。自己封装HTTP请求的优点是透明可控,每一个请求头、每一个超时参数都在你手里,出问题一眼能看到根因。缺点是要自己处理鉴权、重试、流式解析这些脏活累活,前期投入大。
我的建议是分场景:如果只是快速验证、做原型,直接用官方SDK,半天就能跑通;如果是要上生产、要长期维护、要做多模型适配,建议自己封装一层薄薄的HTTP客户端,把SDK当作参考实现而不是直接依赖。下面这张表是我在实际项目中总结的对比,供你决策时参考。
| 维度 | 官方SDK | 自封装HTTP |
|---|---|---|
| 上手速度 | 快,几行代码跑通 | 慢,需要处理鉴权与解析 |
| 可控性 | 中,部分参数不暴露 | 高,完全透明 |
| 排查难度 | 较高,抽象层厚 | 低,链路清晰 |
| 多模型适配 | 差,各家用各家SDK | 好,统一封装层 |
| 维护成本 | 低,官方跟进 | 中,需自己维护 |
| 适合场景 | 原型验证、单一模型 | 生产环境、多模型 |
选HTTP客户端库时,Python生态里httpx对同步异步都支持得不错,流式处理也顺手;Node.js里原生的fetch已经够用,配合undici性能更好。这里不展开具体库的对比,核心原则是选一个你团队熟悉、社区活跃、流式支持良好的库,别在这上面纠结太久。
1.3 分层架构:把接入代码放在哪一层
接入代码放错层是很多项目后期难以维护的根源。我见过把API调用直接写进Controller的,也见过写进前端页面的,前者导致业务逻辑和外部依赖纠缠,后者直接泄露密钥。合理的做法是单独抽一个接入层(或者叫客户端层),向上暴露简洁的业务方法,向下封装所有与外部API交互的细节。
这个接入层至少要承担四件事:密钥的读取与注入、请求的组装与发送、响应的解析与错误映射、重试与超时的统一处理。业务代码只跟接入层打交道,不关心底层用的是哪个模型、哪个版本、哪个端点。这样做的好处是,将来要换模型供应商、要升级API版本、要调整重试策略,都只改接入层一处,业务代码零改动。
分层还有一个隐性收益:测试变得容易。接入层可以单独做单元测试,用Mock替换真实请求,验证参数组装、错误映射、重试逻辑是否正确,不需要真的花钱调API。这一点在CI流水线里尤其重要,能大幅降低测试成本。
2. 密钥配置:从硬编码到安全管理的完整路径
2.1 密钥泄露的代价与常见泄露途径
API密钥本质上就是一张不记名的信用卡,谁拿到谁就能刷。大模型API按Token计费,密钥泄露后被恶意调用,账单可能在几小时内飙到四位数。我见过最惨的案例是密钥被提交到了公开仓库,第二天收到一封天价账单邮件。所以密钥管理不是"锦上添花",而是接入工作的第一道红线。
常见的泄露途径有这么几类:硬编码在源码里然后提交到版本库、写在前端代码里被打包进JS、打印在日志里、贴在聊天工具里传给同事、存在配置文件里但配置文件没加忽略规则。这几类里,前端硬编码和日志打印是最隐蔽的,因为代码能跑、功能正常,问题要等到账单来了才暴露。
注意:任何以
NEXT_PUBLIC_、VITE_、REACT_APP_这类前缀开头的环境变量,都会被构建工具打包进前端产物,等于公开。密钥绝对不能放在这类变量里。
2.2 环境变量与密钥管理服务的分层方案
密钥管理要分层,不同环境、不同规模用不同方案。最小可用方案是环境变量:本地开发用.env文件,配合.gitignore忽略;服务器上用系统环境变量或容器编排的Secret机制注入。这个方案简单直接,适合中小团队。
规模再大一点,建议引入专门的密钥管理服务,比如云厂商提供的密钥托管服务,或者开源的Vault类工具。这类服务的核心价值是:密钥加密存储、访问审计、自动轮换、细粒度权限控制。密钥不再以明文形式出现在任何配置文件里,应用启动时通过受控通道拉取。
下面是我推荐的分层配置方案,从开发到生产逐级收紧:
| 环境 | 存储方式 | 访问控制 | 轮换策略 |
|---|---|---|---|
| 本地开发 | .env文件,git忽略 | 个人本地 | 不强制 |
| 测试环境 | CI/CD变量注入 | 团队可见 | 季度轮换 |
| 预发环境 | 密钥管理服务 | 受限角色 | 月度轮换 |
| 生产环境 | 密钥管理服务+审计 | 最小权限 | 按需+定期 |
环境变量的读取我习惯封装一个统一的配置模块,而不是在每个文件里散落os.environ.get。这样做的原因是:集中读取可以做校验(缺失时启动即报错,而不是运行到一半才炸)、可以做类型转换、可以给默认值、方便测试时替换。下面是一段Python的配置模块示例,思路在其他语言里同样适用。
import os from dataclasses import dataclass @dataclass(frozen=True) class LLMConfig: api_key: str base_url: str timeout: float max_retries: int def load_config() -> LLMConfig: api_key = os.environ.get("LLM_API_KEY") if not api_key: raise RuntimeError("LLM_API_KEY 未配置,拒绝启动") return LLMConfig( api_key=api_key, base_url=os.environ.get("LLM_BASE_URL", "https://api.example.com/v1"), timeout=float(os.environ.get("LLM_TIMEOUT", "30")), max_retries=int(os.environ.get("LLM_MAX_RETRIES", "3")), )这段代码的关键点在frozen=True和启动即校验。frozen保证配置加载后不可变,避免运行期被意外修改;启动即校验保证配置缺失时进程直接起不来,而不是等到第一个请求进来才发现问题。这种"快速失败"的思路在配置管理里非常重要。
2.3 密钥轮换与最小权限实践
密钥轮换是很多团队忽略的环节。理想情况下,密钥应该像密码一样定期更换,并且支持多密钥并存(新旧密钥同时有效一段时间,平滑过渡)。实现轮换的前提是密钥不硬编码、不散落,而是集中管理,这样换的时候只改一处。
最小权限则是指:给这个密钥分配它能完成任务的最小范围。如果供应商支持按项目、按模型、按额度分配密钥,就按需分配,不要用一个万能密钥打通所有场景。这样即使某个密钥泄露,损失也是可控的。另外,建议给密钥设置用量告警,比如日消费超过阈值就发通知,这是发现异常调用的最后一道防线。
3. 请求封装:构建可复用的客户端层
3.1 请求封装要解决的核心问题
请求封装的目标不是"把请求发出去",而是"把请求发得稳、发得可维护、发得可观测"。一个合格的封装层要解决这几个问题:统一的鉴权头注入、统一的超时控制、统一的错误映射、统一的日志记录、统一的参数校验。这五件事如果不在封装层做,就会散落到每个调用点,重复且容易遗漏。
我见过不少项目的请求代码是这样的:每个业务函数里都写一遍headers = {"Authorization": f"Bearer {key}"},超时参数有的设有的不设,错误处理各写各的。这种代码在只有两三个调用点时还能忍,到十几个调用点时就彻底失控了。封装层的价值就在于把这些横切关注点收敛到一处。
封装层的接口设计也有讲究。向上暴露的方法应该是业务语义的,比如chat(messages)、embed(text),而不是post(url, body)这种HTTP语义的。业务代码不应该知道底层是POST还是GET,不应该关心请求体长什么样。这种"业务语义接口"的设计能让上层代码更稳定,底层实现怎么变都不影响调用方。
3.2 超时、连接池与并发控制参数
超时设置是封装层最容易被忽视、又最容易出问题的参数。大模型API的响应时间波动很大,简单问答可能一两秒,复杂推理可能几十秒。超时设太短,正常请求被误杀;设太长,异常请求占着连接不放,拖垮整个服务。
我的经验是把超时拆成两段:连接超时和读取超时。连接超时管的是建立TCP连接的时间,一般设3到5秒足够;读取超时管的是等待响应数据的时间,非流式请求建议设30到60秒,流式请求则要按"两个数据块之间的最大间隔"来设,而不是总时长。这个区分很关键,因为流式响应的总时长可能很长,但块与块之间的间隔通常很短,用总时长做超时会误杀长回答。
连接池是另一个容易被忽略的点。如果每次请求都新建连接,在高并发下会大量消耗文件描述符和握手开销。HTTP客户端库一般都支持连接池,需要配置最大连接数、空闲连接存活时间等参数。最大连接数要根据你的并发量和下游限流来定,设太大反而可能触发下游限流。
并发控制方面,如果你的服务会同时发起大量请求,建议在封装层加一个信号量或令牌桶,限制同时进行的请求数。这样做一是保护下游不被打爆,二是保护自己不被限流拖垮。下面是一个带超时和并发控制的封装示例:
import httpx import asyncio class LLMClient: def __init__(self, config, max_concurrency=10): self.config = config self._semaphore = asyncio.Semaphore(max_concurrency) self._client = httpx.AsyncClient( base_url=config.base_url, timeout=httpx.Timeout(connect=5.0, read=60.0, write=10.0, pool=5.0), limits=httpx.Limits(max_connections=20, max_keepalive_connections=10), headers={"Authorization": f"Bearer {config.api_key}"}, ) async def chat(self, messages, **kwargs): async with self._semaphore: payload = {"messages": messages, **kwargs} resp = await self._client.post("/chat/completions", json=payload) resp.raise_for_status() return resp.json()这段代码里,httpx.Timeout把超时拆成了四段,Semaphore控制并发,AsyncClient复用了连接池。这些参数不是拍脑袋定的,而是根据实际压测结果调的,你在自己项目里也要压测后再定。
3.3 请求参数设计与业务语义映射
请求参数的设计直接决定模型输出的质量。温度、最大Token数、系统提示词这几个参数是最常调的,但很多人只是抄了个默认值就不管了。实际上不同业务场景对参数的要求差别很大。
温度控制输出的随机性,值越低越确定、越高越发散。做事实性问答、代码生成这类需要稳定输出的场景,温度设0到0.3;做创意写作、头脑风暴这类需要多样性的场景,设0.7到1.0。最大Token数要结合业务预期来设,设太小回答被截断,设太大浪费额度还可能拖慢响应。系统提示词则是把业务规则前置,比如"你是一个只回答技术问题的助手",能有效约束输出范围。
封装层应该把这些参数做成有默认值、可覆盖的配置,而不是让每个调用点都手写。我习惯在封装层定义几套预设,比如PRESET_FACTUAL、PRESET_CREATIVE,业务代码按场景选预设,需要微调时再覆盖个别参数。这样既保证了参数一致性,又保留了灵活性。
4. 错误处理与重试:让接入层具备韧性
4.1 错误分类:哪些该重试,哪些不该
重试不是万能的,盲目重试反而会放大问题。第一步是把错误分类,搞清楚哪些错误重试有意义,哪些重试只会浪费资源。
大模型API的错误大致分四类:网络类(连接超时、连接重置)、限流类(429 Too Many Requests)、服务端类(500、502、503)、客户端类(400参数错误、401鉴权失败、403无权限、404路径错误)。前两类和部分服务端错误是瞬时的,重试大概率能成功;客户端类错误是确定性的,重试多少次结果都一样,只会白白消耗额度。
| 错误类型 | 典型状态码 | 是否重试 | 处理策略 |
|---|---|---|---|
| 网络超时 | 无状态码 | 是 | 指数退避重试 |
| 限流 | 429 | 是 | 读Retry-After头,退避重试 |
| 服务端错误 | 500/502/503 | 是 | 指数退避,限制次数 |
| 参数错误 | 400 | 否 | 直接抛给调用方修正 |
| 鉴权失败 | 401 | 否 | 告警,检查密钥 |
| 无权限 | 403 | 否 | 告警,检查权限配置 |
这张表是我实际项目里用的分类,你可以直接参考。关键原则是:只重试瞬时性错误,确定性错误快速失败。把确定性错误也拿去重试,不仅浪费额度,还会掩盖真正的bug。
4.2 指数退避与抖动:重试节奏的设计
确定了要重试,下一个问题是"怎么重试"。固定间隔重试的问题在于,如果下游是因为瞬时过载而报错,固定间隔会让所有客户端在同一时刻再次冲击,形成"重试风暴"。指数退避的思路是每次重试间隔翻倍,给下游喘息时间;抖动则是在退避基础上加一个随机量,打散重试时刻,避免同步冲击。
标准的做法是"指数退避+全抖动":第n次重试的等待时间在[0, base * 2^n]之间随机取值。这样既保证了整体退避趋势,又打散了峰值。base一般设0.5到1秒,最大重试次数设3到5次,总等待时间控制在几十秒以内,超过就放弃并报错。
还有一个细节:如果响应头里带了Retry-After,要优先按它来,而不是用自己的退避算法。这是下游明确告诉你"多久之后再来",尊重它比自作聪明更靠谱。下面是一段带抖动的重试实现:
import random import asyncio RETRYABLE_STATUS = {429, 500, 502, 503, 504} async def request_with_retry(client, method, url, max_retries=3, **kwargs): for attempt in range(max_retries + 1): try: resp = await client.request(method, url, **kwargs) if resp.status_code not in RETRYABLE_STATUS: return resp if attempt == max_retries: return resp retry_after = resp.headers.get("Retry-After") if retry_after: wait = float(retry_after) else: wait = random.uniform(0, 0.5 * (2 ** attempt)) await asyncio.sleep(wait) except (httpx.ConnectTimeout, httpx.ReadTimeout) as e: if attempt == max_retries: raise await asyncio.sleep(random.uniform(0, 0.5 * (2 ** attempt)))这段代码里,RETRYABLE_STATUS集合定义了哪些状态码值得重试,Retry-After优先,抖动用random.uniform实现。注意异常分支里也要做退避,不能一超时就立刻重试。
4.3 熔断与降级:重试之外的兜底
重试解决的是"偶发失败",但如果下游持续不可用,重试只会让请求越积越多,最终拖垮自己。这时候需要熔断:连续失败达到阈值后,直接快速失败一段时间,不再发起真实请求,等冷却期过了再试探性放行。
熔断器的核心参数是失败阈值、冷却时长、半开试探数量。失败阈值一般设连续5到10次,冷却时长设30到60秒,半开时放1到2个请求试探。这些参数要根据你的流量和下游稳定性来调,没有标准答案。
降级则是熔断触发后的兜底策略。比如主模型不可用时切到备用模型,或者返回一个缓存的、简化版的回答,或者直接给用户一个友好的错误提示。降级策略要提前设计好,不能等故障来了才临时想。我见过最优雅的降级是:主模型超时后,自动切到一个更小更快的模型给出简短回答,用户体验几乎无感。
5. 流式响应:从协议解析到前端呈现
5.1 流式响应的协议格式与解析要点
流式响应是提升大模型应用体验的关键。非流式请求要等模型生成完整回答才返回,用户盯着转圈圈等十几秒;流式响应则是模型生成一个字就推一个字,用户能实时看到内容涌现,感知延迟大幅降低。
流式响应的底层协议通常是Server-Sent Events(SSE),格式是每行以data:开头,后跟一个JSON片段,片段之间用空行分隔,结束时发送data: [DONE]。解析时的核心难点在于:网络传输是分块的,一个数据块可能包含半条消息,也可能包含多条消息,不能假设"一次读取=一条完整消息"。
正确的解析方式是维护一个缓冲区,每次读取后按换行符切分,把不完整的尾部留在缓冲区里,等下次读取再拼接。这个"缓冲区+按分隔符切分"的模式是流式解析的通用套路,几乎所有流式协议都这么处理。下面是一段解析示例:
async def parse_stream(response): buffer = "" async for chunk in response.aiter_text(): buffer += chunk while "\n" in buffer: line, buffer = buffer.split("\n", 1) line = line.strip() if not line or not line.startswith("data:"): continue data = line[len("data:"):].strip() if data == "[DONE]": return yield json.loads(data)这段代码的关键在buffer的处理:每次读取追加到缓冲区,然后循环切分,切出完整行就处理,剩下的留在缓冲区。这样无论网络怎么分块,解析结果都是正确的。
5.2 流式场景下的超时与中断处理
流式响应的超时设置和非流式完全不同。非流式设的是"总响应时间",流式设的应该是"两个数据块之间的最大间隔"。因为流式回答的总时长可能很长(比如生成一篇长文),但正常情况下块与块之间的间隔很短,如果间隔超过阈值,说明连接可能已经僵死,应该主动断开。
中断处理也是流式场景特有的问题。用户可能中途关闭页面、取消请求,这时候要及时关闭连接,释放资源,避免后台还在傻傻地生成。实现上要监听取消信号,在取消时主动关闭响应流。另外,流式响应一旦开始就无法"重试",因为已经推给用户的内容收不回来,所以流式场景的重试只能发生在"第一个数据块到达之前",一旦开始推送就不能重试了。
提示:流式请求建议单独设一个"首块超时",比如15秒内没收到第一个数据块就判定失败并重试,收到首块后切换到"块间超时"逻辑。这样既保证了首响速度,又不会误杀长回答。
5.3 前端呈现与背压处理
流式响应到了前端,还要处理好呈现节奏。模型生成速度可能快于用户阅读速度,如果来一个字就渲染一次,会造成频繁重排,页面卡顿。常见的优化是攒一小批字符再渲染,比如每50毫秒或每积累10个字符渲染一次,用requestAnimationFrame做节流。
背压是另一个容易被忽略的问题。如果前端消费速度跟不上后端推送速度,数据会在缓冲区里堆积。处理方式有两种:一是前端主动控制读取节奏,读一块处理一块;二是后端根据消费速度调整推送。实际项目里前者更常见,用异步迭代器逐块消费即可。
还有一个体验细节:流式输出时最好加一个"光标"效果,让用户知道还在生成中;生成结束后移除光标,显示完整内容。这个小细节对感知体验的提升很明显,成本却很低。
6. 日志、监控与成本控制
6.1 请求日志该记什么、不该记什么
日志是排查问题的生命线,但日志记错了反而会带来风险。大模型请求的日志要记这些:请求时间、耗时、模型名、Token用量、状态码、重试次数、请求ID。这些信息足够定位大部分问题。
不该记的也很明确:完整的请求体和响应体默认不记,因为里面可能包含用户隐私数据;密钥绝对不记,任何形式的密钥出现在日志里都是事故。如果确实需要排查内容问题,可以记请求体的哈希值或者脱敏后的摘要,而不是原文。
请求ID的设计值得单独说一句。每次请求生成一个唯一ID,贯穿日志、监控、告警,出问题时用这个ID能把整条链路串起来。这个ID最好也返回给前端,用户报障时提供ID,你就能快速定位到那次请求的完整记录。
6.2 Token用量监控与成本预警
大模型API按Token计费,成本控制的核心是监控Token用量。要监控的指标包括:单次请求的输入Token、输出Token、累计日用量、按业务线拆分的用量。这些指标要能实时看到,而不是月底看账单才发现超支。
成本预警要设多级阈值:日用量达到预算的70%发提醒,达到90%发告警,达到100%触发限流或降级。这样能在超支前介入,而不是事后补救。另外,建议对异常调用做检测,比如某个用户短时间内发起大量请求、单次请求Token异常大,这些都要能自动识别并告警。
6.3 关键指标与告警阈值设计
除了成本,还要监控可用性和性能指标。核心指标有这么几个:请求成功率、P95响应时间、重试率、限流触发次数、熔断触发次数。这几个指标能覆盖大部分故障场景。
| 指标 | 含义 | 建议告警阈值 |
|---|---|---|
| 成功率 | 成功请求/总请求 | 低于95%告警 |
| P95延迟 | 95%请求的耗时上限 | 超过设定值告警 |
| 重试率 | 重试次数/总请求 | 高于10%告警 |
| 限流次数 | 被下游限流的次数 | 持续大于0告警 |
| 熔断次数 | 熔断器打开次数 | 大于0即告警 |
阈值不是一成不变的,要根据业务的实际SLA来定。比如面向C端的实时对话,P95延迟要求可能到2秒;面向内部的批处理任务,几十秒都能接受。阈值定得太松起不到告警作用,定得太紧天天误报也会让人麻木,需要在实践中反复调。
7. 上线前的检查清单与常见坑位
7.1 上线前必须逐项确认的清单
接入代码写完、测试跑通,不等于可以上线。上线前有一批检查项必须逐条过一遍,我把它整理成清单,你可以直接拿去用。
- 密钥是否通过环境变量或密钥服务注入,源码和前端产物里是否搜不到密钥字符串
- 超时参数是否区分了连接超时和读取超时,流式是否单独设了首块超时
- 重试是否只针对可重试错误,是否有最大次数限制和退避抖动
- 是否配置了熔断和降级策略,降级路径是否测试过
- 流式解析是否处理了分块边界,是否有
[DONE]终止判断 - 日志是否脱敏,是否包含请求ID,是否避免记录完整请求体
- 是否配置了Token用量监控和成本预警
- 是否有并发控制,是否压测过极限并发下的表现
- 错误码是否做了业务语义映射,调用方能否据此做不同处理
这份清单看着长,但每一条都对应过真实事故。逐条确认一遍,能挡掉大部分上线后的问题。
7.2 高频踩坑实录与排查思路
坑一:流式响应偶发截断。表现是回答生成到一半突然停止,没有报错。排查下来多半是块间超时设得太短,模型在思考复杂问题时两个块之间间隔较长,被误判为超时断开。解决办法是把块间超时调大,或者改成"收到首块后不再设总超时,只设块间超时"。
坑二:重试导致重复计费。表现是账单比预期高。原因是请求其实已经成功,但响应在返回途中超时,客户端重试后又发了一次,两次都计费了。这种情况无法完全避免,但可以通过幂等键(如果供应商支持)或者缩短读取超时来降低概率。
坑三:并发高了之后成功率骤降。表现是低并发一切正常,压测到一定并发就开始大量429。原因是没做并发控制,瞬间打满了下游配额。解决办法是在封装层加信号量,把并发控制在配额以内,配合退避重试平滑处理。
坑四:日志里出现用户隐私数据。表现是合规检查时被点名。原因是调试时图方便把请求体整个打进了日志,上线忘了删。解决办法是从一开始就定好日志规范,用脱敏函数处理后再记,并且把"日志不含敏感信息"写进上线检查清单。
坑五:换模型后参数不兼容。表现是切换模型供应商后大量400错误。原因是不同供应商的参数名、取值范围、必填项不一样。解决办法是在封装层做参数适配,把业务语义参数映射到各家供应商的具体参数,业务代码不直接接触供应商参数。
7.3 从能跑到能扛:生产化的最后一步
"能跑"和"能扛"之间隔着的是工程化。能跑的代码关注功能,能扛的代码关注失败模式。我个人的体会是,接入大模型API,写通请求只占两成工作量,剩下八成都在处理各种边界:超时、重试、限流、熔断、流式分块、日志脱敏、成本控制。这些工作不产生新功能,但决定了系统在真实流量下能不能稳住。
最后分享一个我常用的自检方法:假设下游API明天会出各种故障——超时、限流、返回乱码、中途断开——你的系统能不能优雅地应对,用户能不能得到合理的反馈,账单会不会失控。如果这三个问题的答案都是肯定的,那这套接入就算过关了。这个方法比任何检查清单都管用,因为它逼着你从故障视角审视自己的代码。