资讯动态

OpenClaw Skill 与 OpenAI Function Calling:AI工具调用的两种机制

发布时间:2026/10/20 19:14:11 来源:尧图企业网站定制
最近好几个读者私信问我同一个困惑OpenClaw 的 Skill 和 OpenAI 的 Function Calling看起来都是让模型去调工具到底为什么搞成两套机制这个问题我太有共鸣了因为我第一次接触 OpenClaw 的 Skill 系统时第一反应也是“这不就是 Function Calling 套了个壳吗”。后来真的在项目里把两种方案各跑了一遍才明白这俩表面都叫“工具调用”实际上一个是在给 AI 装技能另一个是在给 AI 开接口压根不是一个层面的东西。这篇文章我就把两者的设计初衷、定义方式、执行流程、适用场景全部摊开讲一遍。不会只甩概念我会给技能卡的写法和函数调用的代码示例也会把我在实际折腾中踩过的坑一块儿放进来。无论你是刚开始做个人助理自动化还是已经在用 Function Calling 做业务系统读完应该都能搞清楚自己手头的需求到底该选哪一套。1. 定位对比一个管“能力积累”一个管“接口接入”1.1 OpenClaw Skill 到底解决什么问题OpenClaw 这类开源个人 AI 助手框架核心目标是让大模型像管家一样直接操作电脑上的各种真实环境终端、浏览器、文件系统、邮箱、日历、聊天应用等等。它要做的事不是“回答一个问题”而是“替你完成一整套操作”。比如自动整理报销单、定时抓取网页数据、把邮件附件里的账单汇总成表格——这些都不是一次 API 调用能解决的而是一连串的动作编排。Skill 就是为这种场景发明的。在我理解里一个 Skill 更像“任务配方”它用自然语言告诉 Agent 在什么情况下使用这个能力、任务可以拆成哪几步、每一步需要调什么动作脚本。配方是给模型读的不是给编译器读的。同一台电脑上可以装几十个技能Agent 每次接到用户指令时会根据上下文自己挑技能、组合技能、按步骤执行。社区的技能市场里还会有人共享做好的技能包装了就能用不需要你自己写代码。这个机制的真正价值在于能力积累。你的 Agent 今天学会了一个“整理报销单”技能明天你再去别的设备上部署同一套 OpenClaw这个技能可以直接复用不用重新教一遍。这跟传统软件里“装插件”的思路很像但插件面向的是固定功能Skill 面向的是“让模型学会做一件模糊的事情”定义权在模型手里不在程序员手里。1.2 OpenAI Function Calling 到底解决什么问题OpenAI 的 Function Calling官方现在更常叫 Tool Use / Tool Calling是模型 API 层面的一项能力。它的核心问题只有一个怎么让大模型在生成文本的过程中稳定地请求外部程序执行一段函数。你在 API 请求里通过 tools 参数传一组函数声明模型根据用户的话判断需要调用哪个函数然后返回一个结构化的调用请求包含函数名和参数。执行完你的代码再把结果作为一条 tool 消息传回给模型模型基于结果继续生成回复。这套机制出现的历史背景是当时大家发现大模型如果纯粹靠“对话”输出无法可靠地对接业务系统。你想让它查个数据库、发个邮件它只能“假装”做了或者在文本里输出一段含糊的描述。Function Calling 的意义在于它把“模型想调用什么函数、参数是什么”这件事变成了严格的 JSON 输出你的代码可以直接解析、执行、校验。它保证的是接口层面的确定性而不是任务编排层面的智能。1.3 一层抽象的差距决定了工具定位差异把两者放在一起看最核心的区别在于抽象层级。Function Calling 站在“单次人机交互”的粒度上解决的是模型和外部代码之间的一次握手。Skill 站在“完整任务周期”的粒度上解决的是 Agent 如何在长时间、多步骤、多环境里组织自己的能力。所以你会看到Function Calling 完全不知道“做完这一步之后下一步干嘛”它只是响应式地告诉外部程序“现在请调用这个函数”。而 Skill 可以把“打开邮件 → 找到附件 → 提取金额 → 生成汇总 → 发送提醒”写成一个技能卡让 Agent 自己按顺序做。这不是“一个工具的两种叫法”而是“一层抽象的差距”。想清楚这层差距后面所有细节对比才有意义。2. 定义语言人看的技能卡 vs 机器读的接口契约2.1 Skill 用自然语言写“能力说明书”OpenClaw 的 Skill 格式不同版本细节有差异但大体上是一份 Markdown 格式的技能卡。里面一般会写明技能名称、用途描述、触发时机、执行步骤以及关联的动作脚本。我常用的写法如下--- name: daily_report_generator description: 自动汇总当天工作记录并生成日报 triggers: - 用户要求生成日报 - 时间到达每日18:00 steps: 1. 扫描工作日志目录中的当日记录 2. 提取关键任务和耗时项 3. 按日报模板填充并输出 actions: - command: generate_report params: date: today注意这里有一个很关键的点steps 里的内容不是严格的编程指令而是“描述给 Agent 听的意图”。真正执行层面generate_report 这个动作会对应一个脚本脚本负责读写文件、调用模板等实际操作。也就是说技能卡本身是一份“人可读、模型可理解”的能力说明底层的动作脚本才是真正干活的工具。这种设计的直接好处是维护成本低。你想给 Agent 加一个新能力不需要像写 API 那样定义一堆参数结构只需要写清楚“什么情况能用、大概怎么做”。我实际用下来的感受是一份一百来字的技能卡效果通常能顶得上几十行工具定义代码而且改起来也方便——想调整步骤改文本就行不用重新编译部署。2.2 Function Calling 用 JSON Schema 写“接口契约”Function Calling 的定义方式则完全是另一套思路。它要求你为每一个函数提供一个 JSON Schema用结构化字段精确描述函数名、用途、参数类型、是否必填、取值范围。一个典型的天气查询函数定义长这样{ type: function, function: { name: get_weather, description: 查询指定城市的实时天气, parameters: { type: object, properties: { city: { type: string, description: 城市名 }, unit: { type: string, enum: [celsius, fahrenheit], description: 温度单位 } }, required: [city] } } }调用时你在请求里把 tools 数组传进去模型在需要时就会返回一个 tool_calls 结构。代码里通常这样写response client.chat.completions.create( modelgpt-4o, messagesmessages, tools[weather_tool], ) tool_call response.choices[0].message.tool_calls[0] # 拿到函数名和参数再自己写逻辑去执行这里有个容易误会的地方模型永远不会替你执行任何函数它只是输出“我希望调用这个函数参数是这些”。真正执行函数、拿到结果后再把结果喂回模型的工作全在你自己的代码里。所以我说它是“接口契约”——它规范的是模型和程序之间的通信格式不负责业务逻辑的编排。2.3 两种定义的侧重点差异自然语言技能卡和结构化接口契约各自的侧重点完全不同。技能卡追求的是“表达能力的边界和步骤”它能描述模糊的目标比如“整理一下今天的账单”也能描述复杂的条件分支比如“如果附件里没有明细则提示用户补发”。但代价是它不保证参数的合法性模型可能脑补出不存在的参数也可能跳过某个你觉得很重要的步骤。JSON Schema 追求的是“参数的确定性和可校验性”。模型输出的参数可以直接拿来执行类型不对可以被 JSON Schema 校验拦住参数缺失可以通过 required 约束提前规避。代价是它表达不了“怎么做”的编排逻辑只能表达“调用什么、传什么”。用生活化的比喻来说技能卡是一本菜谱写着“准备食材、热锅、翻炒、装盘”Function Calling 是一个点菜接口输入“菜名、口味、忌口”就能下单但怎么炒菜完全不归接口管。3. 执行控制权在谁手里这是最本质的分水岭3.1 Skill 模式下控制权在 Agent 自己手里两种机制最大的分水岭是执行控制权。Skill 模式下决策主体是 Agent。用户发来一句“帮我整理报销单”Agent 的大脑也就是大模型会先在自己的技能列表里搜索找到报销单技能读取技能卡里的步骤然后按照步骤一步步调用动作脚本。执行完一步结果会回到对话上下文里Agent 看结果再决定下一步怎么做——结果不对就重试缺附件就提示用户甚至临时改计划。这种自驱执行的好处是它能应对模糊任务和意外情况。比如步骤 1 扫描目录时发现文件格式不对技能卡里没写这条Agent 也能根据常识判断要不要换个方式扫描或者问用户。它不会因为“程序里没写这个分支”就卡死因为模型本身具备推理能力。坏处则是不可预测你没法保证 Agent 每次都严格执行同样的步骤它可能会擅自调整流程甚至调用错误的技能。所以 OpenClaw 这类框架一般会做权限管理把高风险操作限制在可审计范围内。3.2 Function Calling 模式下控制权在应用代码手里Function Calling 模式完全是另一套控制流。整个流程本质是一个 agent loop由你的代码驱动你把用户消息和 tools 数组发给模型。模型判断需要调用某个函数时返回 tool_calls。你的代码解析 tool_calls自己执行对应函数拿到结果。把执行结果作为 role 为 tool 的消息返回给模型。模型看到结果要么继续请求下一个函数要么输出最终回答。每一步都不由模型主动推进而由外部程序循环驱动。模型从来不会说“我先把邮件附件扫描了然后再提取金额”它只会单次输出一个“请调用 scan_mail”的请求。你要做多步任务就必须自己在代码里写好“如果 scan_mail 返回成功就把结果的 attachments 字段传给 extract_total 函数”这种编排逻辑。你就是那个编排者模型只是一个个孤立的“决策点”。这种模式的特征是可预测、可审计。每一步调用都能打日志哪个函数被调用了、传了什么参数、花了多少时间全都清清楚楚。适合对稳定性和安全性要求高的业务场景。代价是一旦任务变复杂你的编排代码会呈指数级膨胀。我见过有人为了做一个“查询订单状态并自动发短信”的小功能写了整整一百多行循环和分支逻辑而那点事在 Skill 模式下也就是一张几十字的技能卡。3.3 六个维度快速对比对比维度OpenClaw SkillOpenAI Function Calling抽象层级Agent 应用层面向完整任务模型 API 层面向单次工具调用定义语言自然语言技能卡加动作脚本JSON Schema 结构化声明决策主体模型自主选择技能、编排步骤外部应用代码驱动调用循环状态维护上下文和记忆里自带任务状态需要应用自己维护状态机错误恢复模型可根据结果自愈重试需代码显式处理分支语焉不详可预测性较低但灵活较高适合审计这张表基本就是全文的核心。看懂了它你就能理解为什么一个叫“技能”一个叫“函数调用”——技能是 Agent 自己的能力函数调用是外部程序递给模型的接口。4. 同一个需求两套落地方案怎么走4.1 Skill 方案一张技能卡搞定复合任务拿一个很常见的个人助理需求举例“抓取邮件附件里的账单汇总金额并生成提醒。”这种任务在真实工作中特别典型在 OpenClaw 里实现时核心就是一张技能卡加两个动作脚本。技能卡长这样--- name: bill_summarizer description: 读取邮件附件中的账单文件汇总金额并生成提醒 triggers: - 用户要求汇总账单 - 检测到新邮件含账单附件 steps: 1. 从收件箱筛选包含“账单”或“invoice”字样的邮件 2. 下载附件中的表格或 PDF 文件 3. 提取金额、日期、商户条目 4. 汇总总金额生成摘要文本 5. 通过通知动作推送给用户 actions: - command: scan_mail params: { keyword: 账单 } - command: summarize_bills params: { source_dir: ./downloads }动作脚本我一般用 Python 写核心逻辑就是一个扫描目录、解析表格、求和的过程# summarize_bills.py def run(params): total 0 for f in list_files(params[source_dir]): amount parse_bill_amount(f) total amount return f本月账单合计: {total} 元共 {len(files)} 笔重点是Agent 在运行时看到的是整张技能卡它会自己决定先调 scan_mail 下载文件再调 summarize_bills 汇总。整个过程对用户来说只有一句话交互对开发者来说只需要维护技能卡和底层脚本不需要写任何编排代码。任务步骤再多几层比如“汇总后按类别分类”“超过阈值才提醒”也只需要在技能卡的 steps 里加两行描述。4.2 Function Calling 方案三个函数加一个循环同样这个需求如果用 Function Calling 实现你就得先把任务拆成三个独立函数。我写的 tools 定义大概是这样tools [ {type: function, function: {name: scan_mail, parameters: {...}}}, {type: function, function: {name: summarize_bills, parameters: {...}}}, {type: function, function: {name: send_reminder, parameters: {...}}}, ]然后自己写一个循环来驱动流程def run_agent(messages): for _ in range(10): response client.chat.completions.create( modelgpt-4o, messagesmessages, toolstools, ) msg response.choices[0].message if not msg.tool_calls: return msg.content for tc in msg.tool_calls: result execute_function(tc) messages.append({role: tool, tool_call_id: tc.id, content: result})注意这里即使你已经把三个函数的定义传给了模型模型也只是在每一步单独决定“现在要调用哪一个”。它不会主动把“扫描完了再总结”这个流程串起来。你必须依赖第二步的返回结果再触发第三步。第一次跑这种循环的人十有八九会栽在“模型没有按照你设想的顺序调用函数”上。它可能扫描完邮件之后直接输出“发现三封账单邮件需要我现在汇总吗”——因为模型觉得跟用户确认一下更合理而你还没来得及实现“确认后继续”的分支。这就是编排能力缺失的直接表现模型没法在函数调用里表达“我先做 A如果成功再做 B”。它只能发出一个孤立的调用请求剩下的都得代码来兜。4.3 案例对比小结维度Skill 方案Function Calling 方案任务拆分粒度整张技能卡描述全套流程拆成多个独立函数代码负责串联定义成本一张 Markdown 加两个脚本三个 JSON Schema 加循环编排代码流程控制模型基于技能卡自主编排外部应用强制编排异常处理模型根据结果灵活调整需要提前写好分支逻辑适用任务多步、跨应用、长期任务单步接口调用、事务性操作这个对比不是我硬凑的而是我实际用两种方案实现同类需求后最直观的体感。需求越复合Skill 的优势越明显需求越单一Function Calling 的轻量性就越突出。5. 工程上怎么选型我的三个判断标准5.1 标准一任务规模是一步还是一个流程如果任务本质上是“一次操作”例如根据用户话语查询天气、翻译一句话、搜索一下知识库直接上 Function Calling 就足够了。你只需要定义一个函数模型填好参数你的代码执行并返回完事。如果任务涉及多个步骤、多个系统、甚至需要跨时间周期执行比如“每周五自动汇总本周工作并生成周报”那就必须用 Skill 模式。因为这种任务的关键不是“模型能不能调用一个函数”而是“模型能不能记住整个任务的上下文并且自主推进流程”。Skill 天然把步骤、条件、动作组织在一起能力边界清晰也方便长期维护。5.2 标准二谁来维护这套能力定义这个问题经常被问的人忽略但其实特别关键。Function Calling 的函数定义最终是给应用程序看的要求类型准确、参数可控通常由后端开发维护。Skill 的技能卡是给模型看的描述可以模糊步骤可以随经验调整维护者可以是懂业务但不一定懂编程的人。如果你的团队里有能写脚本的人但缺少专门的后端接口开发或者你们不想为了一个 AI 助理能力长期维护一套 API 服务那 Skill 的 Markdown 加脚本模式会轻松很多。反过来如果你已经有成熟的业务服务只想给现有系统加一个 AI 入口那你肯定会选择 Function Calling——你不需要让模型去编排只需要让模型帮你把用户意图翻译成标准参数。5.3 标准三你有多能容忍模型的“自由发挥”Skill 模式的代价是模型有自由度它可能跳过步骤、改流程、自作主张。为了提高可控性OpenClaw 需要有完善的权限体系、操作审计、沙箱机制。如果你做的是财务、医疗这类高风险场景任何一步都不能出差错那 Skill 的自由发挥就是灾难。Function Calling 可以做到严格约束只允许模型调用白名单函数参数必须通过 JSON Schema 校验执行结果由代码判断后决定是否继续。模型本质上没有决定权只有建议权你的代码才是最终的守门员。我的经验是在不落地的对话场景里Skill 的自由度是优点在要落地的业务系统里自由度高就是隐患。自己搭个人助理优先考虑 Skill给公司业务做 AI 集成优先考虑 Function Calling。5.4 组合用法Skill 编排加 Function Calling 收口需要特别说明的是这两者并不是只能二选一很多人其实已经在混用。一个很自然的组合方式Skill 负责决策和编排动作脚本内部再调用 Function Calling 风格的接口把参数收口到现有业务系统。这样既获得了 Skill 的任务编排能力又获得了函数调用的参数稳定性。我在 OpenClaw 的动作脚本里经常写类似这样的逻辑def run(params): user_query params[query] # 动作脚本内部调用模型接口按 tools 方式请求业务函数 result call_model_with_tools(user_query, tools[existing_api_tool]) return resultSkill 告诉模型“你可以做这件事大概分几步”动作脚本再通过 Function Calling 跟外部系统精确对接。“上层编排靠技能底层参数靠函数”这是我目前用过最顺手的组合推荐你试试。6. 实操踩坑记录与排查速查6.1 我踩过的坑清单坑一技能描述写得太抽象模型死活不触发。我一开始把技能卡 description 写成“账单处理助手”结果 Agent 在用户说“看看这个月花了多少”时完全没想起这个技能。后来改成“用户询问消费金额、账单统计、发票汇总时使用尤其适用于邮件附件里有电子账单的场景”触发率立刻上去了。写技能描述时要站在模型角度想用户什么话会让我想到这个技能。坑二Function Calling 返回的参数不符合预期。最常见的情况是 required 字段没设全模型就敢不传参数。比如城市字段没设 required模型排到“查询当前城市天气”时直接把 city 省略了我的代码拿到空字符串直接报错。解决办法是把必要参数全部标记为 required能在 Schema 里用 enum 约束的就别用自由文本宁可多写几个字段也不要让模型自由发挥。坑三定义太多函数导致上下文膨胀。OpenAI 的 tools 数量理论上限不算低但工具一多模型选错函数的概率也在涨。我实测过十几个工具时就开始偶发漏选二十几个工具时模型经常把相近功能搞混。Skill 模式没有这种困扰因为技能卡可以按需加载。用 Function Calling 时建议分组动态传 tools别一把全塞进去。坑四tool 消息回流格式不对模型开始胡言乱语。执行完函数后必须用正确的 tool_call_id 把结果回传。我一开始图省事直接把执行结果塞在普通 user 消息里结果模型完全无法关联到之前的函数调用请求就开始瞎编结果。正确做法是结构严格写清楚 role、tool_call_id、content对话历史一条都不能错位。坑五Agent 陷入无限循环调用。Skill 模式下模型如果连续几次执行失败它会不停换方式重试直到触及对话长度限制。Function Calling 模式也会出现模型反复请求同一个函数的情况。最好在循环里设置最大迭代次数我一般设 5 到 8 次超过就中断并把中间结果整理成摘要告诉模型“尝试终止”。坑六技能卡里的 steps 和实际动作脚本行为不一致。写技能卡时把步骤写得挺美但脚本实际上只做了前两步后三步根本没实现。Agent 执行完脚本后以为整个任务完成了直接向用户汇报成功其实结果根本不对。所以每次改技能卡必须同步检查动作脚本是否真正实现了所有步骤不能让描述和现实脱节。6.2 排查速查表现象可能原因处理方式Skill 不触发description 与场景关键词不匹配重写技能描述写明用户话语模式Skill 触发后乱做steps 太模糊模型无法对齐流程拆细步骤关键步骤加条件说明函数返回空参数missing required补全 schema 的 required 字段模型选择错误工具工具定义过多或描述相似精简 tools按场景分组加载无限调用循环编排循环缺终止条件代码里设最大迭代次数结果回传失真tool_call_id 错位或格式错误严格按 tool 消息格式回执执行结果与技能描述不符动作脚本未完整实现同步修订脚本与技能卡这套速查表是我自己排查问题时反复用到的基本上覆盖了这两套机制里最常见的坑。如果你遇到的是其他怪异现象建议先确认调用日志里模型实际看到了什么——很多时候真相就在上下文里只不过被自己忽略了。最后再分享一个个人经验如果你要做的是那种“会长期跑、会不断学习新技能”的个人助理Skill 这套思路带来的长期收益非常明显。它真正把能力定义这件事从程序员手里交还给了模型让你能在几天内积累出一整套自己的自动化技能库。而如果你只是想让模型偶尔调一下现有系统的接口别过度设计Function Calling 直接上就行。我在实际折腾中最深的体会是别一上来就纠结“哪个更高级”先想清楚谁该做决策——模型还是你的代码。这个答案定了技术选型基本就定了。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