☰
大模型Skills实战:从Function Calling到工具调用,让AI真正“动起来”
2026/10/21 5:23:42 网站建设 项目流程

前阵子我在调一个基于大模型的应用,遇到一件让我印象很深的事:模型对着一张内部流程文档,能把业务逻辑讲得头头是道,甚至还能指出某个环节设计的隐患。但当我随口问它“今天是几号,五天后的工作日是哪天”的时候,它愣是给了我一个上周的日期,还一本正经地解释了一通。

它不是装傻,它是真的不知道。它的所有“知识”都定格在训练结束的那一刻,对时间、对实时数据、对外部世界,它根本没有触角。这件事让我想明白了一句话:大模型就像一个智商很高但手脚被捆住的人,能思考,但动不了。要让它在真实场景里真正干活,光有“大脑”远远不够,它还需要一套能指挥得动的“手足”——这就是我理解的“Skills”。

1. 为什么一直聊“大模型需要Skills”:先看清模型的天花板

1.1 模型知道很多,但“知道”不等于“能做到”

我们平时用大模型,最直观的感受是它“什么都懂”。它能写代码、能做翻译、能总结文档、能编故事,好像一个无所不知的百科全书。但如果你深挖一层就会发现,它所有的“懂”都来自训练资料里的统计规律,本质是在做“下一段最合理的文本接龙”。

这种能力很强,但有两个硬伤。第一,它没有真正意义上的“记忆”,训练数据只能反映过去某个时间点之前的信息。第二,它没有“动作”,无论它多聪明,它都不能替你关掉一个告警、查一次数据库、算一个精确的值,或者把一个文件从A目录挪到B目录。

很多刚接触大模型开发的人会下意识觉得:“我直接问它不就行了吗?”但实际做业务系统时你会发现,用户要的不是一句滴水不漏的漂亮话,而是一个能落地、能交互、能改变状态的结果。这就逼着我们思考:怎么让模型在“想清楚”之后还能“动起来”?

1.2 一个容易忽视的问题:模型连算术都未必靠谱

先做个简单测试。你让一个普通大模型心算“7.35×0.18”,它可能一本正经地给你一个看似精确、但经不起细算的答案。这是因为模型内部没有真正的计算器,它是在“根据见过的类似题目,推测最像答案的数字”。

我在实际开发中见过很多这样的例子:让它算个汇率,它把结果四舍五入错了;让它统计一段列表里有多少个重复项,它数错了。这些情况不是模型“笨”,而是它根本不适合做精确运算。处理这类任务,正确思路不是逼它做数学题,而是给它配一个计算器,让它在需要时调用计算函数,让函数来保证精确性。

这个“配一个计算器”的动作,就是Skills的雏形。

1.3 信息总是过期的:模型需要一只“向外看的眼睛”

假设你正在做一个客服机器人,用户问“我的订单现在到哪儿了”。你从大模型那里拿不到答案,因为它不知道你系统里的订单状态;你也无法让模型“猜”一个物流轨迹,那会出大问题。你唯一能做的,是让模型识别出用户意图,然后转身去查你的订单系统,拿到真实数据,再组织成用户能看懂的话。

这三步拆开来看正是Skills的完整逻辑:理解意图(大脑)→ 调用工具(手足)→ 整理回答(大脑再加工)。没有这个闭环,模型永远只能活在训练的“过去式”里,对一切实时状态无能为力。

重要。我见过很多项目,前期把精力全花在“写更好的提示词”上,结果模型还是频繁出错。后来发现,问题不在提示词,而在于模型根本没有拿到真实数据的通道。提示词调得再好,也不能凭空变出系统里的实时库存、订单、日志或传感器数据。

2. Skills到底是个什么东西:从概念到架构

2.1 Skills的本质是一种“可调用接口”

很多人一听到Skills,会觉得是什么高深的新技术。其实它的本质特别朴素:你给模型一份“工具清单”,清单里写清楚每个工具叫什么、有什么用、需要哪些参数、返回什么格式。当模型觉得需要某个工具时,它就生成一个结构化的调用请求,你的程序收到请求后,去执行真正的函数,再把结果带回给模型。

打个比方。你雇了一个很聪明的助理,他不认识你公司的门禁卡、不会操作内部系统,但他知道遇到什么情况该找谁。你递给他一张名片簿,上面写着“财务部:负责查工资条,需要提供姓名和工号”“IT部:负责重置密码,需要提供邮箱”。助理收到你的问题后,会先判断该找谁,然后把你的信息整理好转交出去,拿到答复后再转述给你。这张名片簿,就是“Skills”;助理转交和转述的过程,就是模型在思考之后调用工具的过程。

