微软生成式 AI 入门课接 Azure OpenAI:endpoint、部署名、版本

2026-08-18

你按 06-text-generation-apps/python/oai-app.py 跑通了 OpenAI 那条线,公司要求走 Azure,于是把同目录的 aoai-app.py 拿过来,把 key 换成 Azure 的,然后开始收 401 或者 404。这类报错最难受的地方在于:代码只有十几行,看不出哪里错,因为错的不是代码,是你往 .env 里填的四个值的语义。

这四个值在 .env.copy 里长得很像,都以 AZURE_OPENAI_ 开头,但它们分别对应 Azure 侧三个完全不同层级的东西:资源、部署、API 契约。搞混任何一个,报错都不会告诉你搞混的是哪一个。

前置条件:先确认这几件事

仓库根目录的 pyproject.toml 写的是 requires-python = ">=3.10",而 .python-version 里钉的是一个 3.12 的具体版本号(这是仓库当前文件里的值,随版本可能变动)。00-course-setup/02-setup-local.md 的 Prerequisites 表格给的口径是 Python 3.10 +。

依赖方面,第六课自己带了一份 06-text-generation-apps/python/requirements.txt,里面只有两行:openaipython-dotenv,都钉了具体版本。根目录那份 requirements.txt 是全课程的合集,装它也行,但如果你只想跑这一课,装课内那份更轻。

虚拟环境的激活方式,02-setup-local.md 的 Step 2 把两个平台分开写了,原样抄过来是:

python -m venv .venv          # make one
source .venv/bin/activate     # macOS / Linux
.\.venv\Scripts\activate      # Windows PowerShell

Windows 侧要注意,上面第三行是 PowerShell 的路径写法。仓库在讲创建 .env 文件时也单独给了 Windows 的写法,Unix 侧用 touch .env,Windows 侧给的是 cmd 下的 echo . > .env。至于把 .env.copy 复制成 .env00-course-setup/03-providers.md 给的命令是 cp .env.copy .env——这是 bash 写法,在 Git Bash 里可以直接用;在 PowerShell 或 cmd 里用什么等价命令,属于你自己环境的通用做法,不是该项目官方内容。

还有一件事必须提前知道:03-providers.md 的 provider 对比表里,Azure 那一行的 Comments 列写的是需要提前申请访问权限。也就是说这条路线不是注册完就能立刻用的。

四个变量,四个层级

.env.copy 里 Azure OpenAI 这一段的四个变量,03-providers.md 的 Populate 小节给了逐条说明,我把它们按「你实际要去哪里找这个值」重排一下:

变量名它指的是哪一层
AZURE_OPENAI_API_KEY资源级别的授权 key
AZURE_OPENAI_ENDPOINT资源级别的服务地址
AZURE_OPENAI_DEPLOYMENT文本生成模型的部署名
AZURE_OPENAI_EMBEDDINGS_DEPLOYMENT文本 embedding 模型的部署名

前两个是一对,03-providers.md 的「From Portal」小节写明:在 Azure Portal 侧栏点 Keys and Endpoint,点 Show Keys 之后会看到 KEY 1、KEY 2 和 Endpoint 三个值,KEY 1 填 AZURE_OPENAI_API_KEY,Endpoint 填 AZURE_OPENAI_ENDPOINT

后两个是另一对,得去 Microsoft Foundry portal 的 Deployments 标签页拿,而且拿的是部署名,不是模型名。

endpoint 填的是根地址,斜杠由代码来拼

.env.copy 里这一行的占位说明写的是资源 endpoint,形如 https://<resource-name>.openai.azure.com——这是仓库示例里给的形状,你自己的域名前缀不同。

关键在于:你填进去的是根地址,后面的路径由代码拼。06-text-generation-apps/python/aoai-app.py 整段构造客户端的代码是这样的:

client = OpenAI(
  api_key=os.environ['AZURE_OPENAI_API_KEY'],
  base_url=f"{os.environ['AZURE_OPENAI_ENDPOINT'].rstrip('/')}/openai/v1/",
  )

注意那个 .rstrip('/')。它存在的原因很实际:从 portal 复制出来的 endpoint 可能带尾斜杠,不剥掉就会拼出两个连续斜杠。shared/python/api_utils.py 里的 create_azure_openai_client() 用的是同一个写法,同样带 rstrip('/')。所以你不需要自己在 .env 里纠结要不要留尾斜杠,但也不要把 /openai/v1/ 提前拼进环境变量,那样会被拼两遍。

