实验一:AI故事生成平台 调用deepseek大模型

news/2025/10/30 22:28:43/文章来源:https://www.cnblogs.com/ztn195/p/19178260

实验一:AI故事生成平台

实验名称: AI故事生成平台 - 核心数据模型与文本生成

核心任务: 构建平台的后端核心,实现基于关键词的自动故事生成。

任务要求:

设计并实现 Story 数据模型,至少包含标题、故事梗概、正文、创建时间等字段。

集成百度文心一言或其他大语言模型API,开发一个后端服务。该服务接收用户提供的故事关键词(如“宇航员、小狗、月球”),调用AI生成一个完整的儿童故事,并保存至数据库。

API平台:DeepSeek 开放平台

注意要充值!不过并不贵。

spring:datasource:driver-class-name: com.mysql.jdbc.Driverurl: jdbc:mysql://localhost:3306username: password: ai:openai:api-key: base-url: https://api.deepseek.comchat:options:model: deepseek-chat

导入依赖:

pom.xml
     <properties><java.version>17</java.version><spring-ai.version>1.0.1</spring-ai.version></properties><dependency><groupId>org.springframework.ai</groupId><artifactId>spring-ai-starter-model-openai</artifactId></dependency><dependencyManagement><dependencies><dependency><groupId>org.springframework.ai</groupId><artifactId>spring-ai-bom</artifactId><version>${spring-ai.version}</version><type>pom</type><scope>import</scope></dependency></dependencies></dependencyManagement>

配置一下AI

