Function Calling 与 MCP(工具调用)
本页讲模型如何表达工具调用意图、如何设计 schema 与生命周期;当面试进入用户身份委托、短期凭证、策略义务、写操作确认和事故止血时,继续看 企业 Tool Gateway 安全执行系统设计面试题。
工具调用是 Agent 的地基。面试不只考「流程是什么」,更考工程细节:schema 怎么设计、并行调用什么时候开、失败怎么兜底、模型为什么会用工具。本文从机制讲到最佳实践,MCP 协议的完整深挖见 MCP 协议深入,工具权限、HITL、幂等和审计的专门答法见 Agent 工具安全与权限边界。
2026 面试先背这几句话
- Function Calling 是模型输出结构化调用意图,MCP 是工具与数据源的标准接入协议。前者解决「模型怎么说我要调工具」,后者解决「工具怎么被发现、描述、连接和复用」。
- 工具 schema 不是接口文档的搬运,而是给模型看的「路由说明书」:要写清何时用、何时不用、参数边界、返回值语义、失败时怎么解释。
- 生产 Agent 的工具调用要按分布式系统治理:超时、重试、幂等、权限、审计、熔断、降级、最大轮数一个都不能少。
- 工具越多越要做分层路由或 Tool RAG,否则上下文成本、误调用率和调试复杂度都会上升。
- MCP 落地时要能讲清 Host / Client / Server:Host 是 AI 应用,Client 管连接和会话,Server 暴露 tools/resources/prompts。
Function Calling 完整机制
Function Calling 是让模型「调用外部函数/工具」的能力。核心认知:模型本身不执行函数,它只输出「我想调用哪个函数、用什么参数」这一结构化意图,真正执行由你的代码完成。
开发者 模型 你的程序
│ ① 注册工具 schema │ │
│ ───────────────────► │ │
│ ② 用户提问 │ │
│ ───────────────────► │ ③ 返回 tool_calls │
│ │ (函数名 + JSON 参数) │
│ │ ───────────────────────► │ ④ 校验并真正执行
│ │ ⑤ 结果作为 tool 消息回传 │
│ │ ◄─────────────────────── │
│ ⑥ 最终自然语言回答 │ (或继续发起下一次调用 → 循环)
│ ◄─────────────────── │第 ③~⑤ 步可以循环多轮——这个「模型决策 → 执行 → 观察结果 → 再决策」的循环就是 Agent 的最小骨架(ReAct 范式,见 Agent 基础)。
模型为什么会用工具?
工具调用能力是训练出来的,不是 prompt 魔法:
- 后训练阶段用大量「对话 + 工具调用轨迹」数据做 SFT/RL,模型学会输出特定格式(通常由特殊 token 或固定模板包裹的 JSON)。
- 推理时,工具 schema 被注入到上下文(相当于 system prompt 的一部分),模型「看着说明书点菜」。
- 因此 description 是模型决定「用不用、怎么用」的唯一依据——工具描述写得好不好,直接决定调用准确率。
- 很多推理框架还会配合约束解码(限制输出必须符合 JSON Schema),从机制上保证参数格式合法,见 结构化输出详解。
工具 Schema 设计最佳实践
{
"name": "search_orders",
"description": "按条件查询用户订单。适用于用户询问订单状态、物流、历史购买记录。不适用于退款操作(用 refund_order)。",
"parameters": {
"type": "object",
"properties": {
"user_id": { "type": "string", "description": "用户唯一 ID" },
"status": {
"type": "string",
"enum": ["pending", "shipped", "completed"],
"description": "订单状态筛选,不传则查全部"
},
"limit": { "type": "integer", "description": "返回条数,默认 10,最大 50" }
},
"required": ["user_id"]
}
}| 原则 | 说明 |
|---|---|
| 描述写「何时用 + 何时不用」 | 模型靠 description 路由,边界写清楚能显著减少误调用 |
| 能用枚举就用枚举 | enum 比自由字符串可靠得多,约束解码可直接强制 |
| 参数少而扁平 | 深层嵌套对象出错率高;超过 5~7 个参数考虑拆工具 |
| 命名动宾结构 | search_orders 优于 orders / tool1 |
| 返回结果也要设计 | 给模型看的结果要精简、结构化、含单位和字段说明,垃圾进垃圾出 |
| 幂等与只读标注 | 区分读操作与写操作,写操作考虑加确认环节 |
Schema 设计的高频细节
面试里不要只说「定义 name、description、parameters」,要能说出这些工程细节:
| 设计点 | 推荐做法 | 反例 |
|---|---|---|
| 工具粒度 | 一个工具只做一个明确动作,读写分离 | manage_user 同时查、改、删 |
| 参数约束 | 用 enum、范围、格式说明收窄空间 | status: string 让模型自由发挥 |
| 可选参数 | 默认值写进 description,服务端也要兜底 | 只在 prompt 里说默认值 |
| 返回值 | 返回结构化摘要,不把长文档/API 原文全塞回去 | 10KB HTML 原样回传给模型 |
| 错误结构 | { "ok": false, "error_code": "...", "retryable": true } | 只返回「失败了」 |
| 版本演进 | 新增字段保持兼容;破坏性变更换工具名或版本 | 静默改语义导致旧 prompt 失效 |
一个好用的工具描述模板:
用途:什么时候应该调用。
边界:什么时候不应该调用,应该改用哪个工具。
输入:关键字段、单位、格式、默认值、上限。
输出:返回字段含义,是否可能为空。
失败:常见错误码,以及模型应该怎么继续。
风险:是否只读、是否会产生副作用、是否需要用户确认。面试表达:schema 的目标不是让后端能解析,而是让模型少猜、让程序能校验、让失败可恢复。
进阶机制
tool_choice(调用控制):auto(模型自行决定,默认)/ required(必须调用某个工具)/ 指定具体函数 / none。强制调用适合「这一步必然要查库」的工作流节点;但滥用 required 会让模型在无合适工具时硬编参数。
并行工具调用(Parallel Tool Calls):模型一次返回多个相互独立的 tool_calls(如同时查天气和汇率),程序并发执行后一起回传。能省多轮往返、降延迟;但有依赖关系的调用(先查 ID 再用 ID 查详情)不能并行,模型偶尔会误并行,需要在描述中写明依赖。
多轮工具循环的工程要点:设最大轮数上限防死循环;每轮把工具结果追加进消息历史(注意上下文膨胀,长结果要截断/摘要,见 上下文工程);流式场景下 tool_calls 的参数是分片到达的,要拼完整再解析。
上下文窗口与工具结果管理
工具调用很容易把上下文撑爆,尤其是搜索、数据库查询、网页抓取、代码仓库分析这类工具。生产里通常做三层控制:
- 工具侧限流:限制
limit/top_k/page_size,默认返回摘要而不是全量明细。 - 中间层压缩:长结果先做结构化摘取,只保留模型下一步决策所需字段。
- 消息历史裁剪:旧的 tool observation 不长期保留原文,只保留任务状态、关键结论和引用 ID。
常见做法是「大结果落外部存储,模型只拿句柄」:工具返回 file_id/doc_id/query_id 和短摘要,后续需要细节再按 ID 读取。这比把所有结果塞进上下文更稳定,也更省钱。
错误处理与鲁棒性
- 参数校验前置:执行前用 JSON Schema 校验,非法参数不执行。
- 错误信息回传给模型:把「参数 user_id 缺失」「API 超时」作为 tool 结果返回,模型通常能自我修正重试——这比直接报错给用户体验好得多。
- 重试与超时:工具侧设超时与重试上限;连续失败 N 次则降级(告知用户/换工具)。
- 安全边界:工具返回的内容是不可信数据——网页、邮件等外部内容可能包含注入指令(间接 Prompt 注入),不要让模型把工具结果当作指令执行;高危操作(转账、删除)必须人工确认。详见 大模型安全。
生产常见失败模式
| 失败模式 | 表现 | 应对 |
|---|---|---|
| 不该调却调 | 用户闲聊时查库/发请求 | description 写清边界,增加 tool_choice=none 场景 |
| 该调没调 | 模型直接编答案 | 强化系统提示,关键节点用 required,离线评估触发率 |
| 工具选错 | 退款问题调用查订单工具 | 工具命名动宾化,写「不适用场景」,做 Tool RAG |
| 参数幻觉 | 编造 ID、日期、枚举值 | schema 约束 + 服务端校验 + 让模型向用户追问缺失字段 |
| 死循环 | 重复调用同一失败工具 | 最大轮数、错误计数、熔断、降级到人工/普通回答 |
| 副作用失控 | 重复下单、重复发邮件 | 幂等键、确认步骤、写操作串行、审计日志 |
| 工具注入 | 网页内容要求模型泄露系统提示 | 工具结果按不可信数据处理,不允许 observation 覆盖 system 指令 |
工具一多就乱:规模化管理
- 控制单次可见工具数:几十上百个工具全塞上下文,路由准确率和成本都会恶化;经验上单次 10~20 个以内为宜。
- 工具检索(Tool RAG):把工具描述向量化,按用户意图先检索相关工具子集再注入。
- 分层/分组:按域拆分 Agent(订单 Agent、客服 Agent),或用「先选组、再选工具」的两级路由。
- 评估:工具调用准确率有专门基准(如 BFCL,伯克利函数调用排行榜),业务上应建自己的调用评估集(该调时调了吗、函数选对了吗、参数填对了吗)。
规模化工具平台通常还会加一层 Tool Registry:集中管理工具的 schema、权限、版本、owner、SLA、审计日志。这样 Agent 不直接依赖某个函数实现,而是通过注册中心拿到当前可用能力;工具下线、升级、灰度和权限收敛都更容易做。
MCP(Model Context Protocol)
MCP 是 Anthropic 于 2024 年提出的开放标准协议,规范「LLM 应用 ↔ 外部工具/数据源」的连接,被称为「AI 应用的 USB-C」。
解决的问题:此前每个应用接每个工具都要定制集成,是 M×N 组合爆炸;MCP 统一标准后,工具方实现一个 MCP Server、应用方实现一个 MCP Client,变成 M+N。
核心架构:Host(AI 应用)— Client(连接管理)— Server(能力提供方),Server 暴露三大原语:
- Tools:可被模型调用的函数(对应 Function Calling 的工具)
- Resources:可读取的数据/文件
- Prompts:预定义提示模板
更细一点:
- Host:用户真正使用的 AI 应用,例如 IDE、桌面助手、企业知识库问答。
- MCP Client:Host 内部的连接适配层,负责和一个或多个 Server 建立会话、列工具、转发调用、处理权限。
- MCP Server:能力提供方,可以封装本地文件、数据库、浏览器、GitHub、内部 CRM 等。
MCP 落地设计题怎么答
如果面试官问「让你把公司内部系统接入 Agent,你怎么设计 MCP Server?」可以按这条线答:
- 能力建模:把内部系统能力拆成 tools/resources/prompts。查询类优先做 resource 或只读 tool,写操作单独拆出。
- 权限隔离:Server 不信任模型,只信任 Host 传来的用户身份和授权范围;工具按租户、角色、数据域做 ACL。
- 输入校验:所有 tool 参数服务端强校验,不能因为模型生成了 JSON 就直接执行。
- 结果最小化:返回模型完成任务所需的最小字段,敏感字段脱敏,长结果分页。
- 可观测与审计:记录 tool name、参数摘要、耗时、错误码、调用用户、trace_id,便于复盘。
- 降级策略:Server 不可用时 Client 能把错误结构化返回给模型,或切到备用工具。
与 Function Calling 的关系(高频考点):
| Function Calling | MCP | |
|---|---|---|
| 层面 | 模型能力:如何表达调用意图 | 生态协议:工具如何标准化提供与发现 |
| 定义方 | 各模型厂商 API | 开放标准(跨厂商) |
| 关系 | MCP Client 拿到工具清单后,仍通过 FC 让模型发起调用 | MCP 在 FC 之上解决集成与复用 |
一句话:FC 是「模型会点菜」,MCP 是「统一的菜单格式和上菜通道」。MCP 的传输方式(stdio/Streamable HTTP)、生命周期、鉴权与安全风险见 MCP 协议深入;企业内部 Server 平台、Registry、Gateway、审计和上线门禁见 MCP Server 生产化与企业治理高频问答。
MCP 与普通插件/SDK 的区别
| 对比项 | 普通 SDK / 插件 | MCP |
|---|---|---|
| 集成方向 | 应用直接写死调用某 SDK | Host 通过标准协议连接 Server |
| 复用性 | 每个应用重复接一遍 | 一个 Server 可被多个 Host 复用 |
| 能力发现 | 通常靠代码/文档 | Client 可动态列出 tools/resources/prompts |
| 运维治理 | 分散在各应用里 | 可按 Server 做权限、审计、版本和 SLA |
这也是 2026 Agent 岗位爱问 MCP 的原因:它不只是一个协议名,而是把「工具生态」从工程项目里的私有集成,推向可复用、可治理的基础设施。
面试专项:工具调用排障怎么答
面试官常会给一个现象让你定位。不要只说“调 prompt”,按这张表拆:
| 失败类型 | 现象 | 诊断信号 | 修复方式 |
|---|---|---|---|
| 触发失败 | 该查库时模型直接编答案 | tool_calls 为空,答案无引用 | 关键节点用 required,工具描述写清必须调用场景,加入触发评估集 |
| 工具选错 | 退款问题调用查询工具 | tool_name 与意图不匹配 | 工具命名动宾化,description 写“不适用场景”,按领域做 Tool RAG |
| 参数错误 | ID、日期、枚举值填错 | schema 校验失败或业务错误码 | enum/format/range 收窄参数,缺字段让模型追问,错误结构化回传 |
| 工具超时 | Agent 卡住或重复重试 | trace 中工具 latency 高、retry 多 | 工具超时、指数退避、熔断、降级到缓存/人工 |
| 权限拒绝 | 用户让 Agent 查无权数据 | 403/permission_denied | 不重试;提示权限不足;服务端按用户/租户校验,工具集按权限裁剪 |
| 副作用失控 | 重复下单、重复发邮件 | 写工具多次调用、无确认记录 | 写操作串行、幂等键、HITL 确认、审计日志 |
| 工具结果注入 | 网页/邮件诱导 Agent 泄密 | observation 含“忽略系统指令”等内容 | 工具结果按不可信数据处理,注入检测,禁止 observation 覆盖 system 指令 |
可复述版本:
我会先看 trace:模型是否触发工具、选了哪个工具、参数是否合法、工具返回什么、失败后如何恢复。工具调用问题一般分成触发、选择、参数、权限、超时、副作用和注入七类。每类都有不同修复手段,不能只靠 prompt。
面试专项:Function Calling 生产检查清单
Function Calling 上生产时,重点不是“模型能输出 JSON”,而是工具是否可治理、可审计、可回滚。
| 检查项 | 要求 | 面试追问 |
|---|---|---|
| Schema | name 清晰、description 写适用/不适用、参数有 enum/range/format | 模型选错工具怎么办? |
| 服务端校验 | 所有参数执行前强校验,缺字段不执行 | 模型编造 ID 怎么办? |
| 权限 | 按用户、租户、角色裁剪可见工具和数据 | MCP Server 能不能信任 Host 传来的用户? |
| 幂等 | 写操作必须有 idempotency_key 或业务唯一键 | 重复创建工单怎么办? |
| 人工确认 | 转账、删除、发邮件、对外发布先生成待确认动作 | 高危动作如何避免误执行? |
| 事务边界 | 工具返回 pending/succeeded/failed,失败可补偿 | 工具执行一半失败怎么办? |
| 错误结构 | { ok, error_code, retryable, message } | 什么时候让模型重试,什么时候转人工? |
| 审计 | 记录 tool name、参数摘要、调用人、trace_id、结果、耗时 | 出问题如何追责和复盘? |
| 版本 | schema 兼容新增字段,破坏性变更换版本名 | 工具升级如何不破坏旧 Agent? |
写操作工具的事务、幂等与人工确认
写操作建议拆成两步:prepare -> confirm/execute。
用户意图
-> 模型生成写操作草案
-> prepare_create_ticket:校验参数,返回 pending_action_id
-> 展示动作、影响范围、风险提示
-> 用户/审核人确认
-> execute_pending_action:带 idempotency_key 执行
-> 记录审计日志和业务单号这样即使模型重复调用、页面刷新、checkpoint resume 或网络重试,也能靠 pending_action_id 和 idempotency_key 保证“不重复执行”。面试里可以强调:读工具可以并行,写工具默认串行;写操作失败后不要让模型盲目重试,要先查询外部业务状态。
Tool Registry 与生命周期治理
工具多起来后需要注册中心管理:
tool_name、版本、owner、SLA、风险等级、读/写属性。- schema、示例、错误码、权限策略、灰度状态。
- 评估集:该不该调、调哪个、参数是否正确。
- 下线流程:先从可见工具集移除,再保留兼容期,最后删除实现。
一句话:工具是 Agent 的生产 API,不是 prompt 附件。API 需要的版本、权限、SLA、审计和回滚,工具同样需要。
高频追问
Q:模型会真的执行函数吗? 不会。模型只输出调用意图(函数名+参数 JSON),执行由外部代码完成,结果再回传给模型。这是最常见的概念辨析题,务必答清楚。
Q:为什么要专门的 Function Calling 接口,而不是在 prompt 里说「请输出 JSON」? 三个原因:① 模型在后训练中专门学过 FC 的特殊格式,可靠性远高于自由文本模仿;② 接口层可配合约束解码,从机制上保证 JSON 合法;③ 结构化的 tool_calls 字段便于程序解析与多轮管理,不用从自然语言里抠 JSON。
Q:Function Calling 失败/参数错误怎么办? 校验前置(JSON Schema)、把错误信息作为工具结果回传让模型自修正、设重试与轮数上限、降级路径(换工具或转人工)、记录失败 case 回流评估集。
Q:并行工具调用什么时候开、有什么坑? 相互独立的查询类调用开并行能显著降延迟;坑在于模型可能把有依赖的调用误判为可并行(用了还没拿到的结果),以及写操作并行的副作用顺序问题。实践:读操作放开并行,写操作串行+确认。
Q:工具太多模型选错怎么办? 控制单次可见数量、Tool RAG 动态检索、清晰命名与「何时用/不用」描述、分层路由、用调用评估集持续度量。
Q:怎么评估一个 Agent 的工具调用能力? 拆三层:是否该调(触发判断)、调哪个(函数选择)、参数对不对(槽位填充);公开基准可参考 BFCL,业务上用自建评估集 + 线上 bad case 回流,见 模型评估。
Q:MCP Client 和 Server 分别做什么? Host 是 AI 应用本体;Client 在 Host 内管理连接、列能力、转发调用和处理会话;Server 暴露 tools/resources/prompts 并真正访问外部系统。Server 不能信任模型输出,必须自己做鉴权、校验、审计。
Q:工具返回内容太长导致超上下文怎么办? 工具侧分页/限流,返回摘要和引用 ID;中间层做结构化压缩;历史 observation 只保留关键状态。大对象放外部存储,模型需要时再按句柄读取。
Q:工具调用怎么做成本和稳定性治理? 成本看每任务工具次数、LLM 轮数、token、超时重试;稳定性靠最大步数、超时、熔断、幂等键、降级路径、trace 观测和 bad case 回归。工具调用不是「调通就完」,而是要有生产 SLA。