链载Ai

标题: 拨开MCP的迷雾,聊聊LLM工具调用的本质(二):Prompt和API限定法 [打印本页]

作者: 链载Ai    时间: 2 小时前
标题: 拨开MCP的迷雾,聊聊LLM工具调用的本质(二):Prompt和API限定法

前言


书接上文,上文提到了在LLM底层(MaaS API层,不包括各类Agent构建平台和Agent框架的封装),工具调用的实现方法大体可以归纳为四大类:

❄️
    ingFang SC", system-ui, -apple-system, BlinkMacSystemFont, "Helvetica Neue", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;font-size: 15px;line-height: 30px;text-align: left;" class="list-paddingleft-1">
  1. Function Calling
  2. Prompt结构化输出
  3. API结构化输出
  4. 意图识别+预定义函数
第一篇重点介绍了Function Calling实现工具调用的原理和本质,没看过的朋友,可以先移步阅读:MCP的迷雾,聊聊LLM工具调用的本质(一):Function Calling" data-itemshowtype="0" target="_blank" linktype="text" data-linktype="2">拨开MCP的迷雾,聊聊LLM工具调用的本质(一):Function Calling,本文则重点介绍剩下的三种方式。

ingFang SC", system-ui, -apple-system, BlinkMacSystemFont, "Helvetica Neue", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.578px;margin-top: 0px;margin-bottom: 8px;font-size: 22px;padding-bottom: 12px;">方法2:Prompt结构化输出

ingFang SC", system-ui, -apple-system, BlinkMacSystemFont, "Helvetica Neue", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;color: rgb(31, 35, 41);margin: 0px 0px 4px;word-break: break-all;min-height: 20px;">

ingFang SC", system-ui, -apple-system, BlinkMacSystemFont, "Helvetica Neue", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;color: rgb(31, 35, 41);margin: 0px 0px 4px;word-break: break-all;min-height: 20px;">实现工具调用的第二种常用方法,也是灵活性最高的方法,就是通过Prompt中引导模型输出特定的格式(譬如JSON)文本,然后通过正则表达式技术从输出文本中提取结构化信息,用于后续的工具调用。看下面例子:

ingFang SC", system-ui, -apple-system, BlinkMacSystemFont, "Helvetica Neue", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;color: rgb(31, 35, 41);margin: 0px 0px 4px;word-break: break-all;min-height: 20px;">

你是一个航班查询助理,根据用户基于自然语言表达的航班查询需求,按需调用工具完成查询并回复。

【工具定义】
工具名称:flight_search
功能描述:根据用户需求查询实时航班信息
输入参数:
- departure_city(必填,出发城市)
-arrival_city(必填,到达城市)
- date(必填,日期格式YYYY-MM-DD)
- passengers(可选,默认1成人)


}
请用JSON格式返回以下信息:
{
"toolname": "flight_search",
"params": {
"departure_city": "出发城市",
"arrival_city": "到达城市",
"date": "YYYY-MM-DD"
}
}

用户问题:帮我查下后天上海飞广州的航班

ingFang SC", system-ui, -apple-system, BlinkMacSystemFont, "Helvetica Neue", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;color: rgb(31, 35, 41);margin: 0px 0px 4px;word-break: break-all;min-height: 20px;">

ingFang SC", system-ui, -apple-system, BlinkMacSystemFont, "Helvetica Neue", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;color: rgb(31, 35, 41);margin: 0px 0px 4px;word-break: break-all;min-height: 20px;">该方法的工具清单,以及工具调用回复等等行为全都在Prompt中进行了限定,因此无需依赖Function Calling的支持,任何模型都适用,因此灵活性是最好的。在收到模型的回复后,通过第三方开源的LLM Output Parser,或者通过正则表达式,可以很轻松地从输出文本中解析出json文本。如下伪代码所示:

ingFang SC", system-ui, -apple-system, BlinkMacSystemFont, "Helvetica Neue", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;color: rgb(31, 35, 41);margin: 0px 0px 4px;word-break: break-all;min-height: 20px;">

importllm_output_parserfrom'xxxx';
import{flight_search}from'./tools';
// 解析响应中的输出文本
asyncfunctionparseOutput(responseText){
try{
consttoolCall = llm_output_parser.parse(responseText);
if(toolCall?.toolname){
returntoolCall;
}else{
returnnull;
}
}catch(e){

}
}

// 根据响应中的函数调用,执行对应的函数
asyncfunctioninvokeFunction(){
consttoolCall =parseOutput(responseText);
if(toolCall){
if(toolCall.toolname=='flight_search'){
constparams = toolCall.params;
if(isValidFlightSearchParams(params)){
returnawaitflight_search(params);
}else{
// 返回参数不合格的校验结果
}

}else{
//。。。。
}
}
}


functionisValidFlightSearchParams(params){
// 检查params是否符合参数格式和必填要求
// ...
}

ingFang SC", system-ui, -apple-system, BlinkMacSystemFont, "Helvetica Neue", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;color: rgb(31, 35, 41);margin: 0px 0px 4px;word-break: break-all;min-height: 20px;">

ingFang SC", system-ui, -apple-system, BlinkMacSystemFont, "Helvetica Neue", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;color: rgb(31, 35, 41);margin: 0px 0px 4px;word-break: break-all;min-height: 20px;">优点:

缺点:


方法3: API参数约束法

《五台山朝拜之路》


