【Spring AI】Java实现类似langchain的第三方函数调用_原理与详细示例

news2024/10/27 17:23:27

Spring AI 介绍 :简化Java AI开发的统一接口解决方案

在过去,使用Java开发AI应用时面临的主要困境是没有统一且标准的封装库,导致开发者需要针对不同的AI服务提供商分别学习和对接各自的API,这增加了开发难度与迁移成本。而Spring AI的出现解决了这一问题,它提供了一套兼容市面上主要生成任务(如文本、图像生成)的标准接口。通过采用Spring AI,开发者只需编写一次代码,并通过修改配置即可轻松切换至不同AI供应商的服务,极大简化了AI功能集成过程,使得基于Java Spring的应用能够更加便捷地利用先进的AI能力。

Spring AI Alibaba:阿里的Spring AI最佳实践集合

Spring AI Alibaba 是基于 Spring AI 的实现,专为阿里云的百炼系列大模型提供接入服务。它继承了 Spring 生态系统的设计原则,如可移植性和模块化设计,并将其应用于人工智能领域。Spring AI 的核心优势在于其标准化了不同AI提供商(如OpenAI、Azure、阿里云等)的接口实现,使得开发者只需编写一次代码,通过修改配置即可轻松切换不同的AI后端。此外,Spring AI 直接兼容Flux流输出,简化了与市面上大多数基于流的机器人模型的集成过程。这不仅大幅减少了开发和迁移时的工作量,也提高了应用程序的灵活性和适应性。通过使用 Spring AI Alibaba,开发者可以更高效地构建并扩展利用通义千问等大模型的应用程序。

Spring AI函数调用(function calling) 机制探秘

在Spring AI中实现函数调用的原理是,用户首先定义一个实现了Function接口的Java类,该类通过特定的注解如@JsonProperty@JsonPropertyDescription来描述其输入参数、输出结果以及函数的具体作用。这些注解使得开发者能够明确指定函数的行为及其预期用途,为后续步骤打下基础。

当应用程序启动时,Spring AI会自动读取并解析这些注解信息,将它们转换成一种易于大模型理解的文字描述形式,并作为上下文的一部分传递给大模型。这样做是为了让大模型能够根据当前对话或任务的需求,判断是否需要调用某个已定义好的函数来完成更复杂的任务。如果大模型决定调用某项功能,则会在响应中明确指出希望调用哪个具体的函数及所需的参数值。

接下来,Spring AI接收到这一指示后,在本地环境中执行相应的Java方法,获取到执行结果。随后,它不仅将该结果反馈给大模型,同时还会一并将之前的对话历史或相关信息再次提交给大模型,以确保整个交互过程中的连贯性和一致性。这种方式有效地增强了AI系统的功能性与灵活性,使其能够利用外部工具或服务完成更加多样化且复杂的工作流程。

构建后端:Spring Boot集成Spring AI Alibaba实现第三方API调用优化利润分析

实现步骤

1. 环境准备与依赖添加

首先确保JDK版本为17及以上,Spring Boot版本为3.3.x以上。接着,在项目中添加必要的仓库和依赖。

仓库配置

<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>

依赖配置

<dependencies>
    <dependency>
        <groupId>com.alibaba.cloud.ai</groupId>

        <artifactId>spring-ai-alibaba-starter</artifactId>

        <version>1.0.0-M2</version>

    </dependency>

    <!-- 其他必需依赖 -->
</dependencies>
2. 配置阿里云API Key

application.properties文件中设置阿里云通义大模型的API Key。

spring.ai.dashscope.api-key=YOUR_API_KEY
3. 创建函数定义

定义一个用于查询雪球股票利润表的Java函数。此函数将被注册给LLM使用。

import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyDescription;
import org.springframework.web.client.RestTemplate;

public class XueQiuStockService implements Function<XueQiuStockService.StockRequest, String> {

    private final RestTemplate restTemplate = new RestTemplate();

    @Override
    public String apply(StockRequest request) {
        String url = "https://stock.xueqiu.com/v5/stock/finance/cn/income.json?symbol=" + request.getSymbol() + "&type=all&is_detail=true&count=1";
        String response = restTemplate.getForObject(url, String.class);
        // 对返回的数据进行简单处理或直接返回
        return response;
    }

    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配置类将上述定义的函数注册为一个Bean。

@Configuration
public class AppConfig {

