本文是spring ai做函数调用的具体教程 ,代码详细可操作。
例子使用spring ai alibaba + 通义qwen api 完成,您也可以在跑通以后换成自己的实现。
QWen 目前 有100万免费Token额度,可以快速实现需求。同时,因为qwen 也是个开源的模型,我们可以自己搭建模型来实现免费使用
工具调用function call 介绍:工具调用能做什么事?
在智能应用中,工具(Tool)或功能调用(Function Calling)的作用就如同人通过使用望远镜看的更远、通过驾驶汽车跑得更快一样,极大地扩展了大模型的能力边界。具体到spring-cloud-alibaba (sca)环境中,当大型语言模型遇到它自身无法直接处理的问题时,比如需要执行具体的数学运算或者查询特定消息的状态时,可以通过预定义的“工具”来完成这些任务。
以查询消息状态为例,在应用程序中定义并注册了一个名为messageStatusFunction
的功能,这个功能可以理解为一种特殊的“工具”,用于查询指定ID的消息状态。当用户向系统询问某条消息的状态时,大模型并不直接去数据库查找信息,而是识别出此时需要调用已注册的messageStatusFunction
。随后,该函数被激活执行,并将查询结果反馈给大模型,最终由大模型整合这一信息后形成最终回答传递给用户。
这样的机制不仅让智能应用能够利用外部资源增强其解决问题的能力,还使得复杂的逻辑操作得以简化,增强了系统的灵活性与响应速度。就像我们借助电灯开关控制室内照明一样自然流畅,通过适当的接口封装,可以使大模型更加高效地与物理世界交互,执行具体任务。
函数function call是怎么运作的?
在Spring AI Alibaba中,函数调用(Function Calling)通过定义Java函数接口,并使用特定注解来描述函数的输入参数、输出结果及其功能实现。开发者首先需要创建一个实现了java.util.function.Function
接口的类,并在这个类的方法上添加@JsonProperty
和@JsonPropertyDescription
注解,这些注解帮助大模型理解该函数的目的及其所需参数详情。接下来,在配置类中注册这个函数时,通过@Bean
注解并指定一个清晰的功能描述以及唯一的函数名,使得Spring AI能够识别并将此函数信息转换为文本形式提供给大模型。
当用户与系统交互过程中,如果大模型判断为了完成任务需要调用某个特定函数,它会向Spring AI发出指示,指出要使用的函数名称及传递的具体参数。收到这一请求后,Spring AI将在本地环境中执行相应的Java函数,并获取到执行结果。随后,将这个结果连同当前对话的所有上下文信息重新发送回大模型,从而完成了一次完整的函数调用过程。这种方式不仅增强了应用程序处理复杂任务的能力,也提高了人机交互体验的质量。
Qwen的介绍
通义千问Qwen在MMLU、TheoremQA、GPQA等基准测评中表现出色,超越了Llama 3 70B,MMLU GPQA 都是所有主流模型最主要会跑的客观模型。
并在Hugging Face开源大模型排行榜Open LLM Leaderboard上荣登榜首。这一成就彰显了其在多个关键评测指标上的卓越性能和广泛认可度,证明了其在自然语言处理领域的领先地位。
它在思南评测平台 CompassArena 上表现优异,仅次于国际知名的GPT和Claude模型;
同时,在国外视觉大模型竞技场 https://huggingface.co/spaces/lmarena-ai/chatbot-arena-leaderboard 中也稳居中国首位。
Spring AI Alibaba 简单介绍
Spring AI Alibaba是Spring AI的一个实现,专注于接入阿里云的百炼系列云产品大模型。它通过Spring AI的API提供了一套良好的抽象层,让开发者可以轻松地开发基于阿里云通义提供的聊天、图片或语音生成等AI应用。Spring AI Alibaba的核心优势在于其能够简化不同AI服务提供商之间的迁移工作,支持多种生成式模型(如对话、文生图、文生语音等),并提供了包括OutputParser、Prompt Template在内的实用功能,极大减少了开发者的负担。
Spring AI Alibaba 实现 第三方函数调用
分析问题的原因
在这个场景中,我们的目标是使用这种机制来访问外部API(即雪球股票接口),获取财务数据,然后利用大语言模型对该数据进行解读和简要分析。
具体步骤
步骤1:准备开发环境
- 确保JDK版本不低于17。
- Spring Boot版本至少为3.3.x。
- 安装阿里云通义千问或其他支持的AI服务,并获取相应的API Key。
- 配置必要的仓库以从Spring的快照和里程碑仓库拉取依赖。
<repositories>
<repository>
<id>sonatype-snapshots</id>
<url>https://oss.sonatype.org/content/repositories/snapshots</url>
<snapshots><enabled>true</enabled></snapshots>
</repository>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots><enabled>false</enabled></snapshots>
</repository>
<repository>
<id>spring-snapshots</id>
<name>Spring Snapshots</name>
<url>https://repo.spring.io/snapshot</url>
<releases><enabled>false</enabled></releases>
</repository>
</repositories>
步骤2:添加Spring AI Alibaba依赖
在pom.xml
文件中加入如下依赖:
<dependencies>
<dependency>
<groupId>com.alibaba.cloud.ai</groupId>
<artifactId>spring-ai-alibaba-starter</artifactId>
<version>1.0.0-M3.1</version>
</dependency>
<!-- 其他可能需要的依赖 -->
</dependencies>
步骤3:定义用于调用外部API的函数
创建一个新的类,例如StockInfoService
,其中包含一个方法用于向雪球发送请求并解析返回的数据。此方法应当符合java.util.function.Function
接口的要求,并且使用适当的注解使参数信息对大模型可见。
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyDescription;
public class StockInfoService implements Function<StockRequest, String> {
@Override
public String apply(StockRequest request) {
// 这里省略了实际网络请求代码
return "利润率为X%,净利润为Y元"; // 假设这是解析后得到的结果
}
public static class StockRequest {
@JsonProperty(required = true, value = "股票代码")
@JsonPropertyDescription("如SH600900")
private String symbol;
public StockRequest() {}
public StockRequest(String symbol) { this.symbol = symbol; }
public String getSymbol() { return symbol; }
public void setSymbol(String symbol) { this.symbol = symbol; }
}
}
步骤4:注册函数至Spring上下文
通过配置类或直接在主应用类中添加如下配置来注册上述函数。
@Configuration
public class AppConfig {
@Bean
@Description("查询指定股票代码的财务状况")
public Function<StockInfoService.StockRequest, String> stockInfoFunction() {
return new StockInfoService();
}
}
步骤5:设置CORS支持
为了支持跨域请求,在你的控制器中添加@CrossOrigin
注解。
@RestController
@RequestMapping("/ai")
@CrossOrigin(origins = "*")
public class ChatController {
...
}
步骤6:结合Prompt Template与ChatClient创建聊天接口
现在我们可以创建一个RESTful API端点,它会接收用户输入,构建Prompt,并利用之前注册的函数调用来执行操作。
@Autowired
private ChatClient chatClient;
@GetMapping("/chatSteam")
public Flux<String> chatSteam(@RequestParam String input) {
PromptTemplate promptTemplate = new PromptTemplate("请分析以下股票的信息: {input}");
Map<String, Object> map = Map.of("input", input);
DashScopeChatOptionsBuilder opsBuilder = DashScopeChatOptions.builder()
.withFunction("stockInfoFunction");
DashScopeChatOptions ops = opsBuilder.build();
Prompt prompt = promptTemplate.create(map, ops);
return chatClient.prompt(prompt).stream().content();
}
解释
这里我们首先设置了开发所需的基础环境及依赖;接着定义了一个能够被Spring AI识别并供大语言模型使用的函数,该函数负责与第三方API交互并处理响应;之后通过Spring配置将其注入到应用程序上下文中;最后创建了一个支持CORS的API端点,该端点可以接受用户输入,构造合适的Prompt,并借助已定义的功能调用功能获取并分析相关信息。这种方法使得我们可以灵活地扩展应用程序的功能,同时保持代码结构清晰简洁。
构建前端
构建前端
基于提供的我了解的信息,我们了解到构建一个简单的支持流输出的前端项目可以使用React框架,并通过fetch
API与后端进行通信。接下来,将详细介绍如何构建这样一个项目,包括必要的配置和代码实现。
创建并初始化React应用
首先,需要创建一个新的React应用,并安装相关依赖:
npx create-react-app frontend
cd frontend
npm install
这会生成一个基本的React项目结构,接着我们将在这个基础上添加我们的聊天组件。
定值基础文件内容
- public/index.html 文件保持默认即可,不需要做额外修改。
- src/index.js 也不需改动,默认导入了
App
组件作为应用入口点。
编写ChatComponent组件
在src/components/
目录下创建ChatComponent.js
文件,并填入以下内容来实现用户输入消息、发送请求给服务器以及显示响应的功能:
import React, { useState } from 'react';
function ChatComponent() {
const [input, setInput] = useState('');
const [messages, setMessages] = useState('');
const handleInputChange = (event) => {
setInput(event.target.value);
};
const handleSendMessage = async () => {
try {
// 向后端发起GET请求,注意URL中的参数input应当替换为实际的输入文本
const response = await fetch(`http://.../ai/chatSteam?input=${input}`);
if (!response.ok) throw new Error('Network response was not ok');
const reader = response.body.getReader();
const decoder = new TextDecoder('utf-8');
let done = false;
while (!done) {
const { value, done: readerDone } = await reader.read();
done = readerDone;
const chunk = decoder.decode(value, { stream: true });
setMessages((prevMessages) => prevMessages + chunk); // 将新接收到的消息追加到现有消息之后
}
// 请求完成后添加换行符以区分不同次的对话
setMessages((prevMessages) => prevMessages + '\n\n=============================\n\n');
} catch (error) {
console.error('Failed to fetch', error);
}
};
const handleClearMessages = () => {
setMessages('');
};
return (
<div>
<input
type="text"
value={input}
onChange={handleInputChange}
placeholder="Enter your message"
/>
<button onClick={handleSendMessage}>Send</button>
<button onClick={handleClearMessages}>Clear</button>
<div>
<h3>Messages:</h3>
<pre>{messages}</pre>
</div>
</div>
);
}
export default ChatComponent;
请确保将上述代码中fetch
函数调用里的URL地址替换为您的后端服务的实际地址。
更新App组件
打开src/App.js
文件,并更新它以引入刚刚创建的ChatComponent
:
import React from 'react';
import ChatComponent from './components/ChatComponent';
function App() {
return (
<div className="App">
<ChatComponent />
</div>
);
}
export default App;
至此,我们已经完成了所有必需的前端部分的设置。现在您可以运行这个React应用程序来看看效果了。
npm start
这样就完成了一个简单的支持流式输出的前端项目的搭建。此过程中利用了React的状态管理机制处理用户输入及展示从后端获取的数据流,同时也展示了如何处理异步数据流(如Flux)以便于实时显示信息。