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.rhkb.cn/news/331735.html

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

相关文章

JavaFX学习教程二

一、JavaFX 体系结构 JavaFX 场景图(Scene Graph)是构建 JavaFX 应用程序的起点&#xff0c;一种树状数据结构&#xff0c;用于排列&#xff08;和分组&#xff09;图形对象&#xff0c;以便于逻辑表示。 stage:舞台&#xff0c;操作系统窗口的 JavaFX 表示&#xff0c;是所有…

Qt for android 获取USB设备列表(一)Java方式 获取

简介 QtActivity 作为 Qt 应用程序的入口点&#xff0c;负责启动和配置 Qt 应用程序的信息&#xff0c; 后面我们继承 QtActivity 做自定义控制&#xff0c;了解一下 Activity 生命周期概念&#xff0c; 因为 QtActivity 继承自Android的activity&#xff0c;使用周期函数完成我…

VMare下载安装

一.下载 1.百度搜索BROADCOM官网 打开官网&#xff1a; https://www.broadcom.com/​ 2.点击右上角&#xff0c;进行账号注册&#xff0c;注册好后&#xff0c;进行登陆 3.注册好后&#xff0c;进入个人界面&#xff1a;https://support.broadcom.com/#. 按下图所示点击进…

VMware虚拟机中ubuntu使用记录(10)—— 如何在Ubuntu18.04中使用自己的单目摄像头运行ORB_SLAM3(亲测有效,踩坑记录)

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、ORB_SLAM3源码编译二、ORB_SLAM3实时单目相机测试1. 查看摄像头的话题2. 运行测试 三. 运行测试可能的报错1. 报错一(1) 问题描述(2) 原因分析(3) 解决 2. …

大作业爬取手机数据,实现手机推荐系统以及朋友圈手机论坛

1、功能简介 &#xff08;1&#xff09;用户注册与用户登录 &#xff08;2&#xff09;手机搜索、手机比拼、手机个性化推荐 &#xff08;3&#xff09;点击搜索的手机图片会就用户行为&#xff0c;轮播展示用户行为&#xff0c;推荐点击次数靠前的手机 &#xff08;4&#xf…

基于springboot+vue的智慧外贸平台

开发语言&#xff1a;Java框架&#xff1a;springbootJDK版本&#xff1a;JDK1.8服务器&#xff1a;tomcat7数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09;数据库工具&#xff1a;Navicat11开发软件&#xff1a;eclipse/myeclipse/ideaMaven包&#xff1a;…

论文精读:HuggingGPT: Solving AI Tasks with ChatGPT and its Friends in Hugging Face

HuggingGPT: Solving AI Tasks with ChatGPT and its Friends in Hugging Face Status: Reading Author: Dongsheng Li, Kaitao Song, Weiming Lu, Xu Tan, Yongliang Shen, Yueting Zhuang Institution: 微软亚洲研究院&#xff08;Microsoft Research Asia&#xff09;, 浙江…

【408精华知识】指令的寻址方式

文章目录 一、指令寻址&#xff08;一&#xff09;顺序寻址&#xff08;二&#xff09;跳跃寻址 二、数据寻址&#xff08;一&#xff09;隐含寻址&#xff08;二&#xff09;立即&#xff08;数&#xff09;寻址&#xff08;三&#xff09;直接寻址&#xff08;四&#xff09;…

外卖系统源码开发全攻略:外卖小程序与后台管理系统的设计与实现

今天&#xff0c;小编将详细介绍外卖系统源码的开发全攻略&#xff0c;从需求分析到设计与实现&#xff0c;为开发者提供全面指导。 一、需求分析 1.用户需求 用户是外卖系统的核心&#xff0c;需满足以下基本需求&#xff1a; -浏览菜单并下单 -实时追踪订单 -多种支付方…

移动硬盘难题:不显示容量与无法访问的解决策略

在使用移动硬盘的过程中&#xff0c;有时会遇到一些棘手的问题&#xff0c;比如移动硬盘不显示容量且无法访问。这种情况让人十分头疼&#xff0c;因为它不仅影响了数据的正常使用&#xff0c;还可能导致重要数据的丢失。接下来&#xff0c;我们就来详细探讨一下这个问题及其解…

