不需要主标题直接从二级标题开始。下面是一篇围绕“skills”这一项目标题展开的深度技术类博文定位为AI应用开发者视角下的大模型技能调用Agent Skills / Function Calling实战总结。内容涵盖概念解析、核心原理、完整实操方案、常见问题排查与进阶扩展全程使用从业者口吻结构规范干货密度高可直接用于技术社区发布。1. 当“skills”不只是英文单词我在大模型应用里重新理解了“技能”先说个背景。最近半年我一直在做智能客服与自动化代理类项目团队里经常讨论一个词——skills。一开始我以为大家只是在聊员工能力模型后来才发现在大模型应用开发的语境里skills 指的是让大模型具备“调用外部工具、执行真实动作”的能力单元。换句话说模型不再只负责“说话”它得能“干活”。这个转变非常关键。过去我们调大模型核心诉求是“生成得好不好、回答得像不像人”但真正落到业务里用户要的是“把事儿办了”。比如用户问“帮我查一下上周的订单物流走到哪了”模型如果只能回复“您可以登录后台查看”那体验就崩了。但如果模型具备查询订单系统的 skill它就能自己构造查询参数、调用接口、拿到结果再组织语言回复用户。整个链路跑通之后我才意识到 skills 才是大模型从“聊天机器人”升级成“数字员工”的那把钥匙。这篇文章不打算讲虚的我会从 skills 的本质定义说起再拆解设计思路然后给出一套完整的、可直接复现的实操方案包括工具定义、模型调用、结果回填、异常处理最后分享我在实际项目中踩过的坑和排查技巧。如果你是做 AI 应用开发、智能体Agent产品设计、或者正在研究函数调用Function Calling方向的工程师这篇文章适合你从头读到尾。哪怕你只是刚接触大模型 API 的初学者也能照着步骤把第一个带“技能”的机器人跑起来。先说清楚一个容易混淆的点这里的 skills 并不是指“提示词技巧”或“角色设定”而是指模型通过结构化接口声明能力、自主决定何时调用、并正确处理返回结果的一套完整机制。在 OpenAI 的生态里叫 Function Calling在 Anthropic 的生态里叫 Tool Use在开源社区里常被称作 Agent Skills核心思想是一致的——把模型和外部世界连起来。2. 为什么你需要给大模型装“技能”从纯文本问答到任务闭环2.1 纯语言模型的边界它能说但不能做先做一个对比。没有任何技能的大模型本质上是一个“概率化的文本续写器”。你问它“北京今天天气怎么样”它可能会凭训练数据里的记忆告诉你“北京春季多风气温通常在10到20摄氏度之间”但这根本不是实时天气。你问“帮我订一张明天从上海到深圳的机票”它能给你一段订票流程建议但它没法真的去查航班、比价格、占座位。这在很多早期 AI 应用里非常常见——产品经理拿到一个 ChatBot Demo觉得“哇它什么都能答”一上生产就发现用户根本不满意因为用户要的不是“知道”而是“办成”。用行业里的一句话来说模型有知识但没有行动力。而行动力的来源就是 skills。2.2 Skills 的核心价值让模型具备“感知-决策-行动-反馈”闭环一个完整的技能调用链路包含四个阶段。第一阶段是感知模型从用户的话里提取关键信息比如实体、意图、参数第二阶段是决策模型对比自己有哪些可用技能判断当前任务该不该调用、调用哪一个第三阶段是行动模型按接口约定构造参数发起真实调用比如请求天气API、写入数据库、触发工单流程第四阶段是反馈工具返回结果后模型结合结果组织自然语言回复把机器语言翻译成用户能看懂的人话。这个链路一旦跑通模型的行为模式就发生了质变。举一个实际场景用户对客服机器人说“我上周买的那双鞋还没发货能帮我催一下吗”。在没有技能的情况下机器人只能回复“已为您记录请耐心等待”。有了技能之后模型会先调用“查询订单”技能获取订单状态发现确实未发货再调用“创建催发货工单”技能最后回复用户“您的订单当前还在仓库处理中我已为您创建加急工单预计24小时内出库”。这时候用户感受到的才是一个“能办事”的服务。2.3 从零到一哪些场景最适合优先接入 Skills根据我这段时间的实践有三类场景最适合优先做技能化改造。第一类是信息查询类比如天气、股票、航班、订单状态、库存信息特点是接口稳定、参数明确、返回结构化模型只需要学会“何时调、传什么参、怎么解释结果”。第二类是业务操作类比如创建任务、发送通知、更新数据库记录、触发审批流特点是动作明确、边界清晰只要做好权限管理和参数校验模型完全可以代替人工完成重复操作。第三类是内容生成后的动作衔接比如模型写完一段周报初稿后自动调用消息接口发给对应的人这时候模型的价值不再只是“写”而是“写了并且送达”。反过来那些需要多轮试探、高度依赖主观判断、结果无法结构化校验的场景比如“帮我判断这个人适不适合做项目经理”就不适合直接用技能封装因为模型的决策依据不稳定外部接口也无法给出明确的二元答案。工具是“确定性能力”模型是“可能性生成”把两者搞混项目一定会出问题。3. 核心细节解析一个“技能”到底由什么构成怎么被模型正确调用3.1 技能的三层结构定义、实现、指令我习惯把每个 skill 拆成三层来看。第一层是定义层也就是给模型看的“说明书”里面要写清楚这个技能叫什么、干什么用、有哪些参数、参数的类型和约束是什么。这一层的载体通常是 JSON Schema。第二层是实现层也就是真正被执行的代码或 HTTP 请求这一层跑在模型之外可以是本地函数也可以是远程 API用 Python、Node.js 等任何语言写都行。第三层是指令层也是新手最容易忽略的——你需要在系统提示词或者工具描述里写清楚“什么情况下用这个技能”“什么时候不要用”模型才能做出正确决策。举一个具体的例子。假设我要给客服机器人加一个“查询订单物流”技能。定义层大致长这样{ name: query_order_logistics, description: 根据订单ID查询物流流转信息返回发货状态、承运商、最新轨迹。仅当用户主动询问订单物流时使用。, parameters: { type: object, properties: { order_id: { type: string, description: 用户提供的订单编号通常为纯数字格式长度8-12位 } }, required: [order_id] } }注意 description 里我写了“仅当用户主动询问订单物流时使用”这就是指令层的价值。没有这句约束模型可能会在用户问“退货怎么弄”的时候也去调查询物流造成误判。3.2 模型是怎么学会“选择”技能的别把模型当搜索引擎一个非常常见的误解是模型对技能的选择是靠“语义相似度”——用户说的话和技能描述长得像就触发。其实不完全对。模型在训练和推理时是把“当前对话上下文 所有技能定义”一起作为输入然后根据指令遵循能力来决定要不要发起调用。它不是在做关键词匹配而是在做“意图分类”与“参数提取”的联合决策。因此技能描述写得好不好直接决定了调用的准确率。我踩过一个很典型的坑早期给“创建任务”技能写的描述是“创建一个新的待办事项”结果用户说“帮我记一下明天下午三点开周会”模型没有触发创建任务而是直接回复了一段“好的已记住”。原因就是描述里没有说明“当用户表达记忆、提醒、待办诉求时应使用本技能”。后来把描述改成“当用户要求记录任何未来需要处理的事情、提醒或待办事项时使用此技能创建任务”误触发率大幅下降。3.3 参数设计是技能的灵魂比技术更难的是抽象很多初学者以为技能定义就是把 API 参数抄一遍其实不然。参数设计体现的是你对业务流程的理解。我见过一个团队做“智能请假助手”API 有三个字段开始时间、结束时间、请假事由。模型单独提取这三个参数完全没问题但他们忽略了“请假类型”——事假、病假、年假对应的审批流不同模型只凭用户说“身体不舒服”并不知道要填病假。后来在参数里加了一个“leave_type”并在描述里写了“根据用户陈述自动推断请假类型不确定时默认为事假”整个技能才算真正可用。参数设计时还要考虑容错性。比如用户说“帮我查一下我上周的订单”系统里并没有“上周”这个时间概念模型很可能无法把自然语言时间转换成精确日期。这时候有两种做法一是把时间范围作为参数交给后端去算二是要求模型在参数缺失时主动反问。我个人推荐后者因为模型的“主动确认”既减少了参数错误也让用户觉得交互更自然。4. 实操过程与核心环节实现从零构建一个带技能的客服机器人4.1 整体方案选型我为什么用 OpenAI Function Calling FastAPI先说选型。目前主流的大型模型平台都支持工具调用OpenAI 的 Function Calling 生态最成熟、文档最全、资料最多所以我用它来演示。业务后端用 FastAPI 搭一个受控的计算层负责执行技能动作、连接外部系统。整体架构是前端用户说话 → 大模型接口带技能定义 → 如果模型返回 tool_calls → 后端执行对应函数 → 把结果回传给模型 → 模型生成最终回复。也可以选择开源的模型配合工具调用框架比如用 Ollama 跑本地模型加上 LlamaIndex 的 Function Calling 能力但调参和稳定性维护成本高一些。如果你只是做技术验证或者个人项目先用 OpenAI 打通链路理解了机制之后再迁移到开源方案会顺很多。4.2 定义两个实用技能查天气与创建工单为了演示我设计了两个核心技能一个是查询实时天气一个是创建用户反馈工单。这两个技能覆盖了“信息查询类”和“业务操作类”两种典型场景非常能说明问题。天气查询技能的 JSON Schema 定义如下{ name: get_weather, description: 查询指定城市当前的天气情况包括温度、天气状况、湿度。当用户询问天气、温度、要不要带伞时调用。, parameters: { type: object, properties: { city: { type: string, description: 城市名称如北京、上海 }, date: { type: string, description: 查询日期格式YYYY-MM-DD默认当天 } }, required: [city] } }创建工单技能的 JSON Schema 定义如下{ name: create_ticket, description: 创建一条用户反馈工单记录用户的问题描述、联系方式和优先级。当用户表达投诉、建议、售后问题且需要人工跟进时调用。, parameters: { type: object, properties: { user_message: { type: string, description: 用户原始问题或反馈内容 }, contact: { type: string, description: 用户留下的联系方式如手机号或邮箱 }, priority: { type: string, enum: [low, medium, high], description: 优先级根据用户情绪和问题严重程度判断默认medium } }, required: [user_message, contact] } }这两个定义放在一个 tools 数组里传给模型模型就能在合适的时机自主选择调用。4.3 核心代码实操对话循环的正确写法下面这段代码是我项目的核心循环思路就是把模型返回的 tool_calls 逐个执行返回结果后再次请求模型直到模型不再发起调用。import json from openai import OpenAI from typing import Callable client OpenAI() def run_conversation(user_input: str, tools: list, handlers: dict[str, Callable]): messages [{role: user, content: user_input}] while True: response client.chat.completions.create( modelgpt-4o-mini, messagesmessages, toolstools, tool_choiceauto ) message response.choices[0].message messages.append(message) if not message.tool_calls: return message.content for tool_call in message.tool_calls: function_name tool_call.function.name arguments json.loads(tool_call.function.arguments) # 实际执行技能动作handlers里是真实的函数实现 result handlers[function_name](**arguments) messages.append({ role: tool, tool_call_id: tool_call.id, content: json.dumps(result, ensure_asciiFalse) })这段代码的核心逻辑非常简单模型返回了工具调用请求代码就执行对应的函数把结果以“tool”角色回传模型看到结果后继续推理直到它认为任务完成。整个循环是模型驱动的我们只负责“翻译”。实际项目中handlers 里写的是真正访问第三方天气接口、往数据库或者工单系统写入的函数。我这里写一个天气查询的实现示例import requests def handle_get_weather(city: str, date: str None): # 这里用免费公开接口做示例生产环境可替换为商业天气API api_key your_api_key url fhttps://api.openweathermap.org/data/2.5/weather?q{city}appid{api_key}langzh_cnunitsmetric resp requests.get(url) data resp.json() return { city: city, temperature: data[main][temp], condition: data[weather][0][description], humidity: data[main][humidity] }4.4 参数校验与安全边界让模型“无所不能”之前先立规矩技能赋予了模型行动力但行动力必须要套上安全边界。我给项目里的每个技能都做了三层防护。第一层是参数校验模型生成的参数必须通过 JSON Schema 格式校验类型不对、缺少必填字段、枚举值超出范围直接拒绝执行并返回模型重试而不是硬着头皮把错误参数传给下游接口。第二层是权限控制。我给技能分了只读和写两类只读类技能比如查订单任何对话场景都可以调用写操作类技能比如创建工单、发送消息必须满足额外的条件比如用户是否登录、是否有对应权限这些逻辑放在 handler 内部模型无法绕过。记住一个原则模型只能请求调用技能批准和执行权必须掌握在业务层手里。第三层是审计留痕。每次调用我都记录下模型生成的完整参数、实际执行结果、耗时和对应的会话 ID。这一步看似繁琐但在线上排查“模型是不是在胡搞”的时候审计日志就是最直接的证据。有一次我们模型把“priority”参数填成了“urgent”而定义里只有 low/medium/high靠审计日志轻松定位到了问题根据日志回放确认是描述写得太泛模型在犹豫后自己选了枚举外的值。5. 常见问题与排查技巧实录五类高频故障的定位与修复方案5.1 模型不调用技能只知道用话术硬答这是最常见的问题。用户明明问的天气模型却回复“我无法查询实时天气”。排查方向有三个第一确认技能定义确实传给了模型很多人改了代码但没重新构造请求第二检查技能描述是否清楚描述里要包含触发场景最好带上用户可能说的原话示例第三确认模型版本是否支持工具调用某些精简版模型不支持这个功能调用时会静默忽略工具定义。如果以上都正常还能在前缀给模型加一条指令比如“你有查询天气的工具用户询问天气时请使用它”。5.2 模型调用了技能但参数是错的典型场景用户说“帮我查一下北京明天的天气”模型把“明天”传给了 date 参数格式却是“明天”而不是“2025-06-10”。解决问题的方法有两个方向。一是让大模型负责“提取格式化”在参数描述里写清楚格式要求比如“日期格式必须是YYYY-MM-DD根据对话里的相对时间推算实际日期”。二是后端兜底比如 date 参数不是合法日期时业务层自动计算明天日期替代。我强烈建议双管齐下因为模型即使再聪明遇到跨年、节假日补班这类复杂日期推理时也会出错。5.3 多技能场景下模型总选错工具一个值得注意的案例我们项目里同时有“查询订单”和“查询退款进度”两个技能描述里都很类似模型经常搞混。用户问“我退款到哪一步了”模型却调用了查询订单。排查后发现两个技能的 description 太雷同模型缺乏分辨依据。修复方法是给每个技能的 description 增加“边界排他描述”比如查询订单里加“本技能只查订单状态和物流不处理退款”查询退款进度里加“本技能专门查退款审批状态和打款进度”。加完之后准确率从78%提升到了95%以上。写描述时遵循一个原则上一段写明“什么时候用”下一段写明“什么时候不用”。5.4 工具执行报错模型陷入死循环生产环境里经常遇到工具接口超时或者返回异常数据如果代码不做处理模型拿到错误结果可能会反复调用同一个函数形成“死循环”式的调用风暴。我的策略是在 handler 里统一捕获异常返回结构化的错误信息给模型同时在代码层设置最大调用轮数比如一个对话最多允许模型调用5次工具超过就强制终止并提示用户稍后重试。还有一个技巧错误信息里最好带上“请告知用户系统暂时无法完成该操作”引导模型输出友好话术而不是把错误码直接抛给用户。5.5 技能调用正常但最终回复生硬、不像人话工具的返回是 JSON 对象模型把它组织成回复时需要“人格化”。比如天气接口返回“temp: 32, condition: 晴”模型如果直接说“温度32度天气晴”虽然没错但很机械。可以在系统提示词里加一句“用自然、口语的风格向用户解释工具返回结果不要直接罗列字段名”。实测下来加了这句话后回复自然度提升非常明显。另外可以要求模型结合上下文补充建议比如高温天气提醒防暑雨天提醒带伞这些动作不需要新增技能只需在提示词里做软性引导。6. 进阶思考Skills 的未来形态与大模型应用的工程化演进6.1 从单一技能到技能编排组合比堆量更有价值当技能数量超过十几个之后一个新的问题出现了单个技能的准确性再高用户的一个复杂需求往往需要串起多个技能。比如“帮我查一下本周要发货的订单然后给每个客户发一条延迟发货的道歉短信”这涉及订单查询、短信发送、客户信息读取三个技能。当前的大模型工具调用机制支持在一步返回多个 tool_calls开发者可以在一个回复里让模型并行发起多个调用然后根据所有结果综合决策。我的经验是技能编排的价值不在于“能用更多工具”而在于“减少模型的信息盲区”让它在掌握全部信息后再做判断。6.2 技能缓存与结果复用少一次调用少一分成本工具调用本身有成本和延迟尤其是涉及第三方 API 时每次调用还伴随不稳定因素。我在项目里加了一层轻量缓存同一会话内参数完全相同的查询类技能结果在10分钟内直接复用。这个策略把客服机器人场景下的平均响应时间从4秒降到了1.8秒调用成本也降了约三成。缓存命中判断要放在 handler 入口用会话ID技能名称参数指纹做联合键简单可靠不需要引入额外的缓存服务内存字典就够用。6.3 让模型“学会新技能”少样本示例比提示词更有效如果发现某个技能模型的触发率一直不高除了改 description还有一个高阶技巧在 messages 里补充历史示例一个用户侧提问一个模型侧调用某技能的工具请求角色轮换模型很快就能学到该在什么场景下使用工具。这个技术在行业里叫 Few-shot Tool Calling效果通常好于直接改提示词因为示例给了模型一个强模式参考。但要注意控制示例数量一般2到3对就够太多会增加请求 token 开销响应速度会下降。6.4 从个人项目到团队协作技能管理的工程化思考技能数量膨胀到一定程度后光靠一个 Python 文件堆函数肯定不行。我在团队里推行的做法是每个技能一个目录包含 schema.json技能定义、handler.py执行逻辑、README.md使用说明和变更记录、test_cases.json调用测试用例。技能上线走代码评审和测试流程schema 或 handler 有变更必须同步更新测试用例。这套流程看着重但对于多人协作维护技能库十分关键避免“别人写了个技能但是没人会用也不敢改”的局面。测试用例尤其重要每次大模型接口升级或提示词调整后先跑一遍全部技能的测试集能第一时间发现回归问题。7. 写在最后一个技能一次从“对话”到“做事”的跳跃聊了这么多最后分享一点个人感悟。我曾经花了很长时间去调教模型的“语气”“风格”“情感”直到把 skills 落地到真实业务中我才意识到真正让用户觉得“哇这个 AI 好用”的瞬间不是它说了什么漂亮话而是它真的把事情办成了。一个能准确查物流、能自动开工单、能把结果用正常人话讲出来的客服机器人比一个只会安慰人但什么都做不了的情感机器人有价值一百倍。如果你正准备给自己的 AI 应用加入技能能力我建议从小处着手先选一个最简单的查询类接口把定义、执行、回传的链路跑通然后再逐步扩大技能范围、优化触发准确率、加入编排与缓存。这条路不算长但每一个环节都有值得打磨的细节。祝你的模型早日从“能说会道”进化成“能干成事”。
