news 2026/10/27 17:03:07

Agent工具调用、MCP与Skill:从Function Calling到能力封装

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Agent工具调用、MCP与Skill:从Function Calling到能力封装

1. 从一个困惑说起:Agent 到底凭什么“会做事”

很多人第一次接触 Agent 这个概念时,脑子里浮现的画面大概是这样的:一个能自己思考、自己规划、自己动手的智能体,你给它一句话,它就能帮你把活干完。但真正上手搭过 Agent 的人都知道,事情远没有这么浪漫。模型本身再强,它也只是个“嘴强王者”——能说会道,但手是断的。你让它查个天气,它只能凭训练数据瞎编;你让它读个本地文件,它连文件系统都摸不到;你让它调个接口,它连 HTTP 请求都发不出去。

所以问题就来了:Agent 的能力到底从哪来?我的答案很直接——Agent 的能力 = 模型推理能力 × 外部工具调用能力 × 上下文管理能力。模型负责“想”,工具负责“做”,上下文负责“记住做过什么、接下来该做什么”。这三者缺一不可,而其中最容易被低估、也最能拉开差距的,就是工具调用这一层。

这篇文章我想把工具调用、MCP(Model Context Protocol)和 Skill 这三个概念彻底讲清楚。它们不是三个平行的热词,而是一条递进的能力链:Function Calling 解决“能不能调”的问题,MCP 解决“怎么标准化地调”的问题,Skill 解决“怎么把一套调法封装成可复用能力”的问题。如果你正在做 Agent 开发,或者只是好奇为什么同样是接大模型,别人的 Agent 能干活而你的只能聊天,那这篇内容应该能帮你把思路理顺。

我会从底层原理讲到实操细节,包括参数怎么设计、工具描述怎么写、MCP 的通信机制是怎么回事、Skill 和普通工具的区别在哪,以及我在实际项目里踩过的坑。全程尽量说人话,不堆术语,能上代码的地方上代码,能画表格的地方画表格。

2. 工具调用:Agent 的“手”是怎么长出来的

2.1 Function Calling 的本质:让模型学会“填表”

要理解工具调用,得先理解大模型的一个根本限制:它是一个纯文本进、纯文本出的函数。你给它一段文字,它预测下一段文字。它没有内存、没有文件系统、没有网络权限,甚至连“现在几点”都不知道。那它是怎么做到“调用工具”的呢?

答案有点反直觉:模型并没有真的调用工具,它只是生成了一段结构化的文本,告诉外面的程序“我想调用这个工具,参数是这些”。真正执行工具的是你写的代码,模型只负责“填表”。

这就是 Function Calling 的核心机制。你提前定义好一组工具,每个工具有名字、描述和参数 schema,然后把这份工具清单塞进模型的上下文里。模型在推理时,如果判断需要调用某个工具,就会输出一段符合 schema 的 JSON,比如:

{ "name": "get_weather", "arguments": { "city": "杭州", "unit": "celsius" } }

你的程序解析这段 JSON,执行对应的函数,把结果再塞回上下文,模型基于结果继续推理。整个过程模型始终在“说话”,只是说的内容从自然语言变成了结构化指令。

我刚开始做这块的时候,最大的误区是以为模型“理解”了工具的功能。其实不是,模型对工具的理解完全来自于你写的描述文本。描述写得含糊,模型就会乱调;参数 schema 设计得不合理,模型就会填错。这一点后面会展开讲。

2.2 工具描述怎么写,模型才不犯傻

工具描述是 Function Calling 里最被低估的环节。很多人写描述就一句话:“获取天气”。然后发现模型要么不调,要么传错参数。问题出在哪?出在模型不知道什么时候该调、什么时候不该调、参数该长什么样。