AiConfig
package com.example.config;import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.client.advisor.MessageChatMemoryAdvisor;
import org.springframework.ai.chat.client.advisor.SimpleLoggerAdvisor;
import org.springframework.ai.chat.memory.ChatMemory;
import org.springframework.ai.chat.memory.ChatMemoryRepository;
import org.springframework.ai.chat.memory.InMemoryChatMemoryRepository;
import org.springframework.ai.chat.memory.MessageWindowChatMemory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
public class AiConfig {@Beanpublic ChatMemoryRepository chatMemoryRepository() {//聊天记忆库实现,来替换为Redisreturn new InMemoryChatMemoryRepository();}@Beanpublic ChatMemory chatMemory(ChatMemoryRepository chatMemoryRepository) {//注册 聊天上下文机制return MessageWindowChatMemory.builder().chatMemoryRepository(chatMemoryRepository).maxMessages(20) //聊天记忆条数.build();}//角色预设 - 儿童故事生成助手@Beanpublic ChatClient chatClient(ChatClient.Builder chatClientBuilder,ChatMemory chatMemory) {return chatClientBuilder.defaultSystem("你是一个专业的儿童故事生成专家。请根据用户提供的关键词,创作一个温馨、有趣、富有教育意义的儿童故事。故事应该简短易懂,语言生动活泼,适合5-10岁的儿童阅读。故事中应包含积极的价值观,如友谊、勇气、好奇心等。").defaultAdvisors(new SimpleLoggerAdvisor(),// 添加会话记忆MessageChatMemoryAdvisor.builder(chatMemory).build()) //配置日志Advisor.build();}
}

输出内容的形式包括非流式和流式输出。流式输出简单来说就想我们和ai对话时一样,它不是一下输出全部回答,而是逐字输出。

StoryController
package com.example.controller;import com.example.service.StoryService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import reactor.core.publisher.Flux;/*** 故事控制器*/
@RestController
@RequestMapping("/api/story")
public class StoryController {@Autowiredprivate StoryService storyService;/*** 流式生成故事*/@GetMapping(value = "/generate/stream", produces = "text/plain;charset=UTF-8")public Flux<String> generateStoryStream(@RequestParam String keywords, @RequestParam String chatId) {if (keywords == null || keywords.trim().isEmpty()) {return Flux.just("错误:关键词不能为空");}return storyService.generateStoryStream(keywords, chatId);}
}
StoryService
package com.example.service;import reactor.core.publisher.Flux;/*** 故事服务接口*/
public interface StoryService {/*** 流式生成故事* @param keywords 故事关键词* @param chatId 会话ID* @return 故事内容的流式输出*/Flux<String> generateStoryStream(String keywords, String chatId);
}
StoryServiceImpl
package com.example.service.impl;import com.example.entity.Story;
import com.example.mapper.StoryMapper;
import com.example.service.StoryService;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Flux;
import reactor.core.scheduler.Schedulers;
import java.time.LocalDateTime;
import java.util.concurrent.atomic.AtomicReference;
import java.util.regex.Matcher;
import java.util.regex.Pattern;import static org.springframework.ai.chat.memory.ChatMemory.CONVERSATION_ID;/*** 故事服务实现类*/
@Service
public class StoryServiceImpl implements StoryService {@Autowiredprivate StoryMapper storyMapper;@Autowiredprivate ChatClient chatClient;@Overridepublic Flux<String> generateStoryStream(String keywords, String chatId) {// 创建提示词,指导AI生成儿童故事String prompt = String.format("请创作一个适合儿童的短故事,包含以下关键词:%s。\n" +"请按照以下格式返回:\n" +"【标题】故事的标题\n" +"【梗概】故事的简要内容概述(100字以内)\n" +"【正文】完整的故事内容(500-1000字)\n" +"故事应该积极向上,富有想象力,适合儿童阅读。", keywords);try {// 使用AtomicReference收集完整的故事内容AtomicReference<StringBuilder> fullContent = new AtomicReference<>(new StringBuilder());// 使用ChatClient进行流式调用return chatClient.prompt().user(prompt).advisors(a -> a.param(CONVERSATION_ID, chatId)).stream().content()// 收集内容并在完成时保存到数据库.doOnNext(chunk -> {fullContent.get().append(chunk);}).doFinally(type -> {// 在流结束后异步保存到数据库String completeStory = fullContent.get().toString();saveStoryToDatabase(completeStory, keywords);});} catch (Exception e) {// 错误处理,返回简单的故事内容String fallbackStory = "【标题】关键词的故事\n" +"【梗概】这是一个关于关键词的有趣故事。\n" +"【正文】很久很久以前,在一个神奇的王国里,关键词们快乐地生活着...";// 保存错误情况下的故事到数据库saveStoryToDatabase(fallbackStory, keywords);return Flux.just(fallbackStory);}}/*** 将故事保存到数据库*/private void saveStoryToDatabase(String storyContent, String keywords) {// 在独立线程中异步保存,不阻塞流式响应Schedulers.boundedElastic().schedule(() -> {try {// 解析AI返回的结果Story story = parseStoryResponse(storyContent, keywords);// 设置创建时间story.setCreateTime(LocalDateTime.now());story.setUpdateTime(LocalDateTime.now());// 保存到数据库storyMapper.insert(story);} catch (Exception e) {// 记录保存失败的错误,不影响用户体验e.printStackTrace();}});}/*** 解析AI返回的故事内容*/private Story parseStoryResponse(String response, String keywords) {Story story = new Story();story.setKeywords(keywords);// 使用正则表达式提取标题、梗概和正文// 标题提取Pattern titlePattern = Pattern.compile("【标题】(.*?)(?=【梗概】|$)", Pattern.DOTALL);Matcher titleMatcher = titlePattern.matcher(response);if (titleMatcher.find()) {story.setTitle(titleMatcher.group(1).trim());} else {story.setTitle("AI生成的故事:" + keywords);}// 梗概提取Pattern summaryPattern = Pattern.compile("【梗概】(.*?)(?=【正文】|$)", Pattern.DOTALL);Matcher summaryMatcher = summaryPattern.matcher(response);if (summaryMatcher.find()) {story.setSummary(summaryMatcher.group(1).trim());} else {story.setSummary("一个关于" + keywords + "的故事。");}// 正文提取Pattern contentPattern = Pattern.compile("【正文】(.*)", Pattern.DOTALL);Matcher contentMatcher = contentPattern.matcher(response);if (contentMatcher.find()) {story.setContent(contentMatcher.group(1).trim());} else {// 如果没有按照格式返回,就使用整个响应作为正文story.setContent(response.trim());}return story;}
}

实现效果:

image

本文参考:黑马程序员SpringAI+DeepSeek大模型应用开发实战视频教程,传统Java项目AI化转型必学课程_哔哩哔哩_bilibili

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

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

相关文章

矩阵快速幂常用矩阵构造

斐波那契数列: \(F_i = F_{i-1} + F_{i-2}\) \[\begin{bmatrix} F_i \\ F_{i-1} \end{bmatrix}= \begin{bmatrix} 1 & 1 \\ 1 & 0 \end{bmatrix} \times \begin{bmatrix} F_{i-1} \\ F_{i-2} \end{bmatrix} = …

顶级CTF工具与资源大全

精心整理的CTF夺旗赛工具资源集合,包含创建和解题所需的各类框架、库、软件及教程,帮助CTF新手和经验选手一站式找到所需资源,提升竞赛技能。Awesome CTF 一个精心整理的Capture The Flag(CTF)框架、库、资源、软…

第二章 数列极限

第二章 数列极限1 有界序列与无穷小序列1.a 有界序列 1.b 无穷小序列 1.c 有界序列与无穷小序列的性质2 收敛序列2.a 收敛序列的定义 2.b 收敛序列的性质 2.c 收敛序列与不等式3 收敛原理3.a 单调收敛原理 3.b 闭区间套…

小白也能看懂的RL-PPO

原文链接:https://mp.weixin.qq.com/s/cx3qY42Lp0L3RaSOgsH77A 1. 强化学习基本概念 强化学习(Reinforcement Learning, RL)作为机器学习的重要分支,目标是让智能体(agent)与环境(environment)不断交互,学习任…

第二十三天

今日深入学习了线索二叉树,这个数据结构的设计思路让我对“优化”与“权衡”有了更具体的认知。 此前学习普通二叉树时,其空指针域的浪费一直是明显的痛点——n个节点的二叉树有n+1个空指针,这些闲置的存储空间若能…

ICPC2022南京 游记(VP)

有效时长 $150min$,打出银尾。省流 有效时长 \(150min\),打出银尾。10.29 内含剧透,请vp后再来。 不是题解!!!!!!! 赛时 比赛开始,光速有人通过 I 题,我和 qwsxza 就去看。题目给了一个字符串,要求修改其…

[KaibaMath]1015 关于收敛数列迫敛性的证明

[KaibaMath]1015 关于收敛数列迫敛性的证明收敛数列的迫敛性(又称夹逼准则)是数列极限的核心性质。其可理解为:若三个数列满足“两边夹”关系且两边数列收敛到同一值,中间数列必收敛且极限相同。下面给出相应的证明…

Week 2 Homework

1. 找第k小的数的分治算法 首先,我们要先去找一个划分点,然后我们要去对划分点左右两边的数进行划分。 划分完之后,我们能得到 pivot 也就是划分点的最终位置,这个位置也是 pivot 最终排序的位置。 当我们发现 piv…

Manancher

初始化的len易错,忘记打mini的终止条件易错,写成 2*n-1咳咳,要不要仔细校准一下,容易眼花QAQ,作者:江海一归客,原文链接:https://www.cnblogs.com/jhygk/p/19178237

搜维尔科技:【技术分享】解析Xsens动捕与人形机器人的训练术语

随着人形机器人在现实应用中的普及,理解支撑其类人动作的技术非常重要。Xsens怀着浓厚兴趣探索这一领域,致力于为人形机器人训练开发最佳动作捕捉解决方案。为帮助您更好地理解相关术语,我们整理了正在塑造人形机器…

搜维尔科技:【技术分享】解析Xsens动捕与人形机器人的训练术语

随着人形机器人在现实应用中的普及,理解支撑其类人动作的技术非常重要。Xsens怀着浓厚兴趣探索这一领域,致力于为人形机器人训练开发最佳动作捕捉解决方案。为帮助您更好地理解相关术语,我们整理了正在塑造人形机器…

Python while循环 _ 捕捉日落

Python while循环 _ 捕捉日落count = 0total = 0user_input = input("请输入数字(完成所有数字输入后,请输入q终止程序):")while user_input != "q": num = float(user_input) total +=…

搜维尔科技:IROS 2025圆满落幕|MANUS手套展示世界级手部追踪技术,从遥操作到具身智能!

IROS 2025圆满落幕MANUS展示世界级手部追踪技术!「顶级水准。世界级的手部追踪技术,难以再超越。」 这是我们这几天在杭州IROS 2025展位上最常听到的话。 从遥操作到具身智能再到灵巧手的遥操作应用,MANUS正在为全球…

2024 暑期模拟赛 #9

100 + 100 + 100 + 20 = 320, Rank 1/6.2024暑期CSP-S&NOIP模拟赛第9套 链接:link 题解:link 的题解部分 时间:4h (2025..10.30 14:00~18:00) 题目数:4 难度:A B C D\(\color{#F39C11} 橙\) \(\color{#FFC116…

三值纠缠模型:智能价值权衡的元能力与实现路径探索

三值纠缠模型:智能价值权衡的元能力与实现路径探索 笔名/岐金兰 摘要 本研究基于岐金兰提出的“AI元人文”理论框架,深入剖析了智能进化下一阶段的本质特征——价值权衡元能力。通过系统梳理“三值纠缠模型”的理论内…

三值纠缠模型:智能价值权衡的元能力与实现路径探索

三值纠缠模型:智能价值权衡的元能力与实现路径探索 笔名/岐金兰 摘要 本研究基于岐金兰提出的“AI元人文”理论框架,深入剖析了智能进化下一阶段的本质特征——价值权衡元能力。通过系统梳理“三值纠缠模型”的理论内…

OceanBase系列---【如何拆分PMAX分区?】

OceanBase系列---【如何拆分PMAX分区?】因为OceanBase的oracle模式不支持自动创建分区,所以为了节省时间,我们往往会手动预先创建很多分区,然而,随着时间的流逝,手动创建的总会有用完的时候。为了防止数据插入报…

矩阵快速幂的构造技巧:从递推式到矩阵

矩阵快速幂是解决线性递推问题的“利器”,它能将递推的时间复杂度从朴素的 \(O(n)\) 优化到 \(O(\log n)\)。而这一技巧的核心,在于构造合适的转移矩阵。 一、从斐波那契数列入门:最基础的矩阵构造 斐波那契数列的递…

AutoDL+Deepseek 7B

1.下载huggingface_hub并指定源 pip install -U huggingface_hub export HF_ENDPOINT=https://hf-mirror.com2.下载DeepSeek-R1-Distill-Qwen-7B模型 mkdir autodl-tmp/DeepSeek-R1 huggingface-cli download --resume…