From a4008af72ada3648b9ad9bd6cdbc5224a72483e3 Mon Sep 17 00:00:00 2001 From: huangyongxing <1473504781@qq.com> Date: Mon, 30 Mar 2026 21:28:38 +0800 Subject: [PATCH] =?UTF-8?q?3.30=E5=AD=A6=E4=B9=A0=E7=AC=94=E8=AE=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../3.30黄永兴-软件功能总结.docx | Bin 0 -> 12766 bytes 黄永兴学习笔记/3.30黄永兴.docx | Bin 0 -> 21561 bytes .../StudySpringAI/.idea/.gitignore | 10 + .../.idea/MarsCodeWorkspaceAppSettings.xml | 7 + .../StudySpringAI/.idea/compiler.xml | 20 ++ .../StudySpringAI/.idea/encodings.xml | 7 + .../StudySpringAI/.idea/jarRepositories.xml | 35 ++++ 黄永兴学习笔记/StudySpringAI/.idea/misc.xml | 15 ++ .../StudySpringAI/.idea/modules.xml | 8 + 黄永兴学习笔记/StudySpringAI/.idea/vcs.xml | 6 + .../StudySpringAI/SpringAIEmbedding/.gitignore | 33 +++ .../.mvn/wrapper/maven-wrapper.properties | 3 + .../StudySpringAI/SpringAIEmbedding/pom.xml | 100 +++++++++ .../SpringAiEmbeddingApplication.java | 13 ++ .../controller/EmbeddingController.java | 54 +++++ .../controller/RagController.java | 22 ++ .../springaiembedding/service/RagService.java | 87 ++++++++ .../service/TextSimilarityService.java | 93 +++++++++ .../springaiembedding/tool/DocumentLoader.java | 28 +++ .../src/main/resources/application.properties | 21 ++ .../src/main/resources/knowledge.txt | 8 + .../SpringAiEmbeddingApplicationTests.java | 13 ++ .../StudySpringAI/SpringAIQuickStart/.gitignore | 33 +++ .../.mvn/wrapper/maven-wrapper.properties | 3 + .../StudySpringAI/SpringAIQuickStart/pom.xml | 104 ++++++++++ .../SpringAiQuickStartApplication.java | 13 ++ .../controller/ChatController.java | 127 ++++++++++++ .../src/main/resources/application.properties | 13 ++ .../src/main/resources/static/index.html | 224 +++++++++++++++++++++ .../SpringAiQuickStartApplicationTests.java | 13 ++ .../StudySpringAI/StudySpringAI.iml | 8 + 31 files changed, 1121 insertions(+) create mode 100644 黄永兴学习笔记/3.30黄永兴-软件功能总结.docx create mode 100644 黄永兴学习笔记/3.30黄永兴.docx create mode 100644 黄永兴学习笔记/StudySpringAI/.idea/.gitignore create mode 100644 黄永兴学习笔记/StudySpringAI/.idea/MarsCodeWorkspaceAppSettings.xml create mode 100644 黄永兴学习笔记/StudySpringAI/.idea/compiler.xml create mode 100644 黄永兴学习笔记/StudySpringAI/.idea/encodings.xml create mode 100644 黄永兴学习笔记/StudySpringAI/.idea/jarRepositories.xml create mode 100644 黄永兴学习笔记/StudySpringAI/.idea/misc.xml create mode 100644 黄永兴学习笔记/StudySpringAI/.idea/modules.xml create mode 100644 黄永兴学习笔记/StudySpringAI/.idea/vcs.xml create mode 100644 黄永兴学习笔记/StudySpringAI/SpringAIEmbedding/.gitignore create mode 100644 黄永兴学习笔记/StudySpringAI/SpringAIEmbedding/.mvn/wrapper/maven-wrapper.properties create mode 100644 黄永兴学习笔记/StudySpringAI/SpringAIEmbedding/pom.xml create mode 100644 黄永兴学习笔记/StudySpringAI/SpringAIEmbedding/src/main/java/com/example/springaiembedding/SpringAiEmbeddingApplication.java create mode 100644 黄永兴学习笔记/StudySpringAI/SpringAIEmbedding/src/main/java/com/example/springaiembedding/controller/EmbeddingController.java create mode 100644 黄永兴学习笔记/StudySpringAI/SpringAIEmbedding/src/main/java/com/example/springaiembedding/controller/RagController.java create mode 100644 黄永兴学习笔记/StudySpringAI/SpringAIEmbedding/src/main/java/com/example/springaiembedding/service/RagService.java create mode 100644 黄永兴学习笔记/StudySpringAI/SpringAIEmbedding/src/main/java/com/example/springaiembedding/service/TextSimilarityService.java create mode 100644 黄永兴学习笔记/StudySpringAI/SpringAIEmbedding/src/main/java/com/example/springaiembedding/tool/DocumentLoader.java create mode 100644 黄永兴学习笔记/StudySpringAI/SpringAIEmbedding/src/main/resources/application.properties create mode 100644 黄永兴学习笔记/StudySpringAI/SpringAIEmbedding/src/main/resources/knowledge.txt create mode 100644 黄永兴学习笔记/StudySpringAI/SpringAIEmbedding/src/test/java/com/example/springaiembedding/SpringAiEmbeddingApplicationTests.java create mode 100644 黄永兴学习笔记/StudySpringAI/SpringAIQuickStart/.gitignore create mode 100644 黄永兴学习笔记/StudySpringAI/SpringAIQuickStart/.mvn/wrapper/maven-wrapper.properties create mode 100644 黄永兴学习笔记/StudySpringAI/SpringAIQuickStart/pom.xml create mode 100644 黄永兴学习笔记/StudySpringAI/SpringAIQuickStart/src/main/java/com/example/springaiquickstart/SpringAiQuickStartApplication.java create mode 100644 黄永兴学习笔记/StudySpringAI/SpringAIQuickStart/src/main/java/com/example/springaiquickstart/controller/ChatController.java create mode 100644 黄永兴学习笔记/StudySpringAI/SpringAIQuickStart/src/main/resources/application.properties create mode 100644 黄永兴学习笔记/StudySpringAI/SpringAIQuickStart/src/main/resources/static/index.html create mode 100644 黄永兴学习笔记/StudySpringAI/SpringAIQuickStart/src/test/java/com/example/springaiquickstart/SpringAiQuickStartApplicationTests.java create mode 100644 黄永兴学习笔记/StudySpringAI/StudySpringAI.iml diff --git a/黄永兴学习笔记/3.30黄永兴-软件功能总结.docx b/黄永兴学习笔记/3.30黄永兴-软件功能总结.docx new file mode 100644 index 0000000000000000000000000000000000000000..c6d3ba9bb6667aa3ca41f2141b8bbf8c348d42fa GIT binary patch literal 12766 zcma)iWmp|evn}rK?i$?P-QC?Cf&~xm!8O6%-GaM&a3{ECLvRS;Zt{Mw<$UKnw}0$C z&rGlG>Z<9Q>Z+wI2Mz%T^4h``cZ6U6|7@_p3v(wkWf!Nnu1w0nZWzEDFu%HiDmB-# zgMxsdf`Ndb{Ii?M+qaCK4)$3py^ej%80|`{Lh7w8GT@M5Ur02EY`(

_+5Yr%2i* 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 literal 0 HcmV?d00001 diff --git a/黄永兴学习笔记/StudySpringAI/.idea/.gitignore b/黄永兴学习笔记/StudySpringAI/.idea/.gitignore new file mode 100644 index 0000000..b6b1ecf --- /dev/null +++ b/黄永兴学习笔记/StudySpringAI/.idea/.gitignore @@ -0,0 +1,10 @@ +# 默认忽略的文件 +/shelf/ +/workspace.xml +# 已忽略包含查询文件的默认文件夹 +/queries/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml +# 基于编辑器的 HTTP 客户端请求 +/httpRequests/ diff --git a/黄永兴学习笔记/StudySpringAI/.idea/MarsCodeWorkspaceAppSettings.xml b/黄永兴学习笔记/StudySpringAI/.idea/MarsCodeWorkspaceAppSettings.xml new file mode 100644 index 0000000..12c0aa4 --- /dev/null +++ b/黄永兴学习笔记/StudySpringAI/.idea/MarsCodeWorkspaceAppSettings.xml @@ -0,0 +1,7 @@ + + + + + \ No newline at end of file diff --git a/黄永兴学习笔记/StudySpringAI/.idea/compiler.xml b/黄永兴学习笔记/StudySpringAI/.idea/compiler.xml new file mode 100644 index 0000000..14e84ae --- /dev/null +++ b/黄永兴学习笔记/StudySpringAI/.idea/compiler.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/黄永兴学习笔记/StudySpringAI/.idea/encodings.xml b/黄永兴学习笔记/StudySpringAI/.idea/encodings.xml new file mode 100644 index 0000000..f822134 --- /dev/null +++ b/黄永兴学习笔记/StudySpringAI/.idea/encodings.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/黄永兴学习笔记/StudySpringAI/.idea/jarRepositories.xml b/黄永兴学习笔记/StudySpringAI/.idea/jarRepositories.xml new file mode 100644 index 0000000..cd9845b --- /dev/null +++ b/黄永兴学习笔记/StudySpringAI/.idea/jarRepositories.xml @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/黄永兴学习笔记/StudySpringAI/.idea/misc.xml b/黄永兴学习笔记/StudySpringAI/.idea/misc.xml new file mode 100644 index 0000000..75a1ef4 --- /dev/null +++ b/黄永兴学习笔记/StudySpringAI/.idea/misc.xml @@ -0,0 +1,15 @@ + + + + + + + + + + \ No newline at end of file diff --git a/黄永兴学习笔记/StudySpringAI/.idea/modules.xml b/黄永兴学习笔记/StudySpringAI/.idea/modules.xml new file mode 100644 index 0000000..36f3002 --- /dev/null +++ b/黄永兴学习笔记/StudySpringAI/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/黄永兴学习笔记/StudySpringAI/.idea/vcs.xml b/黄永兴学习笔记/StudySpringAI/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/黄永兴学习笔记/StudySpringAI/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/黄永兴学习笔记/StudySpringAI/SpringAIEmbedding/.gitignore b/黄永兴学习笔记/StudySpringAI/SpringAIEmbedding/.gitignore new file mode 100644 index 0000000..667aaef --- /dev/null +++ b/黄永兴学习笔记/StudySpringAI/SpringAIEmbedding/.gitignore @@ -0,0 +1,33 @@ +HELP.md +target/ +.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ diff --git a/黄永兴学习笔记/StudySpringAI/SpringAIEmbedding/.mvn/wrapper/maven-wrapper.properties b/黄永兴学习笔记/StudySpringAI/SpringAIEmbedding/.mvn/wrapper/maven-wrapper.properties new file mode 100644 index 0000000..c595b00 --- /dev/null +++ b/黄永兴学习笔记/StudySpringAI/SpringAIEmbedding/.mvn/wrapper/maven-wrapper.properties @@ -0,0 +1,3 @@ +wrapperVersion=3.3.4 +distributionType=only-script +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.14/apache-maven-3.9.14-bin.zip diff --git a/黄永兴学习笔记/StudySpringAI/SpringAIEmbedding/pom.xml b/黄永兴学习笔记/StudySpringAI/SpringAIEmbedding/pom.xml new file mode 100644 index 0000000..8ca984d --- /dev/null +++ b/黄永兴学习笔记/StudySpringAI/SpringAIEmbedding/pom.xml @@ -0,0 +1,100 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 3.5.13 + + + com.example + SpringAIEmbedding + 0.0.1-SNAPSHOT + SpringAIEmbedding + SpringAIEmbedding + + + + + + + + + + + + + + + 17 + 1.1.3 + + + + + + org.springframework.ai + spring-ai-bom + 1.0.0-SNAPSHOT + pom + import + + + + + + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.ai + spring-ai-starter-model-zhipuai + + + + + org.springframework.ai + spring-ai-client-chat + 1.0.0 + + + + + + + + + spring-milestones + Spring Milestones + https://repo.spring.io/milestone + + false + + + + + spring-snapshots + Spring Snapshots + https://repo.spring.io/snapshot + + false + + + true + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + diff --git a/黄永兴学习笔记/StudySpringAI/SpringAIEmbedding/src/main/java/com/example/springaiembedding/SpringAiEmbeddingApplication.java b/黄永兴学习笔记/StudySpringAI/SpringAIEmbedding/src/main/java/com/example/springaiembedding/SpringAiEmbeddingApplication.java new file mode 100644 index 0000000..8b2eadb --- /dev/null +++ b/黄永兴学习笔记/StudySpringAI/SpringAIEmbedding/src/main/java/com/example/springaiembedding/SpringAiEmbeddingApplication.java @@ -0,0 +1,13 @@ +package com.example.springaiembedding; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class SpringAiEmbeddingApplication { + + public static void main(String[] args) { + SpringApplication.run(SpringAiEmbeddingApplication.class, args); + } + +} diff --git a/黄永兴学习笔记/StudySpringAI/SpringAIEmbedding/src/main/java/com/example/springaiembedding/controller/EmbeddingController.java b/黄永兴学习笔记/StudySpringAI/SpringAIEmbedding/src/main/java/com/example/springaiembedding/controller/EmbeddingController.java new file mode 100644 index 0000000..e21032d --- /dev/null +++ b/黄永兴学习笔记/StudySpringAI/SpringAIEmbedding/src/main/java/com/example/springaiembedding/controller/EmbeddingController.java @@ -0,0 +1,54 @@ +package com.example.springaiembedding.controller; + +import com.example.springaiembedding.service.TextSimilarityService; +import jakarta.annotation.Resource; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.ai.embedding.EmbeddingModel; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import java.util.Arrays; +import java.util.Map; + +@RestController +@RequestMapping("/ai") +public class EmbeddingController { + + @Autowired + private EmbeddingModel embeddingModel; + + //对用户传入的文本进行向量化处理,测试embedding模型 + + @RequestMapping("/embedding") + public Map embedding( + @RequestParam(value = "message",defaultValue = "给我讲个笑话") String message + ){ + + // 对用户传入的文本进行向量化处理 + float[] embedding = embeddingModel.embed(message); + + return Map.of("message",message, + "vector",embedding); + } + + + @Resource + private TextSimilarityService textSimilarityService; + + /** + * 查找相似文本接口 + * @param message 查询文本 + * @return 相似文本+相似度 + */ + @GetMapping("/similarity") + public Map findSimilarText(@RequestParam String message) { + // 查询Top3相似文本 + return textSimilarityService.findSimilarTexts(message, 3); + } + + +} diff --git a/黄永兴学习笔记/StudySpringAI/SpringAIEmbedding/src/main/java/com/example/springaiembedding/controller/RagController.java b/黄永兴学习笔记/StudySpringAI/SpringAIEmbedding/src/main/java/com/example/springaiembedding/controller/RagController.java new file mode 100644 index 0000000..bdcc487 --- /dev/null +++ b/黄永兴学习笔记/StudySpringAI/SpringAIEmbedding/src/main/java/com/example/springaiembedding/controller/RagController.java @@ -0,0 +1,22 @@ +package com.example.springaiembedding.controller; + +import com.example.springaiembedding.service.RagService; +import jakarta.annotation.Resource; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + + +@RestController +@RequestMapping("/ai/rag") +public class RagController { + @Resource + private RagService ragService; + + // 问答接口 + @GetMapping("/query") + public String query(@RequestParam String question) { + return ragService.generateAnswer(question); + } +} \ No newline at end of file diff --git a/黄永兴学习笔记/StudySpringAI/SpringAIEmbedding/src/main/java/com/example/springaiembedding/service/RagService.java b/黄永兴学习笔记/StudySpringAI/SpringAIEmbedding/src/main/java/com/example/springaiembedding/service/RagService.java new file mode 100644 index 0000000..1dcbab8 --- /dev/null +++ b/黄永兴学习笔记/StudySpringAI/SpringAIEmbedding/src/main/java/com/example/springaiembedding/service/RagService.java @@ -0,0 +1,87 @@ +package com.example.springaiembedding.service; + +import com.example.springaiembedding.tool.DocumentLoader; +import jakarta.annotation.PostConstruct; +import jakarta.annotation.Resource; +import org.springframework.ai.chat.client.ChatClient; +import org.springframework.ai.chat.model.ChatModel; +import org.springframework.ai.embedding.EmbeddingModel; +import org.springframework.ai.embedding.EmbeddingResponse; +import org.springframework.stereotype.Service; + +import java.io.IOException; +import java.util.*; +import java.util.stream.Collectors; + +@Service +public class RagService { + @Resource + private EmbeddingModel embeddingModel; + @Resource + private ChatModel chatModel; + + // 内存存储:知识库片段 → 对应向量 + private Map knowledgeVectors = new HashMap<>(); + + // 项目启动时自动加载并向量化知识库 + @PostConstruct + public void initKnowledgeBase() throws IOException { + // 1. 加载并拆分文档 + List chunks = DocumentLoader.loadAndSplit("knowledge.txt"); + // 2. 批量向量化(一次请求生成所有片段向量) + EmbeddingResponse embeddingResponse = embeddingModel.embedForResponse(chunks); + // 3. 存储片段与向量的映射 + for (int i = 0; i < chunks.size(); i++) { + knowledgeVectors.put(chunks.get(i), embeddingResponse.getResults().get(i).getOutput()); + } + } + + // 检索与用户问题最相似的 Top N 片段 + private List retrieveSimilarChunks(String query, int topN) { + // 1. 用户问题向量化 + float[] queryVector = embeddingModel.embed(query); + // 2. 计算与所有知识库片段的相似度 + Map similarityMap = new HashMap<>(); + //Entry是什么?Map.Entry entry 是一个 Map.Entry 对象,用于遍历 Map 中的键值对。 + for (Map.Entry entry : knowledgeVectors.entrySet()) { + similarityMap.put(entry.getKey(), CosineSimilarityCalculator.calculate(queryVector, entry.getValue())); + } + // 3. 按相似度降序排序,取 Top N + return similarityMap.entrySet().stream() + .sorted(Collections.reverseOrder(Map.Entry.comparingByValue())) + .limit(topN) + .map(Map.Entry::getKey) + .collect(Collectors.toList()); + } + + // 生成最终回答 + public String generateAnswer(String query) { + // 1. 检索相似片段(取 Top 2) + List similarChunks = retrieveSimilarChunks(query, 2); + // 2. 拼接 Prompt(约束模型基于知识库回答) + StringBuilder prompt = new StringBuilder(); + prompt.append("请基于以下知识库内容回答用户问题,禁止编造信息:\n"); + for (String chunk : similarChunks) { + prompt.append("- ").append(chunk).append("\n"); + } + prompt.append("\n用户问题:").append(query); + // 3. 调用 Chat 模型生成回答 + ChatClient chatClient = ChatClient.builder(chatModel).build(); + return chatClient.prompt().user(prompt.toString()).call().content(); + } + + public class CosineSimilarityCalculator { + // 计算两个 float[] 向量的余弦相似度 + public static double calculate(float[] vectorA, float[] vectorB) { + double dotProduct = 0.0; + double normA = 0.0; + double normB = 0.0; + for (int i = 0; i < vectorA.length; i++) { + dotProduct += vectorA[i] * vectorB[i]; + normA += Math.pow(vectorA[i], 2); + normB += Math.pow(vectorB[i], 2); + } + return dotProduct / (Math.sqrt(normA) * Math.sqrt(normB)); + } + } +} \ No newline at end of file diff --git a/黄永兴学习笔记/StudySpringAI/SpringAIEmbedding/src/main/java/com/example/springaiembedding/service/TextSimilarityService.java b/黄永兴学习笔记/StudySpringAI/SpringAIEmbedding/src/main/java/com/example/springaiembedding/service/TextSimilarityService.java new file mode 100644 index 0000000..819acd7 --- /dev/null +++ b/黄永兴学习笔记/StudySpringAI/SpringAIEmbedding/src/main/java/com/example/springaiembedding/service/TextSimilarityService.java @@ -0,0 +1,93 @@ +package com.example.springaiembedding.service; + +import org.springframework.ai.embedding.EmbeddingModel; +import org.springframework.ai.embedding.EmbeddingResponse; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.*; +import java.util.stream.Collectors; + +@Service +public class TextSimilarityService { + + @Autowired + private EmbeddingModel embeddingModel; + + // 模拟本地文本库(可替换为数据库/向量库) + private final List TEXT_LIBRARY = Arrays.asList( + "我爱Java编程", + "SpringBoot是最流行的后端框架", + "人工智能改变世界", + "大模型应用开发", + "我喜欢学习编程", + "向量数据库用于存储Embedding", + "SpringAI简化大模型开发" + ); + + /** + * 查找最相似的文本 + * @param queryText 查询文本 + * @param topN 返回前N条结果 + * @return 相似文本+相似度 + */ + + public Map findSimilarTexts(String queryText, int topN) { + // 1. 将查询文本转为向量 + //float[] queryVector = embeddingModel.embed(queryText); + EmbeddingResponse queryEmbedding = embeddingModel.embedForResponse(List.of(queryText)); + float[] queryVector = queryEmbedding.getResults().get(0).getOutput(); + + // 2. 将文本库所有文本转为向量 + // 2. 将文本库所有文本转为向量 + Map textVectorMap = new HashMap<>(); + for (String text : TEXT_LIBRARY) { + float[] textVector = embeddingModel.embed(text); + //put是将文本和向量存储到Map中 + textVectorMap.put(text, textVector); + } + + // 3. 计算相似度并排序 + Map similarityMap = new HashMap<>(); + for (Map.Entry entry : textVectorMap.entrySet()) { + double similarity = CosineSimilarityCalculator.calculate(queryVector, entry.getValue()); + similarityMap.put(entry.getKey(), similarity); + } + + // 4. 按相似度降序排序,取TopN + return similarityMap.entrySet().stream() + .sorted(Collections.reverseOrder(Map.Entry.comparingByValue())) + .limit(topN) + .collect(Collectors.toMap( + Map.Entry::getKey, + Map.Entry::getValue, + (oldValue, newValue) -> oldValue, + LinkedHashMap::new + )); + } + + /** + * 余弦相似度工具类(计算文本向量相似度) + */ + public class CosineSimilarityCalculator { + + /** + * 计算两个向量的余弦相似度 + * @param vectorA 向量A + * @param vectorB 向量B + * @return 相似度 0~1,值越大越相似 + */ + public static double calculate(float[] vectorA, float[] vectorB) { + double dotProduct = 0.0; + double normA = 0.0; + double normB = 0.0; + + for (int i = 0; i < vectorA.length; i++) { + dotProduct += vectorA[i] * vectorB[i]; + normA += Math.pow(vectorA[i], 2); + normB += Math.pow(vectorB[i], 2); + } + return dotProduct / (Math.sqrt(normA) * Math.sqrt(normB)); + } +} +} \ No newline at end of file diff --git a/黄永兴学习笔记/StudySpringAI/SpringAIEmbedding/src/main/java/com/example/springaiembedding/tool/DocumentLoader.java b/黄永兴学习笔记/StudySpringAI/SpringAIEmbedding/src/main/java/com/example/springaiembedding/tool/DocumentLoader.java new file mode 100644 index 0000000..784bbeb --- /dev/null +++ b/黄永兴学习笔记/StudySpringAI/SpringAIEmbedding/src/main/java/com/example/springaiembedding/tool/DocumentLoader.java @@ -0,0 +1,28 @@ +package com.example.springaiembedding.tool; + +import org.springframework.core.io.ClassPathResource; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + + +// 文档加载工具类 +public class DocumentLoader { + // 加载并拆分知识库文档 + public static List loadAndSplit(String resourcePath) throws IOException { + ClassPathResource resource = new ClassPathResource(resourcePath); // 从类路径加载资源 + BufferedReader reader = new BufferedReader(new InputStreamReader(resource.getInputStream())); // 按行读取 + String content = reader.lines().collect(Collectors.joining("\n")); // 读取文件内容,将所有行连接起来一个字符串 + + // 按 "---" 拆分片段,过滤空内容 + List chunks = new ArrayList<>(); + for (String chunk : content.split("---")) { + String trimmed = chunk.trim(); + if (!trimmed.isEmpty()) chunks.add(trimmed); + } + return chunks; + } +} \ No newline at end of file diff --git a/黄永兴学习笔记/StudySpringAI/SpringAIEmbedding/src/main/resources/application.properties b/黄永兴学习笔记/StudySpringAI/SpringAIEmbedding/src/main/resources/application.properties new file mode 100644 index 0000000..2d4fc1e --- /dev/null +++ b/黄永兴学习笔记/StudySpringAI/SpringAIEmbedding/src/main/resources/application.properties @@ -0,0 +1,21 @@ +spring.application.name=SpringAIEmbedding + +server.port=8080 + +## ????AI ???? +# Embedding ?? +spring.ai.zhipuai.api-key=c721340a438942d0942148b48a22e50e.5VCKagzmQdyHpwyc +spring.ai.zhipuai.base-url=https://open.bigmodel.cn/api/paas + +# Embedding ?? +spring.ai.zhipuai.embedding-model=embedding-2 +# Chat ?? +spring.ai.zhipuai.chat.options.model=GLM-4.7-Flash + +# ?????? +logging.pattern.console=%-5level %logger - %msg%n + + + + + diff --git a/黄永兴学习笔记/StudySpringAI/SpringAIEmbedding/src/main/resources/knowledge.txt b/黄永兴学习笔记/StudySpringAI/SpringAIEmbedding/src/main/resources/knowledge.txt new file mode 100644 index 0000000..88cf6fb --- /dev/null +++ b/黄永兴学习笔记/StudySpringAI/SpringAIEmbedding/src/main/resources/knowledge.txt @@ -0,0 +1,8 @@ +--- +Spring AI 是一个用于简化大模型应用开发的框架,支持智谱AI、OpenAI 等多种大模型厂商。 +--- +智谱AI Embedding 模型可将文本转换为向量,用于语义检索、相似匹配等场景。 +--- +RAG(检索增强生成)通过检索本地知识库内容,辅助大模型生成更准确的回答,避免幻觉。 +--- +Spring AI 提供 EmbeddingModel 和 ChatModel 接口,让开发者快速接入大模型能力。 \ No newline at end of file diff --git a/黄永兴学习笔记/StudySpringAI/SpringAIEmbedding/src/test/java/com/example/springaiembedding/SpringAiEmbeddingApplicationTests.java b/黄永兴学习笔记/StudySpringAI/SpringAIEmbedding/src/test/java/com/example/springaiembedding/SpringAiEmbeddingApplicationTests.java new file mode 100644 index 0000000..b79cd15 --- /dev/null +++ b/黄永兴学习笔记/StudySpringAI/SpringAIEmbedding/src/test/java/com/example/springaiembedding/SpringAiEmbeddingApplicationTests.java @@ -0,0 +1,13 @@ +package com.example.springaiembedding; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +class SpringAiEmbeddingApplicationTests { + + @Test + void contextLoads() { + } + +} diff --git a/黄永兴学习笔记/StudySpringAI/SpringAIQuickStart/.gitignore b/黄永兴学习笔记/StudySpringAI/SpringAIQuickStart/.gitignore new file mode 100644 index 0000000..667aaef --- /dev/null +++ b/黄永兴学习笔记/StudySpringAI/SpringAIQuickStart/.gitignore @@ -0,0 +1,33 @@ +HELP.md +target/ +.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ diff --git a/黄永兴学习笔记/StudySpringAI/SpringAIQuickStart/.mvn/wrapper/maven-wrapper.properties b/黄永兴学习笔记/StudySpringAI/SpringAIQuickStart/.mvn/wrapper/maven-wrapper.properties new file mode 100644 index 0000000..c595b00 --- /dev/null +++ b/黄永兴学习笔记/StudySpringAI/SpringAIQuickStart/.mvn/wrapper/maven-wrapper.properties @@ -0,0 +1,3 @@ +wrapperVersion=3.3.4 +distributionType=only-script +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.14/apache-maven-3.9.14-bin.zip diff --git a/黄永兴学习笔记/StudySpringAI/SpringAIQuickStart/pom.xml b/黄永兴学习笔记/StudySpringAI/SpringAIQuickStart/pom.xml new file mode 100644 index 0000000..f9ec54f --- /dev/null +++ b/黄永兴学习笔记/StudySpringAI/SpringAIQuickStart/pom.xml @@ -0,0 +1,104 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 3.5.13 + + + com.example + SpringAIQuickStart + 0.0.1-SNAPSHOT + SpringAIQuickStart + SpringAIQuickStart + + + + + + + + + + + + + + + 17 + + + + + + org.springframework.ai + spring-ai-bom + 1.0.0-SNAPSHOT + pom + import + + + + + + + org.springframework.boot + spring-boot-starter + + + + org.springframework.boot + spring-boot-starter-web + + + + + org.springframework.ai + spring-ai-starter-model-deepseek + + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + + spring-milestones + Spring Milestones + https://repo.spring.io/milestone + + false + + + + + spring-snapshots + Spring Snapshots + https://repo.spring.io/snapshot + + false + + + true + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + diff --git a/黄永兴学习笔记/StudySpringAI/SpringAIQuickStart/src/main/java/com/example/springaiquickstart/SpringAiQuickStartApplication.java b/黄永兴学习笔记/StudySpringAI/SpringAIQuickStart/src/main/java/com/example/springaiquickstart/SpringAiQuickStartApplication.java new file mode 100644 index 0000000..42f5322 --- /dev/null +++ b/黄永兴学习笔记/StudySpringAI/SpringAIQuickStart/src/main/java/com/example/springaiquickstart/SpringAiQuickStartApplication.java @@ -0,0 +1,13 @@ +package com.example.springaiquickstart; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class SpringAiQuickStartApplication { + + public static void main(String[] args) { + SpringApplication.run(SpringAiQuickStartApplication.class, args); + } + +} diff --git a/黄永兴学习笔记/StudySpringAI/SpringAIQuickStart/src/main/java/com/example/springaiquickstart/controller/ChatController.java b/黄永兴学习笔记/StudySpringAI/SpringAIQuickStart/src/main/java/com/example/springaiquickstart/controller/ChatController.java new file mode 100644 index 0000000..a755f92 --- /dev/null +++ b/黄永兴学习笔记/StudySpringAI/SpringAIQuickStart/src/main/java/com/example/springaiquickstart/controller/ChatController.java @@ -0,0 +1,127 @@ +package com.example.springaiquickstart.controller; + + +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.ai.chat.messages.AssistantMessage; +import org.springframework.ai.chat.messages.Message; +import org.springframework.ai.chat.messages.SystemMessage; +import org.springframework.ai.chat.messages.UserMessage; +import org.springframework.ai.chat.model.ChatResponse; +import org.springframework.ai.chat.model.Generation; +import org.springframework.ai.chat.prompt.ChatOptions; +import org.springframework.ai.chat.prompt.Prompt; +import org.springframework.ai.deepseek.DeepSeekChatModel; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpRequest; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; +import reactor.core.publisher.Flux; + +import java.util.Arrays; +import java.util.List; + +@RestController +@RequestMapping("/ai") +public class ChatController { + + @Autowired + private DeepSeekChatModel chatModel; + + // 与模型直接对话,返回字符串响应 + @GetMapping("/generate") + public String generate(@RequestParam(value = "message",defaultValue = "你好,你是谁?") String message) { + System.out.println("message: " + message); + + // 与模型直接对话,调用chatModel的call方法生成响应 + String response = chatModel.call(message); + + System.out.println("response: " + response); + + return response; + } + + // 与模型对话,流式返回内容 + @GetMapping("/generateStream1") + public Flux generateStream1(@RequestParam(value = "message",defaultValue = "你好,你是谁?") String message) { + System.out.println("message: " + message); + + // 与模型对话,流式返回内容 + Prompt prompt = new Prompt(message); + Flux stream = chatModel.stream(prompt); + + System.out.println("stream: " + stream); + + return stream; + } + + // 与模型对话,流式返回内容,转换为字符串流 + // 解决中文乱码问题 + // 用lambda表达式简化 + @GetMapping("/generateStream2") + public Flux generateStream2( + @RequestParam(value = "message",defaultValue = "你好,你是谁?") String message, + HttpServletResponse response + ) { + //设置字符编码为UTF-8,解决中文乱码问题 + response.setCharacterEncoding("UTF-8"); + System.out.println("message: " + message); + + // 与模型对话,流式返回内容 + Prompt prompt = new Prompt(message); + Flux stream = chatModel.stream(prompt); + + // 转换为字符串流,用lambda表达式简化 + Flux result = stream.map(ChatResponse -> + ChatResponse.getResult().getOutput().getText() + ); + // 转换为字符串流,用方法引用简化 +// Flux result = stream.map(ChatResponse::getResult) +// .map(Generation::getOutput) +// .map(AssistantMessage::getText); + + + System.out.println("result: " + result); + + return result; + } + + //运行时设置模型参数 + @GetMapping("/runtimeOptions") + public Flux runtimeOptions( + @RequestParam(value = "message",defaultValue = "你好,你是谁?") String message, + @RequestParam(value = "temperature",required = false) Double temp, + HttpServletResponse response + ) { + //设置字符编码为UTF-8,解决中文乱码问题 + response.setCharacterEncoding("UTF-8"); + // 构建系统提示 + SystemMessage systemMessage = new SystemMessage("你是一个资深Java开发工程师,回答要简洁专业"); + // 构建用户消息 + UserMessage userMessage = new UserMessage(message); + // 构建历史消息(多轮对话时加入) + List messages = Arrays.asList(systemMessage, userMessage); + + // 封装成 Prompt,还可以设置模型参数 + Prompt prompt = new Prompt(messages, + ChatOptions.builder() + .temperature(temp) + .maxTokens(1000) + .build() + ); + + // 流式调用 + Flux stream = chatModel.stream(prompt); + // 转换为字符串流,用lambda表达式简化 + Flux result = stream.map(ChatResponse -> + ChatResponse.getResult().getOutput().getText() + ); + + System.out.println("result: " + result); + return result; + } + + +} diff --git a/黄永兴学习笔记/StudySpringAI/SpringAIQuickStart/src/main/resources/application.properties b/黄永兴学习笔记/StudySpringAI/SpringAIQuickStart/src/main/resources/application.properties new file mode 100644 index 0000000..04b888c --- /dev/null +++ b/黄永兴学习笔记/StudySpringAI/SpringAIQuickStart/src/main/resources/application.properties @@ -0,0 +1,13 @@ +spring.application.name=SpringAIQuickStart + +server.port=8080 + +#?? Deepseek ???????url????????? +spring.ai.deepseek.base-url=https://api.deepseek.com +spring.ai.deepseek.api-key=sk-ccbfe09f433148129cd98df6150653e8 +spring.ai.deepseek.chat.options.model=deepseek-chat + +#??0-2???0????????2??????? +spring.ai.deepseek.chat.options.temperature=0.8 + + diff --git a/黄永兴学习笔记/StudySpringAI/SpringAIQuickStart/src/main/resources/static/index.html b/黄永兴学习笔记/StudySpringAI/SpringAIQuickStart/src/main/resources/static/index.html new file mode 100644 index 0000000..b15dd6d --- /dev/null +++ b/黄永兴学习笔记/StudySpringAI/SpringAIQuickStart/src/main/resources/static/index.html @@ -0,0 +1,224 @@ + + + + + + Spring AI 聊天助手 + + + + + + + +

+
+
+ +

Spring AI 聊天助手

+
+
+ + + 在线 + +
+
+
+ + +
+ +
+ +
+

AI 助手

+

基于 DeepSeek 模型

+
+ + +
+ +
+
+
+ +
+
+

AI 助手

+

你好!我是基于 DeepSeek 模型的 AI 助手,有什么可以帮助你的吗?

+
+
+
+
+ + +
+
+ + +
+
+
+ + +
+

功能说明

+
    +
  • + + 支持自然语言对话 +
  • +
  • + + 基于 DeepSeek 大语言模型 +
  • +
  • + + 实时响应生成 +
  • +
  • + + 简洁美观的用户界面 +
  • +
+
+
+ + + + + + + \ No newline at end of file diff --git a/黄永兴学习笔记/StudySpringAI/SpringAIQuickStart/src/test/java/com/example/springaiquickstart/SpringAiQuickStartApplicationTests.java b/黄永兴学习笔记/StudySpringAI/SpringAIQuickStart/src/test/java/com/example/springaiquickstart/SpringAiQuickStartApplicationTests.java new file mode 100644 index 0000000..85ad504 --- /dev/null +++ b/黄永兴学习笔记/StudySpringAI/SpringAIQuickStart/src/test/java/com/example/springaiquickstart/SpringAiQuickStartApplicationTests.java @@ -0,0 +1,13 @@ +package com.example.springaiquickstart; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +class SpringAiQuickStartApplicationTests { + + @Test + void contextLoads() { + } + +} diff --git a/黄永兴学习笔记/StudySpringAI/StudySpringAI.iml b/黄永兴学习笔记/StudySpringAI/StudySpringAI.iml new file mode 100644 index 0000000..9a5cfce --- /dev/null +++ b/黄永兴学习笔记/StudySpringAI/StudySpringAI.iml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file