01-python-agent-framework.ipynb 拆解:微软 AI Agent 入门课

2026-08-18

打开 01-intro-to-ai-agents/code_samples/01-python-agent-framework.ipynb,你会发现它短得有点反直觉:装包、导入、定义一个函数、造一个 agent、跑两次。但这类”看着简单”的入门 notebook 恰恰最容易卡住——不是卡在概念,是卡在三处具体位置:client 到底吃哪几个参数、agent 是从哪个对象身上”变”出来的、run 为什么在同一份代码里出现了两种写法。这篇就对着这个文件把这三处拆开。

下面所有事实都来自 github.com/microsoft/ai-agents-for-beginners 仓库里的文件,我们没有跑过其中任何一段代码。该课程持续更新,以仓库最新内容为准。

一、先说前置条件,这一段跳过去必踩坑

仓库根目录的 AGENTS.md 在 Prerequisites 一节写明:Python 3.12 或更高、一个 Azure 订阅(用于 Microsoft Foundry)、安装并已登录的 Azure CLI(az login)。00-course-setup/README.md 里还专门补了一句:如果你没装 Python 3.12,先装,然后用 python3.12 建 venv,以保证从 requirements.txt 装到正确版本。

建虚拟环境两个平台不一样,仓库把两种都写了:

python -m venv venv
# zsh/bash
source venv/bin/activate
# Command Prompt for Windows
venv\Scripts\activate

.env 文件的创建同样分开写:

# zsh/bash
cp .env.example .env
# PowerShell
Copy-Item .env.example .env

还有一点值得先说:00-course-setup/README.md 提醒完整仓库连历史带文件都不小,如果你只想跑前两课,它给了浅克隆与稀疏检出两条命令,后者只把你点名的目录拉下来:

git clone --depth 1 --filter=blob:none --sparse https://github.com/<your-username>/ai-agents-for-beginners.git
git sparse-checkout set 00-course-setup 01-intro-to-ai-agents

注意 URL 里的 <your-username> 要换成你自己 fork 的地址;仓库同时提醒,如果之后还要别的课,再调整 sparse-checkout 的目录列表即可。

notebook 的 Setup 单元格列出的要求是:一个部署了 chat 模型的 Microsoft Foundry 项目、在终端里跑过 az login、以及两个环境变量 AZURE_AI_PROJECT_ENDPOINTAZURE_AI_MODEL_DEPLOYMENT_NAME.env.example 里给的模型名写的是 gpt-5-mini——这是仓库里的示例值,不是要求你必须用它,.env.example 的注释只要求用一个”未弃用、且支持 Responses API”的模型。

这里有一处需要你自己拿主意的不一致

notebook 的第一个代码单元格是:

%pip install agent-framework azure-ai-projects azure-identity -q

而仓库根目录的 requirements.txt 装的不是 agent-framework 这个 meta 包,而是把 agent-framework-coreagent-framework-foundryagent-framework-openai 三个子包分别写死在同一条次版本线上(core== 锁死,另两个用 ~= 约束;具体版本号以仓库当前的 requirements.txt 为准,它会随课程更新变动)。

注释里写明了这么做的理由(仓库文档自述):agent-framework 从 1.11.0 起引入了课程 notebook 用到的破坏性 API 变更——移除了 ChatMessageHostedWebSearchTool、改了 Message 构造函数、并且去掉了 Agent.run() 上的 model= 参数;而直接钉 agent-framework-core 不走 meta 包,是为了避免 [all] extras 拉进未钉版本的集成子包造成 pip 冲突。

也就是说:notebook 单元格里那句 %pip install agent-framework 会去装 meta 包且不带版本约束,和 requirements.txt 的做法不是一回事。这两处都是仓库里白纸黑字写着的,我们只把它们并排放在这里,至于你在自己的环境里选哪条路——先跑 pip install -r requirements.txt,还是直接执行 notebook 的那个单元格——请以你执行时仓库的最新内容为准。

二、client 初始化:三个参数,一个都不能少

01-python-agent-framework.ipynb 的第二个代码单元格做完了全部准备工作,值得逐行看:

import logging
logging.getLogger("agent_framework.foundry").setLevel(logging.ERROR)

