skill 和 tool 是两回事:微软 AI Agent 入门课自己就给了两套
克隆下 ai-agents-for-beginners 之后,很多人会同时看到两个东西:仓库根目录有个 .agents/skills/,里面躺着若干个 SKILL.md;课程正文里 04-tool-use/code_samples/04-python-agent-framework.ipynb 又在教你用 @tool 装饰器把函数注册成工具。名字听起来是一个层次的,于是就有人问:skill 是不是新版的 tool,或者 tool 是不是 skill 的简化写法。
不是。它们在这个仓库里连服务对象都不同。下面三个维度都能在仓库文件里逐条对上,我们只比对得上的部分。
一、触发时机:谁把它拉起来
04-tool-use/README.md 把 tool 的触发链路写得很直白:一份包含函数描述的 schema 会被发给 LLM,模型比对用户请求之后,返回它选中的函数名与参数——注意这一步返回的是 tool call,不是最终答案;框架拿到函数名和参数去执行,把执行结果再送回模型,模型才组织出最后的回复。该文档也提醒,你会看到 function 和 tool 两个词混着用,因为函数就是 agent 用的工具。
也就是说,tool 的触发时机是模型在一轮推理中做出的决定,落到代码里是 Responses API 那份 tools 列表加上 tool_choice="auto"。换到 Microsoft Agent Framework,README 写明框架会自动把函数和参数序列化成发给模型的 schema,你不用自己拼 JSON。
skill 的触发完全是另一回事。打开 .agents/skills/local-ai-agents/SKILL.md,它的 frontmatter 里 description 字段不是一句人读的介绍,而是塞满了检索用的触发语:先是一段功能概述,然后是 USE FOR: 后面一长串场景短语,再接一段 DO NOT USE FOR: 明确划走不该由它接管的场景(比如把云端规模化部署推给 deploying-scalable-agents)。正文里还单列了 ## Triggers 小节,逐条写「Activate this skill when a learner wants to …」。
这一点在几个 skill 里是统一的:.agents/skills/ 下的 SKILL.md 都以 YAML frontmatter 开头,字段是 name 和 description,部分还带 license。真正决定「什么时候轮到我」的信息全压在 description 这一个字段里,正文只在助手被激活之后才起作用。azure-openai-to-responses 的 description 甚至把「Python-only」「Node/TypeScript/C#/Java/Go 的迁移不归我管」写进了 DO NOT USE FOR:——这种把适用边界写进触发描述的做法,在 tool 那边没有对应位置可放,函数的适用边界只能塞进 docstring。
所以 skill 的触发者是读到这些描述的那个编码助手,靠的是语义匹配到 description 与 Triggers;tool 的触发者是你写的那个 agent 背后的模型,靠的是函数 schema。这两条链路在仓库里不交叉:.agents/skills/ 下的任何一个 skill 都不会出现在 notebook 的 tools=[...] 里。
二、承载内容:一个是函数签名,一个是一整个目录
tool 承载的东西小而硬。04-python-agent-framework.ipynb 里的写法是把普通 Python 函数加上装饰器,比如这一段(原文摘录):
@tool(approval_mode="always_require")
def book_flight(
origin: Annotated[str, "Origin airport code"],
destination: Annotated[str, "Destination airport code"],
passenger_name: Annotated[str, "Full name of the passenger"],
) -> str:
"""Book a flight for a passenger. Requires approval before executing."""
notebook 的说明写明了三件事:docstring 会成为模型看到的工具描述;类型注解(含 Annotated 里的说明文字)决定工具 schema;approval_mode 决定这次调用要不要人先点头。它给出的两个取值是 "never_require" 与 "always_require",并建议有副作用的操作(订票、扣款这类)用后者,把人留在环里。同一个 notebook 还有一节讲 response_format:说明文字写的是把 response_format 设成一个 Pydantic 模型,agent 就会返回结构化 JSON 而不是自由文本,示例定义了 BookingRecommendation 与套着它的 TravelPlan。这里有个细节值得留意——那一节的代码单元只定义了这两个模型,实际构造 structured_agent 的 as_agent(...) 调用里并没有传 response_format 参数,说明文字和代码对不齐。你照着读的时候别以为定义了模型就自动生效。至于工具函数里那些目的地和航班信息,都是仓库示例里写死的返回值,用途是让 notebook 能自洽地演示流程,不是真实数据源。
skill 承载的是一个目录。.agents/skills/jupyter-notebook/ 下同时有 SKILL.md、references/(notebook-structure.md、quality-checklist.md、tutorial-patterns.md 这类)、assets/(experiment-template.ipynb 与 tutorial-template.ipynb)和 scripts/new_notebook.py。SKILL.md 里给的是决策树和工作流:先判定要做的是 experiment 还是 tutorial,再用脚本从模板生成骨架而不是手写 notebook 的 JSON。azure-openai-to-responses 那个更典型,references/ 下分了速查表、测试迁移、疑难排查三份,scripts/detect_legacy.py 是一个扫描器,源码开头的 docstring 写明它按目录扫描遗留的 Chat Completions 写法,并用退出码区分「没发现」与「需要迁移」。
对比落在这里就清楚了:tool 里能放的只有一个函数签名加一段 docstring,模型看到的信息量被 schema 卡死;skill 可以把判断标准、模板文件、可执行脚本一起打包,但它们不是模型能直接调用的函数——detect_legacy.py 是助手在你的终端里以命令行方式运行的,SKILL.md 里的示例也是这么写的。
三、给谁用的:运行时的 agent,还是你手边的助手
CHANGELOG.md 在介绍这批 skill 时用的措辞是「Learner skills. New Agent Skills under .agents/skills/」,并说明 deploying-scalable-agents 与 local-ai-agents 分别打包了第 16、17 课的指导,testing-course-samples 讲的是怎么拿 scripts/validate-notebooks.ps1 去验证课程 notebook。这三个 skill 的服务对象都是正在学这门课、正在改这个仓库的人(以及替他们干活的助手)。
04-tool-use 里那三个工具的服务对象是你要交付的那个旅行 agent——它上线之后被终端用户的提问触发。两者在生命周期上就不在一个阶段。
顺带说一句,同仓的 .github/agents/dotnet-notebook-migration.agent.md 是第三种形态:它的 frontmatter 里也有 tools: 字段,但里面列的是 'runCommands'、'edit/createFile'、'search' 这类宿主环境提供的能力名,和课程里用 @tool 定义的函数工具不是一码事。看到 tools 这个词先看它在哪个文件的 frontmatter 里,能省掉不少误会。
四、从你的处境倒推
- 你希望 agent 在对话过程中能去查一个外部状态(库存、可用性、报价):这是 tool。做法是写函数、加
@tool、把函数对象放进as_agent(...)的tools参数里。 - 这个动作会花钱或产生不可撤销的副作用:仍然是 tool,但按 notebook 的建议把
approval_mode设成"always_require"。 - 你希望下游代码能直接吃返回值:仍在 tool 这条链路上解决,但旋钮不在
@tool上,而是 agent 侧的response_format,notebook 的说明写的是给它传一个 Pydantic 模型(注意上文提到的那处说明与代码不一致,以仓库最新代码为准)。 - 你要让 agent 一次问答里连着调好几个工具:notebook 的做法是把函数对象收进一个列表(示例里叫
travel_tools)一起传进去,README 说明框架会代你处理模型与代码之间的来回,包括自动完成工具调用这一步,你不必自己解析 tool call 再回填结果。 - 你希望「每次改这类文件都按同一套规矩来」:这是 skill。它要沉淀的是流程与判断标准,而不是一次函数调用。
- 这套规矩还要配模板文件、参考文档和一个扫描脚本:只能是 skill,tool 的形态装不下这些。
Windows 上有一处值得单说:testing-course-samples/SKILL.md 给出的运行方式是 pwsh scripts/validate-notebooks.ps1,并专门留了 -Python 参数,注释写明是给「python 不在 PATH 上,例如 Windows 商店别名」的情况用的。仓库 AGENTS.md 里创建虚拟环境那一步写的是 python3 -m venv venv 加 source venv/bin/activate,并在同一行用注释标出了 Windows 侧要改成 venv\Scripts\activate。这两处提醒的是同一件事:skill 里的脚本要在你本机执行,解释器路径与激活方式得你自己对上,模型帮不了你。
五、不比的,以及一处对不上的地方
skill 被加载时的优先级、多个 skill 同时命中怎么裁决、description 里的触发语按什么方式检索——这些在仓库里我们没有找到说明,不比。两者各自的实际运行表现我们也没有依据,本文不涉及。
倒是有一处仓库内部对不上的地方值得记一笔:04-tool-use/code_samples/test_demo_plugins.py 的第一行是 from demo_tool_agent import TimePlugin, CalculatorPlugin,但该目录下当前只有两份 .NET 素材、那个 Python notebook 和这个测试文件本身,全仓也搜不到 demo_tool_agent 这个模块。也就是说这个测试文件引用的模块当前不在仓库里。照着它去理解工具怎么注册会扑空,直接看 notebook 那份即可。课程持续更新,上面提到的文件路径与接口写法都以仓库最新内容为准。
本文依据 github.com/microsoft/ai-agents-for-beginners 仓库于 2026-08-18 的公开内容整理,
事实来自仓库内的课程正文与代码示例。我们没有跑过文中涉及的代码,
因此不涉及运行结果、耗时与 Agent 实际表现的任何描述。
该课程持续更新,文中涉及的文件路径、依赖与接口写法随版本变动,请以仓库最新内容为准。
文中涉及的云端服务调用会产生费用并可能上传数据,请自行评估密钥与数据边界。
本文对照的是同一项目内的两种用法,依据均为上述仓库内容,不对两种用法做优劣排名, 选型结论只在仓库文档写明的能力边界内成立。
安全与合规相关做法请结合自身环境评估,本文不构成安全方案建议。