Spring AI实战之二:Chat API基础知识大串讲(重要)

欢迎访问我的GitHub

这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos

Spring AI实战全系列链接

  1. Spring AI实战之一:快速体验(OpenAI)
  2. Spring AI实战之二:Chat API基础知识大串讲(重要)
  3. SpringAI+Ollama三部曲之一:极速体验
  4. SpringAI+Ollama三部曲之二:细说开发

本篇概览

  • 如果说前文是最简单的介绍Spring AI,满足Java程序员的好奇心,那么本篇就是正儿八经的基础课了:梳理Spring AI框架中的Chat核心API、类、接口,SpringAI的能力就是依靠它们释放出来的
  • 本篇的目标:学习SpringAI库的最基本的类、接口、API,并了解它们的具体用途

用一个问题开篇

  • 首先回答一个问题,为什么标题是Chat API基础知识,而不是Spring AI基础知识?
  • 因为Spring AI内部由很多部分组成,Chat只是其中之一,或者说大模型提供的能力有很多,聊天只是其中一部分,SpringAI提供的能力如下所示
    在这里插入图片描述
  • 所以,Spring AI的内容很多,Chat只是其中一部分,但是这部分非常重要且基础,适合用来入门
  • 接下来开始正式学习吧,东西不多,总结下来就是:六个概念+三个层次,掌握了它们,各种大模型都能轻松驾驭了

关于Chat API的六个核心概念和三个层次

  • 从业务逻辑上看,Chat API涉及到六个概念,分为三类,如下图所示
    在这里插入图片描述
  1. client:这个好理解,代表各模型的客户端,负责请求和响应的
  2. prompt:理解成请求的最外层封装,里面有message和option
  3. message:这个好理解了,发送到大模型的内容,另外还包含了一些属性在里面,以及消息类型
  4. option:相当于参数、控制项,例如本次对话的temperature(值越小,大模型回答越严谨,值越大,大模型回答越有创造性)
  5. response:响应对象,里面封装了大模型返回的信息,主要是generation
  6. geenration:这里面是具体的返回内容
  • 再来看三个层次,前面我们知道SpringAI支持大模型的多种能力,聊天只是其中一种,因此就有一个代表最顶层的抽象层,与大模型有关的各种能力,都在此有个定义,然后是代表各种能力的抽象层,如聊天、图片、嵌入式处理等,最后是每一种能力在各类具体大模型上的实现,如下图所示
    在这里插入图片描述
  • 到现在为止咱们还没有看一行代码一个API,但是从理论上对Chat API的定位、关系已经基本了解了,是时候结合代码来看了

官方图

  • 下图来自官方文档,结合前面的分析来看一下,后面有导读
    在这里插入图片描述
  • 先看最下面橙色这层,中间是client,这里有两种,ModelClient代表了常规的请求响应,StreamingModelClient代表了流式响应(数据并非一次性传输,而是建立链接后源源不断的输出)
  • client的左侧是request,里面包含了option,至于prompt,那是Chat的概念,所以不会出现在橙色这一层
  • client右侧是response,同样只有抽象的ResponseMeta和ResultMetaData,generation是Chat的概念,不会在橙色这一层出现
  • 再往上看,绿色的就是功能抽象层了,ChatClient继承了ModelClient,Prompt继承了ModelRequest,代表Chat领域的请求,同理CharResponse继承了ModelResponse
  • 有了理论基础,一张官方图就让我们看清了Chat API的大概,现在还缺点东西,就是具体的实现层,毕竟有很多种大模型能,最终编码时还是要用到实现层的类,有没有什么方式将实现层完美的展现出来?
  • 感谢SpringAI官方,实现层和功能抽象层的关系,被下面的官方图梳理得清清楚楚
    在这里插入图片描述
  • 此刻再来回顾Spring AI实战之一:快速体验(OpenAI)一文的代码,如下所示,尽管调用了OpenAI的接口,但是并未看到OpenAI相关的类,这是因为Spring已经做好了封装,咱们直接用依赖注入的ChatClient即可,这是抽象层接口,具体实现是SpringAI根据propeties的配置实例化的OpeAIChatClient对象
