前段时间开源了一个简单的 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