我总结了一个工具描述的四要素模板,实测下来能显著降低误调率:

  • 功能说明:这个工具做什么,一句话说清,动词开头。
  • 使用场景:什么情况下应该调用它,什么情况下不应该。这一条最关键,很多误调都是因为模型不知道边界。
  • 参数说明:每个参数的含义、类型、取值范围、是否必填,最好给例子。
  • 返回说明:返回什么格式的数据,模型需要知道拿到结果后怎么解读。

举个例子,同样是查天气,我现在的写法是这样的:

{ "name": "get_current_weather", "description": "查询指定城市当前的实时天气。当用户询问天气、气温、是否下雨等实时气象信息时调用。不要用于查询历史天气或未来预报。", "parameters": { "type": "object", "properties": { "city": { "type": "string", "description": "城市名称,使用中文,例如'杭州'、'北京'。不要传省份名或区县名。" }, "unit": { "type": "string", "enum": ["celsius", "fahrenheit"], "description": "温度单位,默认摄氏度。" } }, "required": ["city"] } }

对比一下就能看出差别。“不要用于查询历史天气”这句话直接砍掉了一类误调;“不要传省份名”砍掉了另一类。这些边界信息模型自己是猜不出来的,必须你告诉它。

实操心得:工具描述不是写给人看的文档,是写给模型看的 prompt。判断标准很简单——如果一个新来的实习生只看这段描述就能正确使用工具,那模型大概率也能。

2.3 参数设计的三个坑

参数 schema 设计有几个常见的坑,我一个个说。

第一个坑是参数太自由。比如你把date定义成 string,不限制格式,模型可能传“明天”、可能传“2024-01-01”、可能传“Jan 1”。正确做法是用 enum 或者明确的格式说明,比如"格式:YYYY-MM-DD,例如 2024-01-15"。

第二个坑是参数太多。一个工具塞十几个参数,模型填错的概率指数级上升。我的经验是单个工具参数不超过 5 个,超过就拆成多个工具。比如“创建订单”这个工具,如果既要传商品信息又要传收货地址又要传支付方式,不如拆成“创建订单”“设置收货地址”“选择支付方式”三个。

第三个坑是缺少默认值。可选参数一定要给默认值说明,否则模型可能传 null 或者干脆不传,导致你的代码报错。在 schema 里用default字段标出来,同时在描述里写清楚“不传时默认为 X”。

2.4 多轮工具调用的编排逻辑

单个工具调用好理解,但真实场景往往需要连续调用多个工具。比如用户问“帮我看看杭州明天适不适合跑步”,Agent 可能需要:先调天气工具查温度,再调空气质量工具查 AQI,最后综合判断。这就涉及到多轮工具调用的编排。

编排的核心是让模型自己决定调用顺序,而不是你在代码里写死。具体做法是:把工具结果作为新的消息追加到上下文,让模型基于已有结果决定下一步。如果模型认为信息够了,就输出最终回答;如果不够,就继续调工具。

这里有个细节要注意:每轮工具调用的结果都要完整保留在上下文里,不要只保留最后一次。因为模型可能需要对比多次结果,或者引用之前的数据。上下文长度不够时,优先压缩早期的自然语言对话,保留工具调用记录。

# 简化的多轮工具调用循环 messages = [{"role": "user", "content": user_input}] while True: response = model.chat(messages, tools=tool_schemas) if response.finish_reason == "tool_calls": for tool_call in response.tool_calls: result = execute_tool(tool_call.name, tool_call.arguments) messages.append({"role": "tool", "content": result, "tool_call_id": tool_call.id}) else: return response.content

这个循环看起来简单,但实际项目里要处理超时、重试、工具报错、死循环等问题。我一般会加一个最大轮次限制,比如 10 轮,超过就强制返回,避免模型陷入无限调用。

3. MCP:把工具调用从“手搓”变成“插拔”

3.1 MCP 到底解决了什么问题