package com.bolingcavalry.helloopenai.controller;import org.springframework.ai.chat.ChatClient;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;import java.util.Map;@RestController
public class SimpleAiController {// 负责处理OpenAI的bean,所需参数来自properties文件private final ChatClient chatClient;public SimpleAiController(ChatClient chatClient) {this.chatClient = chatClient;}@PostMapping("/ai/simple")public Map<String, String> completion(@RequestBody Map<String,String> map) {return Map.of("generation", chatClient.call(map.get("message")));}
}
  • 其实说到这里,您已经对Chat API有了比较清晰的理解了,来看看那六大概念的具体代码吧,接下来是一段自然的、水到渠成的体验,毕竟已经领会了其神,现在是观其形的时候
  • 接下来要看的代码如下图所示
    在这里插入图片描述

ChatClient

  • 大模型聊天功能的客户端接口,在进程中,其实现就是各大模型对应的客户端类
public interface ChatClient extends ModelClient<Prompt, ChatResponse> {default String call(String message) {// implementation omitted}@OverrideChatResponse call(Prompt prompt);
}
  • 可见主要是call方法,这就是最常规的聊天功能,调用call发送请求,返回值就是大模型的响应

StreamingChatClient

  • 这也是客户端类,用于调用大模型的功能,与ChatClient不同的是,ChatClient是请求响应,返回对象ChatResponse就是大模型返回的全部内容,而StreamingChatClient返回的是Flux,这是流式返回,可以讲大模型的响应进行流式输出,如果您使用过各种大模型聊天工具,会发现响应的内容并非一次性展现,而是一段一段的内容,持续不断的展现出来,这就是流式响应的效果
@FunctionalInterface
public interface StreamingChatClient extends StreamingModelClient<Prompt, ChatResponse> {default Flux<String> stream(String message) {Prompt prompt = new Prompt(message);return stream(prompt).map(response -> (response.getResult() == null || response.getResult().getOutput() == null|| response.getResult().getOutput().getContent() == null) ? "": response.getResult().getOutput().getContent());}@OverrideFlux<ChatResponse> stream(Prompt prompt);}
  • 注意注解FunctionalInterface,表明这是个函数式接口

Prompt

  • 前面看过了ChatClient和StreamingChatClient,会发现入参都是Prompt,可见这就是和大模型一次聊天的入参
  • 下面是Prompt的源码,去掉了构造函数、toString这些之后就会发现,最重要的是Message和ChatOption,所以Prompt只是个打包,真正要提交到大模型的其实是Message和ChatOption
public class Prompt implements ModelRequest<List<Message>> {private final List<Message> messages;private ChatOptions modelOptions;@Overridepublic ChatOptions getOptions() {..}@Overridepublic List<Message> getInstructions() {...}public String getContents() {StringBuilder sb = new StringBuilder();for (Message message : getInstructions()) {sb.append(message.getContent());}return sb.toString();}// constructors and utility methods omitted
}
  • 如果您对OpenAI有所了解,就知道prompt(提示词)并非只有用户输入的聊天内容那么简单,而是system、user 、assistant等多种类型 ,所以这里的Prompt并非只是一个外壳那么简单,它与不同类型的message、不同的辅助类等一起提供了完善的提示词功能,这个会有单独的文章来说明和实战,本篇只要记得它的最终形态就是打好的包用于提交给大模型
  • 如果只是最基本的聊天,下面这个构造方法来创建对象就行了
	public Prompt(String contents) {this(new UserMessage(contents));}

Message

  • Message很好理解:在聊天过程中,聊天内容对应的对象,请求和响应用的都是Message,不过由于消息类型的多样性,Message被设计成了接口,根据不同类型都有对应的实现,如下图所示
    在这里插入图片描述
  • Message自身非常简单,能保证使用方取到消息内容、类型即可
public interface Message {String getContent();List<Media> getMedia();Map<String, Object> getProperties();MessageType getMessageType();}
  • 另外要注意的是消息类型,一共四种
public enum MessageType {USER("user"),ASSISTANT("assistant"),SYSTEM("system"),FUNCTION("function");

ChatOptions

