当前位置: 首页 > news >正文

快速入门SpringAI-SpringAI Alibaba实战

文章目录

    • 关于
      • Spring AI Alibaba的技术生态
    • 核心概念
    • 环境搭建与快速入门
      • 版本
    • 大模型选型
      • Ollama本地部署大模型
      • 云端大模型配置
    • 实现一个AI应用聊天机器人
      • ChatClient返回实体类型
      • 使用ChatClient指定系统角色
        • 给系统角色绑定变量
      • 让大模型具有记忆功能
        • 基于内存存储的历史对话
        • 基于Redis内存存储的历史对话
      • 对话模型ChatModel
      • 文生图ImageModel
      • AudioModel文本转语音
      • AudioModel语音转文本
      • 提示词的数据结构
          • Prompt Template
            • 基于 ConfigurablePromptTemplateFactory实现动态提示词模板
            • 基于 PromptTemplate实现动态提示词模板
            • 基于SystemPromptTemplate实现动态提示词模板
      • RAG(静态)
      • 如何实现结构化输出
      • 大模型是如何工作的?
    • RAG和微调
    • Function calling
      • 天气预报获取案例
        • 第一种方法:Function calling
        • 第二种方法:Tool Calling
    • MCP
    • MCP

官方文档:https://java2ai.com/docs/

关于

Java大模型应用开发的生态目前有:

  • SpringAI
    • Spring AI Alibaba (国内阿里巴巴做的,基于阿里云的生态)
  • LangChain4j(python的LangChain的Java版本,适合做一些复杂的工作流版本)

Spring AI Alibaba的技术生态

img

核心概念

  1. 模型model

    大模型、学霸的大脑

  2. 提示词prompt

    提问的问题

  3. 嵌入Embedding

    把概念文字转换为向量、数字

  4. Token

    把提问的文章拆分成一段段的文字

  5. 结构化输出Structured output

    规范大模型回答的内容格式

  6. 微调Fine Tuning

    让学霸专注攻坚某一科,给它某一领域的数据集训练它。

  7. 检索增强RAG

    给大模型外挂一个知识库,让它回答问题的时候去先根据知识库来回答

  8. 函数调用Function Calling

    大模型调用工具(高德,天气)

  9. 评估人工智能的回答Evaluation

环境搭建与快速入门

版本

  • JDK17
  • SpringBoot3.4.0
  • SpringAI 1.0.0-M6
  • SpringAI aliba 1.0.0-M6.1

大模型选型

Ollama本地部署大模型

其实很简单,可以搭配cherrystudio来使用

云端大模型配置

  • 阿里云百炼
  • 硅基流动平台

实现一个AI应用聊天机器人

这个案例是基于云端大模型做的,是基于阿里云百炼平台调用大模型API。

ChatController:

@RestController
public class ChatController {private final ChatClient chatClient;public ChatController(ChatClient.Builder builder) {this.chatClient = builder.build();}//同步输出@GetMapping("/chat")public String chat(@RequestParam(value = "input") String input) {return this.chatClient.prompt().user(input).call().content();}//流式输出@GetMapping(value = "/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)public Flux<String> stream(String input) {return this.chatClient.prompt().user(input).stream().content();}}

application.yaml

spring:application:name: alibaba-ai-demoai:dashscope:api-key: xxxxxx

POM

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>com.fox</groupId><artifactId>ai-demo</artifactId><version>${revision}</version></parent><artifactId>alibaba-ai-demo</artifactId><version>${revision}</version><name>alibaba-ai-demo</name><description>alibaba-ai-demo</description><properties></properties><dependencies><-- 加这个依赖!--><dependency><groupId>com.alibaba.cloud.ai</groupId><artifactId>spring-ai-alibaba-starter</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build></project>

ChatClient返回实体类型

其实ChatClient就是对操作大模型的一个底层封装,使得开发者能够专注于业务逻辑而非底层开发。

您经常希望返回一个预先定义好的实体类型响应,Spring AI 框架可以自动替我们完成从 String 到实体类的转换,调用entity() 方法可完成响应数据转换。

@RestController
public class ChatController {private final ChatClient chatClient;public ChatController(ChatClient.Builder builder) {this.chatClient = builder.build();}/*** 演员电影信息类*/static class ActorFilms {private final String actor;private final List<String> movies;public ActorFilms(String actor, List<String> movies) {this.actor = actor;this.movies = movies;}public String getActor() {return actor;}public List<String> getMovies() {return movies;}}@GetMapping("/movies")public ActorFilms movies(@RequestParam(value = "input") String input) throws Exception {ActorFilms films = chatClient.prompt().user(input).call().entity(ActorFilms.class);return films;}//entity 还有一种带有参数的重载方法 entity(ParameterizedTypeReference<T> type),可让您指定如泛型 List 等类型:@GetMapping("/movies2")public List<ActorFilms> movies2(@RequestParam(value = "input") String input) throws Exception {List<ActorFilms> filmsList = chatClient.prompt().user(input).call().entity(new ParameterizedTypeReference<List<ActorFilms>>() {});return filmsList;}}

使用ChatClient指定系统角色

设置默认 System Message,其实就是设置一个系统角色!给我们的AI一个人设!

