Agent技能与函数调用的实现差异

作者:新兰2026.11.03 12:59浏览量:0

简介:本文从代码实现角度对比Agent技能(Skill)与函数调用(Function Calling)的异同。读者将了解两者在职责边界、上下文组织、宿主实现和选型场景上的核心区别,掌握如何在不增加业务函数的前提下为现有工具循环引入技能机制,并明确何时该用函数调用、何时该用技能包。

agent-">Agent技能与函数调用的实现差异:从代码层面看清职责边界

在构建智能体(Agent)系统时,开发者经常会遇到两个容易混淆的概念:函数调用(Function Calling) 和技能(Skill)。前者是模型与外部工具交互的经典机制,后者是近年来在智能体架构中逐渐流行的一种能力封装方式。两者在代码层面究竟如何实现?它们各自的职责边界在哪里?什么时候应该用哪一个?

本文将从代码实现的角度,对这两个概念进行系统对比。这里讨论的“技能”特指带有 SKILL.md 描述文件的Agent Skills开放格式,而不是某些框架中把任意函数简单命名为skill的旧用法。

一、对比背景:为什么需要区分两者

在早期的智能体系统中,函数调用几乎是模型使用外部能力的唯一方式。开发者定义一组工具(tools),模型根据用户请求决定调用哪个工具、传什么参数,宿主程序负责执行并返回结果。这个循环简单直接,但它有一个隐含假设:模型已经知道在什么场景下该用哪个工具,以及如何组织多步操作。

当任务变得复杂——比如“检查CSV数据质量并输出摘要”——模型不仅需要知道有“读取文件”和“运行脚本”这两个工具,还需要知道:

  • 先确认主键列和空值定义,不要自行猜测;
  • 使用特定脚本进行检查,不修改输入文件;
  • 报告行数、缺失值数、重复键数;
  • 执行失败时说明原因,不编造统计数字。

这些“操作方法”和“流程约束”如果全部塞进系统提示词,会变得臃肿且难以复用。技能机制正是为了解决这个问题而出现的。

二、对象定义:函数调用与技能分别是什么

函数调用是模型输出结构化指令、宿主执行外部函数并返回结果的机制。它解决的是“调用哪个工具、传什么参数”的表达问题。在代码层面,它表现为模型返回一个包含工具名和参数JSON的对象,宿主通过分发器执行对应函数。

技能是一组可复用的操作知识包,通常包含一个 SKILL.md 文件(提供名称、描述和Markdown格式的操作方法)以及可选的脚本、参考材料和模板。它解决的是“什么时候用、按什么步骤做、要参考哪些材料”的组织问题。

用一个类比来理解:函数调用像是给你一把锤子和一把螺丝刀,告诉你它们能干什么;技能则像是一本“组装书架指南”,告诉你先做什么、后做什么、遇到什么问题该用哪个工具。

三、相同点分析

尽管两者定位不同,但它们在设计目标上有若干共同点:

  • 都服务于智能体的任务执行:最终目的都是让模型能够完成超出纯文本生成的实际操作。
  • 都依赖宿主的可信执行环境:无论是函数调用还是技能中的脚本,都需要宿主进行权限校验、路径检查和受控执行。
  • 都需要模型具备一定的推理能力:模型必须理解任务、匹配能力、决定行动顺序。
  • 都可以组合使用:一个技能内部可以引导模型调用多个函数;函数调用的结果也可以作为技能流程的输入。
  • 都涉及上下文管理:函数调用的schema和技能的元数据都需要被组织进模型的上下文中,占用token预算。

四、核心差异分析

4.1 技术架构与职责边界

函数调用的核心是一个循环:模型返回工具调用请求 → 宿主执行 → 结果回传 → 模型继续推理。在这个循环中,模型负责决策,宿主负责执行。

技能的核心是一个上下文注入与按需加载机制:宿主在启动时扫描技能目录,解析元数据,将技能目录注入系统提示词;当模型判断任务匹配某个技能时,主动读取对应的 SKILL.md,获取详细操作指南,然后再决定调用哪些已有工具。

关键差异在于:函数调用不自动增加新函数。放一个 SKILL.md 文件在目录里,并不会让模型多出一个“csv-audit”工具。它只是让模型知道“有这么一种做事方法可用”。

4.2 接入方式与开发改造量

维度 函数调用 技能
新增能力 需要定义工具schema、实现函数、注册分发 只需放置 SKILL.md 和可选脚本
模型侧变化 工具列表变化,模型直接看到新工具 系统提示词中增加技能目录,模型按需读取
宿主侧变化 需要实现分发器、参数校验、执行环境 需要实现技能发现、元数据解析、文件读取
对现有循环的影响 通常需要修改工具注册逻辑 可以在不增加业务函数的前提下引入

从改造量来看,技能机制对现有工具循环的侵入性更小。你可以在已有的 read_file 和 run_process 两个工具之上,仅通过增加技能发现和目录渲染逻辑,就实现技能支持。

4.3 上下文组织方式

函数调用的上下文组织相对直接:工具schema以结构化形式放入请求,模型在每次推理时都能看到完整工具列表。

技能的上下文组织是两层的:

  1. 目录层:启动时扫描技能目录,解析名称、描述和文件位置,渲染成技能目录注入系统提示词。这一层占用token较少。
  2. 详情层:当模型判断任务匹配时,主动调用 read_file 读取 SKILL.md,获取完整的操作方法和约束。这一层按需加载,避免了一次性注入所有技能详情。

这种设计的好处是:技能数量增加时,目录层仍然保持精简,只有实际用到的技能才会展开详情。

4.4 表达能力的差异

