自 Spring Framework 出现在软件开发领域已有 20 多年的历史,自 Spring Boot 1.0 版本发布也已有 10 年的历史。到目前为止,没有人应该怀疑 Spring 创造了一种独特的风格,通过这种风格,开发人员可以从重复的任务中解放出来,专注于业务价值交付。随着时间的推移,Spring 的技术深度不断增加,涵盖了广泛的开发领域和技术。另一方面,随着更集中的解决方案的试验、概念验证的创建以及最终在项目保护伞下的推广(向技术深度迈进),其技术广度不断扩大。
新的 Spring AI 项目就是一个这样的例子,根据其参考文档,该项目旨在在将生成人工智能层合并到应用程序中时简化开发。开发人员再次从重复性任务中解放出来,并提供了简单的界面,可以与包含实际处理算法的预训练模型直接交互。
通过直接或通过 Spring AI 以编程方式与生成式预训练变压器 (GPT) 交互,用户(开发人员)不需要(尽管它很有用)拥有广泛的机器学习知识。作为一名工程师,我坚信,即使这样的(开发人员)工具可以相当容易和快速地用于产生结果,建议我们锻炼自己以切换到观察模式并首先尝试对基本概念有一个很好的理解。而且,遵循这条道路,结果可能会更有用。
目的
本文展示了如何将 Spring AI 集成到 Spring Boot 应用程序中并实现与 Open AI 的编程交互。一般认为,即时设计(即时工程)是一项最先进的活动。因此,实验过程中使用的提示相当说教,没有太多适用性。这里重点关注的是通信接口,即Spring AI API。
实施前
首先,除了希望以更短的时间和更低的成本提供更高质量的服务之外,还应阐明合并和使用 GPT 解决方案的基本原理。
据说生成式AI擅长更快、更高效地完成大量耗时的任务,并输出结果。此外,如果这些结果得到经验丰富、明智的人的进一步验证,获得有用东西的机会就会增加。幸运的是,人仍然是风景的一部分。
接下来,人们应该抵制住直接跳入实现的诱惑,至少花一些时间来熟悉一下一般概念。对生成式人工智能概念的深入探索远远超出了本文的范围。尽管如此,互动中出现的“主要参与者”还是简要概述如下。
- 舞台 – 生成式人工智能是机器学习的一部分,也是人工智能的一部分
- 输入 – 提供的数据(传入)
- 输出 – 计算结果(传出)
- 大型语言模型(LLM) – 基于解释输入的微调算法产生输出
- 提示 – 最先进的界面,通过它将输入传递给模型
- 提示模板 – 允许构建结构化参数化提示的组件
- 令牌 – 算法在内部将输入转换为的组件,然后用于编译结果并最终构造输出
- 模型的上下文窗口 – 模型限制每次调用的令牌数量的阈值(通常,使用的令牌越多,操作的成本就越高)
最后,可以开始实施,但随着实施的进展,建议重新审视并完善前两个步骤。
提示
在本练习中,我们要求以下内容:
写出 {count = 3} 个为什么 {location = 罗马尼亚} 的人应该考虑从事 {job = 软件架构师} 工作的理由。
这些理由必须简短,以便适合海报。
例如,“{job} 工作是有回报的。”
这基本上代表了提示。根据建议,应在提示中提供清晰的主题、明确的任务含义以及其他有用的信息,以提高结果的准确性。
提示包含三个参数,可以覆盖不同地点的各种作业。
count
– 作为输出一部分的原因数量job
– 领域、感兴趣的工作地点
– 求职者居住的国家、城镇、地区等
概念证明
在这篇文章中,简单的概念验证旨在实现以下目标:
- 将 Spring AI 集成到 Spring Boot 应用程序中并使用它。
- 允许客户端通过应用程序与 Open AI 进行通信。
- 客户端向应用程序发出参数化 HTTP 请求。
- 应用程序使用提示创建输入,将其发送到 Open AI 检索输出。
- 应用程序将响应发送给客户端。
设置
- Java 21
- Maven 3.9.2
- Spring Boot – v.3.2.2
- Spring AI – v. 0.8.0-SNAPSHOT(仍在开发中,实验性)
实施
Spring AI 集成
通常,这是一个基本步骤,不一定值得一提。不过,由于 Spring AI 目前是作为快照发布的,为了能够集成 Open AI 自动配置依赖项,需要添加对 Spring Milestone/Snapshot 存储库的引用。
下一步是添加 spring-ai-openai-spring-boot-starter
Maven 依赖项。
<依赖项>
org.springframework.ai
spring-ai-openai-spring-boot-starter
<版本>0.8.0-SNAPSHOT版本>
依赖> 代码> 预>
Open AI ChatClient
现在是应用程序类路径的一部分。它是用于将输入发送到 Open AI 并检索输出的组件。
为了能够连接到 AI 模型,需要在 application.properties
中设置 spring.ai.openai.api-key
属性文件。
spring.ai.openai.api-key = api-key-value
其值代表进行通信的用户的有效 API 密钥。通过访问开放AI平台,您可以注册或登录并生成一个。
客户端:Spring Boot应用程序通信
概念验证的第一部分是客户端应用程序(例如浏览器、cURL 等)和所开发的应用程序之间的通信。这是通过 REST 控制器完成的,可通过 HTTP GET 请求访问。
URL 为 /job-reasons
以及前面定义提示时概述的三个参数,其结果如下:
/job-reasons?count={count}&job={job}&location={location}
以及相应的控制器:
@RestController
公共类 OpenAiController {
@GetMapping("/工作原因")
公共 ResponseEntity jobReasons(@RequestParam(value = "count", required = false, defaultValue = "3") int count,
@RequestParam("job") 字符串作业,
@RequestParam("位置") 字符串位置) {
返回 ResponseEntity.ok().build();
}
}
由于 Open AI 的响应将是一个 String
,因此控制器返回一个封装 String
的 ResponseEntity
。如果我们运行应用程序并发出请求,当前响应正文中不会返回任何内容。
客户端:开放式人工智能通信
Spring AI 目前专注于处理语言并生成语言或数字的 AI 模型。前一类开放 AI 模型的示例有 GPT4-openai
或 GPT3.5-openai
。
为了实现与这些 AI 模型(实际上指定开放 AI 算法)的交互,Spring AI 提供了统一的接口。
ChatClient
接口目前支持文本输入和输出,并且有一个简单的契约。
@FunctionalInterface
公共接口 ChatClient 扩展 ModelClient {
默认字符串调用(字符串消息){
提示提示 = new Prompt(new UserMessage(message));
返回调用(提示).getResult().getOutput().getContent();
}
ChatResponse调用(Prompt提示);
}
功能接口的实际方法是通常使用的方法。
就我们的概念验证而言,这正是我们所需要的:一种调用 Open AI 并将目标参数化 Prompt
作为参数发送的方法。以下 OpenAiService
是在注入 ChatClient
实例的位置定义的。
@Service
公共类 OpenAiService {
私有最终 ChatClient 客户端;
公共 OpenAiService(OpenAiChatClient aiClient) {
this.client = aiClient;
}
公共字符串jobReasons(int计数,字符串域,字符串位置){
最终字符串提示文本 = """
写出 {count} 个理由说明为什么 {location} 的人应该考虑一份 {job} 的工作。
这些理由必须简短,以便适合海报。
例如,“{job} 工作是有回报的”。
""";
最终 PromptTemplate 提示模板 = new PromptTemplate(promptText);
PromptTemplate.add("计数", 计数);
PromptTemplate.add("工作", 域);
PromptTemplate.add("位置", 位置);
ChatResponse 响应 = client.call(promptTemplate.create());
返回response.getResult().getOutput().getContent();
}
}
应用程序运行时,如果从浏览器执行以下请求:
然后检索到以下结果:
- 收入丰厚的职业:软件架构师职位提供有竞争力的薪资和绝佳的发展机会,确保罗马尼亚的财务稳定和成功。
- 紧缺职业:随着技术需求的不断增长,软件架构师在罗马尼亚和全球范围内备受追捧,提供了丰富的就业前景和工作保障。
- 创造性地解决问题:软件架构师在设计和开发创新软件解决方案方面发挥着至关重要的作用,使他们能够释放创造力并对各个行业产生重大影响。
这正是它的目的 - 一个简单的界面,通过该界面,可以要求 Open AI GPT 模型写出几个为什么特定地点的特定工作有吸引力的原因。
调整和观察
到目前为止开发的简单概念验证主要使用可用的默认配置。
ChatClient
实例可以根据所需的需要通过各种属性进行配置。由于这超出了本文的范围,因此这里仅举例说明两个。
spring.ai.openai.chat.options.model
指定要使用的 AI 模型。默认情况下,它是“gpt-35-turbo”,但“gpt-4”和“gpt-4-32k”指定最新版本。尽管可用,但人们可能无法使用即用即付计划来访问这些内容,但 Open AI 网站上提供了额外的信息来适应它。
另一个值得一提的属性是spring.ai.openai.chat.options.Temperature
。根据参考文档,采样温度控制“响应的创造性”。据说较高的值使输出“更加随机”,而较低的值“更加集中和确定性”。默认值为 0.8,如果我们将其减少到0.3,重新启动应用程序,并使用相同的请求参数再次询问,检索到以下结果。
- 丰厚的职业机会:罗马尼亚的软件架构师职位提供有竞争力的薪资和良好的发展前景,对于寻求财务稳定和职业发展的个人来说是一个有吸引力的职业选择。
- 具有挑战性和智力刺激的工作:作为软件架构师,您将负责设计和实现复杂的软件系统、解决复杂的技术问题,并与才华横溢的团队合作。该职位提供持续学习的机会以及研究尖端技术的机会。
- 高需求和工作保障:随着各行业对技术和数字化转型的依赖日益增加,对熟练软件架构师的需求不断增加。在罗马尼亚选择软件架构师工作可以确保工作保障和广泛的本地和国际就业选择。
可以看出,在这种情况下,输出更具描述性。
最后一个考虑因素与所获得的输出的结构有关。如果能够将收到的实际有效负载映射到 Java 对象(例如,类或记录),将会很方便。到目前为止,表示是文本的,实现也是如此。输出解析器可以实现这一点,类似于 Spring JDBC 的映射结构。
在此概念验证中,使用了 BeanOutputParser
,它允许直接在 Java 记录中反序列化结果,如下所示:
公共记录 JobReasons(String job,
字符串位置,
列出原因){
}
这是通过将 {format}
作为提示文本的一部分并将其作为指令提供给 AI 模型来完成的。
OpenAiService
方法变为:
public JobReasons formattedJobReasons(int count, String job, String location) {
最终字符串提示文本 = """
写出 {count} 个理由说明为什么 {location} 的人应该考虑一份 {job} 的工作。
这些理由必须简短,以便适合海报。
例如,“{job} 工作是有回报的”。
{格式}
""";
BeanOutputParser outputParser = new BeanOutputParser<>(JobReasons.class);
最终 PromptTemplate 提示模板 = new PromptTemplate(promptText);
PromptTemplate.add("计数", 计数);
PromptTemplate.add("工作", 工作);
PromptTemplate.add("位置", 位置);
PromptTemplate.add("格式", outputParser.getFormat());
PromptTemplate.setOutputParser(outputParser);
最终提示提示=提示模板.create();
ChatResponse 响应 = client.call(提示);
返回outputParser.parse(response.getResult().getOutput().getContent());
}
再次调用时,输出如下:
{
"job":"软件架构师",
"location":"罗马尼亚",
“原因”:[
“高需求”,
“有竞争力的薪资”,
“成长的机会”
]
}
格式是预期的格式,但原因似乎不太解释,这意味着需要进行额外的调整才能实现更好的可用性。但从概念验证的角度来看,这是可以接受的,因为重点是形式。
结论
提示设计是任务的重要组成部分 - 提示表达得越好,输入就越好,输出质量就越高。
使用 Spring AI 与各种聊天模型集成非常简单 - 这篇文章展示了开放 AI 集成。
尽管如此,就一般人工智能而言,就像几乎所有技术一样,至少首先熟悉一般概念非常重要。然后,尝试理解通信执行方式背后的魔力,然后才开始编写“生产”代码。
最后但并非最不重要的一点是,建议进一步探索 Spring AI API 以了解其实现并在其发展和改进时保持最新状态。
代码可在此处获取。 p>