Python | Leetcode Python题解之第112题路径总和

题目&#xff1a; 题解&#xff1a; class Solution:def hasPathSum(self, root: TreeNode, sum: int) -> bool:if not root:return Falseif not root.left and not root.right:return sum root.valreturn self.hasPathSum(root.left, sum - root.val) or self.hasPathSum…

太狠了,凌晨5点面试。。

(关注数据结构和算法&#xff0c;了解更多新知识) 网上看到一网友发文说收到面试邀请&#xff0c;面试时间竟然是早晨5点&#xff0c;这是要猝死的节奏。有的网友说应该是下午 5 点&#xff0c;如果是下午 5 点直接写下午 5 点就行了&#xff0c;或者写 17 点也行&#xff0c;直…

Unity Assembly Definition Dotween 引用

原理&#xff1a; 具体Unity程序集原理用法&#xff0c;暂时留坑&#xff0c;不介绍了&#xff0c;相信有很多人也写过了 这里简单放个官方API链接 https://docs.unity3d.com/cn/current/Manual/ScriptCompilationAssemblyDefinitionFiles.html 现象 &#xff1a;Dotween引用…

捕捉二氧化碳也能赚钱?深入探索CCUS技术与商业前景

引言 随着全球变暖和气候变化的加剧&#xff0c;如何有效减少二氧化碳&#xff08;CO2&#xff09;排放成为各国亟待解决的问题。近日&#xff0c;全球最大的二氧化碳捕集工厂在冰岛正式运营&#xff0c;这一消息引起了广泛关注。本文将深入探讨捕集二氧化碳技术&#xff08;C…

Java进阶学习笔记20——枚举

认识枚举&#xff1a; 枚举是一种特殊的类。 枚举类的格式&#xff1a; 说明&#xff1a; 第一行是罗列枚举的对象名称。只能写合法的标识符&#xff08;名称&#xff09;&#xff0c;多个名称用逗号隔开。 这些名称本质上都是常量&#xff0c;每个变量都会记住枚举类的一个…

RobotFramework测试框架(13)--内置测试库

Builtln Evaluate方法 Evaluate。它可以做很多事情&#xff0c;主要的作用是可以直接调用Python的方法 一般用Evaluate都是前面放变量接收值&#xff0c;第三列是具体的运算表达式&#xff0c;第四列是要用到的Python的module。这里就是用random来进行一个随机数的生成 Cons…

python写接口性能测试

import time import requestsdef measure_response_time(api_url):try:start_time time.time()response requests.get(api_url, timeout10) # 设置超时时间为10秒end_time time.time()response_time end_time - start_timeprint(f"接口 {api_url} 的响应时间为&#…

nodeJs学习(第一周)

文章目录 学习总结nodejs基础知识核心模块&#xff08;内置模块&#xff09;fs&#xff08;file-system&#xff09;文件系统fs增删查改urlQuery String httprequest根据不同的请求路径发送不同的响应结果requireip地址和端口号Content-Type 第三方模块 express登录接口逻辑分析…

乡村振兴的乡村文化传承与活化:活化乡村传统文化,传承乡村文化基因,打造具有文化魅力的美丽乡村

目录 一、引言 二、乡村文化的独特价值与现状 三、活化乡村传统文化的策略 1、挖掘乡村文化资源 2、创新文化表达方式 3、加强文化产业发展 四、传承乡村文化基因的途径 1、加强文化教育 2、培育文化人才 3、弘扬文化精神 五、打造具有文化魅力的美丽乡村 1、规划乡…

Photoshop插件(UXP)编写过程中,如何更新sp-checkbox的选中状态

✨问题说明 sp-checkbox是uxpSpectrum UXP Widgets下的一个小组件&#xff0c;内置样式大概是这样&#xff1a; 那么&#xff0c;如果用js动态的改变选中的状态&#xff0c;应该如何做呢&#xff1f; 如果直接是html来写&#xff1a; <sp-checkbox checked>Checked<…