Client/Agent/Tools/Session 四层:微软 AI Agent 入门课第 2 课全景
第 2 课 02-explore-agentic-frameworks/ 底下有三份代码示例:code_samples/02-python-agent-framework.ipynb、code_samples/02-python-agent-framework-azure-openai.ipynb、code_samples/02-dotnet-agent-framework.cs(配套说明在同目录的 .md)。第一次翻的人容易把它们当成三条互相独立的教程,挑一条跑通就走。
实际上课程给的是同一套分层结构的三个切面。02-python-agent-framework.ipynb 里有一段 markdown 把这层意思写得很直白,它画了一张图:
Client → Agent → Tools
→ Session
并逐条解释:Client 负责连模型端点、处理认证与请求响应的编解码;Agent 由 client 派生,把模型访问和 instructions(系统提示)、tools 绑在一起;Tools 是被 @tool 装饰的 Python 函数;Session 保存会话历史,让多轮对话中 agent 记得上文。
这四层就是本文要走的路径。下面拿这一课自己的例子——问一句「哪些目的地现在可以订」——沿代码走一遍。
第一层 Client:同一课里有两个入口,环境变量名不一样
这是同一课里最容易串的地方。两个 Python notebook 用的是不同的 client 类,来自不同的子模块。
02-python-agent-framework.ipynb 走的是:
from agent_framework.foundry import FoundryChatClient
from azure.identity import AzureCliCredential
endpoint = os.getenv("AZURE_AI_PROJECT_ENDPOINT")
model = os.getenv("AZURE_AI_MODEL_DEPLOYMENT_NAME")
provider = FoundryChatClient(
project_endpoint=endpoint,
model=model,
credential=AzureCliCredential()
)
02-python-agent-framework-azure-openai.ipynb 走的是另一条:
from agent_framework.openai import OpenAIChatClient
endpoint = os.environ["AZURE_OPENAI_ENDPOINT"]
deployment = os.environ["AZURE_OPENAI_DEPLOYMENT"]
chat_client = OpenAIChatClient(
model=deployment,
azure_endpoint=endpoint,
credential=AzureCliCredential(),
)
两处的参数名都不同:一个叫 project_endpoint,一个叫 azure_endpoint;环境变量一个是 AZURE_AI_PROJECT_ENDPOINT / AZURE_AI_MODEL_DEPLOYMENT_NAME,另一个是 AZURE_OPENAI_ENDPOINT / AZURE_OPENAI_DEPLOYMENT。
这个差别在配置环节就会咬人。仓库根目录的 .env.example 里,上面四个变量都在,但 00-course-setup/README.md 的配置那一步只让你填前两个,填完还跟了一句 That’s it for most lessons。也就是说照着 setup 指引走完,AZURE_OPENAI_ENDPOINT / AZURE_OPENAI_DEPLOYMENT 那两行还停在模板里的占位值上——而 Azure OpenAI 那个 notebook 恰好只认这两个。
后一个 notebook 的注释还写明:OpenAIChatClient 指向 Azure OpenAI 稳定的 /openai/v1/ endpoint,默认使用 Responses API。同一 notebook 顶部有一段 Migration note,逐字写明这份示例原先用的是 Semantic Kernel 加 GitHub Models,而 GitHub Models 已废弃(notebook 里的措辞是 deprecated,retiring July 2026——按今天的日历,这个时间点已经过去了),现已换成 Azure OpenAI。你在网上搜到的这一课的旧写法多半是迁移前那版,别直接照抄。
两条路都用 AzureCliCredential。README 里对这层的说法是:框架用 AzureCliCredential(或 DefaultAzureCredential)做无密钥认证,不需要你直接管理 API key。也就是说这层依赖你本机 az login 的登录态,而不是某个写在文件里的 <YOUR_API_KEY>。
第二层 Agent:说明文字和可执行 cell 对不上
02-python-agent-framework.ipynb 里真正能跑的那个 cell 写的是:
agent = provider.as_agent(
name="TravelAvailabilityAgent",
instructions=(
"You are a travel booking agent. Help users check destination availability "
"and make recommendations. Always check availability before recommending a destination."
),
tools=[check_destination_availability],
)
但同一个 notebook 的架构说明段落和末尾 Summary 表格里,写的都是 provider.create_agent()。README 里所有示例用的也是 as_agent(...)。
这就是两处白纸黑字放在一起能看出来的不一致:同一课程文件内部,散文部分的方法名和可执行 cell 的方法名不同。我们没有跑过这份代码,无法判断哪个在当前版本的包里可用,只能提醒一句:以能执行的 cell 和仓库最新代码为准,遇到 AttributeError 先去对这一处。
Agent 这层接的三个东西——name、instructions、tools——就是「模型 + 人设 + 能力」的绑定点。README 里出现的几个 agent(my_agent、weather_agent、travel_agent、planner、executor)彼此的差别,都落在这三个参数上——as_agent 是否还接别的参数,这一课没有写,我们也不替它补。
第三层 Tools:装饰器、docstring 和 approval_mode
@tool 那一层,课程给的例子是:
@tool(approval_mode="never_require")
def check_destination_availability(
destination: Annotated[str, "The destination to check availability for"]
) -> str:
"""Check if a vacation destination is currently available for booking."""
...
notebook 的说明列了三条要点,值得逐条对着看:用 Annotated[type, "description"] 让模型理解每个参数的含义;函数的 docstring 会成为模型看到的 tool description;approval_mode="never_require" 的意思是这个工具无需用户确认即自动执行。
第三条容易读反。它不是「框架没有审批机制」,而是「这份示例显式把审批关掉了」——参数名本身就说明存在别的取值。仓库这一课里我们只看到 never_require 这一个值被用到,其它可选值课程正文没有列出,这里不替它补。工具一旦会写数据、花钱或触发外部动作,这个开关就该被认真对待。
还有一处值得对着看:README 讲 Tools 的小节里,那个 get_weather 例子是没有装饰器的普通函数,直接就塞进了 tools=[get_weather]:
def get_weather(location: str) -> str:
"""Get the current weather for a location."""
...
agent = provider.as_agent(
name="weather_agent",
instructions="Help users check the weather.",
tools=[get_weather],
)
而同一份 README 更靠前的 book_flight 例子、两个 notebook 里的工具,都带着 @tool(...)。README 对这一段的原话是:框架支持把 Python 函数定义为工具、由 agent 自动调用,工具在创建 agent 时注册。
两种写法在同一课里并存,是这一课读起来最容易犯迷糊的地方之一。仓库没有说明「不加装饰器时参数描述从哪来」,我们也不替它推断。实际写的时候,带 @tool 加 Annotated 的那条路径在课程里出现得更完整:参数描述、tool description、审批开关三样都有明确落点,可读性也更好。
第四层 Session:多轮的状态挂在哪
session = agent.create_session()
response = await agent.run(
"Which destinations do you have available?",
session=session,
)
notebook 的说法是:AgentSession 由 agent.create_session() 创建,记录会话中的全部消息;把同一个 session 传给每次 agent.run(),agent 就能拿到完整历史、引用先前的消息。第二个 notebook 在这基础上多传了一个 stream=True,改成 async for chunk in agent.run(user_input, session=session, stream=True) 逐块取。
到这里四层就齐了:client 决定连谁,agent 决定它是谁、能干什么,tools 决定它能伸手够到什么,session 决定它记得多少。
顺带说一句「多智能体」在这一课里长什么样。README 讲 Microsoft Agent Framework 核心概念时,Multi-Agent Coordination 那一条给的代码,是从同一个 provider 上派生出两个 agent,然后手工把前一个的输出喂给后一个:
planner = provider.as_agent(
name="planner",
instructions="Break down complex tasks into steps.",
)
executor = provider.as_agent(
name="executor",
instructions="Execute the planned steps using available tools.",
tools=[execute_tool],
)
plan = await planner.run("Plan a trip to Paris")
result = await executor.run(f"Execute this plan: {plan}")
README 更靠前的「协作工具」一节里还有一个例子,角色换成 dataretrieval 和 dataanalysis,结构一模一样:两次 provider.as_agent(...),然后 analysis_result = await agent_analyze.run(f"Analyze this data: {retrieval_result}") 把上一步的结果拼进下一句提示词。也就是说在第 2 课的层面上,多 agent 没有引入新的层,只是把 Agent 这一层实例化多份,编排逻辑还写在你自己的 Python 代码里。真正的编排机制要往后面的课去看。
同样四层,在 .NET 侧换了一套名字
02-dotnet-agent-framework.cs 把同一条路径写了一遍,层次完全对得上,名字全换了:
var azureClient = new AzureOpenAIClient(new Uri(azureEndpoint), new AzureCliCredential());
AIAgent agent = azureClient
.GetChatClient(deployment)
.AsAIAgent(
name: AGENT_NAME,
instructions: AGENT_INSTRUCTIONS,
tools: [AIFunctionFactory.Create(GetRandomDestination)]
);
AgentSession session = await agent.CreateSessionAsync();
await foreach (var update in agent.RunStreamingAsync("Plan me a day trip", session))
{
Console.Write(update);
}
工具描述的挂法也换了:Python 靠 docstring 加 Annotated,C# 靠方法上的 [Description("Provides a random vacation destination.")] 特性,再由 AIFunctionFactory.Create(...) 包成工具。
前置条件在 code_samples/02-dotnet-agent-framework.md 的 Prerequisites 一节:需要 .NET 10 SDK 或更高、一个带模型部署的 Azure OpenAI 资源,以及装好 Azure CLI 并 az login。这份 .cs 用的是单文件写法,首行是 #!/usr/bin/dotnet run,依赖直接以 #:package 行声明在文件顶部。
Windows 这边要注意运行方式的差别。该 .md 给了两套环境变量设置,PowerShell 那套是:
$env:AZURE_OPENAI_ENDPOINT = "https://<your-resource>.openai.azure.com"
$env:AZURE_OPENAI_DEPLOYMENT = "gpt-5-mini"
az login
(其中的部署名是仓库示例里的值,不是你必须用的值。)运行命令,文档给的 bash 路径是 chmod +x 之后直接执行脚本——这条在 Windows 上用不了,用文档同时给出的另一条:dotnet run ./02-dotnet-agent-framework.cs。
少配一个环境变量,三个文件的反应各不相同
这是排查时的实用差别,也是「每层落在哪」最直接的验证方式:
02-python-agent-framework.ipynb用os.getenv取值,然后显式判空并raise ValueError,错误信息里点名了缺哪两个变量;02-python-agent-framework-azure-openai.ipynb用os.environ["..."]直接下标,缺变量抛的是KeyError,只有变量名,没有提示;02-dotnet-agent-framework.cs里 endpoint 缺失会throw new InvalidOperationException("AZURE_OPENAI_ENDPOINT is not set."),但 deployment 缺失不报错,而是回落到一个默认部署名(?? "gpt-5-mini",这是仓库当前代码里的默认值,随版本可能变动)。
最后这条尤其值得记一下:.NET 侧少配部署名不会当场失败,它会拿一个你未必部署过的名字去请求。看到一个和自己配置无关的部署名出现在报错里,先回来看这一行。
还有一层不在这四层里
第 2 课 README 除了 Microsoft Agent Framework,还讲了 Microsoft Foundry Agent Service。README 里这部分的示例代码是另一套 API:AIProjectClient.from_connection_string(...) 建 client,project_client.agents.create_agent(model=..., name=..., instructions=..., tools=...) 建 agent,然后是 create_thread()、create_message(thread_id=..., role=..., content=...)、create_and_process_run(thread_id=..., agent_id=...)、list_messages(thread_id=...)。
注意状态那层的概念名换了:这里没有 session,对应物叫 thread。README 对 thread 的描述是它代表一次 agent 与用户的会话,可用来跟踪进展、保存上下文、管理交互状态;并特别提到 thread 里的消息可以是文本、图片或文件等不同类型。
README 明确写了这项服务目前处于 Public Preview,支持用 Python 和 C# 构建 agent。preview 状态意味着接口可能变,做技术选型时这一句不能略过。
至于两者怎么选,README 自己给了答案(属仓库文档自述,不是我们的判断):先用 Microsoft Agent Framework 把 agent 逻辑搭出来,需要在生产环境部署和扩展时再用 Microsoft Foundry Agent Service。README 的差异表也把两者的定位分开写:前者是带工具调用的 agent SDK,后者是 Foundry 里的平台与部署服务,自带对 Azure AI Search、Bing Search、代码执行等能力的接入。
所以第 2 课的「全景」其实是两个维度叠在一起:横着是 Client → Agent → Tools/Session 四层,竖着是 SDK 层与平台服务层。后面几课往哪一层加东西,回头对着这两个维度找文件就行——比如工具那层的展开在 04-tool-use/,状态那层的展开在 13-agent-memory/。
以上代码片段均按仓库文件原样引用;组合多段时的写法为按仓库代码中的接口语义组合的示例,未经实测,以仓库最新代码为准。
本文依据 github.com/microsoft/ai-agents-for-beginners 仓库于 2026-08-18 的公开内容整理,
事实来自仓库内的课程正文与代码示例。我们没有跑过文中涉及的代码,
因此不涉及运行结果、耗时与 Agent 实际表现的任何描述。
该课程持续更新,文中涉及的文件路径、依赖与接口写法随版本变动,请以仓库最新内容为准。
文中涉及的云端服务调用会产生费用并可能上传数据,请自行评估密钥与数据边界。