请解释 LLM 是如何学会调用外部 API 或工具的?(可以从 Function Calling 的角度解释)
P1 · tool_calling · 🏢 OpenAI
1️⃣ 考察意图
面试官想考察你对 LLM 工具调用(Function Calling)的整条链路理解,而非仅背诵概念。这是典型的系统设计 + 工程取舍型问题。刁钻点在于:多数人只知“微调让模型输出 JSON”,但面试官真正想看的是——你是否理解训练数据构造的细节(如函数描述怎么写才不 bias)、推理时如何避免幻觉参数(如日期格式错误)、以及多轮交互中上下文管理的坑。答好了能展示你对 LLM 落地中“可控性”和“鲁棒性”的硬实力。
2️⃣ 标准答
LLM 学会调用工具的核心是 Function Calling 机制,分为训练和推理两个阶段。下面从数据、训练、推理、执行四个层面拆解。
数据构造:函数描述的“黄金法则”
- 函数定义:用 JSON Schema 描述函数名、参数类型、必填项、枚举值。例如
get_weather(location: string, date: string),date 格式必须写"format": "YYYY-MM-DD",否则模型可能输出"2024/8/5"这种非标准格式。 - 训练样本:构造
user -> assistant (含 function_call)的对话。关键技巧:混合“调用”和“不调用”样本。如果所有样本都调用函数,模型会过度调用;需要加入 20-30% 的“用户问题无需调用”样本(如“你好”),让模型学会拒绝。 - 坑:函数描述不要写“如果用户问天气,就调用此函数”——这会让模型依赖提示词而非推理。正确做法:只写函数签名和自然语言描述(如“获取指定地点和日期的天气信息”),让模型自己判断意图。
训练阶段:微调 vs. 上下文学习
- 微调(SFT):在基础模型上,用上述数据做监督微调。模型学会将用户意图映射到
function_calltoken 序列。OpenAI 的 GPT-4 系列用此方法,效果稳定但成本高。 - 上下文学习(In-Context Learning):在 prompt 中注入函数定义(如
tools参数),模型通过注意力机制理解调用规则。适用于小模型或快速迭代场景,但受限于上下文长度(如 8K tokens 内只能放 5-10 个函数)。 - Trade-off:微调更准确但更新函数需重新训练;上下文学习灵活但易受 prompt 格式影响(如函数定义顺序改变可能降低 5-10% 准确率)。
推理阶段:三步走
- 意图识别:模型根据用户输入和函数描述,输出
function_call对象(如{"name": "get_weather", "arguments": {"location": "北京", "date": "2024-08-05"}})。这里用 logit bias 技术:在生成function_calltoken 时,强制模型只输出合法函数名(通过 mask 非函数名 token)。 - 参数填充:模型生成 JSON 参数。常见问题:参数幻觉——模型可能输出不存在的参数(如
"unit": "celsius"但函数未定义)。解法:在训练时加入“参数校验”样本,或推理时用 JSON Schema 校验器 后处理,拒绝非法参数并让模型重试。 - 多轮交互:系统调用 API 后,将结果(如
{"temperature": 30})作为tool角色消息注入,模型据此生成最终回答。上下文管理:如果函数调用链过长(如先查天气再查航班),需控制历史消息长度,避免超出上下文窗口。常用策略:只保留最近 2-3 轮函数调用结果,丢弃中间细节。
执行层:错误处理
- API 超时:设置 5 秒超时,超时后返回
{"error": "timeout"},模型应输出“服务暂时不可用,请稍后再试”。 - 参数格式错误:如用户说“后天”,模型可能输出
"date": "后天"。解法:在函数描述中写"date": "YYYY-MM-DD",并在训练数据中加入“日期转换”样本(如“后天” -> “2024-08-07”)。 - 安全校验:禁止模型调用危险函数(如
delete_user)。在推理时用 allowlist 过滤:只允许白名单内的函数被调用。
3️⃣ 答题模板(30 秒电梯版)
“这个问题我从数据构造、训练方式、推理流程、执行错误处理四个层面回答。数据层面,函数描述必须用 JSON Schema 且混合调用/不调用样本;训练层面,微调更准但上下文学习更灵活;推理时,用 logit bias 控制函数名生成,并用 JSON Schema 校验器防止参数幻觉;执行层,设置超时和 allowlist 做安全兜底。总结一句:Function Calling 的核心是让模型学会‘何时调用、调用哪个、参数怎么填’,并通过工程手段保证鲁棒性。”
4️⃣ 高频追问 & 应对
追问 1:如果用户同时问两个问题,需要调用两个不同的函数,模型怎么处理?
模型可以输出多个
function_call(如 OpenAI 支持tool_choice: "auto"时返回数组)。但注意:并行调用时,参数可能互相依赖(如先查城市 ID 再查天气)。解法:在训练数据中加入“链式调用”样本(如“先调用get_city_id,再用结果调用get_weather”),并在推理时让模型分步输出。如果函数无依赖,可并行执行以降低延迟。
追问 2:如何评估 Function Calling 的准确率?
用三个指标:① 调用准确率:模型是否在需要时调用、不需要时拒绝(F1 score);② 参数精确率:生成的 JSON 参数是否合法且符合用户意图(用 exact match 或 BLEU);③ 端到端成功率:用户是否满意最终回答(人工评估或 LLM-as-judge)。实际落地中,参数精确率常是瓶颈(如日期格式错误占 30% 失败案例),需针对性优化。
追问 3:如果函数数量超过 100 个,上下文放不下怎么办?
用函数检索:将函数描述向量化(如用 text-embedding-3-small),推理时先检索 top-5 相关函数,再注入 prompt。注意:检索质量直接影响准确率,需用 BM25 + embedding 混合检索(如 0.3 BM25 + 0.7 cosine)。另一种方案:分层函数——将函数分组(如“天气类”“金融类”),先让模型选组,再选具体函数。
5️⃣ 避坑 · 常见错误答法
- ❌ 说“Function Calling 就是让模型输出 JSON,然后系统解析执行” → ✅ 正确切入:必须强调训练数据构造(混合样本、参数格式)、推理时 logit bias 和 JSON Schema 校验、以及错误处理(超时、非法参数)等工程细节。
- ❌ 说“模型通过强化学习学会调用工具” → ✅ 正确切入:主流方法是 SFT + 上下文学习,RL(如 GRPO)用于优化调用策略(如减少冗余调用),而非基础能力。不要混淆。
- ❌ 说“函数描述越详细越好” → ✅ 正确切入:描述太详细(如“当用户说‘今天热吗’时调用此函数”)会导致模型过拟合,降低泛化能力。应保持简洁,只写函数签名和自然语言描述。
6️⃣ 简历呼应
- 如果你有 RAG 项目:从“工具调用与检索的协同”切入——如何用 Function Calling 调用搜索引擎 API,并将结果注入 RAG 流程(如先调用
search_web再生成回答)。强调你处理过参数幻觉(如日期格式)和上下文管理(如截断历史)。 - 如果你只做过传统 NLP:用“意图识别 + 槽位填充”类比——Function Calling 相当于多轮意图识别(选择函数)和槽位填充(填参数),但多了 JSON Schema 校验和错误重试。展示你理解序列标注到结构化输出的迁移。
- 如果你是校招无项目:聚焦论文复现——引用 OpenAI 的《Function Calling in GPT-4》博客和《Toolformer》论文,说明你理解训练数据构造(如自监督生成调用样本)和推理时 logit bias 原理。可提你写过一个 demo:用 5 个函数(天气、计算器、翻译)测试调用准确率。
- OpenAI 官方博客:Function Calling and GPT-4
- 论文:Toolformer: Language Models Can Teach Themselves to Use Tools
- 论文:Gorilla: Large Language Model Connected with Massive APIs
- 博客:How to Build a Reliable Function Calling System (LangChain 官方)
- 工具:JSON Schema 校验器(ajv / jsonschema Python 库)