Agent工具调用、MCP与Skill:从Function Calling到能力封装 1. 从一个困惑说起Agent 到底凭什么“会做事”很多人第一次接触 Agent 这个概念时脑子里浮现的画面大概是这样的一个能自己思考、自己规划、自己动手的智能体你给它一句话它就能帮你把活干完。但真正上手搭过 Agent 的人都知道事情远没有这么浪漫。模型本身再强它也只是个“嘴强王者”——能说会道但手是断的。你让它查个天气它只能凭训练数据瞎编你让它读个本地文件它连文件系统都摸不到你让它调个接口它连 HTTP 请求都发不出去。所以问题就来了Agent 的能力到底从哪来我的答案很直接——Agent 的能力 模型推理能力 × 外部工具调用能力 × 上下文管理能力。模型负责“想”工具负责“做”上下文负责“记住做过什么、接下来该做什么”。这三者缺一不可而其中最容易被低估、也最能拉开差距的就是工具调用这一层。这篇文章我想把工具调用、MCPModel 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, toolstool_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、PromptsMCP 不只是工具调用它定义了三类能力很多人只知道 Tools其实另外两类也很有用。Tools就是可调用的函数和 Function Calling 里的工具对应。模型可以主动调用执行有副作用比如发邮件、写文件、调接口。Resources是只读的数据源比如文件内容、数据库记录、API 返回的数据。模型可以读取但不能修改。Resources 的价值在于把“数据获取”和“数据操作”分开让权限控制更清晰。Prompts是预定义的提示词模板Server 可以提供一些常用的 promptClient 直接调用。这个能力用得相对少但在一些标准化场景下很有用比如“代码审查”“文档总结”这类固定流程。三类能力的对比能力类型是否可写谁触发典型场景Tools可写模型主动调用发邮件、写文件、调接口Resources只读模型或用户读取读文件、查数据库、获取配置Prompts只读用户选择代码审查、文档总结模板3.3 MCP 的通信机制stdio 和 SSEMCP 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 为例走一遍完整流程。首先安装 SDKpip 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( namequery_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(fSELECT * FROM users LIMIT {limit}).fetchall() conn.close() return [TextContent(typetext, textstr(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 知道该用哪个 SkillSkill 定义好了怎么让 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_tokens8000): 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_choicerequired强制模型必须调工具适合调试。6.2 工具调用参数错误怎么排查模型调了工具但参数填错了。常见原因和解决问题现象可能原因解决方法参数类型错误schema 定义不清晰明确 type 和格式说明参数值超出范围没限制取值范围用 enum 或加范围说明必填参数没传required 没标或描述不清检查 required 字段参数名拼错模型幻觉参数名用常见英文单词传了多余参数schema 没限制加 additionalProperties: false6.3 MCP 连接失败的排查清单MCP 连接问题通常出在配置和网络。排查顺序检查 Server 是否启动stdio 方式看进程在不在SSE 方式看端口通不通。检查配置格式JSON 格式对不对路径对不对命令能不能执行。检查权限Server 访问的资源有没有权限认证信息对不对。检查版本兼容Client 和 Server 的 MCP 协议版本是否匹配。看日志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 的能力自然就上来了。