2.2 谁是“大脑”,谁是“手足”:各司其职

在这样一套系统里,大模型依然负责核心的语义理解、任务拆解和语言组织,也就是“大脑”;而Skills工具则负责那些模型不擅长或不掌握的事情,比如精确计算、数据查询、文件操作、第三方服务对接,也就是“手足”。

我常用一句话来概括分工:模型负责“决定做什么”,工具负责“确保做对”。模型可以判断“用户想要的是本季度销售汇总”,但它不会自己去数那几百条订单记录;工具函数会去数据库里跑一次精确的聚合查询,把最终数字交还给模型,让模型把它润色成一段漂亮的汇报文字。

2.3 从Function Calling到Skills:名字不同,内核相通

在技术圈里,你可能听过好几个相近的说法:Function Calling、Tool Calling、Agent技能、插件、MCP,等等。我理解为,它们描述的是同一件事在不同阶段的形态。

  • Function Calling是基础能力:模型学会“输出一个函数调用”的格式。
  • Tools是一组Function的集合:你可以同时给模型提供查询天气、查询日历、发送消息等多个工具。
  • Skills则更像是“打包好的、有场景属性的工具组合”:它可能包含多个函数、一段说明文档,以及模型在调用时需要注意的业务规则。

从开发视角看,你不需要纠结术语。你只需要清楚:模型需要一套明确的、机器可读的“动作清单”,并且这套清单要能安全、可控地执行。Skills就是这套东西的组织化表达。

2.4 一次完整的“思考-行动-观察”闭环

任何一个带Skills的交互,本质上都在走同一个循环:

第一,用户提出请求;第二,模型阅读上下文和工具清单,决定是否需要调用工具;第三,如果需要,模型输出结构化的调用指令;第四,程序执行对应函数并返回结果;第五,模型拿到新结果,继续推理或生成最终答复。

这个循环可以转很多轮。比如用户问“帮我查一下这个月所有未处理的工单,并按紧急程度排序”,模型可能先调用“查询工单列表”得到一个长列表,然后又调用“按紧急程度排序”处理数据,最后还要调用“生成摘要”来组织语言。每一步之间,模型都在观察新拿到的信息,再决定下一步做什么。这正是“大脑”指挥“手足”反复协同的过程。

3. 从零实现一个最小可用的Skills系统

这里我分享一个我最近写的Demo,它不依赖任何特定厂商的大模型,核心思路通用。你先看懂这个最小闭环,再去套用任何主流产品,都会非常顺手。

3.1 先想清楚设计原则

我在动手前给自己定了几条原则,也推荐你参考:

  • 工具要“小”:每个函数只干一件事,参数越少越好。
  • 描述要“白”:给模型看的说明要直白,别用抽象术语。
  • 执行要“稳”:函数内部要有异常处理,不能让一次报错中断整个对话。
  • 记录要“全”:每次调用谁、传了什么参数、返回了什么结果,都要记录下来,方便排查。

这几条看起来简单,但很多人一开始会在“工具描述”上偷懒,结果模型调用成功率直线下降。后面我会专门讲这里踩过的坑。

3.2 定义一个工具集:给“手足”写说明书

我用Python做了一个极简工具集,包含两个函数:一个是获取当天日期,一个是计算N天前后的日期。这两个功能看似简单,但恰好覆盖了大量真实场景中时间相关的问题。

import datetime import json def get_current_date(): """返回今天的日期,格式为 YYYY-MM-DD""" return datetime.date.today().isoformat() def get_date_offset(days: int): """返回距今天 days 天后的日期,days 为负数表示向前推算""" return (datetime.date.today() + datetime.timedelta(days=days)).isoformat()

接下来是关键:把这两个函数翻译成模型能理解的JSON结构。很多模型服务都支持类似的tools参数格式,核心字段就是name(函数名)、description(函数说明)、parameters(参数结构)。

tools = [ { "type": "function", "function": { "name": "get_current_date", "description": "获取今天的日期,返回格式为 YYYY-MM-DD,不需要任何参数。", "parameters": { "type": "object", "properties": {} } } }, { "type": "function", "function": { "name": "get_date_offset", "description": "计算从今天开始偏移 N 天后的日期。如果用户问'三天后',则传入 days=3;如果问'三天前',则传入 days=-3。", "parameters": { "type": "object", "properties": { "days": { "type": "integer", "description": "偏移的天数,正数表示未来,负数表示过去" } }, "required": ["days"] } } } ]

