1.概要
我们使用AI大模型开发程序时,比如我需要查一下平台中有多少个客户。这个时候大模型肯定时不知道的,如果大模型不知道,他可能会回答不知道或者胡乱回答,这个时候就需要借助函数时调用来解决这些问题。
大模型胡乱回答实际是大模型幻觉的问题,解决幻觉问题有以下方案
- 模型微调
这种情况一般公司不用做,因为需要自己训练,费用比较贵效果不一定好。 - 检索增强生成 (RAG)
- 使用外部的知识库,作为大模型知识的来源
- 使用向量的相似性查找相关文档
- 相关的文档作为大模型的一部分
- 方法调用 (function calling)
- 将问题和函数作为问题一起发给大模型
- 大模型解析出函数的参数
- 应用调用函数返回结果
- 将结果和上下文的数据丢给大模型,由大模型返回结果
2.函数调用过程
- 应用构造用户的问题和我们定义的函数的信息提交给大模型。
请求信息
{
"model": "qwen-plus",
"input": {
"messages": [
{
"role": "user",
"content": "广州有多少叫张三的人"
}
]
},
"parameters": {
"temperature": 0,
"result_format": "message",
"tools": [
{
"function": {
"name": "add",
"description": "add two numbers",
"parameters": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"v1": {
"type": "integer",
"format": "int32"
},
"v2": {
"type": "integer",
"format": "int32"
}
}
}
},
"type": "function"
},
{
"function": {
"name": "loation",
"description": "某地区有多少叫什么名字的人",
"parameters": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"address": {
"type": "string"
},
"name": {
"type": "string"
}
}
}
},
"type": "function"
}
]
}
}
我们将问题和配置的函数一起发给大模型,如果大模型判断当前的问题是否需要调用函数,它会返回是否需要函数调用,并同时计算出函数的参数。
- 大模型解析出是否需要调用函数,如果不需要则直接返回
{
"choices": [
{
"finish_reason": "tool_calls",
"message": {
"role": "assistant",
"tool_calls": [
{
"function": {
"name": "loation",
"arguments": "{\"address\": \"广州\", \"name\": \"张三\"}"
},
"id": "",
"type": "function"
}
],
"content": ""
}
}
]
}
- 如果需要 那么应用则调用函数。
spring ai 框架会去调用函数,获取函数名和参数,调用我们定义的参数。 - 函数返回结果,并将返回结果组成提示词再次发给大模型
- 大模型获取数据后得到完整的输出。
3.主要代码如下
3.1 定义函数
public class LocationFunction implements Function<LocationRequest, LocationResponse> {
private final Logger LOGGER = LoggerFactory.getLogger(getClass());
@Override
public LocationResponse apply(LocationRequest locationRequest) {
LOGGER.info("调用某个地方有多少叫什么的人 {}", locationRequest);
int amount=10;
if(locationRequest.name().equals("张三")){
amount=5;
}
return new LocationResponse(amount);
}
}
请求参数定义
public record LocationRequest(String name, String address) {
}
返回数据定义
public record LocationResponse (int amount){
}
配置函数
@Bean
@Description("某地区有多少叫什么名字的人")
public Function<LocationRequest, LocationResponse> loation() {
return new LocationFunction();
}
定义 chatclient
@Bean
public ChatClient chatClient(
FunctionCallbackContext functionCallbackContext) {
return new DashscopeChatClient(new DashscopeApi(),
DashscopeChatOptions.builder()
//使用千问plus 数据
.withModel(DashscopeModelName.QWEN_PLUS)
.withTemperature(0.0f)
//定义函数
.withFunction("add")
//定义第二个函数
.withFunction("loation")
.build(),
functionCallbackContext);
}
这里我们可以定义多个函数,但是还是不要定义太多的函数,这样发送给大模型的包太大,会需要消耗更多的token ,会影响大模型收费。