ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

AI Agent工具系统实战:Function Calling、MCP与Skills设计指南

AI Agent工具系统实战:Function Calling、MCP与Skills设计指南 1. 为什么说没有工具系统的 Agent 只是个缸中之脑很多人第一次接触 AI Agent 这个概念时脑子里浮现的画面是一个能自己思考、自己规划、自己执行的全能助手。但真正动手搭过 Agent 的人都知道一个没有工具系统的 Agent本质上就是一个被困在对话框里的缸中之脑——它能说会道能分析问题能给出建议但它什么都做不了。你问它帮我查一下今天北京的天气它会告诉你很抱歉我无法获取实时数据。你让它把这段代码保存成文件它会说我无法直接操作你的文件系统。这不是它不够聪明而是它没有手脚。工具系统就是 Agent 的手脚。它让 Agent 从只会说变成能去做。这个转变看起来简单实际上涉及一整套设计哲学和工程实现。我在过去一年多的 Agent 开发实践中踩过不少坑也总结出了一些真正管用的经验。这篇文章会从最基础的概念讲起逐步深入到 Function Calling 的实现细节、MCP 协议的运作机制、Skills 的设计思路以及在实际项目中如何把这些东西串起来。不管你是刚入门 AI Agent 开发的新手还是已经搭过几个 Demo 但总觉得不够丝滑的开发者这篇文章都应该能给你一些可以直接抄作业的东西。我会尽量用大白话把原理讲清楚同时给出可复现的代码和配置让你看完就能动手试。2. 工具系统的本质给 LLM 装上可调用的外部能力2.1 LLM 的能力边界在哪里要理解工具系统为什么重要首先得搞清楚 LLM 本身能做什么、不能做什么。以 DeepSeek、GPT 这类大语言模型为例它们的核心能力是根据输入预测输出。你给它一段文字它根据训练时学到的模式生成一段最可能的续写。这个过程不涉及任何外部世界的交互。这意味着 LLM 有几个天然的能力边界无法获取实时信息训练数据有截止日期之后发生的事情它不知道。无法执行副作用操作它不能写文件、发请求、操作数据库。无法进行精确计算虽然它能做数学题但复杂计算容易出错因为它本质是在预测答案而不是计算答案。无法访问私有数据你公司的内部文档、数据库里的用户信息它一概不知。这些边界不是靠把模型做大就能解决的。你就算把模型参数翻十倍它还是没法帮你查今天的天气。因为这不是智能问题是连接问题。工具系统的核心思路就是既然 LLM 不能直接做这些事那就给它一套工具让它能通过调用这些工具来间接完成。LLM 负责理解和决策工具负责执行和返回结果。2.2 工具系统的三层架构在实际工程中一个完整的工具系统通常包含三个层次第一层是工具定义层。这一层负责描述有哪些工具可用、每个工具接受什么参数、返回什么结果。这就像是给 Agent 一本工具手册告诉它工具箱里有什么。第二层是调用决策层。这一层由 LLM 驱动负责判断当前这个任务需不需要调用工具、调用哪个工具、参数怎么填。这是 Agent 智能的体现。第三层是执行层。这一层负责真正去执行工具调用包括参数校验、实际执行、错误处理、结果格式化。这是工程实现的部分。很多人搭 Agent 时只关注第二层觉得只要模型够聪明它自然知道该调什么工具。但实际上第一层的工具描述质量和第三层的执行健壮性往往才是决定 Agent 好不好用的关键。2.3 一个生活化的类比你可以把工具系统想象成给一个刚入职的实习生配电脑和权限。这个实习生LLM很聪明学习能力很强但他刚来公司什么系统都没权限什么工具都不会用。你需要告诉他公司有哪些系统可以用工具定义教他什么场景下该用哪个系统调用决策给他开通对应的账号权限执行层配置如果你只告诉他公司有 CRM 系统但没告诉他怎么登录、能查什么数据他还是干不了活。工具系统的设计也是同样的道理——光有工具列表不够还得让 Agent 知道每个工具的适用场景、参数含义、返回格式。3. Function Calling工具系统最基础的实现方式3.1 Function Calling 到底在做什么Function Calling 是目前最主流的工具调用实现方式。它的核心机制其实很简单你在调用 LLM API 时除了传入用户的消息还传入一份工具清单。LLM 在生成回复时如果判断需要调用某个工具它不会直接生成自然语言回复而是生成一个结构化的调用请求包含工具名和参数。举个例子你定义了一个查天气的工具{ name: get_weather, description: 查询指定城市的当前天气, parameters: { type: object, properties: { city: { type: string, description: 城市名称如北京、上海 } }, required: [city] } }当用户问北京今天天气怎么样时LLM 不会直接回答而是返回{ tool_calls: [ { function: { name: get_weather, arguments: {\city\: \北京\} } } ] }你的代码拿到这个调用请求后去实际执行查天气的操作把结果再传回给 LLMLLM 最后生成自然语言回复北京今天晴气温 15-25 度。3.2 工具描述怎么写才有效这是我在实践中踩坑最多的地方。工具描述写得好不好直接决定了 LLM 能不能正确调用。我总结了几条经验描述要具体不要抽象。不要写处理用户数据要写根据用户 ID 查询用户的订单历史记录返回最近 30 天的订单列表。LLM 需要知道这个工具具体能做什么才能判断什么时候该用它。参数说明要包含格式示例。比如日期参数你要写清楚格式为 YYYY-MM-DD如 2024-01-15。不然 LLM 可能传今天或者1月15日这种它自己理解的格式。明确边界条件。如果工具有限制比如最多返回 100 条记录一定要写在描述里。否则 LLM 可能期望返回 1000 条结果拿到 100 条后不知道怎么处理。避免功能重叠的工具。如果你有两个工具都能查用户信息LLM 会困惑该用哪个。要么合并成一个要么在描述里明确区分场景。3.3 多轮工具调用的处理逻辑实际场景中一个任务往往需要多次工具调用。比如用户说帮我查一下北京天气如果下雨就提醒我带伞这需要调用查天气工具根据结果判断是否下雨如果下雨调用提醒工具处理这种多轮调用的标准流程是messages [{role: user, content: user_input}] while True: response llm.chat(messagesmessages, toolstools) if 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: result }) else: # 没有工具调用了返回最终回复 return response.content这个循环会一直执行直到 LLM 不再请求调用工具为止。这里有个坑一定要设置最大循环次数否则如果 LLM 陷入调用工具-得到结果-再调用同一个工具的死循环你的程序就卡死了。我一般设置 10 次作为上限。3.4 错误处理工具调用失败怎么办工具调用失败是常态不是异常。网络超时、参数错误、权限不足、返回数据格式不对这些都会发生。关键是怎么把错误信息有效地传回给 LLM让它能做出合理的下一步决策。我的做法是永远不要把原始异常直接抛给 LLM。比如ConnectionError: timeout这种信息LLM 看了也不知道怎么办。你应该把它转换成结构化的错误信息{ error: true, error_type: timeout, message: 查询天气服务超时请稍后重试, suggestion: 可以尝试重新调用或告知用户服务暂时不可用 }这样 LLM 就知道该怎么处理了——要么重试要么告诉用户稍后再试。4. MCP 协议让工具系统标准化的尝试4.1 MCP 解决了什么问题Function Calling 虽然能用但有个大问题每个 Agent 框架都有自己的工具定义格式。你在 LangChain 里写的工具搬到另一个框架里就得重写。这就像每个手机品牌都有自己的充电接口换个手机就得换一堆线。MCPModel Context Protocol就是为了解决这个问题而提出的。它定义了一套标准的工具描述和调用协议让工具可以跨框架、跨平台复用。你可以把它理解为工具系统的 USB-C 接口。MCP 的核心概念包括Server提供工具的一方负责定义和实现工具Client使用工具的一方通常是 Agent 框架Resources工具可以访问的数据源Tools具体的可调用工具Prompts预定义的提示模板4.2 MCP Server 的实际写法写一个 MCP Server 其实不复杂。以 Python 为例你可以用官方提供的 SDKfrom mcp.server import Server from mcp.types import Tool, TextContent server Server(weather-server) server.list_tools() async def list_tools(): return [ Tool( nameget_weather, description查询指定城市的当前天气, inputSchema{ type: object, properties: { city: {type: string, description: 城市名称} }, required: [city] } ) ] server.call_tool() async def call_tool(name: str, arguments: dict): if name get_weather: city arguments[city] # 实际查询逻辑 result await fetch_weather(city) return [TextContent(typetext, textresult)]这个 Server 写好后任何支持 MCP 的 Client 都可以连接它并使用这些工具。你不需要为每个框架单独适配。4.3 MCP 在实际项目中的取舍MCP 听起来很美好但实际用起来也有一些需要注意的地方。我在项目中总结了几点适合场景当你需要把工具能力开放给多个不同的 Agent 或框架使用时MCP 的价值最大。比如你公司内部有一套数据查询工具想让不同团队开发的 Agent 都能用那封装成 MCP Server 就很合适。不太适合的场景如果你的 Agent 只用一种框架工具也只在这个 Agent 里用那直接写 Function Calling 可能更简单。引入 MCP 会增加一层抽象调试起来也更麻烦。性能考虑MCP 通常涉及进程间通信比直接函数调用多了一层开销。对于高频调用的工具这个开销可能不可忽略。我一般会把高频工具直接内置低频工具才走 MCP。调试难度MCP 的调用链路比直接 Function Calling 长出问题时排查起来更费劲。建议在开发阶段加详细的日志记录每次工具调用的请求和响应。4.4 MCP 生态的现状目前 MCP 生态还在快速发展中。已经有一些现成的 MCP Server 可以直接用比如文件系统操作、数据库查询、网页抓取等。也有一些工具平台开始支持 MCP 协议让用户可以直接把平台能力接入 Agent。但要注意的是MCP 协议本身还在演进不同版本之间可能有兼容性问题。如果你打算在生产环境使用建议锁定版本不要盲目追新。5. Skills比工具更高一层的抽象5.1 Skills 和工具的区别如果说工具是 Agent 的手脚那 Skills 更像是技能包。一个 Skill 可能包含多个工具的调用逻辑加上特定的提示词和处理流程形成一个完整的能力单元。举个例子查天气是一个工具但根据天气情况给出穿衣建议就是一个 Skill。这个 Skill 内部会调用查天气工具然后根据温度、湿度、风力等数据结合预设的规则或额外的 LLM 推理给出穿衣建议。Skills 的价值在于它把怎么做一件事的完整逻辑封装起来Agent 只需要知道有这个技能可用不需要关心内部细节。这大大降低了 Agent 的决策复杂度。5.2 Skills 的典型结构一个 Skill 通常包含以下几个部分名称和描述告诉 Agent 这个技能是做什么的触发条件什么情况下应该使用这个技能执行步骤具体的操作流程可能包含多个工具调用输入输出定义需要什么参数返回什么结果异常处理出错时怎么处理在实际实现中Skills 可以是一个配置文件也可以是一段代码。关键是要让 Agent 能够理解和使用。5.3 设计 Skills 的实践经验我在设计 Skills 时总结了几条原则一个 Skill 只做一件事。不要设计处理用户请求这种大而全的 Skill要拆成查询订单、修改地址、申请退款等具体技能。粒度太粗的 Skill 会让 Agent 难以判断何时使用。Skill 的描述要包含使用场景。不要只写查询订单要写当用户询问订单状态、物流信息、预计送达时间时使用此技能。这样 Agent 才能准确匹配。Skill 之间尽量解耦。一个 Skill 不应该依赖另一个 Skill 的内部实现。如果确实需要组合应该通过 Agent 的规划能力来编排而不是硬编码依赖关系。提供清晰的失败反馈。Skill 执行失败时要返回足够的信息让 Agent 知道下一步该怎么做。是重试、换一个 Skill、还是告知用户这些决策依赖于失败信息的质量。5.4 Skills 的复用与管理当 Skill 数量增多时管理就成了问题。我的做法是建立一个 Skill 注册中心所有 Skill 在这里注册Agent 启动时动态加载。这样新增 Skill 不需要改 Agent 代码只需要在注册中心添加配置。同时我会给每个 Skill 打标签比如查询类、操作类、分析类。Agent 在规划时可以先按标签筛选缩小选择范围提高决策效率。6. 把工具、MCP、Skills 串起来一个完整的 Agent 工具系统6.1 整体架构设计在实际项目中我通常会把这三者组合使用底层用 Function Calling 实现具体工具这是最基础的能力单元中层用 MCP 做工具的标准封装和跨框架复用当工具有复用需求时上层用 Skills 做能力编排把多个工具组合成完整的业务能力这样的分层设计既保证了灵活性又兼顾了复用性和可维护性。6.2 一个实际案例智能客服 Agent假设我们要做一个电商智能客服 Agent它需要处理用户的订单查询、退换货、物流跟踪等请求。工具系统的设计可能是这样的工具层query_order(order_id)查询订单详情query_logistics(order_id)查询物流信息create_return_request(order_id, reason)创建退货申请send_notification(user_id, message)发送通知MCP 层把订单系统和物流系统的工具封装成 MCP Server这样其他 Agent比如售后 Agent、推荐 Agent也能复用。Skills 层order_status_inquiry订单状态查询技能组合了查订单和查物流return_process退货处理技能组合了查订单、创建退货、发通知logistics_tracking物流跟踪技能调用查物流并格式化输出Agent 收到用户请求后先匹配 Skill然后执行 Skill 内部的工具调用流程最后生成回复。6.3 性能优化的几个关键点工具系统的性能直接影响 Agent 的响应速度。我总结了几条优化经验工具结果缓存对于查询类工具如果短时间内多次查询同样的参数可以缓存结果。比如用户连续问我的订单到哪了没必要每次都查一遍物流接口。并行调用如果多个工具调用之间没有依赖关系可以并行执行。比如同时查订单和查物流而不是串行等待。超时控制每个工具调用都要设置超时避免因为某个工具卡住导致整个 Agent 无响应。我一般设置 5-10 秒的超时。结果裁剪工具返回的数据可能很大但 LLM 只需要关键信息。在传给 LLM 之前先做一轮裁剪只保留必要字段。这能显著减少 token 消耗和响应时间。6.4 安全与权限控制工具系统让 Agent 有了操作外部世界的能力这也带来了安全风险。一个设计不当的工具系统可能被恶意用户利用来执行未授权的操作。我的做法是最小权限原则每个工具只授予完成其功能所需的最小权限参数校验在工具执行前严格校验参数格式和范围操作审计记录所有工具调用的日志便于追溯敏感操作二次确认对于删除、支付等敏感操作要求 Agent 先向用户确认7. 常见问题与排查思路7.1 Agent 不调用工具怎么办这是新手最常遇到的问题。Agent 明明有工具可用但就是不用直接用自己的知识回答。原因通常有几个工具描述不够清晰LLM 没理解这个工具是干什么的自然不知道什么时候该用。解决方法是把描述写得更具体包含使用场景。系统提示词没引导你需要在系统提示词里明确告诉 Agent当遇到需要实时数据或外部操作时优先使用工具。有时候 LLM 会偷懒觉得自己的知识够用就不调工具了。工具太多导致选择困难如果一次给 LLM 几十个工具它可能反而不知道该用哪个。解决方法是按场景分组每次只给相关的工具。7.2 工具调用参数错误怎么排查参数错误通常表现为 LLM 传了错误的参数名、格式不对、或者缺少必填参数。排查步骤打印 LLM 返回的原始 tool_calls看它实际传了什么对比工具定义的 schema找出不匹配的地方检查工具描述里有没有明确说明参数格式如果问题持续考虑在描述里加更多示例我遇到过一个典型案例LLM 总是把日期传成2024年1月15日而不是2024-01-15。后来在参数描述里加了格式必须为 YYYY-MM-DD例如 2024-01-15问题就解决了。7.3 工具调用陷入死循环怎么处理死循环的表现是 Agent 反复调用同一个工具每次都得到相似的结果但就是不结束。原因可能是工具返回的结果 LLM 无法理解它以为调用失败了就重试任务本身无法完成但 LLM 没有放弃机制工具描述有歧义LLM 不确定是否已经完成解决方法包括设置最大调用次数、在工具返回中明确标注成功或失败、在系统提示词里加入如果连续两次调用同一工具得到相同结果应该停止并告知用户。7.4 工具响应太慢影响体验怎么办工具响应慢是常见问题尤其是涉及外部 API 调用时。优化思路加缓存减少重复调用设置合理的超时超时后返回降级结果对于非关键工具考虑异步执行先给用户一个初步回复如果工具本身慢考虑优化工具实现比如加索引、减少数据传输量8. 我在实际项目中的几点体会工具系统的设计没有标准答案不同的业务场景需要不同的取舍。但有几条原则我觉得是通用的工具描述的重要性被严重低估。很多人花大量时间优化 Agent 的提示词却忽略了工具描述。实际上工具描述是 LLM 理解工具能力的唯一途径它的质量直接决定了工具调用的准确率。错误处理比成功路径更重要。工具调用失败是常态一个好的工具系统应该能优雅地处理各种失败情况而不是一出错就崩溃。不要过度设计。刚开始搭 Agent 时用最简单的 Function Calling 就够了。等确实遇到复用问题、跨框架问题再考虑引入 MCP。Skills 也是等工具数量多了、组合逻辑复杂了再抽象成 Skills。持续观察和迭代。工具系统上线后要持续收集调用日志分析哪些工具调用频繁、哪些经常出错、哪些从来没被调用过。根据这些数据不断优化工具描述和系统提示词。工具系统是 Agent 从玩具变成工具的关键一步。它让 Agent 真正能做事而不只是能聊天。希望这篇文章能帮你少走一些弯路更快地搭出好用的 Agent。
返回列表