    @Bean
    @Description("查询指定股票代码的利润表")
    public Function<XueQiuStockService.StockRequest, String> xueQiuStockFunction() {
        return new XueQiuStockService();
    }
}
5. 使用Prompt Template结合函数提供服务

创建Controller来接收请求并结合Prompt和已注册的函数执行操作。

@RestController
@RequestMapping("/ai")
@CrossOrigin(origins = "*")
public class ChatController {

    private final ChatClient chatClient;

    public ChatController(ChatClient.Builder builder) {
        this.chatClient = builder.build();
    }

    @GetMapping("/chatSteam")
    public Flux<String> chatSteam(@RequestParam String input) {
        PromptTemplate promptTemplate = new PromptTemplate("{input} 的利润率是多少?请给出详细分析。");
        DashScopeChatOptionsBuilder opsBuilder = DashScopeChatOptions.builder()
                .withFunction("xueQiuStockFunction");
        DashScopeChatOptions ops = opsBuilder.build();
        Map<String, Object> map = Map.of("input", input);
        Prompt prompt = promptTemplate.create(map, ops);
        return chatClient.prompt(prompt).stream().content();
    }
}

步骤解释

  1. 环境准备:确保开发环境满足基本要求(JDK、Spring Boot版本)。
  1. 依赖添加:添加了Spring AI Alibaba和其他必要依赖。
  1. 配置API Key:设置了阿里云API Key以启用大模型服务。
  1. 函数定义与注册:创建了一个简单的HTTP客户端函数来获取股票财务数据,并将其作为Spring Bean注册。
  1. 服务实现:编写了一个控制器方法,该方法接受用户输入(股票代码),构建带有特定提示的Prompt,并指示大模型调用之前注册的功能来获取所需数据。最终,返回模型生成的内容流。

这样就完成了基于Spring Boot集成Spring AI Alibaba以及使用其Function Calling能力完成对话和分析模型的任务。

构建前端:一个简单的对话基于React

为了构建一个基于React的简单前端项目,支持从后端接收流数据(flux<String>),可以按照以下步骤进行操作。此示例假设你的后端接口地址为 http://.../ai/chatStream?input=... 并且返回的是一个字符串流。

创建并初始化项目

首先,你需要创建一个新的 React 应用,并安装所有必要的依赖项。打开终端并运行以下命令:

npx create-react-app frontend
cd frontend
npm install

这将生成一个新的 React 项目结构,并自动安装基本的开发环境。

配置基础文件

确保你有如下几个基础文件设置好。这些文件通常不需要做过多修改,但确认它们的存在是重要的一步。

  • public/index.html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Chat App</title>

</head>

<body>
  <div id="root"></div>

</body>

</html>
  • src/index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';

ReactDOM.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
  document.getElementById('root')
);
  • src/App.js
import React from 'react';
import ChatComponent from './components/ChatComponent';

function App() {
  return (
    <div className="App">
      <ChatComponent />
    </div>

  );
}

export default 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 {
      // 注意这里的URL应该替换为你实际使用的后端服务地址
      const response = await fetch(`http://.../ai/chatStream?input=${input}`, {
        method: 'GET', // 或者POST根据实际情况调整
        headers: {
          'Content-Type': 'application/json',
          // 如果需要认证信息或其他头信息,请在此添加
        },
      });

      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);  // 将接收到的数据拼接到现有消息中
      }
    } 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;

这段代码实现了向指定URL发送请求,并通过流式读取响应内容逐步更新状态中的消息列表的功能。注意,如果您的后端API要求特定的HTTP方法或头部信息,则需相应地调整 fetch 请求的配置。

运行项目

最后,启动你的前端应用以查看效果:

npm start

以上步骤详细描述了如何基于React构建一个简单的支持流输出的前端项目。其中涉及到的主要技术包括使用React Hooks管理状态、利用Fetch API与后端通信以及处理服务器返回的流数据。确保在开发过程中保持前后端之间的良好协作,特别是关于CORS策略的设置,以便前端能够顺利访问到后端提供的资源。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2209978.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Android常用界面控件——ProgressBar

