Function Calling
由于AI擅长的是非结构化数据的分析,如果需求中包含严格的逻辑校验或需要读写数据库,纯Prompt模式就难以实现了。
所以这时就需要我们的使用Function Calling,来实现
1.思路分析
假如我要开发一个24小时在线的AI智能客服,可以给用户提供培训课程的咨询服务,帮助用户预约线下课程的试听
整个业务的流程如图:

这里就涉及到了很多数据库操作,比如:
-
查询课程信息
-
查询校区信息
-
新增课程试听预约单
可以看出整个业务流程有一部分任务是负责与用户沟通,获取用户意图的,这些是大模型擅长的事情:
-
大模型的任务:
-
了解、分析用户的兴趣、学历等信息
-
给用户推荐课程
-
引导用户预约试听
-
引导学生留下联系方式
-
还有一些任务是需要操作数据库的,这些任务是传统的Java程序擅长的:
-
传统应用需要完成的任务:
-
根据条件查询课程
-
查询校区信息
-
新增预约单
-
与用户对话并理解用户意图是AI擅长的,数据库操作是Java擅长的。为了能实现智能客服功能,我们就需要结合两者的能力。
Function Calling就是起到这样的作用。
首先,我们可以把数据库的操作都定义成Function,或者也可以叫Tool,也就是工具。
然后,我们可以在提示词中,告诉大模型,什么情况下需要调用什么工具。
比如,我们可以这样来定义提示词:
你是一家名为“木幽”的职业教育公司的智能客服小木。 你的任务给用户提供课程咨询、预约试听服务。 1.课程咨询: - 提供课程建议前必须从用户那里获得:学习兴趣、学员学历信息 - 然后基于用户信息,调用工具查询符合用户需求的课程信息,推荐给用户 - 不要直接告诉用户课程价格,而是想办法让用户预约课程。 - 与用户确认想要了解的课程后,再进入课程预约环节 2.课程预约 - 在帮助用户预约课程之前,你需要询问学生要去哪个校区试听。 - 可以通过工具查询校区列表,供用户选择要预约的校区。 - 你还需要从用户那里获得用户的联系方式、姓名,才能进行课程预约。 - 收集到预约信息后要跟用户最终确认信息是否正确。 -信息无误后,调用工具生成课程预约单。 查询课程的工具如下:xxx 查询校区的工具如下:xxx 新增预约单的工具如下:xxx
也就是说,在提示词中告诉大模型,什么情况下需要调用什么工具,将来用户在与大模型交互的时候,大模型就可以在适当的时候调用工具了。
流程如下:

流程解读:
-
提前把这些操作定义为Function(SpringAI中叫Tool),
-
然后将Function的名称、作用、需要的参数等信息都封装为Prompt提示词与用户的提问一起发送给大模型
-
大模型在与用户交互的过程中,根据用户交流的内容判断是否需要调用Function
-
如果需要则返回Function名称、参数等信息
-
Java解析结果,判断要执行哪个函数,代码执行Function,把结果再次封装到Prompt中发送给AI
-
AI继续与用户交互,直到完成任务
听起来是不是挺复杂,还要解析响应结果,调用对应函数。
不过,有了SpringAI,中间这些复杂的步骤大家就都不用做了!
由于解析大模型响应,找到函数名称、参数,调用函数等这些动作都是固定的,所以SpringAI再次利用AOP的能力,帮我们把中间调用函数的部分自动完成了。

我们要做的事情就简化了:
-
编写基础提示词(不包括Tool的定义)
-
编写Tool(Function)
-
配置Advisor(SpringAI利用AOP帮我们拼接Tool定义到提示词,完成Tool调用动作)
是不是简单多了~
2.基础的准备
实体类呀,controller,service,mapper这些正常准备就可以了
3.定义Tool(Function)
接下来,我们来定义AI要用到的Function,在SpringAI中叫做Tool
我们需要定义三个Function:
-
根据条件筛选和查询课程
-
查询校区列表
-
新增试听预约单
3.1.查询条件分析
先来看下课程表的字段:

