尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
Agent技能实战:从Function Calling到技能库设计,让大模型真正“会做事”
1. agent-skills到底是个什么东西为什么圈内人都在聊最近后台收到不少读者来问 agent-skills 相关的问题大多是同一个困惑我的 Agent 已经能正常对话了也能接上大模型 API可一旦让它“真正干点活”——查个文件、调个接口、改个配置——它就只会说“我无法直接操作”或者给你返回一段正确的废话。这其实就是 Agent 开发里最典型的一道坎模型很聪明但手脚是废的。agent-skills 解决的就是这件事它本质上是给 Agent 配备的“可复用行动单元”让模型从“会说话”进化到“会做事”。我理解的 agent-skills 不是一个固定的开源库名而是一整套关于技能Skill的定义、组织、注册和调用规范。你可以把它理解成 Agent 的“工具箱”每个技能就是箱子里一把趁手的工具模型自己会去挑哪把合适。这篇文章我不会跟你念文档而是把我实际搭技能库、调试技能调度、踩坑排查的过程完整写出来。适合正在做 Agent 应用、研究 Function Calling、或者准备把自动化能力接入业务系统的开发者参考。讲原理但更偏实操所有代码和配置你都能直接抄。1.1 技能的本质从“输出文字”到“执行动作”先说一个最容易被忽略的事实大模型本身就是个“纯文本进出”的系统。你给它一段 prompt它返回一段文本这就是全部。它不会真的去读你磁盘上的文件不会真的去调用支付接口也不会真的帮你把服务器重启了——它只是“生成了”一段看起来像会做这些事的文字。那 Agent 是怎么“动手”的靠的是外部执行器。模型根据当前对话场景从预定义的技能列表里挑一个然后按技能定义里写好的参数格式生成一次调用请求框架收到请求后执行真正的代码逻辑再把执行结果以文本形式回填给模型让模型基于真实结果继续回答。整个过程模型没有直接操作任何东西它只是在“发指令”真正干活的是技能背后的那段函数。我打个比方。新来的实习生很聪明能看懂资料也能写报告但刚入职没开通公司的 OA、ERP 权限也没人教他报销流程长什么样。你让他“去把上个月的订单数据导出来”他只能干瞪眼。skills 就是给这个实习生配的“岗位权限操作手册”告诉他“导出订单时走这个接口”还告诉他“调用时参数怎么填”他才能真正把事情办了。所以技能的本质就是给模型这个“聪明但无手”的实习生装上能干活的手。1.2 为什么不能把逻辑全塞进 System Prompt有人可能想那我直接在系统提示词里把操作步骤写得清清楚楚让模型照着文本里的伪代码“假装执行”然后再由外部程序去解析这段文本不行吗说实话这条路早期确实有人这么干过比如让模型输出固定格式的 JSON再由调度程序解析执行。但它有天花板而且越往后越难受。最明显的问题是上下文窗口。你塞一份完整的操作手册进 System Prompt每个会话都要重复计费占掉的 token 你都要付钱。技能多了以后 prompt 会变得非常臃肿模型反而开始“忘事”你让它执行 A 操作时它注意力可能已经被后面的 C 操作描述分散了。其次文本约定没有类型校验模型输出 JSON 时字段名差一个字母外部解析就直接报错或者更糟解析成功了但执行了错误逻辑。对比一下就能看明白对比维度全部塞进 System Prompt独立的技能定义与注册上下文占用每次会话都全量占用仅在调用时按需注入复用性换个 Agent 要复制粘贴技能独立注册多 Agent 复用参数可靠性靠模型自觉易出错有结构化 Schema 约束调试成本改一处要重新验证全流程单技能单测定位快扩展方式Prompt 越改越长加一个技能包就是一个能力所以正规的 agent-skills 项目几乎都会走“结构化技能定义动态注册”的模式。模型只会在某个技能可能相关时看到技能的名字、描述和参数说明而不是把所有技能和全部操作细节都塞进上下文。1.3 技能、工具调用与 MCP 的关系聊 agent-skills 绕不开三个概念Function Calling、Tool Use、MCP。很多人问我它们是不是一回事其实不是但对 Agent 技能体系来说它们是层层递进的关系。Function Calling 是模型侧的一种能力指模型能输出一次结构化的“函数调用意向”比如“调用 search_files参数是 path/home/userpattern*.log”。Tool Use 是应用侧的实现模式你提供一组工具模型在需要时选择工具。MCPModel Context Protocol则是把这些能力统一成一种标准化协议让技能的定义、发现、调用和传输有统一的格式。agent-skills 更像是在这些基础之上的一层工程化封装你把某项能力做成一个包包含描述、参数、执行体和返回格式然后按一定规则挂载到 Agent 上。理解这层关系很重要因为网上一堆项目名都带 skills有的其实就是 MCP server 的配置集合有的则是 Function Calling 工具的函数列表。你在参考的时候先搞清楚它是哪一层才不会抄错方向。我下面讲的技能设计方法无论底层用 Function Calling 还是走 MCP都能直接套用。2. 技能库的整体设计先想清楚再动手很多入门教程上来就让你写函数、注册工具但我实际做了几个项目后发现最费时间的根本不是写函数本身而是技能库的顶层设计。技能定义得太粗模型看不懂定义得太细维护成本爆炸。这节我把自己的设计思路完整拆开。2.1 技能的最小单元长什么样一个技能在落地时至少要包含五个要素名称、描述、参数定义、执行体、返回约定。缺哪一个都会在运行期出幺蛾子。{ name: search_local_files, description: 根据关键字和路径搜索本地文件支持按文件类型过滤适合用户要求查找或定位文件时使用。, parameters: { type: object, properties: { root_path: { type: string, description: 搜索的起始目录默认当前工作目录 }, keyword: { type: string, description: 文件名中要匹配的关键字支持模糊匹配 }, file_type: { type: string, enum: [all, doc, image, code, log], description: 要筛选的文件大类默认 all }, max_results: { type: integer, description: 最多返回结果数默认 20最大 100 } }, required: [keyword] } }这五要素里最容易翻车的是描述和参数说明。原因后面细说这里先记住一个原则技能的定义不是写给人看的是写给模型看的。模型的“阅读理解”能力决定了它只能依据字面含义去匹配技能所以描述必须具体参数说明必须把边界条件写透。执行体就是实际跑逻辑的那段代码可以用 Python、Node.js 或任何语言实现关键在于它必须是“纯函数式”的给定参数返回结果不依赖外部状态不隐藏副作用。这样技能才能被安全地并发调用、重试和单元测试。返回约定则决定模型能不能读懂执行结果。我强烈建议所有技能都返回结构化 JSON并且包含两个字段success布尔值和summary一段面向模型的话术摘要。很多人只返回数据导致模型拿到一堆原始 JSON 不知道怎么向用户解释这就是后面“驴唇不对马嘴”问题的根源。2.2 技能描述是给模型看的“说明书”技能描述这个东西在本地调试时可以糊弄但一上真实环境模型调不调用你的技能基本就靠这段描述。写得太抽象模型根本不知道该在什么场景用它。举个例子第一个版本我写过“用于文件搜索的工具。”结果是什么模型在用户说“帮我找一下项目里的配置文件”时依然回复“抱歉我无法直接访问你的文件系统”。它压根没意识到这个技能能派上用场。后来我把描述改成了这样“当用户要求查找、定位、搜索本地文件或目录时使用此技能。可指定起始路径、文件名关键字、文件类型过滤条件。典型场景包括帮我找一下某个配置文件、根据关键字搜索日志文件、统计某个目录下有哪些图片文件。” 同样是这个技能模型几乎每次都能正确触发。这里面的逻辑并不玄学。模型只在对话上下文中看到技能的名称和描述看不到技能背后的代码。技能描述就是它做“选哪个工具”这个决策的唯一依据。你描述里写了“搜索”它在遇到“找一下”这种口语时是没法自动把“找”和“search”对齐的除非你把触发场景、同义表达、边界情况都写清楚。2.3 技能分层基础技能、组合技能、编排技能技能一旦多起来几十个技能平铺在列表里模型的选择准确率会明显下降。这不是模型笨而是选择空间太大干扰太多。我后来采用三层结构解决了这个问题。基础技能是最底层的原子操作比如“读文件”“写文件”“执行 shell 命令”“发 HTTP 请求”。这类技能尽量做到细粒度、职责单一每个技能只做一件事。组合技能是在基础技能之上封装出来的业务动作比如“根据关键字搜索日志并统计错误次数”它内部会调用读文件、搜索、正则匹配等多个基础技能。编排技能则更上层通常本身不直接执行操作而是负责决定“先调哪个组合技能、再调哪个基础技能”的流程。层级职责示例特点基础技能原子操作read_file、search_files、http_request无状态、可复用、可单测组合技能业务动作search_logs_and_count_errors编排基础技能有明确业务含义编排技能流程决策analyze_project_and_generate_report面向复杂任务内部多步为什么要分层因为模型不擅长一次处理太长的工具链。你给它一个“分析项目结构并生成报告”的编排技能描述写得再清楚它也很难立刻理解内部步骤。但如果你提供的是清晰的组合技能模型只需要决策“现在调用 analyze_project_report”剩下的内部调度交给代码逻辑成功率会高很多。另一个好处是维护方便。底层接口变了只改对应基础技能上层组合技能和编排技能不用动。复用性也好换个行业场景组合技能在几个 Agent 之间能共享。3. 从零手写一个技能完整实操记录这节我会从头到尾走一遍自己做技能的流程场景选最典型的“技能调度落地”顺便把容易出问题的地方全部标出来。建议你开着编辑器跟我一起写比干看印象深刻。3.1 场景设计与输入输出定义我选择实现的技能是“按关键字搜索本地日志并统计错误类型分布”。这个技能很典型既有文件搜索又有文本解析还要返回统计结果能覆盖技能开发的大部分要点。先想清楚需求输入输出。用户诉求可能是“看看这周 error 日志里有没有数据库连接相关的错误”模型需要知道去哪里找日志、按什么关键字筛、返回什么统计信息。所以我设计的参数包括log_dir日志目录选填默认 ./logs、days只看最近几天的文件默认 7、keyword筛选用关键字选填不填则统计所有级别、top_n返回数量最多前几种错误类型默认 5。输出上我要求执行体返回一个结构化的 JSON包含 success、total_files扫描文件数、total_matches匹配行数、top_errors按类型聚合的结果、summary给模型读的一句话结论。设计这一步想清楚的好处是后面写函数和调试的时候目标非常明确。参数默认值要格外用心。比如log_dir我给了默认当前目录下的 logs 文件夹days给默认值 7这样用户只是含糊地说“查一下日志错误”模型也能直接调用不需要反复追问用户细节。凡是能给默认值的参数一定给默认值。3.2 执行体代码实现与边界处理执行体我用 Python 实现。读日志、按关键字过滤、按错误类型正则提取逻辑不复杂但边界情况特别多。第一步是文件遍历要处理目录不存在、权限不足、文件编码不是 UTF-8 这三种情况第二步才是关键字过滤和类型统计。import os import re import json from collections import Counter from datetime import datetime, timedelta def scan_logs(log_dir: str ./logs, days: int 7, keyword: str , top_n: int 5): # 基础校验 if not os.path.isdir(log_dir): return { success: False, message: f日志目录不存在: {log_dir}, summary: 用户提供的日志目录不存在无法执行搜索。 } cutoff datetime.now() - timedelta(daysdays) total_files 0 total_matches 0 error_counter Counter() for root, _, files in os.walk(log_dir): for fname in files: fpath os.path.join(root, fname) if not fname.endswith((.log, .txt)): continue # 跳过超出时间范围的文件 mtime datetime.fromtimestamp(os.path.getmtime(fpath)) if mtime cutoff: continue total_files 1 try: with open(fpath, r, encodingutf-8, errorsignore) as f: for line in f: if keyword and keyword not in line: continue total_matches 1 # 错误类型通常是 [ERROR] xxx match re.search(r\[(ERROR|WARN|INFO|DEBUG)\]\s*(.), line) if match: error_counter[match.group(1)] 1 except PermissionError: continue top error_counter.most_common(top_n) if error_counter else [] summary f扫描了 {total_files} 个文件匹配到 {total_matches} 条日志。 if top: summary 主要日志级别分布 , .join( f{k} {v} 条 for k, v in top ) else: summary 未发现符合条件的日志级别。 return { success: True, total_files: total_files, total_matches: total_matches, level_distribution: top, summary: summary, }这段代码实际跑通了但有三个经验值得单独说。第一打开文件一定要加errorsignore。日志文件混入 GBK 或 Latin-1 编码是常态不加这个参数一个非法字符就能让整个技能崩溃。第二权限问题要安静跳过而不是直接返回错误。你搜一个目录时有几个文件没权限不应该影响整体结果执行体里把PermissionError吞掉继续往下走最后的 summary 里体现扫描的文件数即可。第三返回的 summary 是给模型看的“人话”必须是一段自然语言。模型拿到这段文字才知道怎么向用户解释结果。你只返回一个 Counter 对象模型看着那串数字很容易开始胡说。3.3 定义技能元数据用模型的视角写参数执行体完成之后接着要写技能元数据。这是很多新手最容易忽略的一步但我可以说90% 的技能调用失败都发生在这一层。参数 JSON Schema 里description字段比type还重要。模型推断参数值时靠的就是这段描述。比如days参数如果你只写“天数”模型可能填 30、7、365 都能对但它不知道你期望的是近几天。我写的是“只统计最近多少天内的日志文件默认 7 天用户没明确说时间范围时就传 7”。enum字段能帮大忙。如果你限定file_type只能是 all、doc、image、code、log 五种模型就只能在里面选降低乱传参的概率。同理max_results设置合理的最大值防止模型填一个 100000 把执行体拖垮。技能描述同样要以“模型视角”来写。不要写“此工具用于日志扫描与错误聚合统计”而要写“当用户要求分析日志、查找错误原因、统计不同级别日志数量时使用此技能。典型问题日志里有没有数据库报错、最近一周 ERROR 主要出现在哪里”。我自己的经验是描述里带上“典型问题”比任何抽象概括都好用。3.4 把技能挂到 Agent 上注册与联调测试技能写好后要挂到 Agent 上。这一步不同框架写法不同但核心动作一致把技能的函数定义名称、描述、参数 Schema注入到模型的工具列表里然后把执行体代码挂到框架的工具调度器上。# 伪代码不同框架 API 可能不同 agent.register_tool( nameanalyze_logs, description分析日志文件并统计各级别日志数量与错误分布, parameterslog_schema, handlerscan_logs )注册完成后我习惯做一轮“五连问测试”分别用直接指令、模糊指令、带具体参数的指令、超出技能边界的指令、完全不相关的指令去调 Agent观察它是否正确触发技能、是否正确传参、是否在技能不适用时果断不调用。我当时就踩了一个典型坑。用“帮我看看日志”这种模糊指令测试时模型触发了技能但log_dir传了一个不存在的路径。后来我在参数描述里加了“默认使用工作目录下的 logs 文件夹只有用户明确指定其他路径时才传值”同时把log_dir的默认值写死到函数签名里这才彻底解决。这轮联调非常值得认真做因为在真实场景里用户不会每次都按标准格式说话。模糊表达能不能正确映射到参数默认值直接决定技能可用性。4. 技能运行中的常见问题与排查实录技能上线跑起来以后真正的挑战才开始。模型调用技能的成功率不会永远 100%这里把我实际遇到过的几类高频问题按优先级整理出来并附上排查方法和最终解法。4.1 模型死活不调用技能先查描述再查参数问题表现用户问“帮我找一下昨天的日志”Agent 回答“我无法直接访问你的日志文件”完全没有触发日志分析技能。排查思路分三步。第一步确认技能是否真的注册成功。很多框架注册工具时是异步的注册完立即测试可能还没生效我遇到过不止一次。第二步检查技能描述是否具体。如果描述还是“用于日志分析”这种抽象写法立即改成“当用户要求分析日志、查找错误、统计日志数量时使用此技能”并在描述里明确列出触发词汇。第三步检查参数 Schema 是否过于严格。required里塞了五个必填参数模型看到传参成本高可能就直接放弃调用了。有一个很实用的调试技巧把技能列表打印出来用你的大模型 API 手动发一条测试消息在返回里看模型有没有给出 function_call 意向。如果没有说明模型看完了技能定义也没找到匹配项问题基本锁定在描述上如果有调用意向但参数不对问题出在参数 Schema。4.2 技能确实执行了但 Agent 回答得驴唇不对马嘴问题表现技能正确执行返回了{success: true, total_matches: 23}但 Agent 跟用户说“已找到 23 个错误”完全忘了total_matches匹配的是含关键字的日志行不一定都是错误。这是最典型的“返回结构设计缺陷”。Agent 没有读代码的能力它只能读返回的 JSON 字段名和值。字段名是total_matches它自然理解为“匹配总数”至于匹配的到底是错误日志还是普通日志靠猜。解法也很直接返回结构里必须有summary字段把所有关键信息翻译成一句模型可以直接引用的话。比如“扫描了 12 个日志文件匹配到 23 条包含关键字‘timeout’的日志行其中 ERROR 级别 5 条”。模型看到这句话回答基本不会跑偏。问题现象可能的根因排查/解法模型不调用技能描述太抽象/触发词缺失描述里加典型问题和触发场景调用但参数乱传parameters 说明模糊每个参数写清楚含义、默认值、可选项返回结果被误读字段含义不直观加 summary 自然语言摘要执行后上下文爆炸返回体过大截断、分页、只返回摘要偶尔调用出错异常没有被捕获执行体顶层加 try-except 并返回错误信息4.3 上下文污染与技能输出爆炸技能返回的数据量过大是一个隐蔽但危害极大的问题。我试过让技能返回文件全文结果 5000 行的日志一下子塞进上下文后续对话质量立刻下降连带着模型开始遗忘前面用户的指令。解决思路是按需返回。搜索类的技能默认只返回前 20 条结果并在 summary 里提示“匹配到 X 条记录已显示前 20 条”。文件读取类的技能按行数截断一般限制在 300 行以内需要更多再让 Agent 二次调用获取下一段。统计类的技能直出聚合结果不返回明细。这里有个测试技巧每开发完一个技能强制用最大参数调用一次看看返回体占多少 token。如果超过 2000 token就要考虑是不是该截断或改成摘要模式。很多技能的“性能问题”其实是输出太大撑爆上下文不是模型本身跑得慢。4.4 重试、超时与幂等越早想越好单机 Demo 可以忽略这类问题但只要技能涉及外部服务比如发请求、写数据库、调第三方 API就必须考虑重试和幂等。我第一次写一个“自动发消息”的技能时没想幂等结果模型超时后自动重试了一次消息被发了两次现场相当尴尬。给非查询类技能设计参数时一定要加一个request_id参数执行体内部对该 ID 去重。同样超时时间要单独设置不能依赖模型层默认超时执行体如果在拉起子进程或请求外部服务应设置自己的超时上限超时后返回整段逻辑提前结束。还有一个和模型层配合的经验在技能描述里标注“该操作不可重试”或“该操作是幂等的可安全重试”。模型在生成调用请求时如果看到不可重试的标注会倾向于一次成功降低自动重试概率。这套机制在纯 Function Calling 场景不明显但接 MCP 后 effect 语义会越来越重要。5. 个人的几条经验和收尾建议写到这我发现踩过的坑基本都集中在同一个根源上我们总把 Agent 当成一个传统的“代码程序”觉得它应该精确理解每个参数而实际上它是个“读说明书做决策”的系统。你要做的不是把逻辑写得更严谨而是把技能的“说明书”写得更好懂、边界更清晰、返回更易读。一个很实用的习惯每个技能建一个 examples 目录放 3 到 5 个测试输入和期望输出。不只是单测用更大价值是调试时快速回看“这个技能当初设计成什么样、模型的什么误解让我改了参数描述”。几次下来你会发现自己对“如何给模型写描述”的判断力会明显提升。最后分享一个看起来很小但收益极高的小技巧技能返回的 summary 里开头永远用“扫描了”“搜索了”“统计了”这类动作动词而不要用“结果如下”“成功返回”这类套话。模型从 summary 里提取用户能听懂的结论时动作动词能让它更快组织口语化回复。这个小改动是我在做了七八个技能之后才总结出来的实测对回答质量的提升比调模型参数还明显。
RELATED