ProgressBar 目录 ProgressBar 在XML中定义ProgressBar ProgressBar风格样式 ProgressBar常用XML属性 在Java代码中控制ProgressBar 实例 什么是ProgressBar&#xff1f; ProgressBar是Android中的一个视图控件&#xff0c;主要用于表示一个任务的进度情况&#xff0c;…

针对Ubuntu20.04 中的 Docker 配置可用镜像源(包含国内可用镜像源)

文章目录 写在前面一、Docker 官方源二、更换Docker 国内可用镜像源 &#xff08;推荐使用&#xff09;参考链接 写在前面 自己的测试环境&#xff1a; Ubuntu20.04&#xff0c;docker-27.3.1 一、Docker 官方源 打开 /etc/docker/daemon.json文件&#xff1a; sudo gedit …

Python快速编程小案例--逢7拍手小游戏

提示&#xff1a;&#xff08;个人学习&#xff09;&#xff0c;案例来自工业和信息化“十三五”人才培养规划教材&#xff0c;《Python快速编程入门》第2版&#xff0c;黑马程序员◎编著 逢7拍手游戏的规则是&#xff1a;从1开始顺序数数&#xff0c;数到有7或者包含7的倍数的…

查缺补漏----时间复杂度

1.如果每一次循环变量都是&#xff0c;那么直接将每一层变量的最大遍历次数相乘 第一个代码段&#xff1a;O&#xff08;n^3&#xff09; 第二个代码段&#xff1a;O&#xff08;n*i*j&#xff09;,由于 i 的范围0~n-1&#xff0c;所以取n&#xff0c;j 同理&#xff0c;所以最…

Java | Leetcode Java题解之第473题火柴拼正方形

题目&#xff1a; 题解&#xff1a; class Solution {public boolean makesquare(int[] matchsticks) {int totalLen Arrays.stream(matchsticks).sum();if (totalLen % 4 ! 0) {return false;}int len totalLen / 4, n matchsticks.length;int[] dp new int[1 << n…

【hot100-java】从前序与中序遍历序列构造二叉树

二叉树篇 首先创建一个映射来存储中序遍历中值与索引的关系&#xff0c;然后通过递归调用函数dfs来构建二叉树。函数dfs接受前序遍历和中序遍历的左右边界索引以及前序遍历和中序遍历的列表和映射作为参数&#xff0c;在每次递归中&#xff0c;先判断边界条件&#xff0c;如果左…

部署 Open WebUI

1. 安装docker 2.启动Hyper-v 3.下载 安装 WSL 4. 打开 DeskDocker 5. 打开 运行 ollama 参考 Windows 部署 ollama-CSDN博客 6. 部署 运行 open webui docker docker run -d -p 3000:8080 --add-hosthost.docker.internal:host-gateway -v open-webui:/app/backend/data -…

Flutter Transform 学习

