skill 和 tool 是两回事:微软 AI Agent 入门课自己就给了两套

2026-08-18

克隆下 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 开头,字段是 namedescription,部分还带 license。真正决定「什么时候轮到我」的信息全压在 description 这一个字段里,正文只在助手被激活之后才起作用。azure-openai-to-responsesdescription 甚至把「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_agentas_agent(...) 调用里并没有传 response_format 参数,说明文字和代码对不齐。你照着读的时候别以为定义了模型就自动生效。至于工具函数里那些目的地和航班信息,都是仓库示例里写死的返回值,用途是让 notebook 能自洽地演示流程,不是真实数据源。

skill 承载的是一个目录。.agents/skills/jupyter-notebook/ 下同时有 SKILL.mdreferences/notebook-structure.mdquality-checklist.mdtutorial-patterns.md 这类)、assets/experiment-template.ipynbtutorial-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-agentslocal-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 venvsource 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 实际表现的任何描述。 该课程持续更新,文中涉及的文件路径、依赖与接口写法随版本变动,请以仓库最新内容为准。 文中涉及的云端服务调用会产生费用并可能上传数据,请自行评估密钥与数据边界。

本文对照的是同一项目内的两种用法,依据均为上述仓库内容,不对两种用法做优劣排名, 选型结论只在仓库文档写明的能力边界内成立。

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

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