技能(Skills)是当下给智能体灌领域知识的主流做法:一个文件夹、一份 SKILL.md,靠渐进式披露控制上下文成本。但 LangChain 在 10 月 7 日的官方博客里指出了企业规模下的三个真实痛点:技能和工具的披露是割裂的——智能体可能没读技能说明就乱调工具,或者读了说明还得自己去搜工具;用户明确点名要某个技能时,还得浪费一轮 read_file 往返;长会话跑着跑着,同事新加的技能它永远看不到。这次的 deepagents 更新把三件事补齐了:工具绑定技能(Skill-Bound Tools)、运行时置顶技能(Pinned Skills)与线程内技能热更新。官方自己的 GTM 智能体挂了五十多个销售技能,这套机制就是为那种规模设计的。本篇按官方博客路径,把三个能力逐个跑通。
先理解:渐进式披露为什么需要这次改造
deepagents 里技能分三级加载:发现——启动时系统提示词里只有每个技能的名称与一句话描述,一个闲置技能在上下文里只占一行;激活——任务匹配到某个技能时,智能体用 read_file 读完整的 SKILL.md;执行——按指令需要再读 scripts、references 等附属文件。这套机制让上千个技能的库也能保持上下文轻盈。改造前的问题是工具不在这套体系里:工具列表是随请求整体给出的,技能里写的操作步骤对应不上按需加载的节奏。这次更新把工具纳入渐进式披露——绑定到技能的工具,在智能体读到该技能之前根本不出现在上下文里,提前调用会报未知工具错误;读到了,工具才随一条新系统消息进场。
版本敏感,先升级再动手。线程内热更新在 0.7.16(9 月 21 日)进入,工具绑定在 0.7.22(10 月 5 日)进入。本篇代码按官方博客叙事给出关键形态,SkillsMiddleware 的完整构造参数以你安装版本的官方 reference 为准——参数名在快速迭代期可能微调。
Step 1:升级并搭一个带技能库的基线 Agent
# 升级到带本次改造的版本
pip install -U deepagents
python -c "import deepagents; print(deepagents.__version__)"
# 工具绑定需 0.7.22+;热更新 0.7.16+ 即可
# 准备一个技能库目录
# ./skills/
# ├── call-transcripts/
# │ └── SKILL.md
# ├── meeting-prep/
# │ └── SKILL.md
# └── competitive-intel-card/
# └── SKILL.md
每个技能就是「目录 + SKILL.md」:YAML frontmatter 写名称与描述,正文写智能体要遵循的操作指令。建一个常规的 deep agent,把技能库挂上,先确认基线行为:启动后系统提示词里只有三个技能的名字与描述,不读正文、不载工具。这就是后面所有改造的对照组。
Step 2:给技能声明工具绑定
# skills/call-transcripts/SKILL.md
---
name: call-transcripts
description: 搜索销售通话记录并阅读转写文本,用于复盘与竞品情报整理
metadata:
include_tools:
- search_calls
- get_transcript
---
# 通话转写使用规范
1. 用 search_calls 按客户名与日期范围检索
2. 用 get_transcript 拉取全文,引用时保留时间戳
3. 复盘输出按「异议—回应—结果」三段组织
关键接线变化:这两个工具不放进 Agent 的常规工具列表,而是传给 SkillsMiddleware,由它按技能激活节奏披露。官方博客的表述是把这些工具交给 SkillsMiddleware(tools=[...]) 而不是 Agent 本身;技能还可以用标签(labels)声明工具组,由解析函数映射到具体工具——比如「某台 MCP Server 上的全部工具」或「按用户权限过滤后的子集」,适合一个技能对应一批工具的场景。
Step 3:验证「先读技能,工具才出现」
# 验证对话一:不提技能,直接让智能体检索通话
# 预期:调用 search_calls 失败 —— 工具不在上下文,报未知工具
# 验证对话二:让智能体「按 call-transcripts 技能复盘上周通话」
# 预期:智能体读 SKILL.md,随后 search_calls 与 get_transcript
# 以一条新系统消息的形式加入对话,调用成功
这个顺序约束是本次改造的核心价值:智能体在能调用工具之前必然已经读过怎么用。另一个容易被忽略的收益在提示缓存上——过去往对话中途加工具,意味着修改请求的工具列表,整段前缀缓存作废,长对话重新计费;在 Anthropic 与 OpenAI 的较新模型上,绑定工具以新系统消息的形式追加在缓存前缀之后,前缀不动、缓存保住。其他模型走旧的追加行为,缓存会失效。
缓存保住与否取决于模型。官方明确「中途加工具不动缓存前缀」只在支持对话中途新增工具的 Anthropic 与 OpenAI 模型上成立;换别的提供商,工具照样能加载,但缓存失效的旧问题会回来。长会话成本敏感的话,选模型时把这一点算进去。
Step 4:Pinned Skills——用户点名即前置加载
# 用户输入 /meeting-prep 时,应用层识别命令后置顶技能:
result = agent.invoke(
{"messages": [{"role": "user",
"content": "/meeting-prep 准备明天与华东客户的会议"}]},
pinned_skills=["meeting-prep"], # 指令在模型调用前就进上下文
)
# 置顶技能绑定的工具也会随之到位
置顶解决的是确定性问题:用户敲了 /meeting-prep,意图毫无歧义,没必要再让模型自己「发现并决定读哪个技能」——应用层直接把技能指令插进下一轮模型调用之前,省掉一轮 read_file 往返,行为从「模型自己挑」变成「应用保证加载」。注意分界:框架本身不解析斜杠命令,检测 /xxx 是你应用层的活,框架只负责置顶动作。
Step 5:长会话热更新技能库
# Python:强制本次调用重扫技能源
result = agent.invoke(
{"messages": messages},
skills_metadata=None,
)
# JavaScript 对应写法
# agent.invoke({ messages }, { skillsMetadata: null })
deepagents 会把发现阶段拿到的技能元数据存在 Agent 状态里,后续轮次复用——这本是省掉重复扫描的优化,副作用是长跑线程对增删改无感。置空 skills_metadata 后,下一轮运行会重扫配置的技能源并替换存量目录:同事刚提交的技能、刚改的描述、刚下线的旧版,全部即时生效,不用重开会话。适用场景是值班型长会话、跨天的多轮项目助理这类「会话活得比技能库变更周期长」的场合。
Step 6:规模化之后,治理边界要想清楚
# 团队技能库的三条治理建议
# 1) 技能入库走评审:SKILL.md 是指令,写错会稳定复现错误行为
# 2) 标签解析函数里做权限过滤:
# 用户权限不同,同一个 label 解析出的工具子集不同
# 3) 用正式环境做回归:技能或绑定关系变更后,
# 跑一遍标准任务清单,确认激活与工具调用行为没漂移
官方在博客里给这套机制的场景画得很清楚:技能库涨到几十上百个、跨团队共享、企业当资产管理时,绑定、置顶、热更新才真正发力;个人开发者三五个技能,感知不明显。同时要警惕一个误区——「工具延迟出现」不等于「工具受控」:权限校验、版本管理、测试与可观测性一样不能少,加载时机只是把暴露面收窄了。
提前调用失败是新契约的一部分。打开工具绑定后,任何「不读技能就调绑定工具」的路径都会报未知工具错误——这通常是对的(逼智能体先读说明),但如果你有外部编排逻辑绕过技能直接派活,要先把这些路径理顺,否则升级后会出现一批「莫名失败」的调用。
常见问题 FAQ
Q:置顶技能和让模型自己发现,怎么选?用户明确点名(斜杠命令、固定入口按钮)走置顶,确定且省一轮;开放任务让模型自己发现,靠描述匹配。两者共存:置顶是确定性通道,发现是兜底通道。
Q:绑定的工具会被计入每次请求吗?不会。绑定工具在技能被读取前不进上下文;读取后以新系统消息追加,支持中途加工具的模型上不影响缓存前缀。
Q:一个技能能绑几十个工具吗?技术上可以,但建议用 labels 走解析函数分组映射(比如整台 MCP Server 的工具、按权限过滤的子集),frontmatter 里逐个列名单在规模化后会难以维护。
Q:和工具搜索(tool search)冲突吗?不冲突,是两种披露策略:工具搜索按需检索全量工具池,绑定按技能组织披露。官方的定位是绑定让「工具与教会它使用方法的说明」同进同出,二选一或混用都行。