  • ChatOptions代表可以传递给大模型的控制参数,具体有哪些参数和大模型自身开放的特性有关,举个例子,下面是OpenAI开放的参数
  1. presencePenalty : 影响模型在生成文本时重复词语或概念的倾向
  2. frequencyPenalty:影响模型在生成文本时对已出现过词语的偏好程度
  • 按照上面的解释,既然各种大模型都有自己的参数,那么设计ChatOptions能干啥?应该能放一些通用的控制参数吧,打开代码一看果然如此,共有三个通用参数,我都加了中文注释,另外请关注类的注释,也说明了这些参数是通用的、可移植、夸模型
/*** The ChatOptions represent the common options, portable across different chat models.*/
public interface ChatOptions extends ModelOptions {// 大模型生成的内容应该更严谨还是更有创造性Float getTemperature();// 返回概率超过P的所有内容Float getTopP();// 返回概率最高的前K个内容Integer getTopK();
}
  • ChatOptions只是接口,对应的实现是ChatOptionsImpl,源码没啥好看的,就是temperature、topP、topK的get和set而已,为了实例化ChatOptionsImpl,还有配套工具ChatOptionsBuilder,用法如下
ChatOptions portablePromptOptions = ChatOptionsBuilder.builder().withTemperature(0.9f).withTopK(100).withTopP(0.6f).build();
  • 代码看到这里,长期CRUD的我不禁产生一个想法:ChatOptions接口应该很不实用,而且用起来也很别扭,因为各大模型特有的参数和这个接口都没有关系,去看了下OpenAiChatClient.java(Ollama的客户端实现类,里面有段代码是用来封装请求的),果然,这代码真是不够优雅(个人感觉)
    在这里插入图片描述

ChatResponse

  • 看完请求该看响应了,既然Generation才是真正的响应内容,那么ChatResponse也就是个壳,里面包了Generation,打开源码一看,只有Generation和ChatResponseMetadata,这个ChatResponseMetadata可以理解为元信息,主要返回了大模型的API的使用情况说明,以及限速的详细信息
public class ChatResponse implements ModelResponse<Generation> {private final ChatResponseMetadata chatResponseMetadata;private final List<Generation> generations;@Overridepublic ChatResponseMetadata getMetadata() {...}@Overridepublic List<Generation> getResults() {...}// other methods omitted
}

Generation

  • Generation中有响应的具体信息,由ChatGenerationMetadata和AssistantMessage组成
public class Generation implements ModelResult<AssistantMessage> {private AssistantMessage assistantMessage;private ChatGenerationMetadata chatGenerationMetadata;@Overridepublic AssistantMessage getOutput() {...}@Overridepublic ChatGenerationMetadata getMetadata() {...}// other methods omitted
}
  • ChatGenerationMetadata代表返回内容的元信息,包含了结束原因、生成内容的过滤规则
  • AssistantMessage更容易理解了:类型是ASSISTANT的消息,这个assistant就是助理角色,assistant消息就是大模型返回的聊天响应,源码如下
public class AssistantMessage extends AbstractMessage {public AssistantMessage(String content) {super(MessageType.ASSISTANT, content);}public AssistantMessage(String content, Map<String, Object> properties) {super(MessageType.ASSISTANT, content, properties);}@Overridepublic String toString() {return "AssistantMessage{" + "content='" + getContent() + '\'' + ", properties=" + properties + ", messageType="+ messageType + '}';}}
  • 至此,基础理论知识已经过了一遍,相信大家和我一样,进入了一看就会,一用就废的微妙阶段,不急,下一篇就是精彩的实战篇,这些知识点终究会在实战中用到,随着一行行代码一次次请求被理解,最终融汇贯通

你不孤单,欣宸原创一路相伴