相关推荐

生产级Agent三层架构:Harness、Loop与Graph实战解析

生产级Agent三层架构:Harness、Loop与Graph实战解析

做 Agent 工程这两年,我最大的感受是:跑通一个 Demo 很容易,写一个能在生产环境扛住真实流量的 Agent 很难。难在"Agent"这三个字母背后藏着一堆没人替你分担的工程问题——工具怎么接、权限怎么控、循环怎么停、流程怎么排、出错了…

📅 2026/10/8 4:39:46
我的世界怀旧RPG服务器六年运营复盘:从猎马斗罗看长线开服之道

我的世界怀旧RPG服务器六年运营复盘:从猎马斗罗看长线开服之道

开服快6年了,有人问我《猎马斗罗》怎么还活着。说实话,我自己有时也恍惚——一个我的世界怀旧RPG服务器,从2017年入驻联机侠平台干到今天,还能稳定有余人在线,中间换过版本、换过机器、甚至换过一代玩家,但…

📅 2026/10/8 4:39:46
双 11 大促前夕的内容安全演练:压测多模态轻量审核集群与前端自愈熔断机制

双 11 大促前夕的内容安全演练:压测多模态轻量审核集群与前端自愈熔断机制

距离双 11 正式开门红还有不到三周,整个前端与风控架构团队在上周五深夜展开了一场代号为“破峰”的内容安全全链路突发演练。 很多工程师以为大促的流量洪峰只存在于订单结算与秒杀库存,却往往忽略了内容流也是黑产与灰产攻击的重灾区。数以亿计的评价、…