另外一个容易被忽略的点:这里用的类是 OpenAI,不是 AzureOpenAI。这一条和下面的版本坑位是同一件事的两面,待会儿一起说。

部署名不等于模型名

AZURE_OPENAI_DEPLOYMENT 是本篇最值得单独拎出来的一个。aoai-app.py 里它是这么用的:

deployment=os.environ['AZURE_OPENAI_DEPLOYMENT']
response = client.responses.create(model=deployment, input=prompt, store=False)

变量名叫 deployment,但传进去的参数名是 model=。在 Azure OpenAI 这条线上,model 这个参数位要填的是你的部署名,不是模型的官方名字。

03-providers.md 对这件事的原话意思是:部署名通常与模型名相同,除非你在部署时显式改过。这句话的重点在后半句——一旦你在 Foundry portal 里给部署起了个 chat-prod-01 之类的名字,那 .env 里就得填 chat-prod-01,填模型名会直接失败。这就是为什么同一份代码在 OpenAI 那条线上跑得好好的,切到 Azure 就 404:OpenAI 那边 model= 收的是模型名,Azure 这边收的是部署名,参数名却一模一样。

仓库文档在推荐部署哪个模型时,给的示例是一个文本生成模型加一个 text embedding 模型,并给出了对应的部署名示例值(这些只是仓库当前文档里的示例,模型目录会变)。

第四个变量 AZURE_OPENAI_EMBEDDINGS_DEPLOYMENT 的性质完全一样,只是指向另一个部署。03-providers.md 的变量说明表把它标为「text embeddings 模型的部署 endpoint」。它不是每课都用,仓库里实际读它的地方集中在做 embedding 与检索的那几课(第七课的 Azure 版作业 notebook、第八课,以及 RAG 那一课),例如 08-building-search-applications/scripts/transcript_enrich_embeddings.py。那个脚本读它的方式值得看一眼:

EMBEDDINGS_DEPLOYMENT_NAME = os.getenv(
    "AZURE_OPENAI_EMBEDDINGS_DEPLOYMENT", "text-embedding-ada-002"
)

用的是 os.getenv 带兜底默认值,而不是 aoai-app.py 里那种 os.environ[...] 的硬取。这个差别的后果是:AZURE_OPENAI_DEPLOYMENT 没配会直接抛 KeyError 当场停住,而 embeddings 这个没配则会静默地回退到脚本里写死的那个兜底名字,再拿这个名字去请求你的资源——而这个兜底名字与 03-providers.md 里推荐部署的那个 embedding 模型并不是同一个。这两处是仓库里白纸黑字并存的写法,把它们放在一起就能看出:embeddings 这一路的报错会比另一路来得更晚、更难定位。

第三个坑位:API 版本字段现在是什么处境

.env.copy 里还有第五行:AZURE_OPENAI_API_VERSION,注释写着 Default is set!,值是一个 GA 版本号(这是仓库当前文件里的默认值,随版本可能变动)。AGENTS.md 的环境变量清单里也列了它。

但你翻遍 aoai-app.py 会发现——它根本不读这个变量。

shared/python/api_utils.pycreate_azure_openai_client() 的 docstring 把原因写清楚了:这个客户端指向的是 Azure OpenAI 的 v1 endpoint(也就是 <endpoint>/openai/v1/),由于用的是 v1 endpoint,不需要 api_version

仓库里 .github/skills/azure-openai-to-responses/SKILL.md 这份 skill 文件把这条迁移路径讲得更直白。它的对照表里写明:azure_endpoint=... 变成 base_url=f"{endpoint.rstrip('/')}/openai/v1/"api_version="2024-..." 这一项则是整个移除。它的 Cleanup 步骤明确要求把 AZURE_OPENAI_API_VERSION.env 和基础设施配置里删掉。同一份文件还写明:AzureOpenAI / AsyncAzureOpenAI 构造器在 openai>=1.108.1 起被标为 deprecated。

于是仓库当前状态是两条路线并存的:aoai-app.pyshared/python/api_utils.pyOpenAI + v1 endpoint、不带 api_version;而 08-building-search-applications/scripts/transcript_enrich_embeddings.py 里仍然是 AzureOpenAI(api_key=..., azure_endpoint=..., api_version="...") 这种老写法,版本号还是硬编码在代码里的。这两处摆在一起就是当前仓库的实情:迁移没有全量铺完。值得一提的是,第六课那份 requirements.txt 钉的 openai 版本号,比 skill 文件里说的废弃起点还要早。

