同一课的 .ipynb 与 .py 差在哪:微软生成式 AI 入门课第 6 课
第一次打开 generative-ai-for-beginners 的 06-text-generation-apps/python/ 目录,多半会愣一下:里面既有 oai-app.py、aoai-app-recipe.py 这种单文件脚本,也有 oai-assigment.ipynb、aoai-assignment.ipynb、githubmodels-assignment.ipynb 这种 notebook。名字里都带 oai / aoai / githubmodels 前缀,看上去像是同一份东西的两种打包方式。
不是。我把这一课的文件逐个读了一遍,两种形态装的内容不一样,停的位置也不一样,甚至同一段代码在两边写法都有出入。搞清楚这层分工,比纠结「用 Jupyter 还是用编辑器」有用得多。
顺带先说一个会浪费你五分钟的细节:OpenAI 路线那份 notebook 的文件名是 oai-assigment.ipynb,assignment 少了一个 n,另外两份 aoai-assignment.ipynb、githubmodels-assignment.ipynb 拼写是完整的。按 oai-assignment 去搜是搜不到的。
notebook 里装的是讲课路径
先看 oai-assigment.ipynb。它绝大多数 cell 是 markdown,真正的代码 cell 屈指可数,依次是:建虚拟环境并装依赖、第一版补全程序、把 prompt 换成菜谱、把菜谱数量和食材改成 input() 读取。
有两个特征值得注意。
第一,每个代码 cell 都是自足的:import os、from openai import OpenAI、load_dotenv()、构造 client、写 prompt、调 client.responses.create(...)、print(response.output_text),从头到尾重写一遍。它不是在前一个 cell 的变量上继续搭,而是把整个程序重贴一次、只改中间那一两行。落到阅读上的结果是:每个代码 cell 单独看都是一份完整程序,不需要回头拼上文。
第二,这些 notebook 里没有保存任何执行输出。预期结果不是以 cell 输出的形式存下来的,而是写在紧随其后的 markdown 里的 ```output 代码块中。也就是说,它是一份教学文档,不是一次运行的记录。这一点和仓库自己的说法对得上:00-course-setup/README.md 写明每一课的编码内容都配有 README,让你不运行任何东西也能看到代码和输出。
Windows 侧有一个坑要单独说。建环境那个代码 cell 里写的是 ! python -m venv venv、! source venv/bin/activate、! pip install openai,是 Unix 形式;Windows 的替代命令 venv\Scripts\activate 只出现在紧跟其后的 markdown NOTE 里,没有进代码 cell。而这个代码 cell 前面那段 markdown 里还有另一条 NOTE,写明如果你在 Codespaces 或 devcontainer 里跑这个 notebook,这一步本来就不需要。两条提示分列代码 cell 前后,只顾着点「运行」很容易两条都错过。
.py 里装的是终态和作业解答
再看同一目录下的脚本。以 OpenAI 路线为例,oai-app.py 对应 notebook 的第一版补全程序,oai-app-recipe.py 对应菜谱生成器,另外还有 oai-history-bot.py 和 oai-study-buddy.py。
后面这两个在 notebook 里根本不存在。它们对应的是章节 README 的 ## Assignment 一节列出的三个可选题目(改进菜谱生成器、做一个 study buddy、做一个 history bot),而 README 的 ## Solution 一节里只给了 prompt 文本,没给完整程序——完整程序在这两个 .py 里。所以 notebook 走到菜谱生成器就停了,脚本目录比它多出整整一段。
菜谱这条线上两边也没停在同一处。notebook 最后一个代码 cell 只做到「数量 + 食材由用户输入」;后续的「加过滤条件」和「用第一次的结果再生成一份购物清单」是写在最后那个 markdown cell 里的片段。而 oai-app-recipe.py 里,这两步是以完整代码的形式落在文件里的:先读 no_recipes、ingredients、filter 三个输入拼 prompt,拿到 response.output_text 存进 old_prompt_result,再把它连同 prompt_shopping 拼成 new_prompt 发第二次请求。两次调用都带了 max_output_tokens=600——这是仓库这个文件里当前写着的示例值,不是什么推荐配置。
一句话概括分工:notebook 是「一步一步长出来」的过程,.py 是每一步的终点快照,外加 notebook 没覆盖的作业解答。
两种形态之间已经存在的出入
这才是真正会咬到你的部分。同一课的两种形态并非机械同步,我核到几处白纸黑字的不一致,照着一边改另一边会出问题。
client 的构造方式不同。 notebook 里写的是 OpenAI(api_key = os.environ['OPENAI_API_KEY']),显式把 key 传进去;oai-app.py 里写的是裸的 OpenAI(),靠 SDK 自己去读环境变量。两种都在仓库里,但如果你从 notebook 复制客户端构造、又参考 .py 的 .env 用法,很容易搞不清 key 到底从哪进去的。
采样参数在两边是反的。 githubmodels-assignment.ipynb 的代码 cell 里,模型取自 os.environ.get("AZURE_INFERENCE_CHAT_MODEL", "Llama-3.3-70B-Instruct"),并且实打实往 client.complete(...) 里传了 temperature、max_tokens、top_p;cell 里的注释解释了原因——temperature/top_p 需要非 reasoning 模型,gpt-5 会拒绝,所以这里用 Llama 模型。而同目录的 githubmodels-app.py 里,model_name 直接写成字符串 "gpt-5-mini",client.complete(...) 的参数列表里一个采样参数都没有,只在末尾留了一行注释,写明 gpt-5 reasoning 模型不接受 temperature/top_p、让它们保持默认。
这不是笔误,是刻意的。章节 README 的「Improve your setup」一节写明:当前 Microsoft Foundry 上未废弃的模型是 reasoning 模型(GPT-5 家族、o 系列),它们不支持 temperature 与 top_p,也不支持 max_tokens(要改用 max_output_tokens),给 gpt-5-mini 传 temperature 会拿到「参数不支持」的报错。仓库根目录的 .env.copy 在 AZURE_OPENAI_DEPLOYMENT 下面重复了同一句提醒,并专门留了 AZURE_INFERENCE_CHAT_MODEL 这个变量给「支持采样参数的模型」用。
所以:从 notebook 抄采样参数、往 .py 的模型上套,是这一课最容易踩的一步。 上面提到的模型名与参数值都只是仓库文件里当前写着的示例值,随课程更新会变,别当成推荐配置照搬。
校验函数有两份。 aoai-app-recipe.py 在文件里内联定义了 get_required_env、validate_number_input、validate_text_input 三个函数,同名的实现在 shared/python/env_utils.py 与 shared/python/input_validation.py 里也有一份,而 tests/ 目录下的 test_env_utils.py、test_input_validation.py 覆盖的是 shared 那一份,不是章节内联的这份。你要把这一课的输入校验搬进自己项目,需要注意这层区别:被测试覆盖的是 shared 那一份,章节内联的那三个函数没有对应测试。tests/test_input_validation.py 就是按 shared.python.input_validation 这个模块路径 import 的,你要复用同一份实现,import 的形态是:
from shared.python.input_validation import validate_text_input
以上为按仓库代码中的接口语义组合的示例,未经实测,以仓库最新代码为准。
注释存在漂移。 oai-app-recipe.py 与 oai-study-buddy.py 顶部的注释写着 # configure Azure OpenAI service client,但下面构造的其实是普通的 OpenAI(),走的是 OpenAI 路线而不是 Azure。另外,oai-app.py 那个「从前有一条不开心的美人鱼」的补全示例,它的预期输出被以注释形式留在了文件末尾;oai-study-buddy.py 与 oai-history-bot.py 的末尾也原样带着这两行,可它们的 prompt 早已不是那个补全题了。读的时候按代码为准,别按注释为准。
三条 provider 路线别混着看
这一课的每种形态都按 provider 分了三份,前缀分别是 oai-(OpenAI)、aoai-(Azure OpenAI)、githubmodels-,总说明在 00-course-setup/03-providers.md。前两条的差别很直白:aoai-app.py 用 AZURE_OPENAI_API_KEY 加 AZURE_OPENAI_ENDPOINT 拼出 .../openai/v1/ 作为 base_url,deployment 名从 AZURE_OPENAI_DEPLOYMENT 读;oai-app.py 什么都不拼。
第三条要特别说明:githubmodels- 这个前缀已经和它实际接入的目标脱节了。仓库在多处逐字写明,GitHub Models 连同它的 GITHUB_TOKEN 变量已于 2026 年 7 月底退役,直接替代者是 Microsoft Foundry Models——00-course-setup/02-setup-local.md、00-course-setup/03-providers.md、00-course-setup/README.md、.env.copy 都写了,githubmodels-app.py 文件头的注释里也写了。这个时间点现在已经过去。所以这条路线现在实际要配的是 Microsoft Foundry Models 的 endpoint 与 key,也就是脚本里读的 AZURE_INFERENCE_ENDPOINT 与 AZURE_INFERENCE_CREDENTIAL,不要再照着 GITHUB_TOKEN 去配。选路线时,第三条应该理解为「Microsoft Foundry Models(原 GitHub Models 路线)」。
顺带一提,githubmodels-assignment.ipynb 的 cell 数比另外两份少,而且中间夹着一个内容为空的代码 cell。这个空 cell 里没有任何代码,仓库里也没有对它的说明,它不影响这条路线两个代码 cell 本身的内容。
从你的处境倒推该点开哪个
- 第一次学这一课:开 notebook。它的价值在四个代码 cell 之间那些 markdown,程序为什么从这一版变成下一版,只有那里写了。在 Codespaces 或 devcontainer 里,建环境那个 cell 可以跳过;Windows 本地跑,记得用 markdown NOTE 里那条
venv\Scripts\activate,而不是代码 cell 里的source那条。 - 要把成果搬进自己项目:直接读
.py。同一目录下的requirements.txt把openai与python-dotenv钉到了具体版本,脚本本身没有叙事、没有中间态,复制过去就是完整程序。校验逻辑记得走shared/python/那份。 - 做作业或找参考解答:只有
.py有。oai-history-bot.py/oai-study-buddy.py(以及对应的aoai-版)是 README 里## Assignment那三个题目的落地形态,README 的## Solution一节只给 prompt。 - 要做代码评审或纳入版本控制:
.ipynb本身是 JSON 容器,改一行代码在文件里体现为 JSON 结构的变化,而.py就是纯文本。这是通用工程做法层面的差别,不是这个课程仓的官方建议。 - 要试 temperature:先确认你打算调用哪个模型,再决定看哪一份文件。这一点上 notebook 和
.py给的是两套不同前提:前者预设你换了支持采样参数的模型,后者预设你用的是 reasoning 模型。按 README 的说法,把前者的参数发给后者会拿到「参数不支持」的报错。
有几个维度我没有依据,就不比:两种形态哪种学习效果更好、哪种上手更快、运行表现有无差异——仓库里没有这类结论,我们也没有跑过其中任何一份代码。这篇要说的只有一件事:它们停在不同的位置,装的内容不重合,选之前先知道自己缺的是过程还是终态。
本文依据 github.com/microsoft/generative-ai-for-beginners 仓库于 2026-08-18 的公开内容整理,
事实来自仓库内的课程正文与代码示例。我们没有跑过文中涉及的代码,
因此不涉及运行结果、耗时与生成质量的任何描述。
该课程持续更新,文中涉及的文件路径、依赖与接口写法随版本变动,请以仓库最新内容为准。
文中涉及的云端服务调用会产生费用并可能上传数据,请自行评估密钥与数据边界。
本文对照的是同一项目内的两种用法,依据均为上述仓库内容,不对两种用法做优劣排名, 选型结论只在仓库文档写明的能力边界内成立。
安全与合规相关做法请结合自身环境评估,本文不构成安全方案建议。