import os
import dotenv
from agent_framework.foundry import FoundryChatClient
from azure.identity import AzureCliCredential
from agent_framework import tool

dotenv.load_dotenv(dotenv.find_dotenv())

endpoint = os.getenv("AZURE_AI_PROJECT_ENDPOINT")
model = os.getenv("AZURE_AI_MODEL_DEPLOYMENT_NAME")

if not endpoint or not model:
    raise ValueError(
        "Missing required environment variables. "
        "Please set AZURE_AI_PROJECT_ENDPOINT and AZURE_AI_MODEL_DEPLOYMENT_NAME in your .env file."
    )

provider = FoundryChatClient(
    project_endpoint=endpoint,
    model=model,
    credential=AzureCliCredential()
)

几处值得停一下的地方:

第一,导入路径分两处。FoundryChatClient 来自 agent_framework.foundry 子模块,tool 装饰器来自顶层 agent_framework。写 import 时把这两个搞混,是这一课最常见的低级错。

第二,dotenv.load_dotenv(dotenv.find_dotenv()) 用的是 find_dotenv() 定位文件,而不是写死一个路径。这一课的实际布局是:notebook 在 01-intro-to-ai-agents/code_samples/ 下,而 00-course-setup/README.md 让你把 .env 建在仓库根目录,两者不在同一个目录里。find_dotenv() 具体按什么规则找这个文件属于 python-dotenv 自己的行为,仓库里没有对此作说明,以该库的文档为准。

第三,那段 raise ValueError 不是装饰性的。它是这一课给你的第一道自检:环境变量没配好,代码在建立任何网络连接之前就先停下来,报错文案直接告诉你缺哪两个变量。

第四,FoundryChatClient 在这里接的是 project_endpointmodelcredential 三个关键字参数,凭据对象是 AzureCliCredential()——它复用你 az login 的会话,所以 .env 里不需要放任何 API key。00-course-setup/README.md 里对此的说法是:多数 notebook 通过 Azure CLI 登录来认证,用的是 azure-identity 包里的 AzureCliCredentialDefaultAzureCredential,因此不需要 API key。

第五,开头那句把 agent_framework.foundry 这个 logger 的级别调到 ERROR,作用范围仅限日志输出。它和功能无关,但你如果在调试连接问题、想看到更多日志,就得知道是这行把它压下去的。

三、agent 构造与运行入口

工具是一个普通 Python 函数加一个装饰器:

@tool(approval_mode="never_require")
def get_destinations() -> list[str]:
    """Get a list of popular vacation destinations."""

函数体返回的是一串写死的城市名,属于课程的占位数据。这里的关键是 approval_mode="never_require"——第 2 课的 notebook(02-explore-agentic-frameworks/code_samples/02-python-agent-framework.ipynb)用一句话解释了它:这个取值意味着工具会自动执行,不需要用户确认。它不是唯一取值,第 4 课 04-tool-use/code_samples/04-python-agent-framework.ipynb 里出现了 @tool(approval_mode="always_require").agents/skills/deploying-scalable-agents/SKILL.md 也把 always_require 描述为”给大额退款这类动作用的人工审批”。第 1 课固定用 never_require,请在往真实业务上搬的时候把这一处重新想一遍。

另外注意那行 docstring。第 1 课的这个工具不带参数,所以文档字符串是模型判断”什么时候调它”的主要依据;第 2 课的示例则用 Annotated[str, "The destination to check availability for"] 给参数补了说明。两种写法在仓库里都有,看你的函数有没有入参。

agent 的构造与运行是同一个单元格:

agent = provider.as_agent(
    name="TravelAgent",
    instructions=(
        "You are a helpful travel agent. Help users find their perfect vacation "
        "destination based on their preferences. Use the get_destinations tool "
        "to see available destinations."
    ),
    tools=[get_destinations],
)

response = await agent.run(
    "I'm looking for a warm beach destination. What do you recommend?"
)
print(response)