注意看我对get_date_offset的描述:我特意写了“如果用户问三天后则传入3,如果问三天前则传入-3”。这不是废话,这是在给模型做“解题提示”。模型不是真的理解正负号,它需要你在描述里给出足够明确的映射关系,它才知道该填什么值。

3.3 让“大脑”看到工具并决定调用

把上面的tools结构连同用户的提问一起发给模型。这一步在不同框架里写法略有差异,但核心都是:把工具清单作为独立参数传进去,而不是塞进提示词里。

我这里用一个伪代码来示意这个流程:

def chat_with_tools(user_query): messages = [{"role": "user", "content": user_query}] response = model_client.chat( messages=messages, tools=tools ) # 返回结果里可能包含正常的文字回复,也可能包含 tool_calls 请求 return response

当用户问“今天是几号”时,模型大概率会在返回结果里带一个tool_calls字段,结构类似:

{ "tool_calls": [ { "id": "call_001", "function": { "name": "get_current_date", "arguments": "{}" } } ] }

看到这个结构就别把内容直接返回给用户了,你的程序现在要进入“执行工具”阶段。

3.4 执行函数并回填结果:闭环的关键

拿到tool_calls后,你需要写一个分发器,根据函数名执行对应的Python函数,并把结果作为一条新的“工具消息”回传给模型。

def execute_tool(tool_call): name = tool_call["function"]["name"] arguments = json.loads(tool_call["function"]["arguments"]) if name == "get_current_date": result = get_current_date() elif name == "get_date_offset": result = get_date_offset(days=arguments["days"]) else: raise ValueError(f"未知工具: {name}") return result def run_agent_loop(user_query): messages = [{"role": "user", "content": user_query}] while True: response = model_client.chat(messages=messages, tools=tools) if not response.tool_calls: return response.content messages.append({ "role": "assistant", "content": response.content, "tool_calls": response.tool_calls }) for tool_call in response.tool_calls: result = execute_tool(tool_call) messages.append({ "role": "tool", "tool_call_id": tool_call["id"], "content": str(result) }) # 循环继续:模型会看到工具返回结果,再决定是继续调用还是给出最终回答

这段代码是这个Demo的心脏。while True保证了模型可以连续多次调用工具。比如用户问“今天的前三天和后五天分别是几号”,模型可能会先调get_current_date拿到今天,再调两次get_date_offset,最后综合所有结果给出一个完整的答案。

我强调一下:回传结果时,tool_call_id必须和模型给的保持一致。很多框架就是靠这个ID把工具结果关联到具体的调用上。这个字段对不上,模型就会“失忆”,搞不清哪个结果对应哪一步操作。

3.5 实测记录:一次真实的交互过程

我用一个本地部署的开源模型跑了一遍这个Demo,把关键过程记录下来。

用户输入:
“帮我算一下,从今天开始,3天后是几号?顺便算一下,10天前又是几号?”

第一轮模型响应:
没有直接回答,而是发起了三次工具调用:get_current_date、get_date_offset(days=3)、get_date_offset(days=-10)。

程序依次执行,返回三个结果:
2025-06-14、2025-06-17、2025-06-04。

第二轮模型拿到这些返回值后,组织了一段最终回复:
“今天是2025-06-14,3天后是2025-06-17,10天前是2025-06-04。”

整个过程没有用到任何“计算”,模型只是做了它擅长的事:识别意图、拆解任务、组织语言。所有精确的日期推算都交给了确定性函数。这就是“大脑”和“手足”配合的标准范式。

4. 工具选型与参数细节:怎么让手脚更听使唤

4.1 工具描述怎么写,直接决定调用成功率

我在最初写工具描述时,吃过不少亏。一开始我写得很简单,比如“计算偏移日期”,结果模型经常不知道该传正数还是负数,甚至自作主张把用户说的“三天后”直接当成字符串塞进参数里。

后来我总结出一个经验:工具描述的每一句话,都是在帮模型降低做决定的不确定性。你要在描述里写清楚:

  • 这个工具解决什么问题;
  • 每个参数具体怎么取值,尤其是用户口语到参数的转换规则;
  • 如果用户请求模糊,应该怎么处理;
  • 返回结果大致是什么格式。

比如get_date_offset的描述里,我加了“正数表示未来,负数表示过去”,模型调用准确率立刻上了一个台阶。很多人忽略这个细节,以为模型读懂函数名就够了,实际上函数名是给人看的,描述才是给模型看的。

