ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

《OpenAI库基础学习总结:Client 初始化、流式输出 delta 拼接与多轮历史 messages 全流程拆解》

《OpenAI库基础学习总结:Client 初始化、流式输出 delta 拼接与多轮历史 messages 全流程拆解》 目录一、模块定位与学习目标二、第 01 集openai 库的基础使用与最小调用2.1 整体就三步拿客户端、调模型、处理结果2.2 第一步导入并创建客户端对象2.3 第二步调用模型——chat.completions.create2.4 messages 的三种角色分别是干什么的2.5 第三步处理结果——choices[0].message.content2.6 openai SDK 常用参数速查表2.7 openai SDK 1.x 与 0.x 写法对比三、第 02 集streamTrue 流式输出模式3.1 什么是流式输出3.2 开启流式就两步3.3 为什么是 delta 而不是 message3.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三种角色到使用streamTrue逐块拼接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取出回答当把streamTrue打开时返回值从一个完整对象变成需要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_urlhttps://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( modelqwen3-max, messages[ {role: system, content: 你是一个Python编程专家并且不说废话简单回答}, {role: assistant, content: 好的我是编程专家并且话不多你要问什么}, {role: user, content: 输出1~10的数字使用Python代码}, ] )这里面两个核心参数model告诉服务端用哪个模型。课程选的是百炼平台上常用的qwen3-max通义千问 Max。messages提供给模型的消息也是最重要的参数。课程特别带着大家数了一遍它的结构——它是一个list列表列表里每个元素又是一个dict字典每个字典有两个 keyrole角色和content内容。正因为它是列表里面的元素可以非常多这就为第 03 集的多轮历史消息埋下了伏笔。2.4 messages 的三种角色分别是干什么的这是第 01 集的重中之重课程逐字讲了三种角色的分工role 取值中文称呼作用课程中的示例 contentsystem系统角色设定助手的整体行为、人设和规则也就是告诉 AI你是个什么东西、该做什么、按什么规矩做你是一个Python编程专家并且不说废话简单回答assistant助手角色代表 AI 助手的回答可以由我们在代码里人为预设一段它已经说过的话好的我是编程专家并且话不多你要问什么user用户角色代表用户真正发出的问题、指令或需求是对模型的具体提问输出1~10的数字使用Python代码课程对assistant角色有一个特别容易被忽略的点AI 的回答表面上看是模型生成的但我们其实可以在代码里人为替它说一句话。上面示例里那句好的我是编程专家……并不是模型这次真的回了而是我们手动设定的、假装 AI 已经说过的回复。理解这一点后面第 03 集把历史对话塞回 messages 时就不会困惑了。另外一个非常实用的省钱技巧也来自system角色课程在跑通后专门点评——AI 往往会说一大堆字但字越多消耗的 tokens也就是免费额度就越多。所以通常会用system角色叮嘱它不说废话、简单回答、只把最核心的告诉我以此节省 token 额度。2.5 第三步处理结果——choices[0].message.contentcreate执行完会返回一个叫ChatCompletion的对象本质上是一个类 JSON 结构里面信息很多有id、用了哪个模型、当前消耗了多少 token 等等。但我们最关心的还是模型给出的回答它藏得比较深print(response.choices[0].message.content)课程带着大家一层层数模型的回答放在choices这个列表里取0号下标再往里是messagemessage里才有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里最常用的几个参数一并整理出来方便实战查阅参数作用课程是否演示常用取值 / 说明model指定调用的模型名是qwen3-max、qwen-plus等以百炼平台模型名为准messages消息列表承载角色与内容是[{role:..., content:...}, ...]stream是否开启流式输出第 02 集演示True/False默认temperature控制输出随机性越高越发散、越低越稳定否常用参数一般0~2写代码/做分类取小值如 0.2创作可取 0.7~1.0max_tokens限制模型单次最多生成的 token 数否常用参数用于控制回复长度、避免输出过长烧额度api_key/base_url客户端级配置是在OpenAI()初始化时传入而非 create 时2.7 openai SDK 1.x 与 0.x 写法对比网上的老教程还存在不少 0.x 时代写法容易让新手照抄后直接报错。下面把两种写法的关键差异放在一起方便拿到资料时先判断新旧对比项1.x 新写法推荐0.x 旧写法易踩坑导入方式from openai import OpenAIimport 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 集streamTrue 流式输出模式3.1 什么是流式输出大家在网页端跟大模型聊天时都有体会回复不是啪地一下整段蹦出来而是像水流一样一个字一个字往外吐。这种输出方式就叫流式输出。课程指出用 openai 库写代码也能复刻这种效果从而获得更好的交互体验。否则如果不开流式程序会长时间卡住没反应等全部内容生成完才一次性打印出来体验很差。3.2 开启流式就两步课程把开启流式总结得非常干脆——两步第一步在create调用里加一个参数streamTrue开启流式输出。第二步拿到response之后不要再像非流式那样直接.message.content而是用for循环去遍历response本身response client.chat.completions.create( modelqwen3-max, messages[ {role: system, content: 你是一个Python编程专家话非常多}, {role: user, content: 讲一讲 for 循环的用法}, ], streamTrue, # 第一步开启流式 ) 第二步for 循环逐块读取 for chunk in response: print(chunk.choices[0].delta.content, end , flushTrue)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流式出来的每一小段都换一行看起来会支离破碎。把结尾符改成空格就能让各段之间用空格连起来读着更连贯。flushTrue在某些系统里输出可能会先放进缓冲区、不会立刻显示。加上flushTrue表示立刻刷新缓冲区这样才能真正看到文字像流水一样实时蹦出来。3.5 流式 vs 非流式对比对比维度非流式stream 默认 False流式streamTrue返回类型一个完整的ChatCompletion对象一个可迭代的流逐段产出 chunk取内容方式response.choices[0].message.content一次取全for chunk in response:里取chunk.choices[0].delta.content用户体验长时间无响应最后整段一次性出现文字逐字逐句实时蹦出体验接近网页聊天是否需要拼接不需要直接就是完整字符串需要按 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( modelqwen3-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_urlhttps://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( modelqwen3-max, messagesmessages, # 每次都把完整历史全量回传 streamTrue, ) answer for chunk in stream: piece chunk.choices[0].delta.content or # 兜底 None answer piece print(piece, end, flushTrue) # 打字机效果 print() messages.append({role: assistant, content: answer}) # 把AI回答也存进历史 chat(小明有两条狗) chat(小红有三只猫) chat(总共有几个宠物呢) # 凭借历史上下文模型答出五个这段脚本把三集要点全部收编用base_url切到通义千问、用列表维护 messages 历史、每轮全量回传、用streamTrue加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、或者抄错路径是最常见的调不通原因。流式与非流式返回类型完全不同streamFalse时response是一个完整对象直接.choices[0].message.contentstreamTrue时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 会一段一行、支离破碎某些终端不写flushTrue会看不到实时效果。历史消息过长要截断全量回传会让 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两个 keysystem定人设规则、assistant代表或预设AI 回复、user是用户提问。怎么用 openai python sdk 实现流式输出答create时传streamTrue再for chunk in response:逐块取chunk.choices[0].delta.content用print(..., end, flushTrue)实时打印并按顺序拼接成完整文本。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)发起请求、用streamTrue配合delta.content实现流式输出以及多轮对话靠messages列表全量回传历史。理解这四条后续提示词工程、RAG 和 LangChain 都能落回同一个底层逻辑。官方文档OpenAI Chat Completions API Reference查看chat.completions.create的完整参数与返回字段。openai-python GitHub 仓库SDK 源码、安装方式与版本说明。阿里云百炼 Model Studio 文档通义千问模型列表、OpenAI 兼容地址与示例代码。推荐阅读LangChain 官方文档继续了解ChatOpenAI、消息抽象与记忆机制。Ollama 官方网站回顾本地模型部署与调用方式。本文开头的相关链接进一步对照课程后续的提示词工程、MCP 与多模态学习笔记。
返回列表