前段时间开源了一个简单的 java 复刻的 Swarm 框架[项目地址],也顺手调研了一下 spring ai 相关的内容,在这里做一个简单的汇总。

简介

spring ai 一个用于将 AI 服务集成到 spring boot 程序的框架,alibaba spring ai 是基于 spring ai 框架的通义 AI 服务的实现,底层和模型直接交互使用的 dashscope 的 api。同时还有 openai (spring 官方自己的实现)

spring ai 中聊天 AI 助手的包装接口是 ChatClient。但同时提供了很多 advisors,用于上下文记忆,对话维护,系统 prompt 这些比较方便的接口。在使用 spring ai 调用模型的 api 时更加方便(不用自己维护上下文对话)

swram 是一个实验性框架,底层是可以和模型直接交互的 api,和 spring ai 的层级一致,都是对模型调用接口的一种封装。

spring ai 中集成了各种向量存储库的支持。比如 Advisors API 是 Spirng 给 ChatClient 的增强功能,方便在模型调用的过程中提供拦截,修改,增强和模型之间的互动,提供对话历史记录记录,提供向量知识库存储等等。

spring 中也集成了对 tool call 的支持(alibaba.spring.ai 还只用的是 function call)

FunctionToolCallback.builder("getCurrentWeather", new MockWeatherService())
    .description("Get the weather in location")
    .inputType(MockWeatherService.Request.class)
    .build()

String response = ChatClient.create(chatModel)
    .prompt()
    .user("What's the weather like in San Francisco?")
    .tools(FunctionToolCallback.builder("getCurrentWeather", new MockWeatherService())
        .description("Get the weather in location")
        .inputType(MockWeatherService.Request.class)
        .build())
    .call()
    .content();

关于 tool_call 的实现部分

spring ai 也提供了一种很 spring 的 tools 定义方案:官方文档

class DateTimeTools {

    @Tool(description = "Set a user alarm for the given time")
    void setAlarm(@ToolParam(description = "Time in ISO-8601 format") String time) {
        LocalDateTime alarmTime = LocalDateTime.parse(time, DateTimeFormatter.ISO_DATE_TIME);
        System.out.println("Alarm set for " + alarmTime);
    }

}

然后采用注解的方式来对 tools 和其参数来进行定义,个人觉得也是一种不错的实现方案。使用注解定义 tool 的描述和 id 名,然后JsonSchemaGenerator 类提供了两个主要的静态方法用于生成 JSON Schema,采用反射的方案实现的函数描述装配:

public static String generateForMethodInput(Method method, SchemaOption... schemaOptions) {
    // ...
    for (int i = 0; i < method.getParameterCount(); i++) {
        String parameterName = method.getParameters()[i].getName();
        Type parameterType = method.getGenericParameterTypes()[i];
        if (isMethodParameterRequired(method, i)) {
            required.add(parameterName);
        }
        ObjectNode parameterNode = SUBTYPE_SCHEMA_GENERATOR.generateSchema(parameterType);
        String parameterDescription = getMethodParameterDescription(method, i);
        if (StringUtils.hasText(parameterDescription)) {
            parameterNode.put("description", parameterDescription);
        }
        properties.set(parameterName, parameterNode);
    }
    // ...
    return schema.toPrettyString();
}

这里有一个小小的问题,如果在编译时没有显式要求变量名,拿到的变量名会是:var1 var2 var3,虽然对实际调用没有什么影响,但是在查 AI 调用的日志信息的时候会不太明确。

生成 Json Schema 是一个成熟的技术了,比如使用 https://github.com/victools/jsonschema-generator 这个项目就可以。(Spring 官方使用的项目)

差异之处

spring ai 提供的功能复杂且全面,主要是为了方便和 spring boot 集成,可以直接业务落地,接口也更加偏向方便调用。swarm 只完成了 tool_call 的实现,作为一个很初期的实验性质的框架,更加简单,偏向实验快速验证。

自己的 java swarm 中好像还没有支持上复杂的 record,阿里百炼的文档倒是没有要求支持复杂的 record,但是 openai 的 api 是支持的,不过我目前的态度是,这只是一个简单的实验性框架,一切从简,复杂的 schema 也要求复杂的参数描述,暂时不需要。

这里的 record 是指 JDK14 以上引入的关键字,功能类似于 @Data