ChatMemory (Spring AI のチャット メモリ インターフェイス)。
LLM自体はステートレスはい、別のメッセージを送信した場合、前のラウンドで何が議論されたかはわかりません。ChatMemory Spring AI によって提供されますセッションコンテキストマネージャー現在の会話のメッセージ リストを保存するために使用されます。各リクエストは自動的に履歴メッセージを大規模モデルにフィードし、継続的な対話を実現します。
大規模モデル自体はデータを保存しないため、サービスを再起動するとすぐに記憶された情報が失われるため、永続化する必要があります。
4種類の記憶
-
メモリストレージ
開発とテストのためのシンプルなメモリ内ストレージ。
-
永続ストレージ
運用環境向けのデータベースベースのストレージ。
-
分散ストレージ
スケーラブルなアプリケーションのための分散ストレージ。
-
カスタムストレージ
特定のニーズに合わせたカスタム ストレージの実装。
メモリ永続化のケース
要件: 顧客と大規模モデルの間の対話の質問と回答を永続ストレージとして Redis に保存します。
-
新しいモジュールを作成します。pom ファイルは次のとおりです。
4.0.0 com.ali springAiAlibaba-workV1 1.0-SNAPSHOT saa08-persistent 17 17 UTF-8 org.springframework.boot spring-boot-starter-web com.alibaba.cloud.ai spring-ai-alibaba-starter-dashscope org.projectlombok lombok true com.alibaba.cloud.ai spring-ai-alibaba-starter-memory-redis redis.clients jedis org.projectlombok lombok -
設定ファイルの内容
server.port=8008 # ????????? server.servlet.encoding.charset=UTF-8 server.servlet.encoding.enabled=true server.servlet.encoding.force=true spring.application.name=saa08-persistent #springai ????config spring.ai.dashscope.api-key=${aliQwen-apikey} #redis config spring.data.redis.host=localhost spring.data.redis.port=6379 spring.data.redis.connect-timeout=3 spring.data.redis.database=0 spring.data.redis.timeout=2 -
構成クラス (2)
@Configuration public class RedisMemoryConfig { @Value("${spring.data.redis.host}") private String host; @Value("${spring.data.redis.port}") private int port; @Bean public RedisChatMemoryRepository getRedisChatMemoryRepository() { return RedisChatMemoryRepository.builder() .host(host).port(port).build(); } }
@Configuration
public class SaaLLMConfig {
private final String DEEPSEEk_MODEL="deepseek-v3";
private final String QWEN_MODEL="qwen-max";
// 方式一
@Value("${spring.ai.dashscope.api-key}")
private String apiKey;
// 配置大模型对外暴露的api
@Bean(name = "deepseekChatModel")
public ChatModel deepseekChatModel() {
return DashScopeChatModel.builder()
// 配置apikey
.dashScopeApi(DashScopeApi.builder().apiKey(apiKey).build())
// 配置模型
.defaultOptions(DashScopeChatOptions.builder().withModel(DEEPSEEk_MODEL).build())
.build();
}
@Bean(name = "qwenChatModel")
public ChatModel qwenChatModel() {
return DashScopeChatModel.builder()
// 配置apikey
.dashScopeApi(DashScopeApi.builder().apiKey(apiKey).build())
// 配置模型
.defaultOptions(DashScopeChatOptions.builder().withModel(QWEN_MODEL).build())
.build();
}
@Bean(name = "deepseekChatClient")
public ChatClient deepseekChatClient(@Qualifier("deepseekChatModel") ChatModel deepseekChatModel,
RedisChatMemoryRepository redisChatMemoryRepository) {
// 配置消息窗口记忆,最多保留10条消息
MessageWindowChatMemory messageWindowChatMemory = MessageWindowChatMemory.builder()
.chatMemoryRepository(redisChatMemoryRepository)
.maxMessages(10)
.build();
return ChatClient.builder(deepseekChatModel)
.defaultAdvisors(MessageChatMemoryAdvisor.builder(messageWindowChatMemory).build())
.build();
}
@Bean("qwenChatClient")
public ChatClient qwenChatClient(@Qualifier("qwenChatModel") ChatModel qwenChatModel,
RedisChatMemoryRepository redisChatMemoryRepository) {
// 配置消息窗口记忆,最多保留10条消息
MessageWindowChatMemory messageWindowChatMemory = MessageWindowChatMemory.builder()
.chatMemoryRepository(redisChatMemoryRepository)
.maxMessages(10)
.build();
return ChatClient.builder(qwenChatModel)
.defaultAdvisors(MessageChatMemoryAdvisor.builder(messageWindowChatMemory).build())
.build();
}
}
-
コントロールクラス
@RestController public class ChatMemoryController { @Resource(name = "deepseekChatClient") private ChatClient deepseekChatClient; @Resource(name = "qwenChatClient") private ChatClient qwenChatClient; /** * 对话记忆 * * @param * @return */ @GetMapping("/chatmemory/chat") public String chat(@RequestParam("msg") String msg, @RequestParam("userId") String userId) { return qwenChatClient.prompt(msg).advisors(new Consumer() { // 顾问增强器 @Override public void accept(ChatClient.AdvisorSpec advisorSpec) { advisorSpec.param("conversationId", userId); } }).call().content(); } /** * 对话记忆 * * @param * @return */ @GetMapping("/chatmemory/chat2") public String chat2(@RequestParam("msg") String msg, @RequestParam("userId") String userId) { // 顾问增强器 return qwenChatClient.prompt(msg).advisors(advisorSpec -> advisorSpec.param("conversationId", userId)) .call().content(); }
このとき、会話内容はRedisに保存されます。
大型モデルでは通常、Alibaba の Qwen-Image または Wan シリーズ (Tongyi Wanxiang) が使用されます。
コード例
ここでの pom ファイルと構成ファイルは、前の記事の基本構成を使用します。
コントロール クラスは次のとおりです。
@RestController
public class Text2ImageController {
public static final String IMAGE_MODEL = "wanx2.1-t2i-turbo";
@Resource
private ImageModel imageModel;
/**
* 文生图
* @param
* @return
*/
@GetMapping("/test2image/image")
public String image(@RequestParam(name="msg",defaultValue = "刺猬") String msg) {
return imageModel.call(
new ImagePrompt(msg, DashScopeImageOptions.builder().withModel(IMAGE_MODEL).build())
)
.getResult()
.getOutput()
.getUrl();
}
}
音声合成モデル CosyVoice を使用します。音色にはlongyingcuiを使用
コントロールクラスを直接与える
@RestController
public class Text2VoiceController {
// 文生音模型
public static final String VOICE_MODEL = "cosyvoice-v2";
// 音色选择
public static final String VOICE_TIMBER = "longyingcui";
@Resource
private SpeechSynthesisModel speechSynthesisModel;
/**
* 文生音
* @param
* @return
*/
@GetMapping("/test2voice/voice")
public String voice(@RequestParam(name="msg",defaultValue = "您的美团外卖已自动为您接单了!") String msg) {
String filePath = "d:/"+ UUID.randomUUID().toString()+".mp3";
// 语音参数设置
DashScopeSpeechSynthesisOptions options = DashScopeSpeechSynthesisOptions.builder()
.voice(VOICE_TIMBER)
.model(VOICE_MODEL)
.build();
// 调用文生音接口
SpeechSynthesisResponse response = speechSynthesisModel.call(new SpeechSynthesisPrompt(msg, options));
// 字节流语音转换
ByteBuffer audioData = response.getResult().getOutput().getAudio();
// 将字节流写入文件
FileOutputStream fos = null;
try {
fos = new FileOutputStream(filePath);
fos.write(audioData.array());
fos.close();
} catch (Exception e) {
e.printStackTrace();
}
return filePath;
}
}
ベクトル: 大きさと方向を持つ量を表します。
テキストのベクトル化
埋め込みモデル: 埋め込みモデル
埋め込みモデルは、テキストのベクトル化に特化して使用される AI モデルです。テキストを入力し、一連のベクトルを出力します (埋め込みベクトル/埋め込み)。中心的な目標は、意味情報をベクトルにエンコードすることです。
埋め込みは、テキスト、グラフィックス、およびビデオをベクトルの浮動小数点配列に変換することによって機能します。

ベクトルデータベース
ベクターの保存、管理、検索に特化したデータベース システム。
ベクトル データベースの次元が高くなるほど、クエリの精度が高くなり、クエリの効果も向上します。
一般的に使用されるベクター データベースには、Elasticsearch、Neo4j、OceanBase、PGvector、Redis Stack などが含まれます。
Redis スタック = ネイティブ Redis + リリース バージョンに一緒にパッケージ化された一連の公式拡張モジュール通常の Redis のキャッシュ機能に加えて、RediSearch (検索 + ベクトル検索)。
Redis 8.0 以降、これらのモジュールは元の Redis に正式に組み込まれました。独立したパッケージ Redis Stack は非推奨としてマークされています。新しいプロジェクトは Redis 8 以降を直接使用できます。
効果:
テキスト、画像、ビデオをベクトルに変換し、データベースに保存します。完全一致ではなく類似性検索を実行します。クエリとしてベクトルが与えられると、VectorStore はクエリ ベクトルに「類似した」ベクトルを返します。
-
複雑な語彙関係(意味上の類似性、同義語、多義性)を捕捉する
-
ベクトル埋め込み電力取得拡張生成 (RAG) アプリケーション
コード例
-
新しいモジュールを作成します。pom は次のとおりです。
4.0.0 com.ali springAiAlibaba-workV1 1.0-SNAPSHOT saa11-Embed2vector 17 17 UTF-8 org.springframework.boot spring-boot-starter-web com.alibaba.cloud.ai spring-ai-alibaba-starter-dashscope org.springframework.ai spring-ai-starter-vector-store-redis org.projectlombok lombok true -
構成ファイルは次のとおりです。
server.port=8011 # ????????? server.servlet.encoding.charset=UTF-8 server.servlet.encoding.enabled=true server.servlet.encoding.force=true spring.application.name=saa11-Embed2vector #springai ????config spring.ai.dashscope.api-key=${aliQwen-apikey} spring.ai.dashscope.chat.options.model=qwen-plus spring.ai.dashscope.embedding.options.model=text-embedding-v3 # Redis Stack spring.data.redis.host=192.168.220.128 spring.data.redis.port=6379 spring.data.redis.password=redis-stack spring.data.redis.username=default spring.ai.vectorstore.redis.initialize-schema=true spring.ai.vectorstore.redis.index-name=custom-index spring.ai.vectorstore.redis.prefix=custom-prefix -
コントロールクラス
@RestController public class Embed2VectorController { @Resource private EmbeddingModel embeddingModel; @Resource private VectorStore vectorStore; // 文本向量化 @GetMapping("/embed2vector/text2embed") public EmbeddingResponse text2embed(@RequestParam("msg") String msg){ // 使用全局模型配置 //EmbeddingResponse embeddingResponse = embeddingModel.call(new EmbeddingRequest(List.of(msg),null); // 使用特定模型 EmbeddingResponse embeddingResponse = embeddingModel.call(new EmbeddingRequest(List.of(msg), DashScopeEmbeddingOptions.builder().withModel("text-embedding-v3").build())); System.out.println(Arrays.toString(embeddingResponse.getResult().getOutput())); return embeddingResponse; } // 文本向量化后存入向量数据库RedisStack @GetMapping("/embed2vector/add") public void add(){ Listdocuments = List.of( new Document("i study LLM"), new Document("i love java") ); vectorStore.add(documents); } // 从向量数据库RedisStack中查找。进行相似度查找 @GetMapping("/embed2vector/get") public List getAll(@RequestParam("msg") String msg){ SearchRequest searchRequest = SearchRequest.builder() .query(msg) .topK(10) .build(); List documents = vectorStore.similaritySearch(searchRequest); System.out.println(documents); return documents; } }