 public ChatController(ChatClient.Builder builder) {this.chatClient = builder.defaultSystem("你是一个演员,请列出你所参演的电影").build();}
给系统角色绑定变量

@RestController
public  class AIController {private final ChatClient chatClient;public AIController(ChatClient.Builder builder) {this.chatClient =  builder.defaultSystem("你是一个友好的聊天机器人,回答问题时要使用{voice}的语气").build();}@GetMapping("/ai")Map<String, String> completion(@RequestParam(value = "message", defaultValue = "说一个笑话") String message, String voice) {return Map.of("completion",this.chatClient.prompt().system(sp -> sp.param("voice", voice))//系统角色绑定变量.user(message).call().content());}}

让大模型具有记忆功能

在这里插入图片描述

基于内存存储的历史对话
@RestController
@RequestMapping("/chat-memory")
public class ChatMemoryController {private final ChatClient chatClient;public ChatMemoryController(ChatModel chatModel) {this.chatClient = ChatClient.builder(chatModel).defaultSystem("你是一个旅游规划师,请根据用户的需求提供旅游规划建议。").defaultAdvisors(new MessageChatMemoryAdvisor(new InMemoryChatMemory()))//这是基于内存存储,实则是一个拦截器
//				.defaultAdvisors(new MessageChatMemoryAdvisor(new RedisChatMemory(
//						"127.0.0.1",
//						6379,
//						null
//				))).build();}/*** 获取内存中的聊天内容* 根据提供的prompt和chatId,从内存中获取相关的聊天内容,并设置响应的字符编码为UTF-8。** @param prompt 用于获取聊天内容的提示信息* @param chatId 聊天的唯一标识符,用于区分不同的聊天会话* @param response HTTP响应对象,用于设置响应的字符编码* @return 返回包含聊天内容的Flux<String>对象*/@GetMapping("/in-memory")public Flux<String> memory(@RequestParam("prompt") String prompt,@RequestParam("chatId") String chatId,HttpServletResponse response) {response.setCharacterEncoding("UTF-8");return chatClient.prompt(prompt).advisors(a -> a.param(CHAT_MEMORY_CONVERSATION_ID_KEY, chatId).param(CHAT_MEMORY_RETRIEVE_SIZE_KEY, 100)).stream().content();}}
基于Redis内存存储的历史对话
@RestController
@RequestMapping("/chat-memory")
public class ChatMemoryController {private final ChatClient chatClient;public ChatMemoryController(ChatModel chatModel) {this.chatClient = ChatClient.builder(chatModel).defaultSystem("你是一个旅游规划师,请根据用户的需求提供旅游规划建议。").defaultAdvisors(new MessageChatMemoryAdvisor(new RedisChatMemory("127.0.0.1",6379,"lilishop"))).build();}/*** 从Redis中获取聊天内容* 根据提供的prompt和chatId,从Redis中检索聊天内容,并以Flux<String>的形式返回** @param prompt 聊天内容的提示或查询关键字* @param chatId 聊天的唯一标识符,用于从Redis中检索特定的聊天内容* @param response HttpServletResponse对象,用于设置响应的字符编码为UTF-8* @return Flux<String> 包含聊天内容的反应式流*/@GetMapping("/redis")public Flux<String> redis(@RequestParam("prompt") String prompt,@RequestParam("chatId") String chatId,HttpServletResponse response) {response.setCharacterEncoding("UTF-8");return chatClient.prompt(prompt).advisors(a -> a.param(CHAT_MEMORY_CONVERSATION_ID_KEY, chatId).param(CHAT_MEMORY_RETRIEVE_SIZE_KEY, 10)).stream().content();}}

需要maven打包下面这个类文件,引入到上一个文件中。

/**** 基于Redis的聊天记忆实现。* 该类实现了ChatMemory接口,提供了将聊天消息存储到Redis中的功能。** @author Fox*/
public class RedisChatMemory implements ChatMemory, AutoCloseable {private static final Logger logger = LoggerFactory.getLogger(RedisChatMemory.class);private static final String DEFAULT_KEY_PREFIX = "chat:";private static final String DEFAULT_HOST = "127.0.0.1";private static final int DEFAULT_PORT = 6379;private static final String DEFAULT_PASSWORD = null;private final JedisPool jedisPool;private final ObjectMapper objectMapper;public RedisChatMemory() {this(DEFAULT_HOST, DEFAULT_PORT, DEFAULT_PASSWORD);}public RedisChatMemory(String host, int port, String password) {JedisPoolConfig poolConfig = new JedisPoolConfig();this.jedisPool = new JedisPool(poolConfig, host, port, 2000, password);this.objectMapper = new ObjectMapper();logger.info("Connected to Redis at {}:{}", host, port);}@Overridepublic void add(String conversationId, List<Message> messages) {String key = DEFAULT_KEY_PREFIX + conversationId;AtomicLong timestamp = new AtomicLong(System.currentTimeMillis());try (Jedis jedis = jedisPool.getResource()) {// 使用pipeline批量操作提升性能var pipeline = jedis.pipelined();messages.forEach(message ->pipeline.hset(key, String.valueOf(timestamp.getAndIncrement()), message.toString()));pipeline.sync();}logger.info("Added messages to conversationId: {}", conversationId);}@Overridepublic List<Message> get(String conversationId, int lastN) {String key = DEFAULT_KEY_PREFIX + conversationId;try (Jedis jedis = jedisPool.getResource()) {Map<String, String> allMessages = jedis.hgetAll(key);if (allMessages.isEmpty()) {return List.of();}return allMessages.entrySet().stream().sorted((e1, e2) ->Long.compare(Long.parseLong(e2.getKey()), Long.parseLong(e1.getKey()))).limit(lastN).map(entry -> new UserMessage(entry.getValue())).collect(Collectors.toList());}}@Overridepublic void clear(String conversationId) {String key = DEFAULT_KEY_PREFIX + conversationId;try (Jedis jedis = jedisPool.getResource()) {jedis.del(key);}logger.info("Cleared messages for conversationId: {}", conversationId);}@Overridepublic void close() {try (Jedis jedis = jedisPool.getResource()) {if (jedis != null) {jedis.close();logger.info("Redis connection closed.");}if (jedisPool != null) {jedisPool.close();logger.info("Jedis pool closed.");}}}public void clearOverLimit(String conversationId, int maxLimit, int deleteSize) {try {String key = DEFAULT_KEY_PREFIX + conversationId;try (Jedis jedis = jedisPool.getResource()) {List<String> all = jedis.lrange(key, 0, -1);if (all.size() >= maxLimit) {all = all.stream().skip(Math.max(0, deleteSize)).toList();}this.clear(conversationId);for (String message : all) {jedis.rpush(key, message);}}}catch (Exception e) {logger.error("Error clearing messages from Redis chat memory", e);throw new RuntimeException(e);}}}

对话模型ChatModel


@RestController
public class ChatModelController {private final ChatModel chatModel;public ChatModelController(@Qualifier("dashscopeChatModel") ChatModel chatModel) {this.chatModel = chatModel;}@RequestMapping("/chat2")public String chat2(String input) {DashScopeChatOptions options = DashScopeChatOptions.builder().withTemperature(0.9).withMaxToken(1500)//     .withTopP(0.01).build();Prompt prompt = new Prompt(input, options);ChatResponse response = chatModel.call(prompt);//ChatResponse response = chatModel.call(new Prompt(input));return response.getResult().getOutput().getText();}@RequestMapping("/streamChat")public Flux<String> streamChat(String input, HttpServletResponse response) throws IOException {response.setContentType("text/event-stream");response.setCharacterEncoding("UTF-8");return chatModel.stream(input);}}

文生图ImageModel

@RestController
public class ImageModelController {private final ImageModel imageModel;ImageModelController(@Qualifier("dashScopeImageModel") ImageModel imageModel) {this.imageModel = imageModel;}@RequestMapping("/image")public String image(String input) {ImageOptions options = ImageOptionsBuilder.builder().model("wanx2.1-t2i-turbo").height(1024).width(1024).build();ImagePrompt imagePrompt = new ImagePrompt(input, options);ImageResponse response = imageModel.call(imagePrompt);String imageUrl = response.getResult().getOutput().getUrl();return "redirect:" + imageUrl;}
}

AudioModel文本转语音

@RestController
@RequestMapping("/audio")
public class AudioModelController {private final SpeechSynthesisModel speechSynthesisModel;@Autowiredpublic AudioModelController(SpeechSynthesisModel speechSynthesisModel) {this.speechSynthesisModel = speechSynthesisModel;}@GetMapping("/synthesize")public ResponseEntity<byte[]> synthesizeSpeech(@RequestParam String text) throws IOException {// 构建语音合成请求SpeechSynthesisPrompt prompt = new SpeechSynthesisPrompt(text);// 调用模型生成语音SpeechSynthesisResponse response = speechSynthesisModel.call(prompt);ByteBuffer audioData = response.getResult().getOutput().getAudio();// 将 ByteBuffer 转换为字节数组byte[] audioBytes = new byte[audioData.remaining()];audioData.get(audioBytes);// 返回音频流(MP3格式)return ResponseEntity.ok().contentType(MediaType.APPLICATION_OCTET_STREAM).header("Content-Disposition", "attachment; filename=output.mp3").body(audioBytes);}}

AudioModel语音转文本

@RestController
public class AudioModelController2 {private static final String AUDIO_RESOURCES_URL = "https://dashscope.oss-cn-beijing.aliyuncs.com/samples/audio/paraformer/hello_world_female2.wav";private final DashScopeAudioTranscriptionModel dashScopeAudioTranscriptionModel; //modelname:sensevoice-v1,paraformer-realtime-v2,paraformer-v2AudioModelController2(DashScopeAudioTranscriptionModel dashScopeAudioTranscriptionModel){this.dashScopeAudioTranscriptionModel = dashScopeAudioTranscriptionModel;}@GetMapping("/audio")public String audio() throws MalformedURLException {Resource resource =new UrlResource(AUDIO_RESOURCES_URL);AudioTranscriptionPrompt prompt = new AudioTranscriptionPrompt(resource,DashScopeAudioTranscriptionOptions.builder().withModel("sensevoice-v1").build());return dashScopeAudioTranscriptionModel.call(prompt).getResult().getOutput();}
}

提示词的数据结构

Prompt中的主要角色Role包括:

  • 系统角色System Role
  • 用户角色User Role
  • 助手角色Assistant Role
  • 工具/功能角色Tool/Function Role
Prompt Template

动态提示词模板,可以按照自定义的提示词模版进行结构化输出。

  • PromptTemplateStringActions 专注于创建和呈现提示字符串,代表提示生成的最基本形式。
  • PromptTemplateMessageActions 专门用于通过生成和操作 Message 对象来创建提示。
  • PromptTemplateActions 旨在返回 Prompt 对象,该对象可以传递给 ChatModel 以生成响应。
基于 ConfigurablePromptTemplateFactory实现动态提示词模板
@RestController
@RequestMapping("/example/ai")
public class PromptTemplateController {private final ChatClient chatClient;private final ConfigurablePromptTemplateFactory configurablePromptTemplateFactory;@Value("classpath:/prompts/joke-prompt.st")private Resource jokeResource;public PromptTemplateController(ChatClient.Builder builder,ConfigurablePromptTemplateFactory configurablePromptTemplateFactory) {this.chatClient = builder.build();this.configurablePromptTemplateFactory = configurablePromptTemplateFactory;}/*** nacos template config [{"name:"test-template","template:"please list the most famous books by this {author}."}]*/@GetMapping("/prompt-template")public AssistantMessage generate(@RequestParam(value = "author", defaultValue = "鲁迅") String author) {ConfigurablePromptTemplate template = configurablePromptTemplateFactory.getTemplate("test-template");if (template == null) {template = configurablePromptTemplateFactory.create("test-template","请列出 {author} 最著名的三本书。");}Prompt prompt;if (StringUtils.hasText(author)) {prompt = template.create(Map.of("author", author));} else {prompt = template.create();}return chatClient.prompt(prompt).call().chatResponse().getResult().getOutput();}}
@Configuration
public class PromptTemplateConfig {@Beanpublic ConfigurablePromptTemplateFactory configurablePromptTemplateFactory() {// 这里假设ConfigurablePromptTemplateFactory有一个无参构造函数return new ConfigurablePromptTemplateFactory();// 如果需要配置参数,可以在这里进行配置// return new ConfigurablePromptTemplateFactory(param1, param2);}
}
基于 PromptTemplate实现动态提示词模板
@RestController
@RequestMapping("/example/ai")
public class PromptTemplateController {private final ChatClient chatClient;private final ConfigurablePromptTemplateFactory configurablePromptTemplateFactory;@Value("classpath:/prompts/joke-prompt.st")private Resource jokeResource;public PromptTemplateController(ChatClient.Builder builder,ConfigurablePromptTemplateFactory configurablePromptTemplateFactory) {this.chatClient = builder.build();this.configurablePromptTemplateFactory = configurablePromptTemplateFactory;}@GetMapping("/prompt")public AssistantMessage completion(@RequestParam(value = "adjective", defaultValue = "有趣") String adjective,@RequestParam(value = "topic", defaultValue = "奶牛") String topic) {PromptTemplate promptTemplate = new PromptTemplate(jokeResource);Prompt prompt = promptTemplate.create(Map.of("adjective", adjective, "topic", topic));return chatClient.prompt(prompt).call().chatResponse().getResult().getOutput();}}

joke-prompt.st:

给我讲一个关于 {topic} 的 {adjective} 笑话
基于SystemPromptTemplate实现动态提示词模板
@RestController
@RequestMapping("/example/ai")
public class RoleController {private final ChatClient chatClient;@Value("classpath:/prompts/system-message.st")private Resource systemResource;@Autowiredpublic RoleController(ChatClient.Builder builder) {this.chatClient = builder.build();}@GetMapping("/roles")public AssistantMessage generate(@RequestParam(value = "message",defaultValue = "请介绍一下海盗黄金时代的三位著名海盗,以及他们为什么这样做。为每个海盗至少写一句话。") String message,@RequestParam(value = "name", defaultValue = "Fox") String name,@RequestParam(value = "voice", defaultValue = "海盗") String voice) {UserMessage userMessage = new UserMessage(message);SystemPromptTemplate systemPromptTemplate = new SystemPromptTemplate(systemResource);Message systemMessage = systemPromptTemplate.createMessage(Map.of("name", name, "voice", voice));return chatClient.prompt(new Prompt(List.of(userMessage, systemMessage))).call().chatResponse().getResult().getOutput();}}

system-message.st:

你是一个有用的 AI 助手。
你是帮助人们查找信息的 AI 助手。
你的名字是 {name}
你应该使用你的姓名和 {voice} 的样式回复用户的请求。

RAG(静态)

静态RAG和动态RAG不一样,动态RAG采用的是结合的向量数据库


@RestController
@RequestMapping("/example/ai")
public class StuffController {private final ChatClient chatClient;@Value("classpath:/docs/bailian.md")private Resource docsToStuffResource;@Value("classpath:/prompts/qa-prompt.st")private Resource qaPromptResource;@Autowiredpublic StuffController(ChatClient.Builder builder) {this.chatClient = builder.build();}@GetMapping(value = "/stuff")public Completion completion(@RequestParam(value = "message", defaultValue = "给我推荐一款百炼系列的手机?") String message, @RequestParam(value = "stuffit", defaultValue = "false") boolean stuffit) {PromptTemplate promptTemplate = new PromptTemplate(qaPromptResource);Map<String, Object> map = new HashMap<>();map.put("question", message);if (stuffit) {map.put("context", docsToStuffResource);} else {map.put("context", "");}return new Completion(chatClient.prompt(promptTemplate.create(map)).call().content());}}

bailian.md:

# **百炼手机产品介绍**欢迎来到未来科技的前沿,探索我们精心打造的智能手机系列,每一款都是为了满足您对科技生活的无限遐想而生。**百炼X1** —— 畅享极致视界:搭载6.7英寸1440 x 3200像素超清屏幕,搭配120Hz刷新率,流畅视觉体验跃然眼前。256GB海量存储空间与12GB RAM强强联合,无论是大型游戏还是多任务处理,都能轻松应对。5000mAh电池长续航,加上超感光四摄系统,记录生活每一刻精彩。参考售价:4599 - 4999**通义Vivid 7** —— 智能摄影新体验:拥有6.5英寸1080 x 2400像素全面屏,AI智能摄影功能让每一张照片都能展现专业级色彩与细节。8GB RAM与128GB存储空间确保流畅操作,4500mAh电池满足日常所需。侧面指纹解锁,便捷又安全。参考售价:2999 - 3299**星尘S9 Pro** —— 创新视觉盛宴:突破性6.9英寸1440 x 3088像素屏下摄像头设计,带来无界视觉享受。512GB存储与16GB RAM的顶级配置,配合6000mAh电池与100W快充技术,让性能与续航并驾齐驱,引领科技潮流。参考售价:5999 - 6499。**百炼Ace Ultra** —— 游戏玩家之选:配备6.67英寸1080 x 2400像素屏幕,内置10GB RAM与256GB存储,确保游戏运行丝滑无阻。5500mAh电池搭配液冷散热系统,长时间游戏也能保持冷静。高动态双扬声器,沉浸式音效升级游戏体验。参考售价:3999 - 4299。**百炼Zephyr Z9** —— 轻薄便携的艺术:轻巧的6.4英寸1080 x 2340像素设计,搭配128GB存储与6GB RAM,日常使用游刃有余。4000mAh电池确保一天无忧,30倍数字变焦镜头捕捉远处细节,轻薄而不失强大。参考售价:2499 - 2799。**百炼Flex Fold+** —— 折叠屏新纪元:集创新与奢华于一身,主屏7.6英寸1800 x 2400像素与外屏4.7英寸1080 x 2400像素,多角度自由悬停设计,满足不同场景需求。512GB存储、12GB RAM,加之4700mAh电池与UTG超薄柔性玻璃,开启折叠屏时代新篇章。此外,这款手机还支持双卡双待、卫星通话,帮助您在世界各地都能畅联通话。参考零售价:9999 - 10999。每一款手机都是匠心独运,只为成就您手中的科技艺术品。选择属于您的智能伙伴,开启未来科技生活的新篇章。

qa-prompt.st:

使用以下上下文来回答最后的问题。
如果你不知道答案,就说你不知道,不要试图编造答案。{context}问题: {question}
有用的答案:

如何实现结构化输出

结构化输出就是返回特定的格式,返回Java对象还是json格式的问题。

@RestController
@RequestMapping("/example/stream")
public class StreamToBeanController {private final ChatClient chatClient;private static final Logger log = LoggerFactory.getLogger(StreamToBeanController.class);public StreamToBeanController(ChatClient.Builder builder) {// 使用builder对象构建ChatClient实例this.chatClient = builder.build();}/*** @return {@link com.fox.structureddemo.stream.StreamToBeanEntity}*/@GetMapping("/play")public StreamToBeanEntity simpleChat(HttpServletResponse response) {response.setCharacterEncoding("UTF-8");var converter = new BeanOutputConverter<>(new ParameterizedTypeReference<StreamToBeanEntity>() { });Flux<String> flux = this.chatClient.prompt().user(u -> u.text("""requirement: 请用大概 120 字,作者为 Fox ,为计算机的发展历史写一首现代诗;format: 以纯文本输出 json,请不要包含任何多余的文字——包括 markdown 格式;outputExample: {"title": {title},"author": {author},"date": {date},"content": {content}};""")).stream().content();String result = String.join("\n", Objects.requireNonNull(flux.collectList().block())).replaceAll("\\n", "").replaceAll("\\s+", " ").replaceAll("\"\\s*:", "\":").replaceAll(":\\s*\"", ":\"");log.info("LLMs 响应的 json 数据为:{}", result);return converter.convert(result);}}

StreamToBeanEntity:

public class StreamToBeanEntity {private String title;private String author;private String date;private String content;public StreamToBeanEntity() {}public String getTitle() {return title;}public void setTitle(String title) {this.title = title;}public String getAuthor() {return author;}public void setAuthor(String author) {this.author = author;}public String getDate() {return date;}public void setDate(String date) {this.date = date;}public String getContent() {return content;}public void setContent(String content) {this.content = content;}@Overridepublic String toString() {return "StreamToBeanEntity{" +"title='" + title + '\'' +", author='" + author + '\'' +", date='" + date + '\'' +", content='" + content + '\'' +'}';}}

还有一种方法来实现这种转换:

@RestController
@RequestMapping("/example/stream/json")
public class StreamToJsonController {private static final String DEFAULT_PROMPT = "你好,请以JSON格式介绍你自己!";private final ChatClient dashScopeChatClient;public StreamToJsonController(ChatModel chatModel) {DashScopeResponseFormat responseFormat = new DashScopeResponseFormat();responseFormat.setType(DashScopeResponseFormat.Type.JSON_OBJECT);this.dashScopeChatClient = ChatClient.builder(chatModel).defaultOptions(DashScopeChatOptions.builder().withTopP(0.7).withResponseFormat(responseFormat).build()).build();}/*** @return {@link String}*/@GetMapping("/play")public String simpleChat(HttpServletResponse response) {response.setCharacterEncoding("UTF-8");return dashScopeChatClient.prompt(DEFAULT_PROMPT).call().content();}}

大模型是如何工作的?

在这里插入图片描述

RAG和微调

RAG就是给大模型外挂一个知识库,检索知识库再回答问题;微调的话是真正的在训练大模型具备回答知识的能力。

RAG适合那种经常变化的知识:比如政策性的;微调适合那种静态知识,比如医疗领域的知识,这种知识变化不大。

在这里插入图片描述

RagConfig:

@Configuration
public class RagConfig {@BeanChatClient chatClient(ChatClient.Builder builder) {return builder.defaultSystem("你将作为一名机器人产品的专家,对于用户的使用需求作出解答").build();}@BeanVectorStore vectorStore(EmbeddingModel embeddingModel) {SimpleVectorStore simpleVectorStore = SimpleVectorStore.builder(embeddingModel).build();// 生成一个机器人产品说明书的文档List<Document> documents = List.of(new Document("产品说明书:产品名称:智能机器人\n" +"产品描述:智能机器人是一个智能设备,能够自动完成各种任务。\n" +"功能:\n" +"1. 自动导航:机器人能够自动导航到指定位置。\n" +"2. 自动抓取:机器人能够自动抓取物品。\n" +"3. 自动放置:机器人能够自动放置物品。\n"));simpleVectorStore.add(documents);return simpleVectorStore;}}

RagController:

@RestController
@RequestMapping("/ai")
public class RagController {@Autowiredprivate ChatClient chatClient;@Autowiredprivate VectorStore vectorStore;@GetMapping(value = "/chat", produces = "text/plain; charset=UTF-8")public String generation(String userInput) {// 发起聊天请求并处理响应return chatClient.prompt().user(userInput).advisors(new QuestionAnswerAdvisor(vectorStore))//这个是关键,向量数据库的开启.call().content();}
}

其实可以直接利用百炼平台创建智能体应用,通过应用关联向量数据库,再在业务系统中通过api调用百炼平台的智能体应用。

Function calling

这是一个让大模型调用外部工具能力的一个工具。

在这里插入图片描述

其实这个Function你可以理解成我们自己写的一个函数工具,也可以是第三方提供的一个函数工具。

在这里插入图片描述

我们接下来实现一个天气预报获取的案例。

天气预报获取案例

我们我们可以用Function calling和method两种方法来实现。

第一种方法:Function calling

构造获取天气预报信息的函数

public class WeatherFunction implements Function<WeatherFunction.WeatherRequest, String> {@Overridepublic String apply(WeatherRequest request) {// 此处省略了实际的天气查询逻辑,直接返回一个示例字符串// 实际应用中需要根据请求参数调用天气API获取天气信息return "The weather in " + request.getCity() + " is sunny.";}public static class WeatherRequest {private String city;public String getCity() { return city; }public void setCity(String city) { this.city = city; }}
}
@Configuration
public class FunctionConfig {@Bean@Description("获取指定城市的天气信息")public Function<WeatherFunction.WeatherRequest, String> weatherFunction() {return new WeatherFunction();}
}

Controller:

@RestController
@RequestMapping("/weather")
public class WeatherController {private final ChatClient dashScopeChatClient;public WeatherController(ChatClient.Builder chatClientBuilder) {this.dashScopeChatClient = chatClientBuilder.defaultFunctions("weatherFunction")//把这里打开.build();}/*** 调用工具版 - function*/@GetMapping("/chat-tool-function")public String chatTranslateFunction(@RequestParam(value = "query", defaultValue = "北京今天的天气") String query) {return dashScopeChatClient.prompt(query).functions("weatherFunction").call().content();}}
第二种方法:Tool Calling

首先定义接口:

public interface WeatherTool {String getWeather(String city);
}

@Tool(description = “获取指定城市的天气信息。”)来描述函数的功能作用。

@Tool的好处就是你的工具拿到结果以后不需要在交给大模型,而是直接返回给客户端。

public class WeatherToolImpl implements WeatherTool {@Override@Tool(description = "获取指定城市的天气信息。")public String getWeather(String city) {return "The weather in " + city + " is sunny.";}
}
@RestController
@RequestMapping("/weather")
public class WeatherController {private final ChatClient dashScopeChatClient;public WeatherController(ChatClient.Builder chatClientBuilder) {this.dashScopeChatClient = chatClientBuilder.defaultTools(new WeatherToolImpl()).build();}/*** 无工具版*/@GetMapping("/chat")public String simpleChat(@RequestParam(value = "query", defaultValue = "北京今天的天气") String query) {return dashScopeChatClient.prompt(query).call().content();}/*** 调用工具版 - method*/@GetMapping("/chat-tool-method")public String chatTranslateMethod(@RequestParam(value = "query", defaultValue = "北京今天的天气") String query) {return dashScopeChatClient.prompt(query).tools(new WeatherToolImpl()).call().content();}
}

MCP

MCP我以前的文章都讲过。

直接点开:都说MCP牛B,牛刀小试了一下,代码案例自取_java mcp代码案例-CSDN博客

在这里插入图片描述

Function calling和MCP的区别就是:

  • Function calling 的外部工具一般都是我们自己写、自己封装的
  • MCP的外部工具都是别人写好的,第三方提供的。
    weather in " + city + " is sunny.";
    }
    }

```java
@RestController
@RequestMapping("/weather")
public class WeatherController {private final ChatClient dashScopeChatClient;public WeatherController(ChatClient.Builder chatClientBuilder) {this.dashScopeChatClient = chatClientBuilder.defaultTools(new WeatherToolImpl()).build();}/*** 无工具版*/@GetMapping("/chat")public String simpleChat(@RequestParam(value = "query", defaultValue = "北京今天的天气") String query) {return dashScopeChatClient.prompt(query).call().content();}/*** 调用工具版 - method*/@GetMapping("/chat-tool-method")public String chatTranslateMethod(@RequestParam(value = "query", defaultValue = "北京今天的天气") String query) {return dashScopeChatClient.prompt(query).tools(new WeatherToolImpl()).call().content();}
}

MCP

MCP我以前的文章都讲过。

直接点开:都说MCP牛B,牛刀小试了一下,代码案例自取_java mcp代码案例-CSDN博客

在这里插入图片描述

Function calling和MCP的区别就是:

  • Function calling 的外部工具一般都是我们自己写、自己封装的
  • MCP的外部工具都是别人写好的,第三方提供的。
http://www.dtcms.com/a/454153.html

相关文章:

  • window显示驱动开发-验证从用户模式发送到内核模式的专用数据
  • 网站建设 中企动力南昌0792在线购物网站 模版
  • 凡科网站设计模板品牌网站分析
  • 【Spring 2】深入剖析 Spring 的 Singleton 作用域:不仅仅是单例
  • 【密码学实战】openHiTLS kdf命令行:密钥派生工具
  • 手机网站微信网站开发南京米雅途做网站如何
  • AQS(抽象队列同步器)
  • 【深度学习计算机视觉】10:转置卷积——从图像上采样到特征图还原的核心技术
  • 做坑人网站二维码全网热度指数
  • 购物网站设计的目的新绛网站建设
  • ERT中正问题和逆问题的传统数学推导
  • 模电基础:电流源电路
  • 58同城济南网站建设网站建设公司人员配置
  • 山西品牌网站建设什么网站上做效果图可以赚钱
  • 尚庭公寓中Redis的使用
  • 网站建设服务采购方案惠阳网站优化
  • Python 学习(2) ---- Python 数据类型
  • 三更app下载网站衡阳网站设计
  • 语言结构的基本单位:语素、词根与词缀
  • 线程邮箱(1)
  • 【深入理解计算机系统第3版】浮点数舍入和在不同位模式下的变换2.52
  • 有了域名和空间怎么做网站网站怎么做下拉刷新
  • 【完整源码+数据集+部署教程】 水果叶片分割系统: yolov8-seg-dyhead
  • 前端如何做双语网站wordpress缓存插件汉化破解版
  • Python回调函数中携带额外状态的完整指南:从基础到高级实践
  • 郑州的设计公司企业网站建设推荐乐云seo
  • OpenAI 推出 ChatGPT 应用系统,开发者可在平台内构建互动式应用
  • 【Coze】1.Coze 的基本介绍
  • 字符串比较函数strcmp和strncmp以及memcmp详解与对比分析
  • linux练习-2