乐于分享
好东西不私藏

【智能体漫游】给AI装上“瑞士军刀“:我终于搞懂了Skill生态的设计哲学

【智能体漫游】给AI装上“瑞士军刀“:我终于搞懂了Skill生态的设计哲学

AI助手用了一段时间后,你有没有这种感觉?


你需要自己判断:先干什么、后干什么、哪个工具负责什么、结果怎么传给下一个。

这件事听起来像是"编排"的问题,但更深层的问题是——工具和Agent之间,缺少一套标准的"连接协议"

今天聊的Skill生态,就是来解决这个问题的。


01 问题的本质:不是工具不够,是"连接"不够

为什么?

因为每个工具都是一个"入口",Agent需要在上下文中感知:现在该用哪个工具?这个工具怎么用?结果怎么处理?

工具越多,感知成本越高。上下文窗口就那么大,全用来装"工具清单"了,真正做任务的空间反而被压缩。

Skill生态的设计思路是:不是让Agent看到所有工具,而是让Agent在需要的时候,恰好知道该用什么。

这就是"渐进式披露"的核心思想。


02 渐进式披露:两阶段加载的艺术

SkillLoaderTool的代码里,有一段注释非常关键:



1
2
3

# 💡 核心点:渐进式披露第一阶段
# 只读 frontmatter,构建轻量 XML 注入 description。
# 主 Agent 看到工具 → 知道"什么场景用什么 Skill",但不加载完整指令。


翻译成人话就是:

第一阶段,Agent只知道"有哪些技能",每个技能是干嘛用的,但不加载具体操作步骤。

这些信息被压缩成一个轻量的XML描述,注入到工具的description里:



1
2
3
4
5
6
7
8
9
10
11
12

<available_skills>
  <skill>
    <name>pdf-processor</name>
    <type>task</type>
    <description>将PDF文件转换为Word文档,支持格式保留...</description>
  </skill>
  <skill>
    <name>mailbox-ops</name>
    <type>task</type>
    <description>发送邮件,支持附件和HTML格式...</description>
  </skill>
</available_skills>


Agent看到这个清单,就知道:"用户要处理PDF,我应该调用pdf-processor这个Skill。"

第二阶段,才是在真正需要的时候,读取完整的SKILL.md文件。



1
2
3

# 💡 核心点:渐进式披露第二阶段
# 读取完整 SKILL.md,剥离 frontmatter,拼接沙盒路径替换指令。
# 结果写入 _instruction_cache,同一 Skill 只读一次文件。



03 参考型 vs 任务型:两种不同的"干活方式"

在Skill生态里,Skill分两种类型,这个设计非常有意思:



1
2
3
4

class SkillLoaderInput(BaseModel):
    skill_type: str = Field(
        description="skill类型:reference(参考型)或 task(任务型)"
    )


参考型Skill(reference)

返回操作规范文档,Agent自己消化、自己执行

适合的场景:

  • • 查文档、看攻略
  • • 理解某个领域的知识
  • • 需要Agent自己综合多个信息源


1
2
3

if skill_info["type"] == "reference":
    # 参考型:直接返回指令文本,不启动 Sub-Crew
    return f"<skill_instructions>\n{instructions}\n</skill_instructions>"


任务型Skill(task)

触发独立的Sub-Crew,在沙盒中隔离执行,上下文完全隔离

适合的场景:

  • • 需要执行文件操作
  • • 需要运行脚本
  • • 可能产生副作用的操作


1
2
3
4
5
6
7

# 任务型:启动独立 Sub-Crew,在沙盒中执行
crew = build_skill_crew(
    skill_name=skill_name,
    skill_instructions=instructions,
    mount_desc=self.sandbox_mount_desc,
    mcp_url=self.sandbox_mcp_url,
)



04 沙盒隔离:安全与自由的平衡

说到任务型Skill,就不得不提沙盒隔离的设计。



1
2
3
4
5
6

