当前位置: 首页 > news >正文

《AI大模型应用开发实战从入门到精通共60篇》030、Function Calling:让大模型调用外部函数与数据库

030 Function Calling:让大模型调用外部函数与数据库

一次让我熬夜到凌晨三点的调试

上个月在给一个智能客服系统做集成,需求很简单:用户问“帮我查一下订单OD20240315的状态”,大模型需要去MySQL里查这条记录,然后返回给用户。我天真地以为,把SQL查询写成函数,注册给GPT-4就完事了。

结果呢?模型返回了一串JSON,里面赫然写着“SELECT * FROM orders WHERE order_id = ‘OD20240315’”——它把SQL语句当成了回答内容,而不是去执行函数。更离谱的是,有一次它居然自己编造了一个订单状态“已发货”,而数据库里那条记录明明是“待支付”。

这就是典型的Function Calling翻车现场。模型要么不会调用函数,要么调用了但无视返回结果,要么更糟——它开始“脑补”数据。

Function Calling到底在解决什么问题

大模型本质上是一个文本生成器,它不知道你数据库里有什么,不知道当前时间,更没法主动去调用API。Function Calling就是给模型装上一套“工具接口”,让它知道:遇到某些问题时,别自己瞎编,去调用我给你的函数。

但这里有个关键认知——模型不会主动执行任何代码。它只是根据你的函数描述,生成一个结构化的调用请求。真正执行函数的是你的应用程序,执行完再把结果塞回给模型,让它基于真实数据生成回答。

函数定义:别让模型猜你的参数

先看一个我踩过坑的例子。最初我这样定义函数:

functions=[{"name":"query_order","description":"查询订单状态","parameters":{"type":"object","properties":{"order_id":{"type":"string","description":"订单号"}}}}]

看起来没问题对吧?但实际测试时,用户说“查一下我的订单”,模型直接调用了query_order,但order_id参数传了个空字符串。为什么?因为description太模糊了,模型不知道“订单号”长什么样。

后来我改成这样:

# 这里踩过坑:description要写清楚格式和示例,别让模型猜"order_id":{"type":"string","description":"订单号,格式为OD后跟8位数字,例如OD20240315"}

加上示例格式后,模型提取参数的准确率从60%飙升到95%。函数定义的description是给模型看的prompt,写得越精确,模型越不会犯错。

参数校验:别相信模型传过来的任何值

你以为模型会乖乖传正确的参数?太天真了。有一次模型把“OD20240315”传成了“OD2024-03-15”,因为用户问的是“2024年3月15日的订单”。模型自作主张做了格式转换。

所以你的函数入口必须做参数校验:

defquery_order(order_id:str):# 别这样写:直接拿order_id去查数据库# 要这样写:importreifnotre.match(r'^OD\d{8}$',order_id):return{"error":"订单号格式不正确,请提供OD后跟8位数字的订单号"}# 校验通过后再查库

这里有个经验:校验失败时返回明确的错误信息,而不是抛异常。因为异常会导致整个调用链中断,而返回错误信息可以让模型重新理解问题,甚至主动向用户索要正确的参数。

多轮对话中的状态管理

Function Calling最容易被忽视的是多轮对话场景。用户说“查一下我的订单”,模型调用了query_order,但参数里没有user_id。这时候怎么办?

我的做法是分两步:

第一轮:模型发现缺少参数,返回一个“需要补充信息”的标记,而不是强行调用函数。

# 函数定义里加一个必填参数校验"required":["order_id"]

如果模型检测到参数不全,它应该生成一条自然语言回复:“请问您的订单号是多少?”而不是硬着头皮调用。

但这里有个坑:有些模型会“假装”调用函数,传一个默认值进去。我遇到过模型把order_id传成“unknown”的情况。解决方案是在函数里加一个白名单校验,所有不在预期格式内的参数直接拒绝。

数据库查询:别让模型直接写SQL

有些教程教你把“执行任意SQL”注册成函数,让模型自己写SQL去查。这简直是灾难。我见过模型生成的SQL里出现“SELECT * FROM users WHERE password = ‘xxx’”——它把用户输入的密码直接拼进了查询条件。

永远不要让模型直接操作数据库。正确的做法是封装成业务函数:

# 别这样写:让模型生成SQLdefexecute_sql(sql:str):# 危险!模型可能生成DROP TABLE# 要这样写:封装业务逻辑defget_order_status(order_id:str):# 这里只查订单状态,模型无法做其他操作sql="SELECT status FROM orders WHERE order_id = %s"# 执行查询...

每个函数只做一件事,权限最小化。查询订单的函数不能修改订单,查询用户信息的函数不能看到密码字段。

函数返回值的处理:模型会选择性失明

这是最让我头疼的问题。函数返回了正确的数据,但模型在生成回答时,要么忽略部分字段,要么自己“润色”数据。

比如函数返回:

{"order_id":"OD20240315","status":"待支付","amount":299.00,"create_time":"2024-03-15 14:30:00"}

模型生成的回答:“您的订单OD20240315已支付成功,金额299元。”——它把“待支付”改成了“已支付成功”。

解决方案是在系统prompt里加一条硬约束:

当你收到函数返回结果时,必须严格使用返回数据中的字段值,不得修改、推断或补充任何信息。如果返回数据中不包含用户询问的信息,请如实告知。