  1. Java系列
  2. Spring系列
  3. Docker系列
  4. kubernetes系列
  5. 数据库+中间件系列
  6. DevOps系列

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

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

相关文章

Linux:进程地址空间、进程控制(一.进程创建、进程终止、进程等待)

上次介绍了环境变量&#xff1a;Linux&#xff1a;进程概念&#xff08;四.main函数的参数、环境变量及其相关操作&#xff09; 文章目录 1.程序地址空间知识点总结上述空间排布结构是在内存吗&#xff1f;&#xff08;进程地址空间引入&#xff09; 2.进程地址空间明确几个点进…

NDIS小端口驱动开发(三)

微型端口驱动程序处理来自过度驱动程序的发送请求&#xff0c;并发出接收指示。 在单个函数调用中&#xff0c;NDIS 微型端口驱动程序可以指示具有多个接收 NET_BUFFER_LIST 结构的链接列表。 微型端口驱动程序可以处理对每个NET_BUFFER_LIST结构上具有多个 NET_BUFFER 结构的多…

JAVA -- > 初识JAVA

初始JAVA 第一个JAVA程序详解 public class Main {public static void main(String[] args) {System.out.println("Hello world");} }1.public class Main: 类型,作为被public修饰的类,必须与文件名一致 2.public static 是JAVA中main函数准写法,记住该格式即可 …

Slash后台管理系统代码阅读笔记 如何实现环形统计图表卡片?

目前&#xff0c;工作台界面的上半部分已经基本梳理完毕了。 接下来&#xff0c;我们看看这个环形图卡片是怎么实现的&#xff1f; 具体代码如下&#xff1a; {/*图表卡片*/} <Row gutter{[16, 16]} className"mt-4" justify"center">{/*环形图表…

U盘引导盘制作Rufus v4.5.2180

软件介绍 Rufus小巧实用开源免费的U盘系统启动盘制作工具和格式化U盘的小工具&#xff0c;它可以快速将ISO镜像文件制作成可引导的USB启动安装盘&#xff0c;支持Windows或Linux启动&#xff0c;堪称写入镜像速度最快的U盘系统制作工具。 软件截图 更新日志 github.com/pbat…

嵌入式全栈开发学习笔记---C语言笔试复习大全24

目录 内存管理 内存分配 堆和栈的区别&#xff1f;&#xff08;面试重点&#xff09; 申请内存的函数 malloc realloc free gcc工具链 编译的过程&#xff08;面试重点&#xff09; 第一步&#xff0c;预处理&#xff1a; 第二步&#xff0c;编译&#xff1a; 第三…

解决LabVIEW通过OPC Server读取PLC地址时的错误180121602

在使用LabVIEW通过OPC Server读取PLC地址时&#xff0c;若遇到错误代码180121602&#xff0c;建议检查网络连接、OPC Server和PLC配置、用户权限及LabVIEW设置。确保网络畅通&#xff0c;正确配置OPC变量&#xff0c;取消缓冲设置以实时读取数据&#xff0c;并使用诊断工具验证…

2 使用香橙派AIpro报错 No module named ‘acllite utils‘

当使用jupyter运行香橙派的notebooks下面的案例的时候启动使用jupyter lab 然后自动跳转到jupyter页面。如下图: 这是自动跳转过来的。然后运行下面的包的导入后报错: 报错为No module named ‘acllite utils’,那么我们打开notebooks文件夹下面的start_notebooks.sh文件:…

【C++练级之路】【Lv.21】C++11——列表初始化和声明

快乐的流畅&#xff1a;个人主页 个人专栏&#xff1a;《算法神殿》《数据结构世界》《进击的C》 远方有一堆篝火&#xff0c;在为久候之人燃烧&#xff01; 文章目录 引言一、列表初始化1.1 内置类型1.2 结构体或类1.3 容器 二、声明2.1 auto2.2 decltype2.3 nullptr 三、STL的…

从“反超”到“引领”,中国卫浴品牌凭何遥遥领先?

作者 | 曾响铃 文 | 响铃说 前不久&#xff0c;第28届中国国际厨房、卫浴设施展览会(以下简称“中国国际厨卫展”)在上海如期举行&#xff0c;就结果来说真的让人大开眼界。 冲水声比蚊子声更小的马桶、能化身无感交互平台的魔镜柜、可以语音交互的淋浴器&#xff0c;这些“…

Keli5烧写STM32程序时出现ST-LINK USB communication error错误(USB 通信错误)

1错误原图 2错误原因 前提驱动安装正确 原因1 usb接触不良&#xff08;极少出现&#xff09; 解决方法 更换USB线 还不行连下载器一起更换 原因2&#xff08;出现概率比较大&#xff09; 下载器的固件出现问题或下载器固件版本与Keli5的版本不匹配 解决方法 在Keli5的…

今日早报 每日精选15条新闻简报 每天一分钟 知晓天下事 5月26日,星期日

每天一分钟&#xff0c;知晓天下事&#xff01; 2024年5月26日 星期日 农历四月十九 1、 医保局&#xff1a;支持将符合条件的村卫生室纳入医保定点&#xff0c;方便农村居民就医。 2、 网传养老金储备严重不足&#xff1f;央视辟谣&#xff1a;这笔钱二十多年来从未动用过&a…

搭建企业级AI应用的流程

搭建企业级AI应用的流程是一个复杂且系统化的工程&#xff0c;它需要从多个维度出发&#xff0c;确保最终的应用既符合企业的业务需求&#xff0c;也具备高效、稳定和可扩展的特性。以下是详细的步骤&#xff1a; 初步接触与需求分析是整个项目的基础。在这一阶段&#xff0c;我…

【C++题解】1698. 请输出带有特殊尾数的数

问题&#xff1a;1698. 请输出带有特殊尾数的数 类型&#xff1a; 题目描述&#xff1a; 请输出1∼n 中所有个位为 1、3、5、7中任意一个数的整数&#xff0c;每行 1 个。( n<1000 ) 比如&#xff0c;假设从键盘读入 20&#xff0c;输出结果如下&#xff1a; 1 3 5 7 11 1…

LLMs之PEFT之Llama-2:《LoRA Learns Less and Forgets LessLoRA学得更少但遗忘得也更少》翻译与解读

LLMs之PEFT之Llama-2&#xff1a;《LoRA Learns Less and Forgets LessLoRA学得更少但遗忘得也更少》翻译与解读 导读&#xff1a;该论文比较了LoRA与完全微调在代码与数学两个领域的表现。 背景问题&#xff1a;微调大规模语言模型需要非常大的GPU内存。LoRA这一参数高效微调方…

OpenStack平台Keystone组件的使用

1. 规划节点 安装基础服务的服务器规划 IP地址 主机名 节点 192.168.100.10 controller Openstack控制节点 2. 基础准备 使用机电云共享的单节点的openstack系统&#xff0c;自行修改虚拟网络编辑器、网络适配器&#xff0c;系统用户名&#xff1a;root&#xff0c;密…

【Basic】Upload-Labs-Linux

文章目录 前言Pass-01Pass-02Pass-03Pass-04Pass-05Pass-06Pass-07Pass-08Pass-09Pass-10Pass-11Pass-12Pass-13Pass-14Pass-15Pass-16解题感悟 前言 美好的一天从刷题开始 Pass-01 我淦20道题&#xff1f;&#xff1f;&#xff1f;一道一道来吧 先看第一道题 先在home里搞一…

原生标签WebComponent

文章目录 介绍一、web Component二、怎么使用三、在Vue中使用使用场景 前端必备工具推荐网站(免费图床、API和ChatAI等实用工具): http://luckycola.com.cn/ 介绍 平常浏览各个网站过程中&#xff0c;经常遇到的一种现象&#xff1a;页面广告。 这种广告按照来源可分为两种&…

蜜罐技术是一种什么防御技术?实现原理是什么?

前言&#xff1a;蜜罐技术的出现改变了这种被动态势&#xff0c;它通过吸引、诱骗攻击者&#xff0c;研究学习攻击者的攻击目的和攻击手段&#xff0c;从而延缓乃至阻止攻击破坏行为的发生&#xff0c;有效保护真实服务资源。 自网络诞生以来&#xff0c;攻击威胁事件层出不穷…

类和对象【六】友元和内部类

文章目录 友元友元的作用友元的缺点友元函数语法&#xff1a;特点&#xff1a; 友元类语法&#xff1a;特点&#xff1a; 内部类概念特点 友元 友元的作用 友元提供了一种打破封装的方式&#xff0c;有时提供了便利。 友元的主要作用就是打破封装 即可以让一个类的友元函数…