课程并不是适用于所有人,会有一些限制条件,比如:学历、课程类型、价格、学习时长等
学生在与智能客服对话时,会有一定的偏好,比如兴趣不同、对价格敏感、对学习时长敏感、学历等。如果把这些条件用SQL来表示,是这样的:
-
edu:例如学生学历是高中,则查询时要满足 edu <= 2
-
type:学生的学习兴趣,要跟类型精确匹配,type = '自媒体'
-
price:学生对价格敏感,则查询时需要按照价格升序排列:order by price asc
-
duration: 学生对学习时长敏感,则查询时要按照时长升序:order by duration asc
我们需要定义一个类,封装这些可能的查询条件。
所以我们会创建一个查询类
package com.ai.entity.query;
import lombok.Data;
import org.springframework.ai.tool.annotation.ToolParam;
import java.util.List;
@Data
public class CourseQuery {
// required表示是不是强制需要的,description表示这个属性是干嘛的
@ToolParam(required = false, description = "课程类型:编程、设计、自媒体、其它")
private String type;
@ToolParam(required = false, description = "学历要求:0-无、1-初中、2-高中、3-大专、4-本科及本科以上")
private Integer edu;
@ToolParam(required = false, description = "排序方式")
private List<Sort> sorts;
@Data
public static class Sort {
@ToolParam(required = false, description = "排序字段: price或duration")
private String field;
@ToolParam(required = false, description = "是否是升序: true/false")
private Boolean asc;
}
}
注意:
这里的@ToolParam注解是SpringAI提供的用来解释Function参数的注解。其中的信息都会通过提示词的方式发送给AI模型。
同样的道理,大家也可以给Function定义专门的VO,作为返回值给到大模型。这里我们就省略了。
3.2.定义Tool
所谓的Function,就是一个个的函数,SpringAI提供了一个@Tool注解来标记这些特殊的函数。我们可以任意定义一个Spring的Bean,然后将其中的方法用@Tool标记即可:
@Component
public class FuncDemo {
@Tool(description="Function的功能描述,将来会作为提示词的一部分,大模型依据这里的描述判断何时调用该函数")
public String func(String param) {
// ...
retun "";
}
}
接下来,我们就来定义上一节说的三个Function:
-
根据条件筛选和查询课程
-
查询校区列表
-
新增试听预约单
例如:
package com.ai.tools;
import com.baomidou.mybatisplus.extension.conditions.query.QueryChainWrapper;
import com.itheima.ai.entity.po.Course;
import com.itheima.ai.entity.po.CourseReservation;
import com.itheima.ai.entity.po.School;
import com.itheima.ai.entity.query.CourseQuery;
import com.itheima.ai.service.ICourseReservationService;
import com.itheima.ai.service.ICourseService;
import com.itheima.ai.service.ISchoolService;
import lombok.RequiredArgsConstructor;
import org.springframework.ai.tool.annotation.Tool;
import org.springframework.ai.tool.annotation.ToolParam;
import org.springframework.stereotype.Component;
import java.util.List;
@RequiredArgsConstructor
@Component
public class CourseTools {
private final ICourseService courseService;
private final ISchoolService schoolService;
private final ICourseReservationService courseReservationService;
@Tool(description = "根据条件查询课程")
public List<Course> queryCourse(@ToolParam(required = false, description = "课程查询条件") CourseQuery query) {
QueryChainWrapper<Course> wrapper = courseService.query();
wrapper
.eq(query.getType() != null, "type", query.getType())
.le(query.getEdu() != null, "edu", query.getEdu());
if(query.getSorts() != null) {
for (CourseQuery.Sort sort : query.getSorts()) {
wrapper.orderBy(true, sort.getAsc(), sort.getField());
}
}
return wrapper.list();
}
@Tool(description = "查询所有校区")
public List<School> queryAllSchools() {
return schoolService.list();
}
@Tool(description = "生成课程预约单,并返回生成的预约单号")
public String generateCourseReservation(
String courseName, String studentName, String contactInfo, String school, String remark) {
CourseReservation courseReservation = new CourseReservation();
courseReservation.setCourse(courseName);
courseReservation.setStudentName(studentName);
courseReservation.setContactInfo(contactInfo);
courseReservation.setSchool(school);
courseReservation.setRemark(remark);
courseReservationService.save(courseReservation);
return String.valueOf(courseReservation.getId());
}
}
这样我们的Tool就定义好了,在外面给ai设置promt工程时,就设置所有的信息必须调用ai来获取,杜绝随意编造数据,我们也不需要告诉ai,什么时候来调用tool,有@Tool注解在,就可以辨别的。
4.配置ChatClient
package com.itheima.ai.config;
// ... 略
import static com.itheima.ai.constants.SystemConstants.CUSTOMER_SERVICE_SYSTEM;
import static com.itheima.ai.constants.SystemConstants.HONG_HONG_SYSTEM;
@Configuration
public class CommonConfiguration {
// ... 略
@Bean
public ChatClient serviceChatClient(
OpenAiChatModel model,
ChatMemory chatMemory,
CourseTools courseTools) {
return ChatClient.builder(model)
.defaultSystem(CUSTOMER_SERVICE_SYSTEM)
.defaultAdvisors(
new MessageChatMemoryAdvisor(chatMemory), // 会话记忆
new SimpleLoggerAdvisor()) // 日志记录
.defaultTools(courseTools)// 添加Tool
.build();
}
}
5.结语
通过以上的操作,就能在跟ai对话的时候,调用我们的数据库里面的数据了,并可以通过对话实现课程预约,这样引申出来,那么我们在商场里面,是不是可以通过ai,快速的找到我们想要的衣服呢?是不是可以快速的添加到购物车,甚至是订单的生成呢?
更多推荐



所有评论(0)