
























传统的 Agent 设计思路:把 Agent 可能需要的所有知识、工具说明、工作流程都写进 system prompt——在启动时一次性加载全部上下文。这种”静态预加载”模式忽视了一个关键事实:上下文窗口是一种具有递减收益边际的有限资源。
这种方式存在三个根本性问题:
deepagents-cli 的解法来自一个简单的洞察:Agent 不需要一开始就知道所有事情,它只需要知道”有哪些事情可以知道”。
这个设计模式来自 Anthropic 的工程实践(其实最早是 Manus 提出来的🤗),称之为 Progressive Disclosure。核心思想是:
这就像一个专业人士的工作方式:他知道自己有哪些参考手册,但只在需要时才翻开具体章节。

Claude Code 是闭源的,但最近 Langchain 推出了 deepagents-cli ,同时也更新了 Claude Code 的 skills 机制。So, 感谢开源,能够学习内部的逻辑实现,接下来让我引用代码来展示这个机制是如何实现的。
每个 skill 是一个独立的目录,必须包含一个 SKILL.md 文件:
SKILL.md 的格式很关键。它用 YAML frontmatter 提供元数据,正文是详细的使用说明:
这个设计的精妙之处在于:frontmatter 和正文都是给 Agent 读的,但在不同阶段按需加载。启动时 Agent 只读取 frontmatter(摘要层)来构建全局索引,知道”有哪些 skill 可用”;运行时当 Agent 判断某个 skill 相关时,才按需读取正文(核心层)来获取完整的使用说明。这正是渐进式披露(Progressive Disclosure)的核心——用元信息替代完整信息,按需加载而非预加载。

这四个字段足够让 Agent 做出”是否需要这个 skill”的判断,同时 path 字段让 Agent 知道去哪里获取详细信息。
具体的加载逻辑有几个值得注意的设计:
路径安全校验。在读取任何文件之前,先验证路径是否在合法目录内:
这防止了目录遍历攻击。比如一个恶意的 skill 目录包含指向 ../../.ssh/id_rsa 的符号链接——这段代码会拒绝它。
文件大小限制。防止 DoS 攻击:
两级覆盖机制。支持用户级和项目级 skills,项目级可以覆盖同名的用户级 skill:
这个设计让团队可以在项目中定制 skill,而不影响个人的全局配置。
要理解 skills 如何在运行时生效,需要先理解 middleware 机制。
Middleware 是一种在请求处理流程中插入自定义逻辑的模式。在 deepagents 中,middleware 可以:
before_agent)wrap_model_call)after_agent)这不是 deepagents 发明的概念——Web 框架(Express、Django)、消息队列、数据库驱动都有类似机制。它的核心价值是解耦:让不同的关注点(skills、memory、human-in-the-loop)可以独立实现和组合。
两个关键方法:
before_agent():此钩子在 agent 开始执行之前运行。换句话说,它发生在代理的实际功能执行之前,用来对代理的初始状态进行准备。每一次 invoke 只会运行一次,也就是说,不管 Agent 后续的行为涉及到多少 model 或者 tool 调用,这个钩子只会在最初的那一刻触发一次,而不会在流程中重复运行。
wrap_model_call():每次 LLM 调用时触发,注入 skills 信息到 system prompt
💡
before_agent
agent.invoke({...}):跑一次;invoke:每次 invoke 又会再跑一遍 before_agent;jump_to: "model"、jump_to: "tools" 不会让 before_agent 再次触发(它们只会重跑 before_model 等)。最终注入的内容大致呈现为:
注意这里只有名称、描述和路径——完整的 SKILL.md 内容并没有被加载。
在创建 Agent 时,SkillsMiddleware 和其他 middleware 一起被注册:
前面阐述了 middleware 的执行顺序和 wrap_model_call 的工作原理,现在来完整地追踪一个 skill 从”被发现”到”被使用”的全过程。理解这个过程很重要,因为它揭示了渐进式披露模式的精髓:Agent 不是一开始就知道如何使用 skill,而是在运行时逐步”学习”的。