如果你只接一两个工具,Function Calling 够用了。但当你需要接十个、二十个工具,而且这些工具可能来自不同的服务、不同的语言、不同的团队时,问题就来了:每个工具的接入方式都不一样,有的要写 HTTP 客户端,有的要读本地文件,有的要连数据库。你的 Agent 代码里塞满了各种胶水逻辑,维护成本极高。

MCP 就是来解决这个问题的。它的全称是 Model Context Protocol,翻译过来叫“模型上下文协议”。它的核心思想是把工具的定义和调用标准化,让 Agent 和工具之间通过统一的协议通信,而不是各接各的。

打个比方:Function Calling 像是你直接给每个供应商打电话下单,每家电话号码、下单格式都不一样;MCP 像是你接入了统一的采购平台,所有供应商都在平台上注册,你用同一套流程就能下单。前者灵活但累,后者标准化但需要供应商配合。

MCP 的架构分两部分:MCP Server和MCP Client。Server 负责暴露工具、资源、提示词等能力,Client 负责连接 Server 并把能力转成模型能理解的格式。Agent 只需要实现一个 MCP Client,就能接入所有符合 MCP 规范的 Server。

3.2 MCP 的三种能力:Tools、Resources、Prompts

MCP 不只是工具调用,它定义了三类能力,很多人只知道 Tools,其实另外两类也很有用。

Tools就是可调用的函数,和 Function Calling 里的工具对应。模型可以主动调用,执行有副作用,比如发邮件、写文件、调接口。

Resources是只读的数据源,比如文件内容、数据库记录、API 返回的数据。模型可以读取,但不能修改。Resources 的价值在于把“数据获取”和“数据操作”分开,让权限控制更清晰。

Prompts是预定义的提示词模板,Server 可以提供一些常用的 prompt,Client 直接调用。这个能力用得相对少,但在一些标准化场景下很有用,比如“代码审查”“文档总结”这类固定流程。

三类能力的对比:

能力类型是否可写谁触发典型场景
Tools可写模型主动调用发邮件、写文件、调接口
Resources只读模型或用户读取读文件、查数据库、获取配置
Prompts只读用户选择代码审查、文档总结模板

3.3 MCP 的通信机制:stdio 和 SSE

MCP Server 和 Client 之间怎么通信?目前主流有两种方式:stdio和SSE。

stdio 是标准输入输出,Server 作为一个子进程运行,Client 通过 stdin 发请求、通过 stdout 收响应。这种方式适合本地工具,比如读写本地文件、执行本地命令。优点是简单、无需网络、启动快;缺点是只能本地用,没法跨机器。

SSE 是 Server-Sent Events,基于 HTTP 的长连接。Server 作为一个 HTTP 服务运行,Client 通过 SSE 接收 Server 推送的消息,通过 POST 发送请求。这种方式适合远程工具,比如云端 API、共享服务。优点是跨机器、可共享;缺点是需要处理网络问题、认证授权。

# stdio 方式启动一个 MCP Server 的配置示例 { "mcpServers": { "filesystem": { "command": "npx", "args": ["-y", "@modelcontextprotocol/server-filesystem", "/path/to/dir"] } } }
// SSE 方式的配置示例 { "mcpServers": { "remote-service": { "url": "https://example.com/mcp/sse", "headers": { "Authorization": "Bearer YOUR_TOKEN" } } } }

选择哪种方式,主要看工具部署在哪里。本地工具用 stdio,远程服务用 SSE。实际项目里经常混用,比如文件操作走 stdio,数据库查询走 SSE。

3.4 自己写一个 MCP Server 的完整流程

光会用现成的 Server 不够,真实项目里经常需要自己写。我以 Python 为例,走一遍完整流程。

首先安装 SDK:

pip install mcp

然后定义一个 Server,暴露一个查询数据库的工具:

from mcp.server import Server from mcp.server.stdio import stdio_server from mcp.types import Tool, TextContent import sqlite3 app = Server("my-db-server") @app.list_tools() async def list_tools(): return [ Tool( name="query_users", description="查询用户表。当需要获取用户信息时调用。", inputSchema={ "type": "object", "properties": { "limit": { "type": "integer", "description": "返回条数,默认10,最大100", "default": 10 } } } ) ] @app.call_tool() async def call_tool(name: str, arguments: dict): if name == "query_users": limit = min(arguments.get("limit", 10), 100) conn = sqlite3.connect("users.db") rows = conn.execute(f"SELECT * FROM users LIMIT {limit}").fetchall() conn.close() return [TextContent(type="text", text=str(rows))] async def main(): async with stdio_server() as (read, write): await app.run(read, write, app.create_initialization_options()) if __name__ == "__main__": import asyncio asyncio.run(main())

这个 Server 暴露了一个query_users工具,Client 连上之后就能调用。注意几个细节:list_tools返回工具定义,call_tool处理实际调用,参数校验和边界处理要自己做,MCP 协议本身不做业务校验。

注意事项:MCP Server 里的工具描述同样要遵循前面说的四要素原则。我见过很多人写 MCP Server 时描述写得很随意,结果模型调用率极低。协议标准化了,但描述质量还是得靠人。

3.5 MCP 生态现状与选型建议

目前 MCP 生态已经有不少现成的 Server,覆盖文件系统、数据库、Git、浏览器、各类 SaaS 服务等。选型时我的建议是:

  • 优先用官方或社区维护的 Server,自己写容易踩坑,尤其是认证和错误处理。
  • 注意权限边界,MCP Server 能访问的资源就是 Agent 能访问的资源,别把整个文件系统都暴露出去。
  • 关注 Server 的更新频率,MCP 协议本身还在演进,老版本 Server 可能不兼容新 Client。
  • 远程 Server 一定要做认证,SSE 方式暴露在公网的服务,没有认证等于裸奔。

4. Skill:把一套能力封装成可复用的“技能包”

4.1 Skill 和 Tool 的区别:一个是动作,一个是套路

很多人搞不清 Skill 和 Tool 的区别,觉得都是“让 Agent 做事的东西”。其实两者的抽象层级不一样。

Tool 是一个原子动作,比如“读文件”“发请求”“查数据库”。它是最小的可调用单元,解决“怎么做”的问题。

Skill 是一套完成特定任务的套路,它可能包含多个 Tool 的调用顺序、参数组合、判断逻辑,甚至包含提示词模板和输出格式要求。它解决的是“做什么、按什么流程做”的问题。

举个例子:Tool 是“切菜”“开火”“翻炒”,Skill 是“做一道番茄炒蛋”。Skill 会告诉 Agent:先切番茄,再打蛋,热锅下油,先炒蛋再下番茄,最后调味。这些步骤可能用到多个 Tool,但 Skill 把它们编排成了一个完整的流程。

用表格对比更清楚:

维度ToolSkill
抽象层级原子动作任务流程
粒度单个函数多个步骤的组合
复用方式直接调用按场景触发
包含内容函数定义 + 参数流程 + 提示词 + 工具组合
典型例子读文件、发请求代码审查、周报生成

4.2 Skill 的组成结构:不只是工具组合

一个完整的 Skill 通常包含这几部分:

  • 触发条件:什么情况下启用这个 Skill。可以是关键词匹配,也可以是模型判断。
  • 执行流程:分几步,每步做什么,用哪些 Tool。
  • 提示词模板:每个步骤的 prompt 怎么写,输出格式是什么。
  • 输入输出定义:Skill 需要什么输入,产出什么结果。
  • 异常处理:某一步失败了怎么办,是否重试,是否降级。

以“代码审查 Skill”为例,它的结构可能是这样的:

name: code_review trigger: 用户提交代码并请求审查 steps: - name: 读取代码 tool: read_file input: 用户指定的文件路径 - name: 静态检查 tool: run_linter input: 上一步的代码内容 - name: 逻辑分析 prompt: | 分析以下代码的逻辑问题,重点关注边界条件、异常处理、性能隐患: {code} - name: 生成报告 prompt: | 基于以下检查结果,生成一份代码审查报告,按严重程度排序: 静态检查:{lint_result} 逻辑分析:{logic_analysis} output: Markdown 格式的审查报告