但即使这样,某些模型还是会“创造性发挥”。我的终极方案是:在函数返回数据里加一个校验字段

defquery_order(order_id:str):result=db_query(order_id)# 加一个签名,让模型无法篡改result["_signature"]=hashlib.md5(str(result).encode()).hexdigest()[:8]returnresult

然后在系统prompt里要求模型在回答时包含这个签名,前端再做校验。虽然麻烦,但对付那些“爱编故事”的模型很有效。

超时与重试:生产环境的必修课

Function Calling在生产环境会遇到各种网络问题。模型调用函数后,你的服务要去查数据库,如果数据库响应慢,模型那边可能已经超时了。

我遇到过的情况:函数执行了5秒,但模型等待函数返回的默认超时是10秒。看起来没问题,但用户在这5秒内又发了一条消息“还在吗?”,导致上下文混乱。

解决方案是给函数调用加一个明确的超时控制:

# 函数执行超时设置为8秒,给模型留2秒缓冲function_call_timeout=8# 秒

如果函数执行超时,不要返回空结果,而是返回一个明确的超时标记:

return{"status":"timeout","message":"查询超时,请稍后重试"}

这样模型至少知道发生了什么,而不是傻等或者自己编一个结果。

个人经验:三个必须遵守的铁律

做了半年Function Calling集成,踩了无数坑,总结三条最实用的经验:

第一,永远假设模型会传错参数。每个函数入口都要做参数校验,校验失败返回友好错误,让模型有机会纠正。不要相信模型会“理解”你的意图。

第二,函数返回数据要“喂到嘴边”。不要返回ID让模型自己去关联,直接把所有需要的信息都返回。比如查询订单时,顺便把用户姓名、商品名称都查出来一起返回。模型处理结构化数据的能力远不如直接给它文本。

第三,日志里记录每一次函数调用。包括模型传入了什么参数、函数返回了什么结果、模型最终生成了什么回答。这是排查问题的唯一手段。我见过太多人出了问题只能靠猜,就是因为没有日志。

最后说一句:Function Calling不是银弹。它解决的是“模型如何获取外部数据”的问题,但解决不了“模型如何正确使用数据”的问题。后者需要你在prompt工程、参数校验、结果验证上持续下功夫。别指望注册几个函数就能让模型变成你的业务专家——它只是个会调用工具的话痨,你得教会它什么时候该用工具,什么时候该闭嘴。

http://www.jsqmd.com/news/718520/

相关文章:

  • **发散创新:用Julia实现高性能科学计算的矩阵分解实战与优化技巧**在现代科学计算领域,**高效、简洁且
  • SpringBoot 消息顺序性保证:分区与顺序消费
  • 屁股决定脑袋-不同的视角看到的落地是不同的
  • 2026年家用呼吸机怎么选?这三点教你避坑找专业 - 天涯视角
  • 一分钟看懂!塑料管浮子流量计生产厂家怎么选?(附TOP3名单) - 品牌推荐大师
  • 你真的理解盈利这个事儿么
  • 青岛婚纱摄影排名:拍摄婚纱照定制、透明与品质的决策时代 - charlieruizvin
  • 多模态AI与哈密顿力学的融合:Akasha 2架构解析
  • 桌游卡牌设计终极神器:如何用CardEditor将制作效率提升300%
  • Oracle数据库动态性能视图概述
  • 评估一件事情的可行性方法
  • 惠斯通电桥测量模块信号采集 支持恒压与恒流驱动
  • 崩坏星穹铁道全自动游戏助手:智能解放你的游戏时间
  • 2026年仪器抗衰榜单出炉,Top5真实测评揭秘 - 速递信息
  • 手把手教你用FPGA实现UDP回环测试(附完整Verilog代码与网口调试助手配置)
  • AEUX:免费的设计转动画终极解决方案,5分钟完成Figma到AE的完美转换
  • 落地能力实测排行:5家靠谱美容连锁品牌盘点 - 奔跑123
  • 2026青岛婚纱摄影避坑指南 选对不踩坑 这10家工作室建议守住底线 - 江湖评测
  • LLM评估技术:从推理型评估器到奖励黑客问题解析
  • 皮肤管理实力连锁品牌盘点 5家机构核心优势解析 - 奔跑123
  • AI绘画模型调试不再难:Z-Image权重测试台开箱即用,实时切换权重亲测
  • 乖乖数学—本源公理体系与量子观测坍缩原理研究
  • 2026年超滤设备公司榜单好评分析:超滤设备品牌哪个靠谱/超滤设备供应企业找哪家/超滤设备生产企业哪家权威 - 品牌策略师
  • 如何选择适合自己的在线学习CPPM考试课程 - 众智商学院课程中心
  • 别再调第三方API了!用原生SpeechSynthesis给你的Vue项目加个‘朗读’功能(附完整Class封装代码)
  • 别再对单个数字做for循环了!PyTorch新手常犯的TypeError: iteration over a 0-d tensor错误详解
  • 2026年全国工业及商用对讲机优选源头厂家采购推荐指南:从“能用”到“耐用”的国产替代必然之路 - 速递信息
  • 【maaath】Flutter for OpenHarmony 定位服务能力集成指南
  • 2026问题肌调理美容连锁品牌名录 附选品核心参考维度 - 奔跑123
  • 新手跨境独立站选择:2026国内外TOP6独立站建站平台优缺点全面解析对比 - 速递信息