第一次接触大模型接口的人,卡点往往高度一致:知道有个叫API Key的东西,也隐约明白它是调用模型的门票,可真打开控制台那一刻,面对一堆英文菜单、账单提示和权限选项,手指就悬在鼠标上不动了。更麻烦的是拿到那串以sk-开头的字符串之后——复制到哪儿?写进哪个文件?会不会一不小心推到公开仓库里被人薅走额度?这些问题在官方说明里往往是分散的,得跳七八个页面才能拼出全貌,而新手最缺的恰恰就是这张完整地图。这篇内容就是把这地图一次性画完整:从API Key 是什么、能干什么,到获取路径、存放方式、测试代码、费用估算、错误排查,全部按真实项目里的顺序讲一遍。适合刚入门的大模型应用开发者、想给自己小工具加个智能问答能力的独立开发者,以及需要给团队定一套密钥管理规范的技术负责人。基础门槛不高,会写几行 Python、能看懂命令行走路就行;至于踩坑经验,那部分你照着抄能省下不少时间。
1. 先搞清楚 API Key 到底是什么东西
1.1 它本质上是一张带额度的门禁卡
把 API Key 理解成一张门禁卡,比理解成"密码"要准确得多。密码证明你是谁,门禁卡除了证明身份,还绑定了你能进哪些门、一天能进几次、卡里还剩多少钱。那串sk-开头的字符串就是这样一个复合凭证:平台靠它识别调用方身份,靠它统计用量,靠它扣费,也靠它在发现异常时第一时间冻结。这也解释了一个新手常有的困惑——为什么它看起来那么长那么随机?因为里面并不是明文存着账号信息,而是一个高熵的随机标识,服务端拿它去查对应的账户和配额,字符串本身不携带任何可推导的信息。
安全上有个很重要的推论:这张卡一旦泄露,别人不需要知道你的账号密码就能花你的钱。真实项目里出过太多类似事故,最典型的是把带 Key 的代码推到公开仓库,几分钟内就被自动化脚本扫到并疯狂调用,等到账单出来才发现。所以后面我会花整整一章讲怎么存放它,这不是洁癖,是省钱的必要动作。反过来说,只要你把 Key 当成"能直接消费的现金"来对待,绝大多数安全事故都能提前避免,剩下的就是工程习惯问题了。
1.2 Key、Token、模型名:三个最容易混淆的概念
新手配置环境时最常撞墙的地方,就是把这三个词混着用。它们完全是不同层级的东西,混了就会写出"把模型名填进密钥字段"这类哭笑不得的代码。用一个表格对比最清楚:
| 概念 | 是什么 | 长什么样 | 用在哪里 |
|---|---|---|---|
| API Key | 身份与计费凭证 | sk-开头的一长串随机字符 | 请求头Authorization: Bearer sk-xxx |
| Model | 你要调用的具体模型 | 类似xxx-chat-latest的模型标识 | 请求体里的model字段 |
| Token | 计费的最小单位 | 不是字符串,是个数量 | 由服务端根据你的输入输出自动统计 |
Token 这层尤其需要讲透。它不是"一个字一个 token",而是模型分词器切出来的片段。英文里常见词大概是 1 个词 1 个 token,生僻词或长数字会被切成好几段;中文大致 1 个汉字对应 1 到 1.5 个 token,标点、空格、换行都算。之所以要关心这个,是因为账单按 token 结算,上下文长度上限也按 token 算。你写提示词时多塞两千字的背景资料,成本是实打实往上涨的,而且会挤占模型能记住的对话空间。很多"模型怎么忘了我前面说啥"的问题,本质是上下文超了,而不是模型笨。
1.3 一个 Key 能干什么:能力边界与常见误用
拿到 Key 之后能干的事其实相当集中,绝大多数人最终落地的场景无非这几类:给产品加一个智能客服或问答入口、做文本摘要与信息抽取、批量做内容分类和打标、代码补全或解释、把非结构化文本转成结构化 JSON、做多轮对话式的助手工具。这些场景的共性是"输入一段自然语言,输出一段自然语言或结构化数据",只要你的需求能塞进这个框架,用 API 就是划算的;如果需求本质是精确计算、查数据库、做严格的规则判断,那还是老老实实写代码,让模型去调你的函数,而不是让它自己去算。
关于误用,我见过最典型的有三个。第一个是把 Key 写进前端代码,浏览器一按 F12 就全暴露了,正确做法是前端调你自己的后端,后端拿着 Key 去调模型接口。第二个是拿它当搜索引擎使,问实时性很强的问题,结果得到一堆看起来很像但完全不对的答案,这类需求得配合检索来做,先把资料捞出来再让模型总结。第三个是不设任何额度上限就上线,某天流量突然涨了十倍,账单也跟着涨十倍。这三条我在不同项目里都亲眼见过,代价都不小。
2. 获取 Key 的完整流程与前置准备
2.1 注册与账号准备:需要提前想清楚的三件事
动手之前,有三件事最好先想明白,否则中途会反复返工。第一是用途定位:这是个人学习测试用,还是要挂在线上产品上?前者一个账号一个 Key 足够,后者要考虑多人协作、按环境隔离、单独核算成本,账号结构最好一开始就规划好,别等三个人共用一把 Key 再回头拆。第二是费用承受度:准备用多少预算试水,这个数字直接决定你后面设多少限额,我一般建议新手第一次先设一个很小的额度,跑通流程之后再逐步放开。第三是是否要发票或对公结算,如果需要,账号的注册主体和付款方式就得提前对齐,事后改起来很麻烦。
准备好一个能收验证信息的邮箱,加上一个平台支持的付款方式,基本就齐了。这里提醒一句:不要用公司公共邮箱注册个人测试账号,也不要拿个人账号跑公司的生产流量,这两件事在后期做权限梳理和成本分摊时会变成巨大的麻烦。我踩过一次,几个项目的用量全混在一个账号里,月底想把成本按项目拆开,只能靠时间戳去猜,最后不了了之。
2.2 控制台里的实际操作路径
不同服务商的界面细节有差异,但主干路径高度一致,基本是这四步,你照着找菜单就行:
- 登录后进入开发者控制台,找到 API 相关的管理入口,通常在账号设置或开发工具分类下。
- 先完成账号的验证与付款方式绑定,很多平台在未绑定前不允许创建可用的 Key,或者创建的 Key 只能调用极少量额度。
- 进入密钥管理页面,点击创建新密钥。创建弹窗里一般会让你填一个名字,这个名字一定要认真填,比如
local-dev-2024、prod-server-a,它是你日后辨认和吊销的唯一线索。 - 复制生成的完整密钥。注意,绝大多数平台只在创建成功的那一刻完整展示一次,关掉弹窗之后就只剩前几位和后几位的掩码,找不回来,只能重新建一个。
这里有个实操细节值得单独说:创建完立刻把 Key 存进你的密码管理器或者项目专用的密钥文件里,别指望"我等会儿再复制"。我见过好几次手快点了关闭,然后愣在原地,最后只能删掉重建,虽然不致命但很打断节奏。另外,界面上的权限选项(如果有)要看清,能只读就别给写权限,能限定模型就别给全模型,最小权限原则在这个环节是免费的安全收益。
2.3 拿到 Key 之后的第一件事不是写代码
很多人拿到 Key 的第一反应是赶紧跑个 demo,我建议先做另一件事:去后台把用量限额设好。绝大多数平台都提供软限额和硬限额两个层次,软限额到点会发提醒,硬限额到点直接拒绝请求。新手最稳妥的设置是:先用一个很小的软限额(比如几美元的量级)把流程跑通,确认代码没问题、用量符合预期,再往上调。这样即使代码里有个死循环在疯狂重试,损失也被关在笼子里。
设置限额这件事还有个隐藏好处:它会强迫你建立一个"成本意识"。当你看到一次测试调用消耗了多少 token、折合多少钱,你对"提示词写长一点"这件事的判断会完全不一样。我在早期项目里就是因为没设限额,调试一个 prompt 时忘了加max_tokens,模型一路输出到上限才停,单次调用成本是正常情况的十几倍,跑了几十次才发现。这种学费交一次就够了。
3. 把 Key 安全地放进项目里
3.1 环境变量:最省事也最靠谱的做法
环境变量是所有方案里性价比最高的:配置简单、不侵入代码、天然和代码仓库分离。做法就是在本机设置一个变量,代码通过读取变量名来拿值,源码里永远不出现明文。下面这几种写法按你的操作系统选一种:
# Linux / macOS,临时生效(只对当前终端会话有效) export OPENAI_API_KEY="sk-你的密钥" # Linux / macOS,永久生效,写进 shell 配置文件 echo 'export OPENAI_API_KEY="sk-你的密钥"' >> ~/.zshrc source ~/.zshrc # Windows PowerShell,当前会话 $env:OPENAI_API_KEY="sk-你的密钥" # Windows PowerShell,永久写入用户环境变量 [Environment]::SetEnvironmentVariable("OPENAI_API_KEY","sk-你的密钥","User")在项目里更推荐的做法是用.env文件配合加载库,好处是每个项目一套配置,互不干扰,也方便后来接手的人照葫芦画瓢:
# .env 文件内容(这个文件绝对不能提交到版本库) OPENAI_API_KEY=sk-你的密钥 OPENAI_BASE_URL=https://你的服务商接口地址/v1 OPENAI_MODEL=你的模型标识注意:
.env一定要写进.gitignore。这句话看起来像废话,但我确实在不止一个公开仓库里见过.env文件被老老实实提交上去的情况。加一条忽略规则成本是零,不加的代价可能是四位数以上的账单。
3.2 配置文件与密钥管理工具怎么选
环境变量够用,但不是所有场景都够用。单人小项目,环境变量加.env就是天花板,不用再折腾。多人协作的中型项目,我更推荐引入一个密钥管理服务或者至少是团队共享的加密配置中心,理由很简单:人一多,.env就会在每个人的机器上分散成好几份副本,改一个值要通知一圈,还容易有人用着过期配置排查半天。集中管理之后,权限、轮换、审计都有地方落。
如果暂时上不了专业工具,可以用一个折中方案:本地开发用.env,服务器上用系统级环境变量或容器编排平台的配置注入功能,两边的变量名保持一致,代码完全不感知差异。再加一条硬规矩——任何情况下密钥都不进入代码、不进入镜像、不进入日志。日志这条特别容易被忽略,我见过有人为了排查问题把整个请求对象打出来,请求头里的 Authorization 跟着进了日志系统,等于把钥匙贴在公告栏上。做个脱敏函数,把所有匹配sk-开头的字符串替换成掩码,几行代码的事。
3.3 团队协作下的 Key 分配策略
一个人用一把 Key,团队用一套策略。我目前用得最顺的做法是按"环境 + 用途"两个维度切分:开发、测试、生产各一把,生产里再按服务模块拆开,比如对话服务和批处理任务分开算。这样做有三个好处——出问题能立刻定位到是哪一块的流量异常,某一把泄露了只吊销那一把不影响全局,成本报表天然按模块分开,月底不用猜。代价是要多花点时间在后台建 Key 和贴标签,但这个投入在第一次做成本复盘时就能回本。
轮换节奏上,我的习惯是固定周期换一次,加上"任何人员变动或疑似泄露立即换"。轮换这件事最怕的是没预案,换 Key 的时候发现有一堆服务写死了旧值,得一个个改。解法是把 Key 的读取全部收敛到一个配置入口,代码里只有一处去拿这个值,轮换时改一处就够了。这条规矩我在项目启动时就会写进 README,比事后救火舒服太多。
4. 测试代码:从跑通第一行到封装成可复用模块
4.1 最小可用示例:先确认能通
心理上先降低预期:第一个目标不是"做出功能",而是"确认链路通"。装好依赖,跑通一次最简单的调用,看到返回内容就算成功,别的都不要管。
pip install openai python-dotenv# test_connection.py import os from dotenv import load_dotenv from openai import OpenAI load_dotenv() # 从 .env 读取配置 client = OpenAI( api_key=os.environ["OPENAI_API_KEY"], base_url=os.environ.get("OPENAI_BASE_URL"), # 用默认服务可删掉这行 ) resp = client.chat.completions.create( model=os.environ["OPENAI_MODEL"], messages=[ {"role": "system", "content": "你是一个简洁的助手,回答控制在两句话以内。"}, {"role": "user", "content": "用一句话解释什么是 API Key。"}, ], temperature=0.3, max_tokens=200, ) print(resp.choices[0].message.content) print("本次用量:", resp.usage)跑通之后你会看到两样东西:模型返回的文本,以及usage里的输入 token 数、输出 token 数和总数。这个 usage 字段是你后面做成本核算的唯一可靠数据源,比任何估算都准,养成每次都看一眼的习惯。
不想装库的话,用 curl 也能验证,排查问题时特别有用,因为它能排除掉 SDK 层的一切干扰:
curl https://你的服务商接口地址/v1/chat/completions \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $OPENAI_API_KEY" \ -d '{ "model": "你的模型标识", "messages": [{"role": "user", "content": "你好,请回复两个字:收到"}], "max_tokens": 20 }'如果 curl 能通而 Python 不通,问题基本锁定在环境变量没读到或者 SDK 参数写错;如果 curl 也不通,那就是 Key 本身、网络链路或接口地址的问题,排查范围一下就缩小了。
4.2 参数怎么调:几个关键旋钮的取值逻辑
参数不用全调,但下面这几个必须知道自己在调什么,否则输出质量会像抽奖:
| 参数 | 作用 | 常用取值 | 调整逻辑 |
|---|---|---|---|
temperature | 控制随机性 | 0.0 ~ 0.3 严谨任务;0.7 ~ 1.0 创意任务 | 要稳定可复现就压低,要发散就抬高 |
max_tokens | 限制输出长度 | 按场景给,别留空 | 这是成本闸门,必须设 |
top_p | 另一种采样控制 | 0.9 ~ 1.0 | 和 temperature 二选一调,别同时大改 |
frequency_penalty | 抑制重复用词 | 0 ~ 0.5 | 发现模型车轱辘话时加一点 |
stream | 流式返回 | True/False | 面向用户交互时开,批处理时关 |
temperature是新手最爱乱调也最容易调坏的一个。做信息抽取、分类、结构化输出这类任务时,我基本固定在 0 到 0.2 之间,因为你要的是确定性,不是文采。做文案、起名、头脑风暴时再放到 0.8 以上。中间那段 0.4 到 0.6 反而用得少,属于"既不够稳也不够野"的尴尬区间。max_tokens则是另一个极端,很多人图省事不填,结果一次跑飞就烧掉不少额度,填上它相当于给每次调用装了个保险丝。
4.3 加一层重试与超时:生产环境的必备动作
demo 能跑通和能上线之间,差的是对异常的处理。真实环境里超时、限流、偶发的服务端错误都是常态,裸调接口的代码遇到这些就整条链路崩掉。加一层带退避的重试,成本很低,收益很高:
# robust_client.py import os import time import random from dotenv import load_dotenv from openai import OpenAI, APIError, APITimeoutError, RateLimitError load_dotenv() client = OpenAI( api_key=os.environ["OPENAI_API_KEY"], base_url=os.environ.get("OPENAI_BASE_URL"), timeout=30.0, # 单次请求超时 max_retries=0, # 关掉 SDK 自带重试,用自己的策略 ) def chat(messages, model=None, max_retries=4, **kwargs): model = model or os.environ["OPENAI_MODEL"] for attempt in range(max_retries): try: return client.chat.completions.create( model=model, messages=messages, **kwargs, ) except RateLimitError: # 被限流:拉长等待,指数退避加抖动 wait = min(2 ** attempt, 20) + random.uniform(0, 1) print(f"[限流] 第 {attempt + 1} 次重试,等待 {wait:.1f}s") time.sleep(wait) except APITimeoutError: wait = 1 + random.uniform(0, 1) print(f"[超时] 第 {attempt + 1} 次重试") time.sleep(wait) except APIError as e: # 5xx 才重试,4xx 一般是请求本身有问题,重试没意义 if getattr(e, "status_code", 500) >= 500: time.sleep(min(2 ** attempt, 10)) continue raise raise RuntimeError("重试次数用尽,请检查日志") if __name__ == "__main__": r = chat( [{"role": "user", "content": "写一句十二字的项目庆祝标语"}], temperature=0.8, max_tokens=64, ) print(r.choices[0].message.content) print(r.usage)这段代码里有两个设计意图值得说明。一是指数退避加随机抖动,等待时间逐次翻倍并叠加一个随机量,避免多个客户端在同一时刻集体重试把服务端再次打爆。二是区分错误类型,5xx 和服务端限流值得重试,4xx 里的参数错误、认证失败重试一万次也没用,只会浪费时间。这两条是我在真实事故里总结出来的,比任何文档都深刻。
4.4 用流式输出把体验拉起来
只要你的产品是给人看的,流式几乎是必须的。非流式要等模型把整段话生成完才一次性返回,长回答下用户会盯着空白屏幕等好几秒;流式则是边生成边吐字,首字延迟通常在一秒以内,感知上的差别巨大。实现上只是多一个循环:
def chat_stream(messages, **kwargs): stream = client.chat.completions.create( model=os.environ["OPENAI_MODEL"], messages=messages, stream=True, **kwargs, ) for chunk in stream: delta = chunk.choices[0].delta if delta and delta.content: yield delta.content # 使用 for piece in chat_stream([{"role": "user", "content": "列三个学习编程的建议"}]): print(piece, end="", flush=True)有两个坑得提前说。第一,流式模式下拿不到完整的 usage,很多服务商需要在请求里额外加一个参数才会在最后一个数据块里返回用量统计,做成本核算时要记得打开。第二,流式一旦开始输出,中途出错就没法整体回滚,前端要准备好"生成中断"的兜底状态,别让用户对着一句说了一半的话发呆。
5. 费用、配额与用量计算
5.1 Token 怎么估:一个能手动算的粗算法
估算方法有两个层次。精确的做法是用分词工具把文本切一遍数出来,但日常开发中没必要,粗估足以做决策。粗估的经验值是:英文按每 4 个字符约 1 个 token 算,中文按每个汉字 1 到 1.5 个 token 算(保守起见按 1.5),标点、空格、代码里的符号都要算进去。一段 500 字的中文提示词,大概就是 750 个 token 的量级。
真正容易算漏的是输出 token。很多人只盯着自己写了多少字,忘了模型回多少字也要钱,而且输出单价通常比输入贵好几倍。所以估算一次调用的成本,公式应该是输入 token 乘输入单价,加上输出 token 乘输出单价,再加个百分之十几的余量。举例说明,假设某模型输入每百万 token 收 0.5 个单位货币、输出每百万 token 收 1.5 个单位货币(这里只是演示用的示例数值,实际单价以你所用服务商页面为准),一次调用输入 800 token、输出 400 token,成本约为 0.0004 加 0.0006 等于 0.001 个单位货币。单次看起来微不足道,但如果你的应用每天被调用十万次,这个数字就得重新掂量了。
5.2 账单和预估对不上:三个常见原因
第一,系统提示词的成本你算漏了。每一轮对话都要把系统提示词重新发一遍,多轮对话里这部分成本是线性累加的,对话轮次越多,重复计费的部分越多。第二,上下文是累积的,第五轮对话时你实际发送的是前四轮的完整历史加上新一轮提问,输入 token 是滚雪球式增长,很多人按单轮估算结果只有实际的三分之一。第三,重试也是要花钱的,超时重试的请求如果已经生成了部分内容,有些情况下仍然计费,日志里那些频繁重试的接口往往是成本黑洞。
对策也很直接:把usage字段落库,按调用维度记录输入输出 token,每天汇总一次,和后台账单对一遍。差得离谱就去查是不是有重复调用或者死循环重试;差得不多就是估算模型太粗,校准一下系数就行。我在一个批处理任务里就是靠这个对账习惯发现了一个隐蔽 bug——某段代码在异常分支里会重复提交请求,单看日志一切正常,只有把 usage 汇总起来才看出请求数是预期的两倍多。
5.3 限流与配额:429 到底在说什么
新手看到 429 就慌,其实它至少有两种完全不同的含义,处理方式也完全相反。一种是速率超限,短时间内请求太密集,触发每分钟请求数或 token 数的限制,处理方式是降速、排队、加退避重试,等一会儿就好。另一种是额度耗尽,账户余额或者配额用完了,这时候重试一万次也没用,得去后台充值或提额。区分方法很简单:看错误信息里的具体描述,以及去后台看余额和用量面板。
还有一种更隐蔽的情况是并发数限制,单看每分钟总量没超,但同时打出去的请求太多被拒。解法是给你的调用加一个信号量做并发控制,或者干脆改成队列消费的模式,用削峰的方式把瞬时压力摊平。这三种情况我都遇到过,最有教育意义的一次是凌晨跑批处理,代码里开了 50 个线程同时调接口,前一分钟还正常,第二分钟开始大面积 429,以为是额度没了,查了半天才发现是并发太猛。加了并发上限之后,同样的任务耗时长了一点,但一次都没再报错。
6. 常见问题与排查实录
6.1 认证与请求类错误速查表
下面这张表是我自己排查时用的,遇到报错先对号入座,能省掉大量瞎猜的时间:
| 错误特征 | 大概率原因 | 处理方式 |
|---|---|---|
| 401 认证失败 | Key 拼错、多余空格、已吊销 | 重新复制,注意首尾空白,去后台确认 Key 状态 |
| 403 无权限 | Key 权限范围不含该模型或接口 | 检查 Key 的权限配置,或换一把权限更完整的 |
| 404 模型不存在 | 模型名写错,或该账号无权访问 | 核对模型标识,去后台看可用模型列表 |
| 400 上下文超长 | 输入加历史超过模型上限 | 裁剪历史、做摘要、分批处理 |
| 429 请求过密 | 触发速率或并发限制 | 加退避重试、控制并发数 |
| 429 额度不足 | 余额或配额耗尽 | 去后台充值或提额,重试无用 |
| 5xx 服务异常 | 服务端临时故障 | 指数退避重试,超过阈值走降级 |
提示:401 里有一个特别阴的坑——从网页复制 Key 时末尾带了一个不可见字符或者换行。肉眼完全看不出来,代码里也只报认证失败。排查时把 Key 的字符长度打印出来对一下,或者用
strip()处理一遍,能省掉半小时的抓狂。
6.2 我踩过的几个坑
第一个坑是环境变量没生效。在终端里export了,代码里读不到,折腾半天才发现 IDE 是从它自己的进程环境启动的,根本不继承你刚在那个终端里设的变量。后来我统一改成用.env文件加载,这个问题再没出现过。第二个坑是虚拟环境装错包,同一个项目里系统 Python 和虚拟环境的版本不一致,import openai一会儿成功一会儿失败,查了半小时才发现是跑错解释器。
第三个坑最有代表性:为了省事,我把模型标识直接硬编码在十几处代码里,后来服务商调整了可用模型,我得挨个文件改。那次之后我把模型名、接口地址、超时时间全部收进配置文件,代码只认变量名。这件事的教训不是"要写配置文件"这么简单,而是凡是会变的东西都别写死在业务逻辑里,模型会变、价格会变、接口地址也可能变,唯一稳定的是你的配置层。
6.3 上线前的一张自检清单
代码在本机能跑通,离能上线还有一段距离。我每次上线前会过一遍这张清单,五条里通常能查出两三条问题:
- 密钥是否只存在于服务端:全局搜一遍代码库,确认没有硬编码的
sk-字符串,前端产物里更不能有。 .env和本地配置是否在忽略列表里:git status看一眼,确认没有被误加进暂存区。- 限额和
max_tokens是否都设了:两个都要有,前者防总量失控,后者防单次跑飞。 - 超时、重试、降级是否都写了:模型服务不可用时,你的功能是报错还是优雅退化,这个得提前决定。
- 用量是否落库:没有用量数据就没法做成本复盘,也没法发现异常调用。
这五条里,我认为最重要的是第一条和第五条的组合。密钥管好了不会破财,用量记好了才知道钱花在哪。剩下的三条属于工程卫生,做一次之后基本一劳永逸。顺便说一句,如果你打算把 Key 分享给同事临时用一下,别直接发聊天消息,走后台新建一把临时 Key,用完就吊销,这个习惯在团队里推广开之后,密钥相关的沟通成本会降很多。
6.4 一个容易被忽略的细节:Key 的生命周期管理
很多人建了 Key 之后就再也没管过,直到出问题才想起来。我建议给每个 Key 记一笔账:创建时间、用途、负责人、上次轮换时间、是否还在用。这份清单不需要什么专业工具,一个表格就够,但它的价值在人员变动时体现得淋漓尽致——某个人离职了,他建的那几把 Key 如果不及时吊销,就变成了无人看管的敞口。
轮换这件事听起来麻烦,做起来其实五分钟:后台新建一把 Key,把配置层的值换掉,重启服务确认正常,然后吊销旧的。关键前提还是前面说的那条,Key 的读取必须收敛到一处,否则轮换就变成了一场全项目搜索。我现在的习惯是每个季度固定做一次,顺便清理一批不再使用的 Key,做完之后后台的列表干净得让人心情舒畅,排查问题时也不会再对着一堆看不懂名字的 Key 发愣。