这个 Skill 编排了三个步骤,用到了两个 Tool 和一个纯 prompt 步骤。Agent 拿到这个 Skill 定义后,就知道遇到代码审查请求时该按什么流程走。

4.3 Skill 的触发机制:怎么让 Agent 知道该用哪个 Skill

Skill 定义好了,怎么让 Agent 在合适的时机用合适的 Skill?常见的有三种触发方式。

第一种是显式触发,用户在输入里明确提到 Skill 名字,比如“用代码审查 Skill 看看这个文件”。这种方式最可控,但需要用户知道有哪些 Skill。

第二种是描述匹配,把 Skill 的描述和用户输入一起给模型,让模型判断该用哪个。这种方式灵活,但对描述质量要求高,描述写不好就会误触发或漏触发。

第三种是路由层,单独用一个轻量模型或规则引擎做意图识别,判断用户请求属于哪个 Skill,再分发给对应的 Skill。这种方式适合 Skill 数量多的场景,但增加了一层复杂度。

我实际项目里用的是第二种加第三种混合:先用规则做粗筛,再用模型做精判。规则负责快速排除明显不相关的 Skill,模型负责在候选集里选最合适的。这样既保证了速度,又保证了准确率。

4.4 从零写一个 Skill 的实操步骤

我以“周报生成 Skill”为例,走一遍完整流程。

第一步,明确输入输出。输入是本周的工作记录(可能是 Git commit、任务列表、聊天记录),输出是一份结构化的周报。

第二步,拆解流程。周报生成可以拆成:收集数据、分类整理、提炼重点、生成文本、格式化输出。

第三步,定义每步的工具和提示词。

skill = { "name": "weekly_report", "description": "根据本周工作记录生成周报。当用户请求生成周报、工作总结时调用。", "steps": [ { "name": "collect", "tools": ["get_git_commits", "get_task_list"], "prompt": "收集本周({start_date} 至 {end_date})的所有工作记录。" }, { "name": "categorize", "prompt": "将以下工作记录按项目分类,每类下列出具体事项:\n{collected_data}" }, { "name": "summarize", "prompt": "提炼每类工作的重点,突出成果和进展,避免流水账:\n{categorized_data}" }, { "name": "format", "prompt": "按以下格式生成周报:\n# 本周工作总结\n## 项目进展\n## 关键成果\n## 下周计划\n\n内容:{summarized_data}" } ] }

第四步,测试和调优。拿真实数据跑几遍,看输出质量。常见问题是分类不准、重点不突出、格式不对,针对性调整对应步骤的提示词。

第五步,封装和注册。把 Skill 定义注册到 Agent 的 Skill 列表里,确保触发描述准确。

实操心得:Skill 的调试比 Tool 麻烦,因为涉及多步编排,中间任何一步出问题都会影响最终结果。我的做法是给每个步骤加日志,记录输入输出,出问题时能快速定位是哪一步的锅。

4.5 Skill 的复用与组合:搭积木的思路

Skill 最大的价值在于复用。一个好的 Skill 应该像积木一样,能单独用,也能组合用。

比如“代码审查 Skill”和“文档生成 Skill”可以组合成“代码文档一体化 Skill”:先审查代码,再把审查结果和代码一起生成文档。组合的方式很简单,把前一个 Skill 的输出作为后一个 Skill 的输入。

组合时要注意几点:输入输出格式要兼容,前一个 Skill 的输出得是后一个能解析的格式;错误处理要传递,前一个失败了后一个要有应对;上下文要控制,组合后的上下文长度可能超限,需要做压缩。

