一、起源:小程序或App AI 助手
- 22年负责浦东执法局督察app的开发,app中仅模块就有100多个,就开发来说正常开发某个功能都需要,全局搜索页面,更别说让督察队员使用的时候找功能页面,所以非常不便捷,所以当时有开发了一个ai助手的功能,可以搜索或语音方式咨询问题,出现搜索结果,供用户选择跳转或自动跳转,因为当时核心工作都是后端,前端真的就是无脑渲染就好了,前端并未感受到什么参与感...
- 主流Ai 助手很常用的功能:
前端通过一个右侧悬浮球,用户点击输入或语音对话方式咨询,ai识别后进行反馈信息展示到搜索结果列表,供用户选择跳转或自动跳转,
例如:帮我查询用户余额、订单记录、帮我打开个人中心页面、督察执法页面、督察审批页面、今天门店督察执法已完成几家店铺...)
二、Function Call 概念及认知
1. 概述:
之前在小程序中集成一共豆包助手,只能问答式的陪你对话(基于的回复,都是训练好的内容进行回复)不能去帮你做任何事,而这个function call 可以根据你在项目中配置的函数(一件事),在对话中模型检测到需要调用本地函数,然后可以对本地函数编写的具体功能进行执行操作,例如你让它跳转首页、帮你打开订单页面、帮你查看当前账号的余额操作,普通的 AI 对话是你问它答,而 Function Call 是给 AI 配上了"手和脚" ------它不再只是用训练数据回答你,而是能调用你定义的真实函数来获取数据、执行操作。
2. 一个场景让你瞬间理解
假设你在小程序中接入了豆包模型,用户问:
用户:"帮我看看有哪些待完成的问卷?"
没有 Function Call 的回答(纯聊天):
AI:"您好!问卷功能通常在个人中心里可以找到,您可以打开小程序看看..."
(答非所问,毫无价值)
有了 Function Call 的回答:
AI:"您目前有 3 份待完成的问卷:① SADL 助听器满意度量表(6题)② 场景适应评估问卷(12题)③ CHHIE-S 听力障碍量表(20题)。建议您先从 SADL 量表开始,只需 3 分钟即可完成。"
差别在哪? 后者调用了真实的业务接口,拿到了用户真实的问卷数据,然后基于数据生成回答。这就是 Function Call 的核心价值------给 AI 配上了能操作真实世界的"手"。
3. 一句话理解 Function Call
传统 AI:你问 → AI 凭记忆答(训练数据)
Function Call:你问 → AI 判断需要什么数据 → 调用你的函数拿数据 → 基于数据回答
3.1 用户发问题 → AI 自己判断:
这个问题我自己回答不了,需要调用你写好的一段代码 / 接口 / 功能,然后:
- AI 告诉你「我要执行哪个函数、需要什么参数」
- 你的小程序本地代码去执行真实逻辑(拿时间、查接口、操作页面)
- 把真实结果回传给 AI
- AI 整理成自然语言,回复用户
- 核心:AI 不再只会说话,能指挥你的代码干活。
用户提问【普通ai回复,对比Function Call回复】
→ 豆包模型判断"需要调用工具吗?"
├─ 不需要 → 直接回答
└─ 需要 → 返回 tool_calls(函数名+参数)
→ 你的代码执行函数 → 拿到结果
→ 把结果回传给豆包模型
→ 豆包基于结果生成最终回答
4. 基于模型对应官方文档,封装本地函数
-
使用不同的模型,需要学习不同模型对应官方封装本地函数的格式和规范,才能够让ai模型能识别调用本地函数,
-
学习豆包大模型官方 Function Call 开发文档,重点关注:函数定义的标准结构、请求参数规范、模型返回的 tool_calls 格式、结果回传的格式要求。
-
避坑点:不同大模型的 Function Call schema 有差异,必须严格遵循豆包的官方规范,否则模型无法识别函数、无法正确触发调用。
-
【实践】豆包通过火山引擎 Ark 平台提供 API,Function Call 的核心数据结构如下:① Tools 定义(你告诉模型有哪些"工具"可用):
javascriptconst tools = [ { type: "function", function: { name: "get_question_list", // 函数名 description: "获取用户的问卷列表,返回所有可用的问卷信息", // 描述(模型靠这个判断何时调用) parameters: { type: "object", properties: { status: { type: "string", enum: ["all", "completed", "pending"], description: "问卷状态筛选:all全部, completed已完成, pending待完成" } }, required: ["status"] } } }, { type: "function", function: { name: "get_user_report", description: "获取用户的听力康复报告数据", parameters: { type: "object", properties: { month: { type: "string", description: "报告月份,格式YYYY-MM,如2026-05" } }, required: ["month"] } } } ];关键理解: description 是最重要的字段!模型全靠它来判断"什么时候该调用这个函数"。描述要写清楚:这个函数做什么、什么时候用、参数含义。② Tool Call 响应(模型返回需要调用的函数):
当模型判断需要调用工具时,响应中的 choices0.message 会包含 tool_calls :
javascript{ "role": "assistant", "content": null, // 触发 tool_call 时 content 为空 "tool_calls": [ { "id": "call_abc123", // 调用ID,回传结果时需要带上 "type": "function", "function": { "name": "get_question_list", "arguments": '{"status": "pending"}' // JSON字符串,需要parse } } ] }③ Tool Result 回传(你把执行结果告诉模型):
javascript// 在下一轮请求中,把tool结果放到messages里 { "role": "tool", "tool_call_id": "call_abc123", // 对应上面的id "content": '{"list": [...], "total": 3}' // 函数执行结果(JSON字符串) }
三、实践步骤
1. 需求
目前我们小程序就有一个这个优化需求,因为小程序中集成的功能很多功能模块,用户群体是没有经验的门店人员和普通老人,在不熟悉小程序的使用的场景下,这么提供用户使用的便捷操作功能 ?
2.写代码的前置了解
如果单纯集成一个ai助手,ai助手不可能知道我们项目中的结构和功能,不能帮你做任何事情,而此时基于模型封装对应的本地项目函数,然后通过模型识别调用本地函数,就能让Ai真正帮我们做一下事情了,来做到ai真正帮用户可以做事的功能,而不是停留人机的回复模式阶段。
-
在写代码之前,必须先把这个流程刻在脑子里。下面我用一个项目中的真实请求的完整生命周期来展示:【问ai,目前项目中有哪些问卷?】
┌──────────────────────────────────────────────────────────────────┐ │ Function Call 完整链路 │ ├──────────────────────────────────────────────────────────────────┤ │ │ │ ① 用户提问 → ② 你组装请求(messages + tools定义) │ │ │ │ │ │ ▼ ▼ │ │ "有哪些问卷?" POST /chat/completions │ │ { │ │ model: "doubao-pro", │ │ messages: [...], │ │ tools: [{ │ │ name: "get_question_list", │ │ description: "获取问卷列表", │ │ parameters: {...} │ │ }] │ │ } │ │ │ │ │ ③ 模型返回 ◄───┘ │ │ { │ │ choices: [{ │ │ finish_reason: "tool_calls", │ │ message: { │ │ role: "assistant", │ │ content: null, ← 注意:触发 tool_call 时无文本 │ │ tool_calls: [{ │ │ id: "call_abc123", │ │ function: { │ │ name: "get_question_list", │ │ arguments: '{"status":"pending"}' │ │ } │ │ }] │ │ } │ │ }] │ │ } │ │ │ │ │ ④ 你执行函数 → ⑤ 拿到真实数据 │ │ │ { │ │ ▼ list: [ │ │ get_question_list( { id:1, title:"SADL量表" }, │ │ status: "pending" { id:2, title:"场景适应" } │ │ ) ], │ │ total: 2 │ │ } │ │ │ │ │ ⑥ 回传结果给模型 → POST /chat/completions │ │ { │ │ messages: [ │ │ ...之前的对话, │ │ { │ │ role: "assistant", │ │ tool_calls: [{ id: "call_abc123", ... }] │ │ }, │ │ { │ │ role: "tool", ← 新增的 tool 角色 │ │ tool_call_id: "call_abc123", ← 必须与上面id一致 │ │ content: '{"list":[...], "total":2}' │ │ } │ │ ] │ │ } │ │ │ │ │ ⑦ 模型基于真实数据生成最终回答 │ │ │ │ │ ▼ │ │ "您有 2 份待完成问卷:SADL量表(6题)和场景适应评估(12题)..." │ │ │ └──────────────────────────────────────────────────────────────────┘ -
定义本地函数(从模型的官方找本地函数的封装格式规范)
-
ai对话,中进行咨询,ai基于调用本地函数,实现基础的项目功能
小总结:其实简单理解就是,项目中开发一个ai助手,用户使用ai助手的时候,ai助手调用大数据模型(不管豆包、deepseek、还是其他),它不可能知道你项目中页面,不可能操作你的项目,所以前端本地代码会封装很多函【给予ai知道你的项目,给予ai操作你项目的权利】,这里封装的函数分别对应着用户可能咨询的问题,给予ai的赋能,ai有了这个权限才能调用小程序中具体的代码层面的模拟跳转页面、查询信息等功能...
最后
这篇文章从下笔到现在,已经躺在我草稿箱中吃灰半个多月了,所以文章可以看出来举例和概念的补充,是不是不一致?没错不同阶段写的,又懒得删,不过表达意思准确就没管其他了,
一直想写完整奈何这两周有家里人过来,以及自己自律能力不行,之前好歹加班学习写文章,这半个多月空闲就是搞下个人小程序的功能迭代及页面优化,还有就是云函数调用的坑和学习吧(现在自认云函数调用部署、文档数据库、mysql数据库这些在云开发这块用到比较熟,这是唯一收获点吧),
这不云函数的定时任务这两天都没调用,模型还有免费token,模型直接走付费节点的问题,官方人员还没给出结论单纯敷衍回复给出仅本次退款后,后续不处理类似问题?