as_agent 是挂在 client 对象上的方法——agent 不是 new 出来的独立类,而是由 provider 变出来的。这里有一处值得先记住的仓库现状:as_agent 这个写法在后面多课的 notebook 里都出现,但仓库里同时还存在 create_agent(...) 这个名字——上面那份第 2 课 notebook 的说明文字里写的是 provider.create_agent()08-multi-agent/code_samples/workflows-agent-framework/python/ 下的 workflow notebook 里也有 chat_client.create_agent(...)。两个名字在仓库里并存,我们只把这个现象摆出来,你照着哪一课写就以那一课当前的代码为准。三个参数各管一件事:name 是这个 agent 的标识,instructions 就是系统提示词(注意它在文案里点了工具名 get_destinations),tools 是一个 list,元素是被 @tool 装饰过的函数对象本身,不是函数名字符串。

运行入口有两种形态,也就是文首说的第三处:

async for chunk in agent.run(
    "Tell me about Tokyo as a travel destination", stream=True
):
    print(chunk, end="", flush=True)

同一个 run 方法,加上 stream=True 之后从 await 变成 async for,逐块产出文本。这两句都是在 notebook 单元格里直接顶层 await / async for 的,依赖的是 notebook 内核对异步的支持;requirements.txt 里同时列了 ipykernelnest-asyncio。以上片段均原样取自该 notebook,未经实测,以仓库最新代码为准。

四、边界:这一课没告诉你的几件事

没有 session。 第 1 课的两次 run 之间不涉及任何会话对象;到第 2 课的 notebook 才出现 session = agent.create_session()。这个差别是两个文件里摆着的,具体语义请看第 2 课那一课自己的说明。

Python 与 .NET 不是同一套环境变量。 同目录下的 01-dotnet-agent-framework.md 走的是另一条路:它的 Required Environment Variables 一节写的是 AZURE_OPENAI_ENDPOINTAZURE_OPENAI_DEPLOYMENT,前置条件是 .NET 10 SDK。你要是照着 .NET 那篇配 .env 再回来跑 Python notebook,cell 里的 raise ValueError 会立刻拦住你。

GitHub Models 这条老路已经不通。 .env.example 的注释写明 GitHub Models 已弃用且不支持 Responses API,示例已全部改用 Azure OpenAI 的 Responses API。网上早期的教程若还在教 GitHub Models 配法,对不上现在这个仓库。

想不连云端跑,这一课的写法就不适用了。 00-course-setup/README.md 有一节 Alternative Provider: Foundry Local,走的不是 FoundryChatClient 而是 OpenAIChatClient

from foundry_local import FoundryLocalManager
from agent_framework.openai import OpenAIChatClient

manager = FoundryLocalManager("phi-4-mini")

chat_client = OpenAIChatClient(
    base_url=manager.endpoint,      # e.g. http://localhost:<port>/v1
    api_key=manager.api_key,        # always "not-required" for Foundry Local
    model_id=manager.get_model_info("phi-4-mini").id,
)

agent = chat_client.as_agent(
    name="LocalAgent",
    instructions="You are a helpful assistant running fully on-device.",
)

phi-4-mini 是这一节里的示例模型名。可以对照着看两点:as_agent 的形态没变,换掉的只是 client 类与它的构造参数;但仓库在这段下面明确加了一条 Note——Foundry Local 暴露的是 OpenAI 兼容的 Chat Completions 端点,若要完整的 Responses API 能力(例如有状态会话),仍需 Azure OpenAI 或 Microsoft Foundry 项目。Windows 侧安装用 winget install Microsoft.FoundryLocal,macOS 侧是 brew install foundrylocal,两条命令仓库都写了。

云端调用会产生费用并上传数据。 这一课的每次 run 都会把你的输入发到你自己的 Foundry 项目,请自行评估密钥与数据边界。

五、怎么确认自己配对了

按仓库写明的顺序自查三步:az account show 确认 Azure CLI 会话在(00-course-setup/README.md 的 Step 3 就是用这条命令做验证);仓库根目录确实存在 .env 且里面两个变量都有值;然后执行 notebook 的第二个代码单元格——它是整份文件里唯一一个纯本地就能判定成败的单元格,不抛 ValueError 就说明变量读到了。至于后面两个单元格的输出会是什么样,那取决于你自己的模型部署,本文不做任何描述。


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

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