我一般会把常用 Skill 组合固化成“超级 Skill”,减少运行时的编排开销。但也不能过度组合,组合太多会导致调试困难、复用性下降。我的经验是单个 Skill 不超过 5 步,组合不超过 3 个 Skill。

5. 三者协同:一个完整 Agent 的能力拼图

5.1 从用户输入到任务完成的完整链路

把工具调用、MCP、Skill 串起来看,一个完整 Agent 的工作链路是这样的:

用户输入 → 意图识别 → Skill 选择 → Skill 流程执行 → 每步调用 Tool → Tool 通过 MCP 协议通信 → 结果回传 → 下一步 → 最终输出

这条链路里,每一层都有各自的职责:Skill 层负责“做什么”,决定用哪个流程;Tool 层负责“怎么做”,执行具体动作;MCP 层负责“怎么连”,标准化通信。三层解耦,各司其职。

解耦的好处是每一层都能独立演进。新增一个 Tool 不影响 Skill 定义,新增一个 Skill 不影响 Tool 实现,换一个 MCP Server 不影响上层逻辑。这种架构在 Agent 规模变大时优势特别明显。

5.2 上下文管理:Agent 的“记忆”怎么管

工具调用和 Skill 执行都会产生大量上下文,怎么管理是个大问题。我的经验是分三层管理:

第一层是短期记忆,当前任务的所有消息,包括用户输入、工具调用、工具结果、模型输出。这一层要完整保留,因为模型需要基于完整上下文推理。

第二层是中期记忆,当前会话的历史任务摘要。当上下文快满时,把早期任务压缩成摘要,保留关键信息,丢弃细节。

第三层是长期记忆,跨会话的用户偏好、常用工具、历史经验。这一层通常存在外部存储里,需要时检索出来注入上下文。

# 上下文压缩的简化逻辑 def compress_context(messages, max_tokens=8000): if count_tokens(messages) <= max_tokens: return messages # 保留最近的消息 recent = messages[-10:] # 压缩早期消息 early = messages[:-10] summary = model.summarize(early) return [{"role": "system", "content": f"历史摘要:{summary}"}] + recent

压缩时要注意:工具调用记录不要轻易丢,因为模型可能需要引用之前调用的结果;错误信息要保留,避免重复踩坑;用户明确的要求要保留,避免偏离需求。

5.3 安全边界:Agent 能做什么,不能做什么

Agent 有了工具调用能力,就有了“动手”的能力,安全问题必须重视。我总结了几条底线:

  • 最小权限原则:Agent 能访问的资源严格限制在任务需要的范围内。读文件就只给读权限,写文件就只给指定目录的写权限。
  • 危险操作二次确认:删除、修改、发送这类有副作用的操作,执行前要确认。可以是用户确认,也可以是规则确认。
  • 工具调用审计:所有工具调用记录日志,包括调用时间、参数、结果。出问题时能追溯。
  • 输入输出过滤:用户输入和工具结果都要过滤,防止注入攻击。尤其是工具结果,可能包含恶意内容。
  • 超时和限流:工具调用要有超时,避免卡死;要有频率限制,避免被滥用。

注意事项:MCP Server 的权限就是 Agent 的权限。如果你给 MCP Server 配了一个能访问整个数据库的账号,那 Agent 就能读写整个数据库。配置权限时一定要按最小必要原则来。

5.4 性能优化:让 Agent 跑得又快又稳

Agent 的性能瓶颈通常在两个地方:模型推理延迟和工具调用延迟。优化思路也分两块。

模型推理方面,能并行就并行。比如多个独立的工具调用可以同时发起,不用串行等待。Skill 流程里没有依赖关系的步骤也可以并行。

工具调用方面,能缓存就缓存。比如查天气这种结果短时间内不变的工具,可以加缓存。能批量的就批量,比如查多个用户信息,一次查完比分多次查快。

# 并行工具调用示例 import asyncio async def parallel_tool_calls(tool_calls): tasks = [execute_tool_async(tc) for tc in tool_calls] return await asyncio.gather(*tasks)