# 💡 核心点:默认沙盒挂载描述
DEFAULT_SANDBOX_MOUNT_DESC = (
    "1. 所有的操作必须在沙盒中执行,不得操作本地文件系统\n"
    "2. 如果需要读取本地文件,则需要本地文件在./workspace/data/目录下\n"
    "3. 任务预期输出的文件,必须写在沙盒绝对路径的/workspace/output/目录下"
)


核心约束:Sub-Crew能操作文件、跑代码,但它活在沙盒里,出不来。

这个设计解决了一个经典的Agent安全问题:

主Agent把任务扔给Sub-Crew,Sub-Crew在沙盒里干活,干完把结果交给主Agent。

中间环节是隔离的、安全的。


05 异步双通道:FastAPI和命令行的兼容设计

代码里有一段很有意思的设计:



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

async def _arun(self, skill_name: str, task_context: str) -> str:
    """
    💡 核心点:FastAPI 异步调用链的主路径,直接 await Sub-Crew
    CrewAI 在 arun() 内部调用 _arun(),框架自动选路
    """
    return await self._execute_skill_async(skill_name, task_context)

def _run(self, skill_name: str, task_context: str) -> str:
    """
    💡 核心点:用 ThreadPoolExecutor 在新线程中运行独立 event loop,
    规避主线程已有 event loop 时 asyncio.run() 报
    'cannot run nested event loop' 的问题。
    """
    with concurrent.futures.ThreadPoolExecutor(max_workers=1) as pool:
        future = pool.submit(ctx.run, _run_in_new_loop)
        return future.result(timeout=300)




1
2
3
4
5

if __name__ == "__main__":
    if "--async" in sys.argv:
        asyncio.run(main_async())
    else:
        main()  # 走同步路径



06 和MCP协议的关系:各司其职

之前我们聊过MCP协议,它是"AI的USB-C",让不同模型、不同工具之间能互相通信。

那么Skill和MCP是什么关系?

简单来说:

协议/框架
职责
MCP协议
工具级别的标准化
——让Agent能调用各种外部工具(数据库、API、文件系统等)
Skill生态
任务级别的封装
——把多个工具组合成可复用的工作单元

打个比方:

MCP解决的是"怎么连",Skill解决的是"拿来干嘛"。


07 实际应用:一个真实的workflow

说了这么多设计理念,来看看实际怎么用:



1
2
3
4
5
6
7
8
9
10
11
12
13

USER_REQUEST = (
    "请将./workspace/data/quarterly_report.pdf里的关键数据提炼出来,"
    "生成一份格式规范的 Word 文档"
)

def build_main_crew() -> Crew:
    skill_loader = SkillLoaderTool()

    orchestrator = Agent(
        role="skill使用助手总管",
        goal="根据用户需求进行分析,拆解,分发任务,最终保证任务的完成",
        tools=[skill_loader, IntermediateTool()],
    )


用户说:"把PDF转成Word。"

主Agent拿到请求,开始拆解:

  1. 1. 分析需求——用户要处理PDF文件,需要用到pdf-processor这个Skill
  2. 2. 加载Skill——调用SkillLoaderTool,按需读取pdf-processor的完整指令
  3. 3. 分发任务——把具体任务描述(task_context)传给Sub-Crew
  4. 4. 沙盒执行——Sub-Crew在隔离环境里完成PDF处理
  5. 5. 汇总结果——把最终产物路径返回给用户

整个过程,主Agent不需要知道"PDF怎么转Word"的具体步骤,它只需要知道"有pdf-processor这个Skill能搞定这件事"。

这就是"让AI管理AI的调度"的具体实现。


写在最后

Skill生态给我的最大启发,不是某个具体的技术实现,而是一种分层解耦的思维

  • • 工具层:MCP统一接口标准
  • • Skill层:把工具组合成可复用单元
  • • Agent层:调度Skill完成任务

每一层只关心自己的事,层与层之间通过清晰的契约连接。

这和微服务架构的设计哲学是一脉相承的——

当你的Agent系统变得越来越复杂,这种分层解耦的思维会越来越重要。


📌 互动话题:

你在给Agent接工具的过程中,遇到过最大的坑是什么?是工具调用失败、上下文爆炸、还是不知道该用什么工具?


(全文完。更多AI智能体实战,关注公众号,我们下期见。)