OpenAI API 使用教程:快速接入 AI 能力
大家好,我是小李,一个双非院校的大二学生,现在在一家本地的小厂做兼职后端开发。虽然学校一般,但靠着自学+死磕,在组里已经干了快两年了(没错,大一暑假就进来了)。我们团队人不多,6个人撑起一个百万 DAU 的工具类产品,日常就是和爬虫、数据库、产品经理斗智斗勇。
我平时写代码基本只用 Vim(别问,问就是信仰),IDE?那是什么?能吃吗?上周五晚上 11 点,我正缩在工位啃泡面改 Bug,突然被组长 @ 了:“小李,你不是对性能优化感兴趣嘛?明天上线前得把简历解析功能加上,用 OpenAI API 做语义提取,不然 HR 那边没法筛人——这可是咱们‘智能求职助手’的核心模块!”
我当时差点把键盘砸了:又要加需求?离 deadline 只有 24 小时了啊!
但转念一想:OpenAI API 我一直想玩,正好借机上手,说不定还能写进简历里(毕竟明年秋招就得投了)……于是,这篇血泪教程就这么诞生了。
为什么我们需要 OpenAI API?
先说说背景。我们产品最近搞了个新功能叫“AI 求职雷达”——用户上传 PDF 简历,系统自动提取姓名、电话、工作经历、技能栈等字段,然后匹配合适的岗位。以前靠正则 + 规则引擎硬刚,结果准确率惨不忍睹,HR 天天在群里咆哮:“这个人的‘Python’技能怎么没识别出来?他写了整整三行啊!”
产品经理拍板:上大模型!
但公司没 GPU 服务器,也没算法团队,自己训模型?做梦吧。于是 OpenAI 成了唯一选择——开箱即用、文档齐全、响应快(至少理论上是这样)。
不过我得吐槽一句:OpenAI 官方文档虽然全,但全是英文,而且例子太“干净”,根本不像我们这种破烂数据能跑通的样子。 实际项目里,简历可能是扫描件 OCR 后的乱码,可能是 Word 转 PDF 产生的格式错乱,甚至有人直接拿手机拍张照片上传……这种 dirty data,GPT-3.5 能扛住吗?
第一步:注册 & 获取 Key(别翻车)
这步其实很简单,但新手容易踩坑。
- 去 https://platform.openai.com 注册账号(需要手机号 + 非中国 IP,懂的都懂)
- 进入 API Keys 页面,Create new secret key
- 立刻复制保存! 因为页面关掉就再也看不到了(别问我怎么知道的,上周刚丢过一次)
⚠️ 安全提醒:千万别把 key 提交到 Git!我们组有个实习生上周把 key commit 上了 GitHub,结果半夜收到账单 $800,被运维追着满楼跑……
建议用 .env 文件管理:
# .env
OPENAI_API_KEY=sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
然后用 Python 的 python-dotenv 加载:
from dotenv import load_dotenv
import os
load_dotenv()
api_key = os.getenv("OPENAI_API_KEY")
第二步:调通第一个请求(Hello, GPT!)
最简单的测试方式是用 curl(Vim 党表示命令行万岁):
curl https://api.openai.com/v1/chat/completions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-d '{
"model": "gpt-3.5-turbo",
"messages": [{"role": "user", "content": "你好,能帮我提取简历信息吗?"}]
}'
如果返回一段 JSON,里面有 "content" 字段,恭喜你!API 调通了。
但别高兴太早——免费额度只有 $5,超了就扣信用卡。所以我们必须精打细算,尤其不能让爬虫流量直接打到 OpenAI(后面会讲怎么防)。
第三步:设计 Prompt —— 决定成败的关键
很多人以为调 API 就是扔文本进去等结果,大错特错! 在我们这个场景里,Prompt 设计直接决定了提取准确率。
最初我写的 prompt 是这样的:
请从以下文本中提取姓名、电话、邮箱、工作经历。
结果 GPT 给我返回了一堆幻想内容:“姓名:张三(假设)”,“电话:138****8888(示例)”……它居然在编造数据!
后来我痛定思痛,重写了 system prompt,强制它只输出结构化 JSON,并禁止任何解释:
你是一个专业的简历信息提取器。请严格从输入文本中提取以下字段:
- name(字符串)
- phone(字符串)
- email(字符串)
- work_experience(列表,每个元素为 {company, position, duration})
要求:
1. 只输出合法的 JSON,不要任何额外文字
2. 如果字段不存在,设为 null
3. 不要猜测或编造信息
4. 工作经历必须按时间倒序排列
输入文本:
"""
{resume_text}
"""
效果立竿见影!准确率从 60% 提升到 92%(我们在 500 份真实简历上测试过)。
📌 小技巧:用 triple quotes 包裹输入文本,避免特殊字符破坏 JSON 结构。
第四步:集成到现有系统(别让爬虫搞崩你)
我们产品有个隐藏问题:每天有大量爬虫来刷接口。之前没做限制,结果某天凌晨 API 调用量暴增 10 倍,差点触发 OpenAI 的速率限制(rate limit)。
所以我在接入层加了三层防护:
- 用户认证:必须登录才能上传简历
- 频率限制:每人每天最多 5 次
- 缓存机制:相同简历哈希值直接返回缓存结果
核心代码(用 Flask 写的):
from flask_limiter import Limiter
from werkzeug.security import gen_salt
limiter = Limiter(app, key_func=lambda: g.user_id)
@app.route('/parse-resume', methods=['POST'])
@limiter.limit("5 per day")
def parse_resume():
resume_text = request.json['text']
resume_hash = hashlib.md5(resume_text.encode()).hexdigest()
# 先查缓存
cached = redis.get(f"resume:{resume_hash}")
if cached:
return jsonify(json.loads(cached))
# 调 OpenAI
response = openai.ChatCompletion.create(
model="gpt-3.5-turbo",
messages=[
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": resume_text}
],
temperature=0.0 # 关闭随机性,保证结果稳定
)
result = response.choices[0].message['content']
# 存缓存,有效期 7 天
redis.setex(f"resume:{resume_hash}", 7*24*3600, result)
return jsonify(json.loads(result))
注意 temperature=0.0 —— 这个参数控制生成的随机性。做信息提取必须设为 0,否则同一篇简历每次结果都不一样,HR 会疯的。
性能与成本优化:省下的都是利润
作为性能优化爱好者,我当然不能忍受“每次请求都调 OpenAI”。于是做了两件事:
1. 批量处理(Batching)
OpenAI 支持一次发多条消息(虽然 Chat Completions API 本身不支持真正的 batch,但我们可以模拟):
def batch_parse(resumes):
results = []
for text in resumes:
# 构造单条请求
msg = [{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": text}]
results.append(msg)
# 并发发送(用 asyncio 或线程池)
实测:并发 10 个请求,总耗时从 12s 降到 3.5s。
2. 模型降级策略
我们对比了几个模型的成本和效果:
| 模型 | 输入价格 ($/1K tokens) | 输出价格 ($/1K tokens) | 准确率 | 响应时间 |
|---|---|---|---|---|
| gpt-4 | 0.03 | 0.06 | 96% | 2.1s |
| gpt-3.5-turbo | 0.0015 | 0.002 | 92% | 0.8s |
| gpt-3.5-turbo-16k | 0.003 | 0.004 | 93% | 1.0s |
结论:gpt-3.5-turbo 完全够用,价格只有 gpt-4 的 1/20。除非用户上传的是 100 页的博士论文(这时候才切到 16k 版本)。
💡 小知识:1 token ≈ 0.75 个英文单词。一份普通简历约 500 tokens,调一次 cost ≈ $0.0015。一天 1 万次也就 $15,老板勉强能接受。
遇到的坑 & 解决方案
坑 1:JSON 格式错误
GPT 有时候会输出带注释的 JSON,或者漏掉引号:
{
name: "张三", // 错!key 必须加引号
phone: null
}
导致 json.loads() 直接崩溃。解决方案:用 ast.literal_eval 或正则清洗,但更稳的方式是 让 GPT 自己校验。
我在 prompt 末尾加了一句:
请确保输出是严格合法的 JSON,可用 Python json.loads() 解析。
效果显著。
坑 2:中文编码乱码
某些简历从 PDF 提取后变成 \u4e2d\u6587 这种 Unicode 转义。解决方法是在发送前 decode:
import codecs
clean_text = codecs.decode(resume_text, 'unicode_escape')
坑 3:上下文长度超限
gpt-3.5-turbo 最大 4096 tokens。有用户上传了 20 年工作经验的简历,直接截断。我们加了预处理:
if num_tokens > 3500: # 留 500 tokens 给 prompt
# 用滑动窗口分段提取,再 merge
segments = split_into_chunks(resume_text, max_tokens=3000)
results = [call_openai(seg) for seg in segments]
final_result = merge_results(results)
虽然麻烦,但比返回半截数据强。
效果如何?真的能帮到求职者吗?
上线两周后,数据来了:
- 简历解析成功率:91.7%
- HR 人工修正率:从 78% 降到 22%
- 用户平均使用时长 +35%
最让我开心的是,有用户留言:“终于不用手动填几十个字段了,你们救了我的秋招!”
作为明年也要投简历的人,我深感这项功能的价值——技术不该只是炫技,而要解决真实痛点。
顺便说一句,我把这个项目写进了简历,上周刚拿到一家大厂的实习面试邀请(嘘,别声张)。
总结 & 心得
- Prompt 是灵魂:花 80% 时间调 prompt,20% 时间写代码。
- 别信默认参数:
temperature=0、max_tokens控制输出长度,这些细节决定稳定性。 - 成本意识很重要:小厂经不起乱花钱,能缓存就缓存,能降级就降级。
- 防御性编程:永远假设输入是垃圾,输出可能崩。
- AI 不是银弹:它只是工具,最终还是要结合业务逻辑(比如我们的缓存 + 限流策略)。
最后自嘲一下:作为一个双非学生,能用 OpenAI API 做出真正上线的功能,还挺自豪的。虽然代码可能不够优雅,但至少没让线上炸掉(笑)。
如果你也在折腾 AI 接入,或者正在为秋招准备项目,欢迎留言交流!说不定我们还能组队刷 LeetCode(或者一起骂产品经理)。
P.S. 本文所有代码已在内部 GitLab 开源(组内可见),外部同学可以参考思路。求 star 不如求 offer,秋招求捞!

评论 0