Function Calling 到底是什么
Function Calling 是很多 AI 应用从“聊天玩具”变成“软件系统”的关键一步。
在普通 Chat 里,模型只返回一段文本。用户问“帮我查一下北京明天的天气”,模型如果没有外部信息,只能基于已有知识猜测或拒绝。但在 Function Calling 里,应用可以提前告诉模型:这里有一个 getWeather 函数,参数需要城市和日期。模型判断需要查询天气时,不直接回答,而是返回“我想调用这个函数,以及这些参数”。
这听起来像模型会写代码、会执行函数,但工程上要先把边界说清楚:模型不会真的执行你的 JavaScript 函数。它只是根据函数说明生成一个结构化的调用请求。真正执行函数的是你的应用。
它解决什么问题
Function Calling 解决的是模型和确定性系统之间的接口问题。
传统程序擅长确定性动作:查数据库、请求 API、计算价格、创建订单、读取文件、发通知。模型擅长理解自然语言、抽取意图、补全参数、组织回答。Function Calling 把两者接起来:模型负责把用户语言翻译成函数调用,应用负责执行真实动作。
对前端工程师来说,可以把它类比成“模型版事件分发”:
- 用户在 UI 里表达意图。
- 模型判断需要哪个 action。
- 模型给出 action payload。
- 应用校验 payload。
- 应用执行 action。
- 应用把执行结果交回模型或直接渲染到界面。
区别在于,普通事件分发来自按钮点击和表单输入,而 Function Calling 的触发条件来自自然语言理解。
Function Calling 不是远程代码执行
很多初学者会误解:既然叫 Function Calling,是不是模型可以直接调用后端函数?
不是。
模型输出的通常是一段结构化数据,表示它“建议调用某个函数”。例如:
这段数据本身不会访问天气服务。你的后端代码需要拿到它,判断函数名是否合法,校验参数是否符合 schema,确认用户是否有权限,再调用真实 API。
所以 Function Calling 的安全模型应该是:
模型在图里只做“选择”和“生成参数”,不拥有执行权。
一个前端熟悉的类比
假设你有一个搜索页面,用户可以输入筛选条件。传统实现可能是:
用户需要通过表单选择状态、输入关键词、点击搜索。
如果加上 Function Calling,用户可以说:
模型把自然语言转换成:
然后应用调用同一个搜索接口。
这不是让模型替代业务逻辑,而是让模型成为一种更灵活的输入层。真正的订单查询、权限判断、分页限制、字段过滤,仍然属于应用。
工程流程
一次完整 Function Calling 通常包含六步。
第一步,定义函数清单。你需要告诉模型有哪些函数可用,每个函数的名字、用途、参数结构、必填字段和枚举值是什么。
第二步,把用户消息和函数定义一起发给模型。模型会根据用户意图决定是直接回答,还是返回函数调用请求。
第三步,解析模型返回的调用请求。此时不要信任参数,要像处理普通外部输入一样处理它。
第四步,校验函数名和参数。函数名必须在白名单里,参数必须通过 Zod、JSON Schema 或后端 DTO 校验。
第五步,执行真实函数。这里可以是调用内部服务、请求第三方 API、查数据库、计算价格,也可以只是读取缓存。
第六步,把函数结果回填给模型。模型拿到结构化结果后,再生成适合用户阅读的回答。
前端不一定直接接触模型返回的函数调用,但前端会受到它的影响:消息流里可能出现“正在查询订单”“工具调用失败”“需要用户确认”等中间状态。
函数定义怎么设计
函数定义不是随便写几句描述。它会直接影响模型是否选对工具、是否生成正确参数。
好的函数名应该表达清晰动作,例如 searchOrders、getInvoiceById、createCalendarEvent。不要使用太宽泛的名字,比如 handleData、process、callApi。
函数描述要说明什么时候使用,而不只是重复函数名。
参数 schema 要尽量收敛。能用枚举就不要用任意字符串,能拆字段就不要让模型生成一整段查询语句。
例如订单搜索工具可以这样设计:
注意这里没有让模型生成 SQL。模型只生成受控字段,数据库查询由应用拼装。
返回值也要结构化
很多项目只约束输入参数,却忽略工具返回值。结果工具执行成功后,模型拿到一大段混乱文本,又开始自由发挥。
更稳的方式是让工具返回结构化结果:
这样模型可以基于明确字段生成总结,前端也可以选择直接渲染订单列表,而不是只展示模型转述。
真实产品里,最终响应不一定完全由模型生成。对于高风险、强结构化场景,前端可以直接渲染工具结果,模型只负责解释、摘要或提示下一步。
常见坑
第一个坑:把模型参数当可信输入。
Function Calling 返回的 arguments 仍然来自模型输出,必须校验。不要因为它看起来符合 JSON,就跳过权限、类型和业务规则检查。
第二个坑:工具太大,职责太宽。
一个 manageUser 工具同时创建、删除、修改权限,会让模型难以判断,也会扩大风险。工具更适合按明确动作拆分。
第三个坑:函数描述写得像内部文档。
模型需要知道“什么时候调用”,不是只知道“这个函数实现了什么”。描述应该面向意图匹配。
第四个坑:让模型生成危险参数。
不要让模型直接生成 SQL、Shell 命令、文件路径或任意 URL。应该让它填受控字段,再由应用映射到安全实现。
第五个坑:忽略失败路径。
工具可能超时、无权限、查不到结果、第三方 API 报错。失败也应该有结构化返回,让模型或前端知道如何继续。
第六个坑:前端只设计最终消息,不设计中间状态。
工具调用会引入排队、执行中、失败、重试、需要确认等状态。聊天界面需要能表达这些状态,否则用户会觉得“AI 卡住了”。
小结
Function Calling 的本质不是让模型获得执行能力,而是把自然语言意图转换成受控的函数调用请求。
模型负责理解用户、选择函数、生成参数;应用负责校验、授权、执行、记录和回填结果。前端工程师理解这一点后,就能把 AI 调用看成一种新的输入协议,而不是不可控的黑盒。
真正可靠的 Function Calling,不靠模型“乖”,而靠清晰的工具定义、严格的 schema、明确的权限边界和完整的错误状态。