17c:穿越时光的诗意低语

核心内容摘要

孟若羽全新MV惊艳上线
解锁“轻熟”生活:你的专属“成人版小红书”来了!

桥本有菜t2u摄影集

大模型 Function Calling 避坑当你的 AI 助手“光说不练”时前言在开发基于大模型LLM的智能助手时Function Calling工具调用是连接模型与外部世界的核心能力。

最近在调试一个控制设备音量的场景时遇到一个非常诡异的 Bug第一轮对话用户说“声音大一点”模型准确识别并调用了set_volume工具执行成功。

第二轮对话用户接着说“声音调小一点”模型却不再调用工具而是直接回复了一句“好的明白”导致设备没有任何反应。

经过深入排查发现问题的根源竟然在于对话历史Context的构建方式。

本文将记录这次排查过程及正确的解决方案希望能帮大家避坑。

问题复现场景描述我正在开发一个语音助手支持通过自然语言控制音量。

错误的历史记录构建为了“节省 Token”或者图省事在第一轮工具调用结束后我在构建传给模型的 messages 历史时手动修改了 Assistant 的回复。

我构建的错误的历史记录[{role:user,content:声音大一点},{role:assistant,content:好的已为您调大音量。

}// ❌ 错误这里丢失了 tool_calls 信息被我替换成了纯文本]结果当用户进行第二轮提问“声音调小一点”时模型看到上面的历史记录它“学到”了一个规律“在这个对话里当用户要求调音量时我只需要口头答应不需要真的去调工具。

”这就是大模型的上下文学习In-Context Learning能力带来的副作用。

模型被我伪造的历史记录误导了。

核心原因分析大模型是基于“预测下一个 token”的机制工作的。

它会极度依赖 messages 列表中的上下文模式。

正确的模式用户提问 - 助手发起tool_calls- tool 返回结果 - 助手

总结。

错误的模式用户提问 - 助手直接回复文本Content。

如果我们在历史记录中抹去了tool_calls的痕迹模型就会认为自己从未调用过工具从而在后续对话中倾向于生成纯文本回复导致 Function Calling 失效。

解决方案标准的 Function Calling 链路要解决这个问题必须如实记录之前的工具调用过程。

一个完整的工具调用闭环在 messages 中必须包含以下 4 个步骤User: 提出需求。

Assistant: 返回tool_calls字段此时content通常为null。

Tool: 客户端执行代码以role: tool返回执行结果。

Assistant: 根据工具结果生成的最终回复。

正确的 JSON 结构示例发给 API 的 Request Body 应该长这样{messages:[{role:system,content:你是一个智能硬件助手...},// --- 第一轮 ---{role:user,content:声音大一点},// 关键点1保留带有 tool_calls 的 assistant 消息{role:assistant,content:null,tool_calls:[{id:call_abc123,type:function,function:{name:set_volume,arguments:{\volumeCode\: \volume_up\}}}]},// 关键点2必须追加 tool 类型的消息传入对应的 call_id 和执行结果{role:tool,tool_call_id:call_abc123,content:success},// 助手对第一轮的

总结{role:assistant,content:好的音量已调大。

},// --- 第二轮 ---{role:user,content:声音调小一点}// 此时模型看到上面发生过 tool_calls这次它就会继续触发 tool_calls]}代码实现 (Python)下面是一个模拟正确构建对话历史的 Python 代码示例importjson# 模拟工具定义tools[{type:function,function:{name:set_volume,description:调整设备音量,parameters:{type:object,properties:{volumeCode:{type:string,enum:[volume_up,volume_down]}},required:[volumeCode]}}}]# 初始化对话历史messages[{role:system,content:你是一个全能AI助理可以控制设备音量。

}]# --- 第一轮交互 ---print( User: 声音大一点)messages.append({role:user,content:声音大一点})# 假设这里调用了大模型 API模型返回了 tool_calls# 模拟模型返回的数据结构response_msg{role:assistant,content:None,tool_calls:[{id:call_123456,type:function,function:{name:set_volume,arguments:{\volumeCode\: \volume_up\}}}]}# 【关键步骤】将模型返回的原始消息含 tool_calls加入历史messages.append(response_msg)# 客户端执行工具逻辑...tool_outputVolume turned up successfullytool_call_idresponse_msg[tool_calls][0][id]# 【关键步骤】将工具执行结果以 roletool 加入历史messages.append({role:tool,tool_call_id:tool_call_id,content:tool_output})# 模型根据工具结果给出的反馈final_feedback{role:assistant,content:好的声音已经调大了。

}messages.append(final_feedback)# --- 第二轮交互 ---print( User: 声音调小一点)messages.append({role:user,content:声音调小一点})# 打印最终发送给模型的 Messages 结构print(json.dumps(messages,indent2,ensure_asciiFalse))# 此时再次调用 API模型会因为看到了历史中的 tool_calls从而正确地再次调用工具

总结不要伪造历史千万不要把 Assistant 的tool_calls响应手动转换成纯文本的content存入历史。

保持链路完整Assistant (tool_calls) - Tool (result)这两步是成对出现的缺一不可。

理解上下文学习模型是很“懒”的如果历史记录告诉它“只动口不动手”也能混过去它下次就真的不动手了。

希望这篇踩坑记录能帮到正在折腾 Function Calling 的朋友们

gogogo高清中文版下载-gogogo高清中文版下载应用

百度百家号客服电话人工服务

123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123