📅 2026/10/8 4:39:46
MORE NEWS

更多资讯

📰

AI Agent skills 完全指南:从安装到自建,避开常见坑

1. 从"skills"这个热词说起:它到底指什么最近一段时间,"skills"这个词在技术社区里出现的频率高得离谱。你随便翻翻开发者群聊、技术论坛或者代码托管平台的热门仓库,总能看到有人在讨论"这个skills怎么装"&qu…

📰

基于Claude Code的MarketingSkills拆解:AI Agents驱动SEO与CRO自动化实战

1. 从“marketingskills”说起:一个被低估的增长工具箱第一次看到“marketingskills”这个词,很多人会以为它只是某个营销课程或者技能清单。但如果你最近在折腾 Claude Code、AI agents,或者正在给自己的独立站做谷歌 SEO,你会发…

📰

OpenShell 智能体外壳框架:从工具注册到沙箱隔离的工程实践

1. 从零认识 OpenShell:它到底解决什么问题第一次听到 OpenShell 这个名字,很多人会下意识以为它跟某个操作系统的命令行外壳有关。实际上,在我接触过的几个项目里,OpenShell 指的是一类面向智能体(Agent)运…

📰

PS5通用化适配指南:手柄跨平台、串流与存储扩展实战

1. AnyPS5到底在解决什么问题1.1 名字背后的三个关键词拿到“AnyPS5”这个标题的时候,我第一反应是把它拆开看:Any,PS,5。“Any”代表的是任何、所有、通用;“PS5”则是目前索尼PlayStation家族的主力机型。拼在一起&a…

📰

AI Agent营销技能库marketingskills:SEO审计与CRO诊断实战

1. 从“marketingskills”说起:一个被低估的AI营销技能库第一次看到marketingskills这个词,是在翻 Claude Code 相关仓库的时候。当时我的第一反应是:这不就是把营销话术塞给 AI 让它写文案吗?但真正把仓库拉下来跑了一遍之后&…

📰

C#停车场收费系统WinForm实战:从数据模型到计费规则

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