目录
- 一、模块定位与学习目标
- 二、第 01 集:openai 库的基础使用与最小调用
- 2.1 整体就三步:拿客户端、调模型、处理结果
- 2.2 第一步:导入并创建客户端对象
- 2.3 第二步:调用模型——chat.completions.create
- 2.4 messages 的三种角色分别是干什么的
- 2.5 第三步:处理结果——choices[0].message.content
- 2.6 openai SDK 常用参数速查表
- 2.7 openai SDK 1.x 与 0.x 写法对比
- 三、第 02 集:stream=True 流式输出模式
- 3.1 什么是流式输出
- 3.2 开启流式就两步
- 3.3 为什么是 delta 而不是 message
- 3.4 end 和 flush 两个 print 小细节
- 3.5 流式 vs 非流式对比
- 四、第 03 集:附带历史消息的多轮对话
- 4.1 为什么 messages 是列表就支持多轮
- 4.2 没有历史消息会怎样
- 4.3 system / user / assistant 在列表里怎么排
- 4.4 为什么要全量回传,以及 token 累积成本
- 4.5 课程点出的局限:内存里的一次性历史
- 4.6 把三集串起来:一个最小可用的多轮流式脚本
- 五、踩坑与环境注意事项
- 六、与前后模块的衔接
- 6.1 学习路线图
- 七、面试与实战常考点
- 八、总结与参考资料
- 官方文档
- 推荐阅读
摘要:本文梳理了黑马程序员《大模型 RAG 与 Agent 智能体项目实战教程》中「OpenAI 库基础」三集内容:从通过OpenAI(api_key, base_url)创建客户端、围绕messages列表组织system/assistant/user三种角色,到使用stream=True逐块拼接delta.content实现流式输出,再到将历史对话全量回传、把模型回答追加进列表实现多轮记忆。文章同时总结了 token 累积成本、环境配置注意事项和面试常见考点,帮助读者打通网页聊天式输出的最小闭环。
相关链接
李沐深度学习191集课程全解析:模块拆解、学习路径-CSDN博客
吴恩达《面向开发者的提示词工程》-CSDN博客
吴恩达 MCP 教程(Model Context Protocol)(一)-CSDN博客
多模态大模型教程学习笔记 — ViT · CLIP · SAM · GLIP · Stable Diffusion
一句话总结:OpenAI 这个 Python SDK 的用法可以浓缩成三件事——用
OpenAI(api_key, base_url)建好客户端、用client.chat.completions.create(model=..., messages=[...])发起对话、再用response.choices[0].message.content取出回答;当把stream=True打开时,返回值从一个完整对象变成需要for chunk in response逐块遍历的流,内容路径也从.message.content变成.delta.content;而所谓"多轮对话记忆",本质就是在messages这个列表里按system / user / assistant的顺序不断追加历史消息、每次请求都把整段历史全量回传给模型。
本模块是黑马程序员《大模型 RAG 与 Agent 智能体项目实战教程》里"OpenAI 库基础"部分,共 3 集:第 01 集讲 openai 库的基础使用与最小调用流程,第 02 集讲流式输出模式,第 03 集讲附带历史消息调用模型。下面按照"模块定位 → 逐集拆解 → 踩坑与环境 → 前后衔接 → 面试考点"的顺序,把这三集的真实讲法和代码完整梳理一遍。
一、模块定位与学习目标
在进入本模块之前,课程已经完成了"前置准备":开通阿里云百炼(通义千问)大模型服务、申请并通过环境变量保护 API Key、部署 Ollama 并跑通本地蒸馏模型。也就是说,到这一模块时,你手里已经有了一把"钥匙"(API Key)和一个"门牌号"(模型接入地址),缺的只是一把"开门的工具"。
这把工具就是 openai 这个 Python SDK。课程里明确讲:openai 库是 OpenAI 官方推出的 Python 软件开发工具包,它的核心作用是让开发者不用自己手写 HTTP 请求、不用手动处理身份验证等底层细节,就能简单、高效地调用大模型能力。更关键的一点是——因为这个库发布得早、接口简单易用,现在绝大多数模型服务商(包括课程使用的阿里云百炼平台)都兼容了 OpenAI SDK 的调用协议。所以我们学的虽然叫"OpenAI 库",但通过修改base_url,它照样能正常调用阿里云上的通义千问模型,而不是真的去调 OpenAI 官方服务。
学完这 3 集,你应当能够独立做到:
用两行代码建好一个指向通义千问的客户端对象,并成功发起一次最小对话调用;
看懂
messages参数为什么是"字典组成的列表",以及system / assistant / user三种角色各自的分工;把一次性返回改造成网页聊天那种"一个字一个字往外蹦"的流式输出;
理解多轮对话是怎么靠"把历史消息塞进 messages 列表、每次全量回传"实现的,并知道这种写法在生产环境的局限。
记住这一模块的定位:它是后续 LangChain 的地基。后面 LangChain 里的ChatOpenAI,本质上就是对本模块这套 openai SDK 的再封装——你现在把裸 SDK 摸透了,将来再看 LangChain 就会觉得"不过是换了层皮"。
二、第 01 集:openai 库的基础使用与最小调用
2.1 整体就三步:拿客户端、调模型、处理结果
课程把 openai 库的使用浓缩成三个流程:
获取客户端对象(Client);
调用模型;
处理结果。
这三步是贯穿整个模块的主线,后面两集都是在"调模型"和"处理结果"这两步上做文章。
2.2 第一步:导入并创建客户端对象
先导包,再实例化OpenAI类:
from openai import OpenAI client = OpenAI( # api_key 一般通过环境变量注入,这里可不显式传 base_url="https://dashscope.aliyuncs.com/compatible-mode/v1" )创建客户端对象主要用到两个参数,课程对它们的强调程度完全不同:
api_key:你的密钥。课程反复强调不建议在代码里明文写死,而是把它封装到环境变量(变量名通常是OPENAI_API_KEY)里,由系统注入。这样做的直接好处是:一旦环境变量配置好,OpenAI()在不传api_key参数时会自动读取它,代码里就再也看不到明文密钥,避免误传到 Git 仓库。base_url:这是"非常重要"的参数,用来锁定模型服务商的 API 接入地址。课程里特意打开阿里云百炼平台,进入"模型服务 → 模型广场 → 任选一个模型(如通义千问 Max)→ 示例代码",把里面给出的 URL 复制粘贴回代码。它的意义在于:改了这个地址,这个库就不再去调 OpenAI 官方服务,而是转而调用阿里云的服务。地址一旦写错,就调不到云上模型了。
踩坑提示:
base_url不要自己瞎编,一定要以百炼平台"示例代码"里给的为准(通义千问的 OpenAI 兼容模式通常形如https://dashscope.aliyuncs.com/compatible-mode/v1,结尾的/v1不能漏)。
2.3 第二步:调用模型——chat.completions.create
拿到client之后,调用模型的链条比较长,课程里让大家"写熟了自然就记住":
response = client.chat.completions.create( model="qwen3-max", messages=[ {"role": "system", "content": "你是一个Python编程专家,并且不说废话,简单回答"}, {"role": "assistant", "content": "好的,我是编程专家,并且话不多,你要问什么"}, {"role": "user", "content": "输出1~10的数字,使用Python代码"}, ] )这里面两个核心参数:
model:告诉服务端用哪个模型。课程选的是百炼平台上常用的qwen3-max(通义千问 Max)。messages:提供给模型的消息,也是最重要的参数。课程特别带着大家数了一遍它的结构——它是一个list(列表),列表里每个元素又是一个dict(字典),每个字典有两个 key:role(角色)和content(内容)。正因为它是列表,里面的元素"可以非常多",这就为第 03 集的多轮历史消息埋下了伏笔。
2.4 messages 的三种角色分别是干什么的
这是第 01 集的重中之重,课程逐字讲了三种角色的分工:
role 取值 | 中文称呼 | 作用 | 课程中的示例 content |
|---|---|---|---|
| 系统角色 | 设定助手的整体行为、人设和规则,也就是告诉 AI"你是个什么东西、该做什么、按什么规矩做" | "你是一个Python编程专家,并且不说废话,简单回答" |
| 助手角色 | 代表 AI 助手的回答;可以由我们在代码里人为预设一段"它已经说过的话" | "好的,我是编程专家,并且话不多,你要问什么" |
| 用户角色 | 代表用户真正发出的问题、指令或需求,是对模型的具体提问 | "输出1~10的数字,使用Python代码" |
课程对assistant角色有一个特别容易被忽略的点:AI 的回答表面上看是模型生成的,但我们其实可以在代码里人为"替它说一句话"。上面示例里那句"好的,我是编程专家……"并不是模型这次真的回了,而是我们手动设定的、假装 AI 已经说过的回复。理解这一点,后面第 03 集把历史对话塞回 messages 时就不会困惑了。
另外一个非常实用的"省钱技巧"也来自system角色:课程在跑通后专门点评——AI 往往会说一大堆字,但字越多消耗的 tokens(也就是免费额度)就越多。所以通常会用system角色叮嘱它"不说废话、简单回答、只把最核心的告诉我",以此节省 token 额度。
2.5 第三步:处理结果——choices[0].message.content
create执行完会返回一个叫ChatCompletion的对象,本质上是一个类 JSON 结构,里面信息很多:有id、用了哪个模型、当前消耗了多少 token 等等。但我们最关心的还是模型给出的回答,它藏得比较深:
print(response.choices[0].message.content)课程带着大家一层层数:模型的回答放在choices这个列表里,取0号下标,再往里是message,message里才有content。这条链式调用response.choices[0].message.content就是取出 AI 回复的标准路径。运行之后,AI 果然用一段简洁的 for 循环打印了 1~10 的数字,验证了"system 设定人设 + user 发起提问"这条链路是通的。
除了回答正文,这个ChatCompletion对象里还带着一份很有价值的"账单":response.usage字段会告诉你这次请求一共消耗了多少 token——包括输入用了多少(prompt_tokens)、输出用了多少(completion_tokens)、合计多少(total_tokens)。课程虽然没展开讲这个字段,但它正是理解"为什么对话越长越贵"的钥匙:你每发一次请求,输入侧都要把整段 messages 算进 prompt_tokens。学会去读response.usage,就能在调试时精准地知道每一轮花了多少额度,而不是凭感觉猜。
2.6 openai SDK 常用参数速查表
除了课程现场用到的model和messages,下面把chat.completions.create里最常用的几个参数一并整理出来,方便实战查阅:
参数 | 作用 | 课程是否演示 | 常用取值 / 说明 |
|---|---|---|---|
| 指定调用的模型名 | 是 |
|
| 消息列表,承载角色与内容 | 是 |
|
| 是否开启流式输出 | 第 02 集演示 |
|
| 控制输出随机性,越高越发散、越低越稳定 | 否(常用参数) | 一般 |
| 限制模型单次最多生成的 token 数 | 否(常用参数) | 用于控制回复长度、避免输出过长烧额度 |
| 客户端级配置 | 是 | 在 |
2.7 openai SDK 1.x 与 0.x 写法对比
网上的老教程还存在不少 0.x 时代写法,容易让新手照抄后直接报错。下面把两种写法的关键差异放在一起,方便拿到资料时先判断新旧:
| 对比项 | 1.x 新写法(推荐) | 0.x 旧写法(易踩坑) |
|---|---|---|
| 导入方式 | from openai import OpenAI | import openai |
| 创建客户端 | client = OpenAI(api_key=..., base_url=...) | 通常直接传api_key,没有独立 client 对象 |
| 调用模型 | client.chat.completions.create(...) | openai.ChatCompletion.create(...) |
| 获取回复路径 | response.choices[0].message.content | 同样走response.choices[0].message.content,但整体返回结构略有差异 |
| 密钥注入方式 | 推荐依赖环境变量,由OpenAI()自动读取 | 旧教程里更常见在代码中手动写api_key |
三、第 02 集:stream=True 流式输出模式
3.1 什么是流式输出
大家在网页端跟大模型聊天时都有体会:回复不是"啪"地一下整段蹦出来,而是像水流一样一个字一个字往外吐。这种输出方式就叫流式输出。课程指出,用 openai 库写代码也能复刻这种效果,从而获得更好的交互体验。否则,如果不开流式,程序会长时间"卡住没反应",等全部内容生成完才一次性打印出来,体验很差。
3.2 开启流式就两步
课程把开启流式总结得非常干脆——两步:
第一步:在create调用里加一个参数stream=True,开启流式输出。
第二步:拿到response之后,不要再像非流式那样直接.message.content,而是用for循环去遍历response本身:
response = client.chat.completions.create( model="qwen3-max", messages=[ {"role": "system", "content": "你是一个Python编程专家,话非常多"}, {"role": "user", "content": "讲一讲 for 循环的用法"}, ], stream=True, # 第一步:开启流式 ) 第二步:for 循环逐块读取 for chunk in response: print(chunk.choices[0].delta.content, end=" ", flush=True)3.3 为什么是 delta 而不是 message
这是流式这一集最容易踩的坑。课程对比了两条取内容的路径:
非流式:
response.choices[0].message.content;流式:循环里每个临时变量(课程里取名叫
chunk)走的是chunk.choices[0].delta.content。
注意后半段从.message变成了.delta。因为流式模式下,服务端不会一次给你完整消息,而是把回答切成一小段一小段(chunk)推送,每一段里只有"这次新增的那一点内容",这个增量就放在delta里。所以chunk.choices[0].delta.content取到的是每一个小分段的文本,把它们按顺序拼接起来,就是完整回答。
课程还为了让流式效果明显,特意把 system 人设从"不说废话"改成了"话非常多",这样跑起来能看到文字噼里啪啦往外蹦。但紧接着它就郑重提醒:平时千万别让它话多,否则免费额度(token)会耗得很快——这和第 01 集"system 省钱"的思路是一脉相承的。
3.4 end 和 flush 两个 print 小细节
课程在print里额外加了两个参数,都是为了显示效果:
end=" ":print 默认每段结束会换行(\n),流式出来的每一小段都换一行,看起来会支离破碎。把结尾符改成空格,就能让各段之间用空格连起来,读着更连贯。flush=True:在某些系统里,输出可能会先放进缓冲区、不会立刻显示。加上flush=True表示立刻刷新缓冲区,这样才能真正看到文字像流水一样实时蹦出来。
3.5 流式 vs 非流式对比
对比维度 | 非流式(stream 默认 False) | 流式(stream=True) |
|---|---|---|
返回类型 | 一个完整的 | 一个可迭代的流,逐段产出 chunk |
取内容方式 |
|
|
用户体验 | 长时间无响应,最后整段一次性出现 | 文字逐字逐句实时蹦出,体验接近网页聊天 |
是否需要拼接 | 不需要,直接就是完整字符串 | 需要,按 chunk 顺序把 delta.content 累加起来才是全文 |
典型用途 | 短回答、后端批量处理、对结果做后处理 | 对话式前端、打字机效果、需要尽早展示首字 |
实战补充:如果你的程序不仅要"打印"还要"留存全文",就在循环里维护一个字符串,把每段
delta.content拼上去:full += chunk.choices[0].delta.content or ""(注意最后一个 chunk 的content可能是None,要用or ""兜一下)。
四、第 03 集:附带历史消息的多轮对话
4.1 为什么 messages 是列表就支持多轮
第 01 集已经埋下伏笔:messages是一个 list,既然是列表,里面就能塞很多字典。第 03 集就是把这个特性用起来——把历史对话一条一条填进列表,让模型在回答最新问题时"看得见"前面的上下文。
课程用了一个特别直观的"算宠物"案例:
response = client.chat.completions.create( model="qwen3-max", messages=[ {"role": "system", "content": "你是一个AI助理,回答很简洁"}, {"role": "user", "content": "小明有两条狗"}, {"role": "assistant", "content": "好的"}, {"role": "user", "content": "小红有三只猫"}, {"role": "assistant", "content": "好的"}, {"role": "user", "content": "总共有几个宠物呢"}, ] ) print(response.choices[0].message.content) 输出:总共五个宠物4.2 没有历史消息会怎样
课程用一句很形象的话点出了多轮的意义:如果不附带历史,你直接问 AI"总共有几个宠物",AI 肯定"一脸懵"——它根本不知道你在说小明的狗还是小红的猫。但当你把前面这一整串对话一次性全部提供给它,最后再抛出"总共有几个宠物呢",它就能算出来:2 条狗 + 3 只猫 = 5 个宠物。这说明模型确实读到了历史消息里的上下文。
这里要特别理解一个机制:大模型本身是"无状态"的。它每次调用都不知道上一次聊了什么,所谓"记忆"完全是靠我们每次都把历史 messages 重新喂给它实现的。这就是为什么多轮对话要"把历史每次全量回传"。
4.3 system / user / assistant 在列表里怎么排
观察上面的列表结构,能总结出多轮消息的排列规律:
通常第一条是
system,定下整段对话的人设和规则(课程里是"你是一个AI助理,回答很简洁",简洁同样是为了省 token);之后严格按照真实对话顺序,
user和assistant交替出现:用户说一句、助手回一句、用户再说一句……;列表最后一条,一定是当前这一轮最新的
user提问,模型就是冲着它来生成新回答的;历史里那些
assistant回复(哪怕是我们手动填的"好的"),作用是告诉模型"之前你是这么答的",从而把对话的来龙去脉交代清楚。
4.4 为什么要全量回传,以及 token 累积成本
因为模型无状态,所以每一轮新对话,都必须把"system + 全部历史 user/assistant + 最新 user"整段重新发一遍。这带来一个必须正视的代价——token 是累积的:
第 1 轮可能只发几十 token;
第 10 轮时,你要把前面 9 轮的对话全部再发一遍,输入 token 会随轮次线性增长;
对话越长,每次请求消耗的额度越多,直到撞上模型的上下文窗口上限。
这也是课程反复强调"system 里要让 AI 回答简洁"的根本原因:既是省输出 token,也是让历史消息别无限膨胀下去。
4.5 课程点出的局限:内存里的一次性历史
课程在结尾非常清醒地指出:我们现在这种写法,历史消息是一次性保存在内存里的——代码一跑完,这些消息就没了,下次运行又得从头再来。如果是生产系统,这种写法肯定不合适,应当把对话记录持久化到文件或数据库里,需要时再取出来拼进 messages。而"怎么优雅地管理短期记忆、长期记忆",正是后面学习 LangChain 时要重点解决的内容(课程预告了短期记忆与长期记忆)。
4.6 把三集串起来:一个最小可用的多轮流式脚本
把前三集的知识点合到一起,就得到一个既能流式打字机输出、又能维护多轮历史的最小骨架。它也是后面写 RAG、写 Agent 时最常复制粘贴的那段模板:
from openai import OpenAI client = OpenAI(base_url="https://dashscope.aliyuncs.com/compatible-mode/v1") 历史消息列表:开头放 system,之后按对话顺序追加 messages = [ {"role": "system", "content": "你是一个AI助理,回答简洁"}, ] def chat(user_input: str): messages.append({"role": "user", "content": user_input}) # 追加本轮用户提问 stream = client.chat.completions.create( model="qwen3-max", messages=messages, # 每次都把完整历史全量回传 stream=True, ) answer = "" for chunk in stream: piece = chunk.choices[0].delta.content or "" # 兜底 None answer += piece print(piece, end="", flush=True) # 打字机效果 print() messages.append({"role": "assistant", "content": answer}) # 把AI回答也存进历史 chat("小明有两条狗") chat("小红有三只猫") chat("总共有几个宠物呢") # 凭借历史上下文,模型答出五个这段脚本把三集要点全部收编:用base_url切到通义千问、用列表维护 messages 历史、每轮全量回传、用stream=True加delta.content做流式、再把生成的回答追加回列表充当下一轮的assistant历史。读懂它,本模块就算真正过关了。
五、踩坑与环境注意事项
把三集里散落的坑集中梳理一遍,都是实战高频会遇到的:
SDK 版本与新旧写法:课程用的是 openai 1.x 新 SDK 写法,即
from openai import OpenAI然后client.chat.completions.create(...)。网上很多老教程还是 0.x 时代的import openai; openai.ChatCompletion.create(api_key=..., messages=...)写法——两者在导入方式、客户端对象、返回结构上都不一样。如果你pip install openai装的是新版,却照抄旧版openai.ChatCompletion.create,会直接报错。课程这种"先建 client、再链式调 create"是新版标准姿势。API Key 不要明文写进代码:通过环境变量(
OPENAI_API_KEY)注入,既安全又能在实例化OpenAI()时自动读取。硬编码密钥一旦把代码传到公开仓库,等于把钱袋子拱手送人。base_url 指向兼容端点:用同一个 openai SDK 调通义千问,靠的就是把
base_url改成百炼的 OpenAI 兼容地址。同理,以后接自建模型、其他厂商的兼容服务,改的也只是base_url和model名,调用代码几乎不用动。地址漏了/v1、或者抄错路径,是最常见的"调不通"原因。流式与非流式返回类型完全不同:
stream=False时response是一个完整对象,直接.choices[0].message.content;stream=True时response变成可迭代流,必须for chunk in response,且取的是chunk.choices[0].delta.content。两者的取值路径不能混用——用流式时去取.message.content,或者非流式时去for循环,都会报错或拿到空值。流式最后一段 content 可能为 None:拼接全文时要用
chunk.choices[0].delta.content or ""兜底,否则会出现TypeError。print 的 end/flush:不写
end=" "会一段一行、支离破碎;某些终端不写flush=True会看不到实时效果。历史消息过长要截断:全量回传会让 token 越攒越多,逼近上下文窗口。生产里通常会做窗口裁剪——只保留最近 N 轮、或对早期历史做摘要压缩,这也是 LangChain 记忆机制要解决的问题之一。
temperature 别乱调:写代码、做抽取这类要求稳定准确的任务,把 temperature 调低(接近 0);开放式创作再调高。否则同样的输入会得到飘忽不定的输出,不利于调试。
六、与前后模块的衔接
向上承接(前置准备模块):前置准备那几集完成了云平台开通、API Key 申请、用环境变量保护 Key、以及 Ollama 本地模型的部署与调用。本模块正是把"钥匙"和"门牌号"正式用起来——把环境变量里的 Key 和百炼给的
base_url交给 openai SDK,才算真正"代码调通云端大模型"。向下衔接(提示词工程 → RAG → LangChain):本模块之后紧接着就是提示词工程(prompt 指南、零样本/少样本、金融文本分类与抽取)。你会发现提示词工程里对
system和user的精心设计,落到代码上就是在本模块这套messages列表里做文章。再往后进入 RAG 开发章节,LangChain 的ChatOpenAI调用大模型、LangChain 的流式输出、LangChain 的消息简写形式,本质上都是对本模块这套 openai SDK 的封装与简化——LangChain 帮你把client.chat.completions.create、messages 构造、历史记忆这些样板代码收纳成了更高级的抽象。理解了裸 SDK,再学 LangChain 就不会被它的"黑盒感"吓住。
6.1 学习路线图
把本模块放回整体课程链路中,学习路径可以按下面的顺序推进:
flowchart LR A[前置准备:开通百炼、申请 API Key、部署 Ollama] --> B[OpenAI 库基础三集] B --> C[提示词工程] C --> D[RAG 开发] D --> E[LangChain 与 Agent]- 本模块的关键任务:把前置准备中拿到的 API Key 和
base_url交给 openai SDK,跑通最小调用闭环。 - 下一步衔接:在
messages列表上继续展开提示词工程,再经 RAG 进入 LangChain 与 Agent。
七、面试与实战常考点
把这 3 集转化成可以直接应对面试和实战的几个问题:
openai 这个 Python SDK 调用大模型分几步?答:建客户端
OpenAI(api_key, base_url)→client.chat.completions.create(model, messages)→ 取response.choices[0].message.content。messages 参数的数据结构是什么?答:字典组成的列表,每个字典含
role与content两个 key;system定人设规则、assistant代表(或预设)AI 回复、user是用户提问。怎么用 openai python sdk 实现流式输出?答:
create时传stream=True,再for chunk in response:,逐块取chunk.choices[0].delta.content,用print(..., end="", flush=True)实时打印,并按顺序拼接成完整文本。chat.completions.create 多轮对话是怎么实现的?答:在 messages 列表里按对话顺序交替追加 user/assistant 消息,连同 system 一起,每轮请求都把完整历史全量回传;模型本身无状态,所谓"记忆"完全来自这份历史列表。
大模型历史消息 messages 全量回传有什么代价?答:输入 token 随轮次线性累积,既花钱又可能撑爆上下文窗口,所以要用 system 约束输出长度、并在长对话里做截断或摘要。
非流式和流式的返回值有何不同?答:前者是完整 ChatCompletion 对象,走
.choices[0].message.content;后者是 chunk 流,走chunk.choices[0].delta.content,必须循环遍历。为什么改个 base_url 就能用同一个 SDK 调通义千问?答:因为通义百炼兼容 OpenAI 的调用协议,SDK 只认"接入地址 + 密钥 + 模型名",地址指向谁就调谁。
temperature 和 max_tokens 分别怎么用?答:temperature 控制随机性,写代码、做抽取时调低以求稳定,开放创作时调高以求多样;max_tokens 限制单次输出长度,既能防止 AI 话痨烧额度,也能在长文场景里留出可控的成本上限。
怎么知道一次请求花了多少 token?答:非流式调用后读
response.usage,里面的prompt_tokens、completion_tokens、total_tokens就是这份"账单";多轮对话时它会随历史增长,是做成本监控的直接依据。
结语:OpenAI 库基础这 3 集,看似只是在教一个 SDK 的三板斧,但它实际上把"如何用代码驱动大模型"这件事的最小闭环讲透了——客户端怎么建、消息怎么组织、流式怎么开、多轮历史怎么喂。把这套原生写法练熟,后面无论是做提示词工程、搭 RAG,还是上 LangChain 写 Agent,你心里都有一份清晰的"底层地图",知道框架替你封装的到底是什么。
八、总结与参考资料
OpenAI 库基础三集的核心可以归纳为四条:用OpenAI(api_key, base_url)建客户端、用client.chat.completions.create(model, messages)发起请求、用stream=True配合delta.content实现流式输出,以及多轮对话靠messages列表全量回传历史。理解这四条,后续提示词工程、RAG 和 LangChain 都能落回同一个底层逻辑。
官方文档
- OpenAI Chat Completions API Reference:查看
chat.completions.create的完整参数与返回字段。 - openai-python GitHub 仓库:SDK 源码、安装方式与版本说明。
- 阿里云百炼 Model Studio 文档:通义千问模型列表、OpenAI 兼容地址与示例代码。
推荐阅读
- LangChain 官方文档:继续了解
ChatOpenAI、消息抽象与记忆机制。 - Ollama 官方网站:回顾本地模型部署与调用方式。
- 本文开头的相关链接:进一步对照课程后续的提示词工程、MCP 与多模态学习笔记。