1.概要

我们使用AI大模型开发程序时,比如我需要查一下平台中有多少个客户。这个时候大模型肯定时不知道的,如果大模型不知道,他可能会回答不知道或者胡乱回答,这个时候就需要借助函数时调用来解决这些问题。

大模型胡乱回答实际是大模型幻觉的问题,解决幻觉问题有以下方案

  1. 模型微调
    这种情况一般公司不用做,因为需要自己训练,费用比较贵效果不一定好。
  2. 检索增强生成 (RAG)
  • 使用外部的知识库,作为大模型知识的来源
  • 使用向量的相似性查找相关文档
  • 相关的文档作为大模型的一部分
  1. 方法调用 (function calling)
  • 将问题和函数作为问题一起发给大模型
  • 大模型解析出函数的参数
  • 应用调用函数返回结果
  • 将结果和上下文的数据丢给大模型,由大模型返回结果

2.函数调用过程

spring ai 函数调用_数据

  1. 应用构造用户的问题和我们定义的函数的信息提交给大模型。
    请求信息
{
    "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"
            }
        ]
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.

我们将问题和配置的函数一起发给大模型,如果大模型判断当前的问题是否需要调用函数,它会返回是否需要函数调用,并同时计算出函数的参数。

  1. 大模型解析出是否需要调用函数,如果不需要则直接返回
{
    "choices": [
        {
            "finish_reason": "tool_calls",
            "message": {
                "role": "assistant",
                "tool_calls": [
                    {
                        "function": {
                            "name": "loation",
                            "arguments": "{\"address\": \"广州\", \"name\": \"张三\"}"
                        },
                        "id": "",
                        "type": "function"
                    }
                ],
                "content": ""
            }
        }
    ]
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  1. 如果需要 那么应用则调用函数。
    spring ai 框架会去调用函数,获取函数名和参数,调用我们定义的参数。
  2. 函数返回结果,并将返回结果组成提示词再次发给大模型
  3. 大模型获取数据后得到完整的输出。

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);
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.

请求参数定义

public record LocationRequest(String name, String address) {
}
  • 1.
  • 2.

返回数据定义

public record LocationResponse (int amount){
}
  • 1.
  • 2.

配置函数

@Bean
  @Description("某地区有多少叫什么名字的人")
  public Function<LocationRequest, LocationResponse> loation() {
    return new LocationFunction();
  }
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.

定义 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);
  }
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.

这里我们可以定义多个函数,但是还是不要定义太多的函数,这样发送给大模型的包太大,会需要消耗更多的token ,会影响大模型收费。