4.2 参数校验不能省:模型也会“手滑”

另一个容易忽视的点是,模型生成的arguments大概率是合法的JSON,但里面的值不一定是合法的。比如用户问“后天是几号”,模型可能生成{"days": "后天"}——类型错了;也可能生成{"days": 2.5}——值不合法。

所以你的执行器里必须有参数校验和异常兜底。不要直接arguments["days"]取来就用,而要加类型判断和默认值处理。一个响应式的异常捕获能避免因为单个工具错误,导致整段对话中断的情况。

def safe_execute_tool(tool_call): try: result = execute_tool(tool_call) return {"ok": True, "result": result} except Exception as e: return {"ok": False, "error": str(e)}

然后把{"ok": false, "error": "..."}作为工具结果回传给模型。很多情况下模型能根据这个错误信息自行调整参数,再来一轮调用。这有点像你发现助理找错了人,助理说“不好意思,我重新找对部门”,而不是整个流程直接崩掉。

4.3 选择模型时,留意它对工具调用的原生支持

不是所有大模型都天生支持这种结构化的tool_calls输出。有些老模型你给它工具清单,它不会输出结构,而是把调用信息写在普通文字里,比如“我应该调用get_current_date”。这就麻烦了,你的解析逻辑会变得很脆弱。

我的建议是:如果要做一个正经的Skills系统,优先选择原生支持工具调用或Function Calling能力的模型。这类模型经过专门训练,输出格式稳定,错误率低得多。开源模型里,近一两年发布的主流模型基本都覆盖了;闭源模型产品也同样,你选型时把这个能力当成和“上下文长度”一样重要的硬指标。

另外,注意看模型对工具的“理解上限”。有些模型能一次返回多个工具调用,有些模型一次只能返回一个。这会导致同一段逻辑在切换到不同模型时表现不同。最好在设计时就支持“单次调用”和“批量调用”两种模式,方便灵活切换。

4.4 超时、重试与可观测性:工程化必须补的课

Demo能跑通只是第一步,上了真实业务,你还要考虑这些工程问题:

  • 工具执行超时。如果一个函数卡住了,不能让它一直占着对话线程。我会给每个工具设置独立的超时时间,比如5秒。
  • 重试策略。对网络请求类工具,我会做“最多重试3次、指数退避”的策略;对纯计算类工具,则不重试,直接报错回传。
  • 调用链路日志。我会记录每次请求时间、模型返回的tool_calls原始内容、工具执行耗时、结果回传后的最终回复。有了这些日志,调起问题来才有抓手;否则用户反馈一句“回答不对”,你根本不知道是模型理解错了,还是工具返回错了,还是回传格式错了。

5. 踩坑实录:常见问题与排查技巧

5.1 症状:模型不调用工具,硬要自己回答

这是最常见的坑。你明明给了工具清单,模型却像没看见一样,凭感觉编答案。我遇到过一次,排查了半天,最后发现是工具描述里有一句话有歧义,模型误解成“只有在用户明确要求时才能使用该工具”。

排查思路:

  • 检查工具描述里是否用了“如果”“可选”“可能”这类模糊词。
  • 检查示例对话里是否有“不要调用工具”之类的负面引导。
  • 尝试把用户问题改写得更明确,比如“请使用日期计算工具来计算”。

我的经验是,模型对工具的使用倾向很大程度取决于描述里的“默认值”。你要明确写“当用户提到日期相关问题,必须调用本工具”,少用“可以”。从“允许使用”到“必须使用”,差别很大。

5.2 症状:模型传错了参数,或者编造参数

模型合理发挥,但参数填得离谱。比如用户问“三天后”,它传给get_date_offset的days却是“2025-06-17”。

这种问题的根源,往往是描述里没有讲清楚参数的单位和语义。我的解决方案是,在参数描述里加“示例值”:

"days": { "type": "integer", "description": "偏移天数。正数表示未来,负数表示过去。例如:三天后传3,三天前传-3。", "examples": [3, -10] }

加了这段之后,参数错误率明显下降。模型看到示例,就知道该填什么格式了。

5.3 症状:工具执行成功了,但回答还是错的

这说明问题不在工具,而在模型拿到结果后的“整理”环节。一种可能是你回传结果时没有标明对应的tool_call_id,模型搞不清结果和数据请求的对应关系;另一种可能是结果太“技术化”,比如你返回了一个很大的JSON,里面有几十个字段,模型抓不住重点。