与Prompt结构化输出方法实现工具调用的路径相似,还有一种变种,就是借助部分模型支持的response_format(强制结构化输出)能力来实现,这是OpenAI在2024年8月份推出的一项API的新功能,可以保证模型100%按照预设的JSON格式输出,为开发者提供确定性。我们把这种方法叫做通过API参数约束法,就如同给模型带上“格式紧箍咒”一样。


response_format设计背景

  1. 1.基于Prompt的LLM结构化输出不稳定:早期开发者需通过复杂的提示词设计或多次请求才能获取结构化数据,且模型可能生成无效JSON(如缺少必填字段、格式错误等),需额外验证和重试。
  2. 2.结构化输出是Agent应用落地的刚需:结构化数据是API集成、数据录入、多步骤智能体工作流(如数据库查询、动态UI生成)的核心需求。例如,电商订单查询需严格匹配数据库字段格式,非结构化输出会增加解析成本。
  3. 3.技术迭代升级:OpenAI在2023年推出JSON模式功能,但仅能生成有效JSON,无法保证模式匹配。此次更新通过算法优化和工程约束实现100%模式可靠性。


response_format功能介绍

OpenAI API的response_format功能通过严格模式(strict mode)实现了对输出结构的精确控制,开发者可通过两种方式启用该功能:


  1. 1.函数调用模式 在工具定义中添加"strict": true参数,并明确指定JSON Schema中的required字段和additionalProperties: false,强制模型仅输出预定义字段。例如,电商订单查询工具可严格限定columns字段仅包含id、status等枚举值,避免无效字段。
  2. 2.response_format参数直接调用 直接在API请求中指定response_format类型为json_schema,并定义完整的Schema结构。例如数学题解答场景中,可要求输出必须包含steps数组(含解题步骤说明)和final_answer字段,且禁止额外属性,确保后续程序直接解析。

该功能还引入了动态递归支持,例如处理嵌套的anyOf类型或递归数据结构(如树状目录),通过上下文无关文法(CFG)实现复杂模式的动态约束。开发者甚至可为每个对象类型设置独立校验规则,例如在数据库查询中,conditions数组的每个元素必须包含column、operator、value三要素,且value允许字符串、数字或子对象的灵活组合。


此外,SDK集成优化显著简化了开发流程:


工具调用示例


// 1.定义工具函数(模拟天气查询)
asyncfunctiongetCurrentWeather({ location }) {
return{
temperature:Math.random()*30+10,// 模拟温度数据
condition: ["晴天","多云","小雨"][Math.floor(Math.random()*3)]// 模拟天气状态
};
}

// 2.配置工具调用参数
consttools = [{
type:"function",
function: {
name:"getCurrentWeather",
description:"获取指定城市的天气信息",
parameters: {
type:"object",
properties: {location: {type:"string"} },
required: ["location"]
},
// 启用结构化输出
strict:true// 强制匹配参数结构
}
}];

// 3.执行工具调用
asyncfunctionqueryWeather(message) {
constcompletion =awaitopenai.chat.completions.create({
model:"gpt-4o-2024-08-06",// 必须使用支持结构化输出的模型
messages: [{role:"user",content: message }],
tools,
response_format: {// 强制结构化响应
type:"json_object",
schema: {
type:"object",
properties: {
temp: {type:"number"},
condition: {type:"string"}
}
}
}
});

// 4.解析并执行工具调用
consttoolCall = completion.choices[0].message.tool_calls[0];
returnawaitgetCurrentWeather(JSON.parse(toolCall.function.arguments));
}

// 示例调用
queryWeather("北京明天的天气如何").then(console.log);// 输出示例:{ temperature: 25.3, condition: "多云" }

response_format的实现原理

算法层面主要是通过以下两种手段来组合实现:


优缺点

优点

缺点


方法4:意图识别+预定义函数

《花竹海上日出》


意图识别实现工具调用的技术路径在大语言模型出现之前就已经被广泛应用,几乎传统的AI助手,如智能音箱、车机语音助手,基本上都是基于这套技术方案实现的。例如早期的Siri的实现方式为:

通过自然语言理解(NLU)模块将用户指令映射到预定义的意图标签,再调用固定的API接口。例如,用户说“播放周杰伦的歌”,系统解析出意图MusicPlay,触发音乐播放工具。此过程需人工编写大量意图-动作映射规则


大语言模型出现之后,这种技术路径也有了很多的变体:

  1. 1.向基于Prompt结构化输出的方式靠拢:将意图清单和槽位清单注入到Prompt中,并在指令中要求模型必须要按照JSON格式输出最为匹配的意图和槽位值。
  2. 2.基于独立小尺寸模型路由分流的模式:这个方案通常应用在应用后面有多个不同的专家模型或者有几个确定性的Workflow的情况,小模型经过精心的SFT,在特定场景下可以实现非常高的可靠性。譬如通用的大模型助理产品通常会在用户提问后,先使用一个意图分类的小尺寸LLM(2B甚至更小),来识别用户问的问题类型,是时效性知识问答,还是百科问答,又或者是信息处理、逻辑推理等等。分类被识别了之后,本质上是将请求路由到了另外一个确定性的流程中,我们也可以把这些处理流程分支抽象为一个函数调用,只是这些函数大多不需要太多参数。


优点

  1. 1.高可靠性





欢迎光临 链载Ai (https://www.lianzai.com/) Powered by Discuz! X3.5