函数调用的表达能力受限于JSON Schema:它擅长表达“参数是什么类型、有哪些枚举值、是否必填”,但不擅长表达“先做什么、再做什么、遇到异常怎么处理、参考哪些材料”。

技能的表达能力来自Markdown的自由格式:它可以写步骤、写约束、写注意事项、写示例。例如:

  1. ---
  2. name: csv-audit
  3. description: 检查 CSV 的缺失值和重复键,输出数据质量摘要。
  4. ---
  5. 先确认主键列和空值定义,不自行猜测。
  6. 使用 scripts/audit.py 检查,不修改输入文件。
  7. 报告行数、缺失值数、重复键数。
  8. 执行失败时说明原因,不编造统计数字。

这段文字无法用工具schema表达,但它是模型正确执行任务的关键。

4.5 安全与权限控制

函数调用的安全边界相对清晰:每个工具可以独立设置权限、参数校验规则和沙箱策略。宿主的分发器可以针对不同工具实施不同的检查。

技能的安全边界更复杂:技能本身可能包含脚本,脚本的执行仍然要通过 run_process 等工具。因此,宿主需要对技能目录的发现、SKILL.md 的读取、以及技能中引用的脚本路径进行额外的校验,防止路径穿越、恶意脚本注入等风险。

4.6 性能与扩展性

从性能角度看,函数调用的开销主要在模型推理和工具执行本身。技能机制增加了一次额外的 read_file 调用(读取 SKILL.md),这会增加一轮交互延迟,但换来了更精准的操作指导,可能减少后续的错误重试。

从扩展性角度看,函数调用在工具数量增长时面临“工具列表膨胀”问题——每个工具的描述都占用上下文,工具太多会导致模型选择困难。技能机制通过“目录+按需加载”缓解了这个问题:技能数量增长时,目录层仍然可控。

五、对比表格总结

对比维度 函数调用(Function Calling) 技能(Skill)
核心职责 表达“调用哪个工具、传什么参数” 组织“什么时候用、按什么步骤做、参考哪些材料”
代码载体 工具schema + 宿主分发器 SKILL.md + 可选脚本/资源
是否新增可调用函数 是,直接增加工具 否,默认不注册新工具
上下文注入方式 工具schema全量注入 目录层全量 + 详情层按需读取
表达能力 结构化参数,适合精确调用 自由文本,适合流程与约束
接入改造量 需要实现工具与分发 需要实现发现与加载
安全关注点 参数校验、权限、沙箱 路径校验、脚本来源、读取权限
扩展性瓶颈 工具列表膨胀 技能目录膨胀(较轻)
典型配合方式 作为执行层被技能引导 作为知识层引导函数调用

六、典型场景选择

优先使用函数调用的场景:

  • 任务边界清晰,模型只需要知道“有这个工具”就能正确使用;
  • 工具数量较少(例如少于10个),上下文压力不大;
  • 需要精确的参数校验和权限控制;
  • 工具的执行逻辑简单,不需要多步指导。

优先使用技能的场景:

  • 任务需要多步操作和流程约束;
  • 操作知识需要复用和版本管理;
  • 希望在不增加业务函数的前提下扩展智能体能力;
  • 技能数量较多,需要按需加载以避免上下文膨胀;
  • 需要向模型传达“不要做什么”的负面约束。

两者配合使用的典型模式:

技能作为“操作手册”,函数调用作为“执行手段”。模型先读取 SKILL.md 了解流程,然后按照流程调用已有的 read_file、run_process 等工具完成任务。这种分工让知识层和执行层各自独立演进。

七、选型建议

如果你的系统目前只有少量工具,且任务流程简单,直接使用函数调用即可,不必过早引入技能机制。

如果你发现系统提示词中塞入了大量“操作步骤”和“注意事项”,或者同一套操作流程需要在多个任务中复用,那么可以考虑引入技能机制。

如果你需要支持非开发者编写操作指南(例如业务专家撰写数据检查流程),技能的Markdown格式比工具schema更友好。

如果你对安全隔离要求极高,需要对每个可执行单元进行独立审计,函数调用的分发器模式可能更容易实施细粒度控制。

八、迁移与使用注意事项

从纯函数调用迁移到“函数调用+技能”模式时,需要注意:

  1. 不要期待技能自动变成工具:技能不会注册新的可调用函数,模型仍然通过原有工具执行操作。
  2. 技能目录的发现逻辑要可控:明确技能根目录,避免扫描到非预期文件。
  3. SKILL.md 的解析要健壮:元数据格式要校验,正文内容要防止注入攻击。
  4. 脚本执行仍然要走受控通道:技能中的脚本应通过已有的 run_process 等工具执行,复用权限检查和沙箱策略。
  5. 上下文预算要监控:技能目录本身也占用token,技能数量增长时需要评估目录渲染的成本。
  6. 错误处理要完整:技能读取失败、脚本执行失败时,模型应能获得明确的错误信息,而不是编造结果。

九、总结

函数调用和技能不是替代关系,而是分层协作关系。函数调用解决“怎么执行”的问题,技能解决“怎么组织执行”的问题。在代码层面,函数调用体现为工具schema和分发循环,技能体现为 SKILL.md 的发现、加载和上下文注入。

理解这个差异的关键在于:放一个技能文件不会自动长出一个函数。技能改变的是模型的知识和决策依据,而不是宿主的可执行能力。宿主真正要补的,是“发现”和“加载”这两个环节——扫描技能目录、解析元数据、渲染目录、按需读取详情。

在实际系统中,两者经常配合使用:技能提供操作指南,函数调用提供执行手段。选型的核心判断依据是:你的瓶颈在于“模型不知道该怎么做”,还是在于“模型没有可用的执行工具”。前者用技能,后者用函数调用,两者都需要时则组合使用。