解决方案有两个:

  • 在回传前对工具结果做“精简化”,只保留模型回答所需的关键信息;
  • 在后续消息里加一句“请根据以上工具结果,用简洁语言回答用户原始问题”。

不要认为模型天然知道该怎么用你给的工具结果。它也需要被引导。

5.4 症状:提示词越来越长,调用却越来越不稳定

很多人在调试时喜欢把“使用工具”的指令直接写进系统提示词里。一开始还有效果,但随着提示词越来越长,模型开始频繁跑偏。

原因是工具清单本身就是一种“上下文噪音”,如果你在提示词里用自然语言重复描述工具,相当于给模型提了两套要求,它们之间一旦互相干扰,模型就会混乱。我现在倾向于:

  • 工具清单始终通过结构化的tools参数传递;
  • 系统提示词只描述角色和对话风格,不重复工具细节;
  • 如果确实需要强调工具使用,用一句话带过,比如“你有一些工具可以查询实时信息,当你需要时记得使用”。

5.5 避坑速查表

症状常见原因优先排查方向
模型完全忽略工具描述措辞模糊、上下文引导冲突检查描述里的“可以/必须”语气
参数错误缺少参数格式说明和示例补全参数描述、增加examples字段
调用成功了但答案不对结果回填不匹配、数据太原始核对tool_call_id、精简结果格式
多轮调用后混乱历史消息里缺少tool消息确认每条工具结果都追加了对应消息
单次调用太多导致超时模型批量输出多个调用限制单轮最大调用数,分批执行

6. 给不同角色的实用建议

6.1 如果你只是想用现成大模型产品

你会发现很多头部产品自带“联网”“文件处理”“图像生成”等能力。这其实就是不同形态的Skills,只不过产品团队替你封装好了。你要做的,是在使用时留意它是否主动调用了工具、是否标注了信息来源。如果某个产品声称能处理本地文件或实时数据,但运行起来完全靠模型“脑补”,那就说明它的Skills链路没做好。用户视角的判断方法很简单:让它处理一件必须靠工具才能完成的事,比如“帮我读取这份Excel的第3列”,看它是真读还是假编。

6.2 如果你在开发自己的业务系统

我给你的建议只有一条:先画清楚“模型必须依赖工具”的清单。把业务里所有需要精确计算、实时查询、权限操作、状态变更的环节列出来,这些都是Skills的用武之地。然后从最小的闭环做起:先接一个工具,跑通“调用—执行—回填—再回答”,再逐步扩张。不要一开始就堆十几个工具上去,工具越多,模型的选择负担越大,错误率也会跟着上升。

工具数量的控制很微妙。我个人经验是:在一个对话场景里,给模型提供5到8个工具是相对舒适的区间。超过10个,模型对某些低频工具的记忆和调用意愿就会明显下降。

6.3 如果你在研究模型机制

可以重点关注模型在“何时决定调用工具”上的行为变化。比如改变工具描述的顺序会不会影响调用偏好?工具结果里包含错误信息时,模型会不会自动纠错?这些问题的答案不只是学术趣味,它们会直接影响你设计Prompt和工具说明的方式。

我观察到的一个有意思现象是:当同一批工具中有一个效果特别稳定、一个偶尔出错时,模型整体调用成功率会被那个“偶尔出错”的工具拖累。它可能尝试调用坏工具失败后,连带着对好工具也失去信心,转而自己瞎编。这就解释了为什么工具质量本身也会影响模型的行为倾向。

最后分享一点我的体会

做这个Skills Demo的过程中,我最大的一个感受是,模型的“聪明”和“能干”完全是两回事。模型再聪明,也需要一套清晰、稳定、可回退的“手足”去触碰真实世界。而真正难的,从来不是把函数实现出来,恰恰好是那些不起眼的细节:工具描述怎么写得让模型一读就懂、参数怎么设才不容易错、工具结果怎么回传才能让模型接得住。

我在实际部署时,还会在工具描述里故意加上一些“一次性提示”,比如“用户提到明天时,应基于今天进行计算”。这种一眼看上去像是废话的话,在降低模型出错率上反而特别有用。模型和人不一样,它不会觉得你啰嗦,它会把你写的每句话都当成决策依据。

如果你正准备给自己的应用接上Skills,别被概念吓退。找一个最小的需求,比如“查日期”“做个计算”,先跑通整个闭环,再把这种模式复制到更多场景里。等你亲手看完一轮又一轮“思考—调用—执行—回答”的循环,你就会理解:让大模型成为真正能干活的助手,关键不是换一个更大的模型,而是给它配好一双手脚,并教它学会使用。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询