在深入细节之前,先看一下 Agent 处理一次用户请求的完整生命周期:
注意 Phase 2 中的”Agent 循环”——这是 Agent 的核心工作模式。Agent 不是一次性完成任务的,而是通过多轮 LLM 调用和工具调用来逐步推进。每一轮 LLM 调用都会触发 wrap_model_call,这意味着 skills 列表会被反复注入到 system prompt 中。
让我们用一个具体的例子来追踪整个过程。用户输入:“帮我搜索一下 deep learning 相关的论文”
当用户按下回车的那一刻,Agent 开始处理这个请求。首先,所有 middleware 的 before_agent() 方法会按顺序执行。对于 SkillsMiddleware 来说,这是它”发现” skills 的时刻:
这个方法做了什么?它调用 list_skills() 函数,扫描用户级和项目级的 skills 目录。对于每个找到的 skill 目录,它会:
SKILL.md 文件SKILL.md 的前几行,解析 YAML frontmattername 和 description 字段假设用户的 skills 目录下有 arxiv-search、web-research、langgraph-docs 三个 skill,扫描完成后,state 中会存储这样的数据:
Note,此时 Agent 只知道这些 skill 的名称和一句话描述,完整的 SKILL.md 内容还没有被读取。这就是”渐进式披露”的第一步:让 Agent 知道有哪些能力可用,但不告诉它具体怎么用。
初始化完成后,Agent 进入核心的工作循环。这个循环会持续进行,直到 Agent 认为任务已经完成。
第一轮 LLM 调用:分析请求,决定使用 skill
在调用 LLM 之前,wrap_model_call() 会被触发。SkillsMiddleware 在这里把 skills 列表注入到 system prompt:
注入后,LLM 看到的 system prompt 末尾会多出这样一段:
现在 LLM 收到了用户的请求”帮我搜索一下 deep learning 相关的论文”,同时它也看到了可用的 skills 列表。LLM 的推理过程大概是这样的:
用户想搜索论文… 让我看看有什么 skill 可以帮忙…
arxiv-search: “Search arXiv preprint repository for papers…” —— 这个看起来很匹配!
按照 Skills System 的指引,我应该先读取这个 skill 的完整说明。
于是 LLM 决定调用 read_file 工具:
Agent 执行 read_file 工具,读取 arxiv-search/SKILL.md 的完整内容。这个文件可能有几百行,包含:
这是渐进式披露的第二步:当 Agent 确定需要某个 skill 时,才读取完整的使用说明。
第二轮 LLM 调用:根据 SKILL.md 执行任务
现在 Agent 已经读取了完整的 SKILL.md,它知道了:
LLM 根据这些信息,决定调用 shell 工具执行搜索:
Agent 执行 shell 命令,运行 arxiv_search.py 脚本。这个脚本会:
脚本输出可能是这样的:
第三轮 LLM 调用:整理结果,回复用户
Agent 收到脚本的输出后,需要把这些原始数据整理成用户友好的回复。LLM 会分析搜索结果,提取关键信息,然后生成一个结构化的回复:
任务完成后,所有 middleware 的 after_agent() 方法会依次执行。对于 SkillsMiddleware 来说,这个阶段通常不需要做什么特殊处理。但其他 middleware 可能会在这里做一些整理工作,比如保存日志、更新缓存等。
假设用户问的是”LangGraph 怎么实现状态管理?“,整个流程会有所不同:
skills_metadatalanggraph-docs skill 更匹配这个问题langgraph-docs/SKILL.md 而不是 arxiv-search/SKILL.mdlanggraph-docs skill 的指引进行如果用户问的是”1+1等于几?“,LLM 可能会判断没有任何 skill 适用于这个简单问题,直接回答而不读取任何 SKILL.md。
这就是渐进式披露的威力:Agent 只在需要时才获取知识,不需要时完全不加载。
回顾整个过程,我们可以看到几个关键的设计决策:
1. 元数据与详情分离
在 Phase 1,Agent 只加载了 skill 的名称和描述。完整的 SKILL.md 只在真正需要时才被读取。如果用户问的是”今天天气怎么样”,arxiv-search 的 SKILL.md 根本不会被加载。
2. LLM 自主决策
Agent 不是被动地执行预设的规则,而是由 LLM 自主判断是否需要使用 skill。这种设计让 Agent 能够灵活应对各种请求,而不需要为每种情况编写硬编码的逻辑。
3. 自然语言作为接口
SKILL.md 是用自然语言写的,LLM 可以直接理解并执行。这比传统的 API 文档或配置文件更灵活——Agent 可以根据具体情况调整执行方式,甚至在遇到问题时自主尝试其他方法。
4. 多轮对话的必要性
整个过程涉及多轮 LLM 调用:第一轮决定使用哪个 skill,第二轮根据 skill 说明执行任务,第三轮整理结果。这种多轮模式是 Agent 能够处理复杂任务的关键。
Skills 机制的核心洞察 —— 延迟加载 + 按需获取——其实是一个通用的系统设计原则。
在操作系统中,这叫虚拟内存:程序不需要一开始就把所有代码加载到内存,用到哪页加载哪页。
在 Web 开发中,这叫懒加载:页面不需要一开始就加载所有资源,滚动到哪里加载哪里。
在知识管理中,这叫渐进式学习:你不需要先学完所有知识才能开始工作,遇到问题时再深入学习。
deepagents-cli 把这个原则应用到了 AI Agent 的能力管理上。随着 Agent 能力越来越丰富,这种”知道自己不知道什么,但知道去哪里找”的设计会越来越重要。
如果你想体验这个机制,可以:
pip install deepagents-clideepagents,然后问一个匹配你 skill 描述的问题观察 Agent 是如何发现并使用你的 skill 的。
Skills 机制展示了一个重要的设计理念:好的抽象不是隐藏复杂性,而是让复杂性在正确的时机出现。Agent 不需要一开始就知道所有事情,它只需要知道如何在需要时获取知识,这可能是构建真正可扩展的 AI Agent 系统的关键。
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。