Transform可以在其子组件绘制时对其应用一些矩阵变换来实现一些特效,允许在渲染子部件之前对它们进行变换。 一、Transform构造函数与属性 class Transform extends SingleChildRenderObjectWidget {/// Creates a widget that transforms its child.const Transform({super.k…

C语言 | 第十六章 | 共用体 家庭收支软件-1

P 151 结构体定义三种形式 2023/3/15 一、创建结构体和结构体变量 方式1-先定义结构体&#xff0c;然后再创建结构体变量。 struct Stu{ char *name; //姓名 int num; //学号 int age; //年龄 char group; //所在学习小组 float score; //成绩 }; struct Stu stu1, stu2; //…

InternVL2-40B 荣登开源模型榜首丨SuperCLUE中文多模态大模型基准10月榜单

在今年 7 月 4 日举行的 2024 WAIC 科学前沿主论坛上&#xff0c;书生万象多模态大模型&#xff08;InternVL 2.0&#xff09;正式发布&#xff0c;并陆续开源了 1B、2B、4B、8B、26B、40B 以及 76B 共 7 个参数版本的模型。书生万象支持图像、视频、文字、语音、三维点云等模态…

Qt媒体播放器实现

本文介绍Qt媒体播放器实现。 Qt应用程序有时会涉及到视频文件的播放&#xff0c;Qt提供了QVideoWidget类实现视频和音频的播放&#xff0c;本文基于QVideoWidget类实现一个简单的媒体播放器。 1.自定义类&#xff08;基于QVideoWidget类&#xff09; 由于Qt并未提供专门的控…

手写简易shell

我们如果要写一个简易的shell我们要&#xff0c;其实我们搞一个程序替换就行了。 我们分为五部完成 在其中我们最难搞的就是环境变量的更新&#xff0c;因为当我们搞一个子程序出来时&#xff0c;我们子进程的环境变量表是从父进程继承下来的&#xff0c;当我们用cd命令时只会更…

C# 屏幕录制工具

屏幕录制工具 开发语音&#xff1a;C# vb.net 下载地址&#xff1a;https://download.csdn.net/download/polloo2012/89879996 功能&#xff1a;屏幕录制&#xff0c;声卡采集&#xff0c;麦克风采集。 屏幕录制&#xff1a;录制屏幕所有操作&#xff0c;并转换视频格式&…

电脑无线网wifi和有线网同时使用(内网+外网同时使用)

一、要求 我这里以无线网wifi为外网&#xff0c;有线网卡为内网为例&#xff1a; 一、基本信息 无线wifi&#xff08;外网&#xff09;&#xff1a;ip是192.168.179.235&#xff0c;网关是192.168.179.95有线网&#xff08;内网&#xff09;&#xff1a;ip是192.168.10.25&…

《鸟哥的Linux私房菜基础篇》---1 Linux的介绍与如何开启Linux之路

目录 一、Linux的简单介绍 1、Linux的简介 2、Linux的起源与发展 3、主要特点 4、应用场景 二、开启Linux之路 1、学习Linux的相关知识 2、正规表示法、管线命令、数据流重导向 前言 整体大纲预览 一、Linux的简单介绍 1、Linux的简介 &#xff08;1&#xff09;Linu…

【Trulens框架】用TruLens 自动化 RAG 应用项目评估测试

前言&#xff1a; 什么是Trulens TruLens是面向神经网络应用的质量评估工具&#xff0c;它可以帮助你使用反馈函数来客观地评估你的基于LLM&#xff08;语言模型&#xff09;的应用的质量和效果。反馈函数可以帮助你以编程的方式评估输入、输出和中间结果的质量&#xff0c;从而…

K8S---01初识Kubernetes

一.简介 摘取官网: 概述 | KubernetesKubernetes 是一个可移植、可扩展的开源平台&#xff0c;用于管理容器化的工作负载和服务&#xff0c;方便进行声明式配置和自动化。Kubernetes 拥有一个庞大且快速增长的生态系统&#xff0c;其服务、支持和工具的使用范围广泛。https://k…

【C++堆(优先队列)】1882. 使用服务器处理任务|1979

本文涉及知识点 C堆(优先队列) LeetCode1882. 使用服务器处理任务 给你两个 下标从 0 开始 的整数数组 servers 和 tasks &#xff0c;长度分别为 n​​​​​​ 和 m​​​​​​ 。servers[i] 是第 i​​​​​​​​​​ 台服务器的 权重 &#xff0c;而 tasks[j] 是处理…

【寻找one piece的算法之路】前缀和(一)

&#x1f490;个人主页&#xff1a;初晴~ &#x1f4da;相关专栏&#xff1a;寻找one piece的刷题之路 什么是前缀和&#xff1f; 主要是通过预先计算数组或矩阵的前缀和&#xff0c;来快速查询子数组或子矩阵的和。这种算法可以用空间换时间&#xff0c;提高查询效率。 概念…

FileOutputStream类

1.操作本地文件的字节输出流&#xff0c;可以把程序中的数据写到本地文件中。 2.书写步骤&#xff1a; 3.实例代码 结果&#xff1a;可以看到my io下面已经生成了一个a.txt文件。 4.注意细节 &#xff08;1&#xff09;参数是字符串表示的路径或者是File对象 &#xff08;2&…