对你的实际影响是:.env 里那行 AZURE_OPENAI_API_VERSION 留着不会让 aoai-app.py 出错,因为它压根不读;但如果你照着某篇旧文章去改 aoai-app.py,把客户端换回 AzureOpenAI(...) 并传 api_version,那就是往回走。以仓库最新内容为准。

边界:这几件事仓库写明了不支持

  • GitHub Models 那条线不支持 Responses APISKILL.md 的模型兼容性小节明确写了这一点,并要求把相关代码路径移除,换到 Azure OpenAI、OpenAI 或兼容的本地 endpoint。所以 githubmodels-app.pyaoai-app.py 的配置不能互相套用。
  • AZURE_INFERENCE_ENDPOINT / AZURE_INFERENCE_CREDENTIAL 是另一条路线,对应的是 Microsoft Foundry Models 那个多 provider 目录,.env.copy 里把它单列成一段。别拿 Azure OpenAI 的 endpoint 去填它。03-providers.md 也写明 GitHub Models 将退役、由 Microsoft Foundry Models 取代。
  • 课程默认的对话模型是 reasoning 模型AGENTS.md 的 Model conventions 小节写明这类模型拒绝 temperaturetop_p,并且用 max_output_tokens 而不是 max_tokens。所以你在 aoai-app.py 里手加 temperature 是会被拒的,同一节还写明,演示 temperature 的示例走的是另一个 provider 变量 AZURE_INFERENCE_CHAT_MODEL
  • store=FalseSKILL.md 的迁移要求里写明每个请求都要设 store=Falseaoai-app.py 也确实带了这个参数。
  • 至于 endpoint 填错时具体返回哪个 HTTP 状态码,仓库里没有找到相关说明。

怎么验证配对了

最短的验证路径是 SKILL.md 里给的那段 smoke test,它原样就是一个最小请求示例(下面这段是仓库里的原文,其中的 max_output_tokens 取值只是示例值):

import os
from openai import OpenAI

client = OpenAI(
    api_key=os.environ["AZURE_OPENAI_API_KEY"],
    base_url=f"{os.environ['AZURE_OPENAI_ENDPOINT'].rstrip('/')}/openai/v1/",
)
resp = client.responses.create(
    model=os.environ["AZURE_OPENAI_DEPLOYMENT"],
    input="ping",
    max_output_tokens=50,
    store=False,
)
print(resp.output_text)

如果你更想直接跑课程示例,AGENTS.md 的 Running Python Examples 小节给的是进到课程目录后执行 python aoai-app.py

想在发请求之前先把变量本身查一遍,shared/python/env_utils.py 提供了两个现成函数:get_required_env(var_name, description) 缺值时抛 ValueError 并在消息里带上变量名,validate_env_vars(*var_names) 一次校验多个、把所有缺的变量名收集起来一起报。用它先兜一道,比等 API 报 401 更容易定位:

from shared.python.env_utils import validate_env_vars

env = validate_env_vars("AZURE_OPENAI_ENDPOINT", "AZURE_OPENAI_API_KEY", "AZURE_OPENAI_DEPLOYMENT")

以上为按仓库代码中的接口语义组合的示例,未经实测,以仓库最新代码为准。

仓库自己也是这么测的:tests/test_api_utils.pyTestCreateAzureOpenAIClient 那个类,用 monkeypatch 分别删掉 AZURE_OPENAI_ENDPOINTAZURE_OPENAI_API_KEY,断言 create_azure_openai_client() 抛出的 ValueError 消息里分别匹配到 endpoint 和 API key。你排查配置时可以照这个思路,一个一个变量地摘掉看报错换不换,比一次性怀疑四个值要快。

最后提醒一句关于 key 的:.env 在仓库里是被 gitignore 的,03-providers.md 特意点了这一点。但如果你把配置复制到别的目录、或者贴进 notebook 单元格里,这层保护就没有了。


本文依据 github.com/microsoft/generative-ai-for-beginners 仓库于 2026-08-18 的公开内容整理, 事实来自仓库内的课程正文与代码示例。我们没有跑过文中涉及的代码, 因此不涉及运行结果、耗时与生成质量的任何描述。 该课程持续更新,文中涉及的文件路径、依赖与接口写法随版本变动,请以仓库最新内容为准。 文中涉及的云端服务调用会产生费用并可能上传数据,请自行评估密钥与数据边界。

安全与合规相关做法请结合自身环境评估,本文不构成安全方案建议。

想系统学会用 AI?报名体系课或加入会员,照着学、照着用。