另外,工具的超时设置要合理。太短容易误判失败,太长会拖慢整体。我的经验是本地工具 5 秒,远程工具 15 秒,超过就降级或报错。

6. 常见问题与排查技巧实录

6.1 模型不调用工具怎么办

这是最常见的问题。模型明明有能力调工具,但就是不用,直接凭训练数据瞎答。排查思路:

  • 检查工具描述:描述是否清晰说明了使用场景?有没有写“什么时候不该用”?
  • 检查工具数量:工具太多模型会挑花眼,超过 20 个建议做分组或路由。
  • 检查提示词:系统提示里有没有明确要求“需要实时信息时必须调用工具”?
  • 检查模型能力:不是所有模型都支持 Function Calling,确认你用的模型支持。
  • 强制调用:有些 API 支持tool_choice="required",强制模型必须调工具,适合调试。

6.2 工具调用参数错误怎么排查

模型调了工具,但参数填错了。常见原因和解决:

问题现象可能原因解决方法
参数类型错误schema 定义不清晰明确 type 和格式说明
参数值超出范围没限制取值范围用 enum 或加范围说明
必填参数没传required 没标或描述不清检查 required 字段
参数名拼错模型幻觉参数名用常见英文单词
传了多余参数schema 没限制加 additionalProperties: false

6.3 MCP 连接失败的排查清单

MCP 连接问题通常出在配置和网络。排查顺序:

  1. 检查 Server 是否启动:stdio 方式看进程在不在,SSE 方式看端口通不通。
  2. 检查配置格式:JSON 格式对不对,路径对不对,命令能不能执行。
  3. 检查权限:Server 访问的资源有没有权限,认证信息对不对。
  4. 检查版本兼容:Client 和 Server 的 MCP 协议版本是否匹配。
  5. 看日志:MCP Server 一般会输出日志,从日志里找错误信息。

6.4 Skill 执行中断的常见原因

Skill 执行到一半断了,可能的原因:

  • 某步工具调用失败:检查该步的工具配置和参数。
  • 上下文超限:中间结果太长,超出模型上下文窗口,需要压缩。
  • 提示词歧义:某步的提示词让模型困惑,输出不符合预期格式。
  • 循环依赖:Skill 步骤之间有循环依赖,导致死循环。
  • 超时:整体执行时间超过限制,需要优化或拆分。

我的做法是给每个 Skill 步骤加超时和重试,单步失败不影响整体,能降级就降级,不能降级就跳过并记录。

6.5 几个我踩过的坑

坑一:工具描述写得太技术化。我一开始按 API 文档的风格写描述,结果模型理解不了。后来改成口语化描述,调用率明显提升。模型不是工程师,它需要的是“人话”。

坑二:MCP Server 暴露太多工具。有次我图省事,把一个 Server 的所有工具都暴露给 Agent,结果模型在各种工具之间反复横跳,效率极低。后来按场景分组,每次只暴露相关工具,效果好很多。

坑三:Skill 步骤太细。我一开始把 Skill 拆得很细,每步只做一件小事,结果步骤太多,编排开销大,还容易出错。后来合并了一些步骤,控制在 5 步以内,稳定性和效率都上来了。

坑四:忽略工具调用的幂等性。有次 Agent 重试了一个“发送邮件”的工具,结果用户收到两封一样的邮件。后来所有有副作用的工具都加了幂等键,重试时先检查是否已执行。

坑五:上下文里工具结果没截断。有次工具返回了一个巨大的 JSON,直接把上下文撑爆了。后来所有工具结果都做了长度限制,超长的截断或摘要后再塞回上下文。

7. 关于能力来源的一点个人体会

回到最初的问题:Agent 的能力从哪来?我的答案是,它不来自某一个单点技术,而来自模型、工具、协议、编排这四层的协同。模型提供推理底座,工具提供行动能力,MCP 提供标准化连接,Skill 提供流程编排。少了任何一层,Agent 都只能是个半成品。

我在实际项目里最大的体会是:工具调用的质量,八成取决于描述和参数设计,两成取决于模型能力。很多人把精力花在换更强的模型上,却忽略了工具描述这个真正的大头。把描述写清楚、参数设计合理、边界说明白,比换个模型带来的提升大得多。

MCP 的价值在于规模化。工具少的时候感觉不到它的好,工具一多,标准化协议的优势就出来了。但它也不是银弹,自己写 Server 该踩的坑一个不少,只是坑的位置从“接入方式”转移到了“Server 实现”。

Skill 是最容易被低估的一层。很多人觉得 Skill 就是提示词模板,其实它是一套完整的能力封装。好的 Skill 能让 Agent 从“会调工具”进化到“会做事”,这个跨越比想象中大。

最后分享一个小技巧:调试 Agent 时,把每次工具调用的输入输出都打出来,包括模型的思考过程(如果有的话)。很多问题看一眼日志就清楚了,比瞎猜快得多。我现在的习惯是每个 Agent 项目都先搭一套完整的日志系统,后面省下的调试时间远超搭建成本。

这个领域变化很快,MCP 协议在演进,Skill 的标准也在形成,但底层的思路是稳定的:让模型负责思考,让工具负责执行,让协议负责连接,让编排负责流程。把这四件事做好,Agent 的能力自然就上来了。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/27 17:02:58

Flask+WebUploader大文件分片上传、断点续传与秒传实践

简介&#xff1a;这份演示项目源码围绕 Flask 与 WebUploader 实现大文件分片上传及流式下载&#xff0c;面向希望理解前后端分片协作、并发上传与低内存文件传输的 Python Web 开发者&#xff0c;也可作为课程设计或内部工具原型参考。压缩包共 26 个文件&#xff0c;约 427KB…

作者头像 李华
网站建设 2026/10/27 17:01:47

Django工作流引擎实战:五张表构建工单流转系统

简介&#xff1a;这是一套基于Django框架实现的工作流引擎与工单系统完整项目源码&#xff0c;面向Python Web开发学习者、软件工程专业学生及需要业务流程管理方案的开发者&#xff0c;尤其适合作为毕业设计选题。项目以Python为主要语言&#xff0c;围绕工作流定义、任务分配…

作者头像 李华
网站建设 2026/10/27 17:01:32

YOLO包装盒缺陷检测:数据格式与训练参数避坑指南

简介&#xff1a;YOLO算法快递包裹包装盒缺陷检测数据集包含1200余张标注图像&#xff0c;覆盖完好箱体、破损箱体、开口包裹和完整包裹四类目标&#xff0c;适合入门至进阶学习者练习目标检测模型的训练与评估。数据集已划分train、val、test目录&#xff0c;并附data.yaml配置…

作者头像 李华
网站建设 2026/10/27 17:00:41

LLM模型评测全指南:基准选择、数据污染与可复现流程

如果你已经花了几个月时间折腾数据集清洗、LoRA 微调、分布式训练&#xff0c;结果模型一上线&#xff0c;连你自己都说不清它到底比 base 强在哪&#xff0c;那我建议你停下来&#xff0c;认真读一读这篇。LLM 训练做到后期&#xff0c;真正拉开差距的往往不是训练脚本写得多花…

作者头像 李华
网站建设 2026/10/27 17:00:21

STM32温湿度采集报警系统实战:DHT11时序、代码与避坑指南

简介&#xff1a;这份资源是面向嵌入式初学者与STM32开发者的温湿度采集报警系统完整工程包&#xff0c;围绕环境监测与超限报警场景展开&#xff0c;可用于农业、仓储、智能家居等方向的课程设计或练手项目。压缩包共204个文件&#xff0c;约3.29MB&#xff0c;以52个c源文件与…

作者头像 李华