Browse Source
Merge branch 'huangyongxing/feature-20260327161252-学习笔记' into milestone-20260325-学习笔记
milestone-20260325-学习笔记
Merge branch 'huangyongxing/feature-20260327161252-学习笔记' into milestone-20260325-学习笔记
milestone-20260325-学习笔记
31 changed files with 1121 additions and 0 deletions
-
BIN黄永兴学习笔记/3.30黄永兴-软件功能总结.docx
-
BIN黄永兴学习笔记/3.30黄永兴.docx
-
10黄永兴学习笔记/StudySpringAI/.idea/.gitignore
-
7黄永兴学习笔记/StudySpringAI/.idea/MarsCodeWorkspaceAppSettings.xml
-
20黄永兴学习笔记/StudySpringAI/.idea/compiler.xml
-
7黄永兴学习笔记/StudySpringAI/.idea/encodings.xml
-
35黄永兴学习笔记/StudySpringAI/.idea/jarRepositories.xml
-
15黄永兴学习笔记/StudySpringAI/.idea/misc.xml
-
8黄永兴学习笔记/StudySpringAI/.idea/modules.xml
-
6黄永兴学习笔记/StudySpringAI/.idea/vcs.xml
-
33黄永兴学习笔记/StudySpringAI/SpringAIEmbedding/.gitignore
-
3黄永兴学习笔记/StudySpringAI/SpringAIEmbedding/.mvn/wrapper/maven-wrapper.properties
-
100黄永兴学习笔记/StudySpringAI/SpringAIEmbedding/pom.xml
-
13黄永兴学习笔记/StudySpringAI/SpringAIEmbedding/src/main/java/com/example/springaiembedding/SpringAiEmbeddingApplication.java
-
54黄永兴学习笔记/StudySpringAI/SpringAIEmbedding/src/main/java/com/example/springaiembedding/controller/EmbeddingController.java
-
22黄永兴学习笔记/StudySpringAI/SpringAIEmbedding/src/main/java/com/example/springaiembedding/controller/RagController.java
-
87黄永兴学习笔记/StudySpringAI/SpringAIEmbedding/src/main/java/com/example/springaiembedding/service/RagService.java
-
93黄永兴学习笔记/StudySpringAI/SpringAIEmbedding/src/main/java/com/example/springaiembedding/service/TextSimilarityService.java
-
28黄永兴学习笔记/StudySpringAI/SpringAIEmbedding/src/main/java/com/example/springaiembedding/tool/DocumentLoader.java
-
21黄永兴学习笔记/StudySpringAI/SpringAIEmbedding/src/main/resources/application.properties
-
8黄永兴学习笔记/StudySpringAI/SpringAIEmbedding/src/main/resources/knowledge.txt
-
13黄永兴学习笔记/StudySpringAI/SpringAIEmbedding/src/test/java/com/example/springaiembedding/SpringAiEmbeddingApplicationTests.java
-
33黄永兴学习笔记/StudySpringAI/SpringAIQuickStart/.gitignore
-
3黄永兴学习笔记/StudySpringAI/SpringAIQuickStart/.mvn/wrapper/maven-wrapper.properties
-
104黄永兴学习笔记/StudySpringAI/SpringAIQuickStart/pom.xml
-
13黄永兴学习笔记/StudySpringAI/SpringAIQuickStart/src/main/java/com/example/springaiquickstart/SpringAiQuickStartApplication.java
-
127黄永兴学习笔记/StudySpringAI/SpringAIQuickStart/src/main/java/com/example/springaiquickstart/controller/ChatController.java
-
13黄永兴学习笔记/StudySpringAI/SpringAIQuickStart/src/main/resources/application.properties
-
224黄永兴学习笔记/StudySpringAI/SpringAIQuickStart/src/main/resources/static/index.html
-
13黄永兴学习笔记/StudySpringAI/SpringAIQuickStart/src/test/java/com/example/springaiquickstart/SpringAiQuickStartApplicationTests.java
-
8黄永兴学习笔记/StudySpringAI/StudySpringAI.iml
@ -0,0 +1,10 @@ |
|||
# 默认忽略的文件 |
|||
/shelf/ |
|||
/workspace.xml |
|||
# 已忽略包含查询文件的默认文件夹 |
|||
/queries/ |
|||
# Datasource local storage ignored files |
|||
/dataSources/ |
|||
/dataSources.local.xml |
|||
# 基于编辑器的 HTTP 客户端请求 |
|||
/httpRequests/ |
|||
@ -0,0 +1,7 @@ |
|||
<?xml version="1.0" encoding="UTF-8"?> |
|||
<project version="4"> |
|||
<component name="com.codeverse.userSettings.MarscodeWorkspaceAppSettingsState"> |
|||
<option name="chatAppRouterInfo" value="builder/69c9f63d3b54644cf72884fa" /> |
|||
<option name="progress" value="1.0" /> |
|||
</component> |
|||
</project> |
|||
@ -0,0 +1,20 @@ |
|||
<?xml version="1.0" encoding="UTF-8"?> |
|||
<project version="4"> |
|||
<component name="CompilerConfiguration"> |
|||
<annotationProcessing> |
|||
<profile name="Maven default annotation processors profile" enabled="true"> |
|||
<sourceOutputDir name="target/generated-sources/annotations" /> |
|||
<sourceTestOutputDir name="target/generated-test-sources/test-annotations" /> |
|||
<outputRelativeToContentRoot value="true" /> |
|||
<module name="SpringAIEmbedding" /> |
|||
<module name="SpringAIQuickStart" /> |
|||
</profile> |
|||
</annotationProcessing> |
|||
</component> |
|||
<component name="JavacSettings"> |
|||
<option name="ADDITIONAL_OPTIONS_OVERRIDE"> |
|||
<module name="SpringAIEmbedding" options="-parameters" /> |
|||
<module name="SpringAIQuickStart" options="-parameters" /> |
|||
</option> |
|||
</component> |
|||
</project> |
|||
@ -0,0 +1,7 @@ |
|||
<?xml version="1.0" encoding="UTF-8"?> |
|||
<project version="4"> |
|||
<component name="Encoding"> |
|||
<file url="file://$PROJECT_DIR$/SpringAIEmbedding/src/main/java" charset="UTF-8" /> |
|||
<file url="file://$PROJECT_DIR$/SpringAIQuickStart/src/main/java" charset="UTF-8" /> |
|||
</component> |
|||
</project> |
|||
@ -0,0 +1,35 @@ |
|||
<?xml version="1.0" encoding="UTF-8"?> |
|||
<project version="4"> |
|||
<component name="RemoteRepositoriesConfiguration"> |
|||
<remote-repository> |
|||
<option name="id" value="central-portal-snapshots" /> |
|||
<option name="name" value="Central Portal Snapshots" /> |
|||
<option name="url" value="https://central.sonatype.com/repository/maven-snapshots/" /> |
|||
</remote-repository> |
|||
<remote-repository> |
|||
<option name="id" value="central" /> |
|||
<option name="name" value="Central Repository" /> |
|||
<option name="url" value="https://repo.maven.apache.org/maven2" /> |
|||
</remote-repository> |
|||
<remote-repository> |
|||
<option name="id" value="spring-milestones" /> |
|||
<option name="name" value="Spring Milestones" /> |
|||
<option name="url" value="https://repo.spring.io/milestone" /> |
|||
</remote-repository> |
|||
<remote-repository> |
|||
<option name="id" value="central" /> |
|||
<option name="name" value="Maven Central repository" /> |
|||
<option name="url" value="https://repo1.maven.org/maven2" /> |
|||
</remote-repository> |
|||
<remote-repository> |
|||
<option name="id" value="spring-snapshots" /> |
|||
<option name="name" value="Spring Snapshots" /> |
|||
<option name="url" value="https://repo.spring.io/snapshot" /> |
|||
</remote-repository> |
|||
<remote-repository> |
|||
<option name="id" value="jboss.community" /> |
|||
<option name="name" value="JBoss Community repository" /> |
|||
<option name="url" value="https://repository.jboss.org/nexus/content/repositories/public/" /> |
|||
</remote-repository> |
|||
</component> |
|||
</project> |
|||
@ -0,0 +1,15 @@ |
|||
<?xml version="1.0" encoding="UTF-8"?> |
|||
<project version="4"> |
|||
<component name="ExternalStorageConfigurationManager" enabled="true" /> |
|||
<component name="MavenProjectsManager"> |
|||
<option name="originalFiles"> |
|||
<list> |
|||
<option value="$PROJECT_DIR$/SpringAIQuickStart/pom.xml" /> |
|||
<option value="$PROJECT_DIR$/SpringAIEmbedding/pom.xml" /> |
|||
</list> |
|||
</option> |
|||
</component> |
|||
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" project-jdk-name="17" project-jdk-type="JavaSDK"> |
|||
<output url="file://$PROJECT_DIR$/out" /> |
|||
</component> |
|||
</project> |
|||
@ -0,0 +1,8 @@ |
|||
<?xml version="1.0" encoding="UTF-8"?> |
|||
<project version="4"> |
|||
<component name="ProjectModuleManager"> |
|||
<modules> |
|||
<module fileurl="file://$PROJECT_DIR$/StudySpringAI.iml" filepath="$PROJECT_DIR$/StudySpringAI.iml" /> |
|||
</modules> |
|||
</component> |
|||
</project> |
|||
@ -0,0 +1,6 @@ |
|||
<?xml version="1.0" encoding="UTF-8"?> |
|||
<project version="4"> |
|||
<component name="VcsDirectoryMappings"> |
|||
<mapping directory="$PROJECT_DIR$" vcs="Git" /> |
|||
</component> |
|||
</project> |
|||
@ -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/ |
|||
@ -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 |
|||
@ -0,0 +1,100 @@ |
|||
<?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>org.springframework.boot</groupId> |
|||
<artifactId>spring-boot-starter-parent</artifactId> |
|||
<version>3.5.13</version> |
|||
<relativePath/> <!-- lookup parent from repository --> |
|||
</parent> |
|||
<groupId>com.example</groupId> |
|||
<artifactId>SpringAIEmbedding</artifactId> |
|||
<version>0.0.1-SNAPSHOT</version> |
|||
<name>SpringAIEmbedding</name> |
|||
<description>SpringAIEmbedding</description> |
|||
<url/> |
|||
<licenses> |
|||
<license/> |
|||
</licenses> |
|||
<developers> |
|||
<developer/> |
|||
</developers> |
|||
<scm> |
|||
<connection/> |
|||
<developerConnection/> |
|||
<tag/> |
|||
<url/> |
|||
</scm> |
|||
<properties> |
|||
<java.version>17</java.version> |
|||
<spring-ai.version>1.1.3</spring-ai.version> |
|||
</properties> |
|||
|
|||
<dependencyManagement> |
|||
<dependencies> |
|||
<dependency> |
|||
<groupId>org.springframework.ai</groupId> |
|||
<artifactId>spring-ai-bom</artifactId> |
|||
<version>1.0.0-SNAPSHOT</version> |
|||
<type>pom</type> |
|||
<scope>import</scope> |
|||
</dependency> |
|||
</dependencies> |
|||
</dependencyManagement> |
|||
|
|||
<dependencies> |
|||
<dependency> |
|||
<groupId>org.springframework.boot</groupId> |
|||
<artifactId>spring-boot-starter-web</artifactId> |
|||
</dependency> |
|||
<!-- Spring AI Zhipuai 模型依赖 --> |
|||
<dependency> |
|||
<groupId>org.springframework.ai</groupId> |
|||
<artifactId>spring-ai-starter-model-zhipuai</artifactId> |
|||
</dependency> |
|||
|
|||
<!-- Spring AI Chat 客户端依赖 中包括TokenTextSplitter、TextReader、Document 等工具类 --> |
|||
<dependency> |
|||
<groupId>org.springframework.ai</groupId> |
|||
<artifactId>spring-ai-client-chat</artifactId> |
|||
<version>1.0.0</version> |
|||
</dependency> |
|||
</dependencies> |
|||
|
|||
|
|||
<!-- 声明仓库,用于获取 Spring AI 以及相关预发布版本 --> |
|||
<repositories> |
|||
<!-- Spring Milestones 仓库 --> |
|||
<repository> |
|||
<id>spring-milestones</id> |
|||
<name>Spring Milestones</name> |
|||
<url>https://repo.spring.io/milestone</url> |
|||
<snapshots> |
|||
<enabled>false</enabled> |
|||
</snapshots> |
|||
</repository> |
|||
<!-- Spring Snapshots 仓库 --> |
|||
<repository> |
|||
<id>spring-snapshots</id> |
|||
<name>Spring Snapshots</name> |
|||
<url>https://repo.spring.io/snapshot</url> |
|||
<releases> |
|||
<enabled>false</enabled> |
|||
</releases> |
|||
<snapshots> |
|||
<enabled>true</enabled> |
|||
</snapshots> |
|||
</repository> |
|||
</repositories> |
|||
|
|||
<build> |
|||
<plugins> |
|||
<plugin> |
|||
<groupId>org.springframework.boot</groupId> |
|||
<artifactId>spring-boot-maven-plugin</artifactId> |
|||
</plugin> |
|||
</plugins> |
|||
</build> |
|||
|
|||
</project> |
|||
@ -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); |
|||
} |
|||
|
|||
} |
|||
@ -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<String,Object> 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<String, Double> findSimilarText(@RequestParam String message) { |
|||
// 查询Top3相似文本 |
|||
return textSimilarityService.findSimilarTexts(message, 3); |
|||
} |
|||
|
|||
|
|||
} |
|||
@ -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); |
|||
} |
|||
} |
|||
@ -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<String, float[]> knowledgeVectors = new HashMap<>(); |
|||
|
|||
// 项目启动时自动加载并向量化知识库 |
|||
@PostConstruct |
|||
public void initKnowledgeBase() throws IOException { |
|||
// 1. 加载并拆分文档 |
|||
List<String> 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<String> retrieveSimilarChunks(String query, int topN) { |
|||
// 1. 用户问题向量化 |
|||
float[] queryVector = embeddingModel.embed(query); |
|||
// 2. 计算与所有知识库片段的相似度 |
|||
Map<String, Double> similarityMap = new HashMap<>(); |
|||
//Entry是什么?Map.Entry<String, float[]> entry 是一个 Map.Entry 对象,用于遍历 Map 中的键值对。 |
|||
for (Map.Entry<String, float[]> 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<String> 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)); |
|||
} |
|||
} |
|||
} |
|||
@ -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<String> TEXT_LIBRARY = Arrays.asList( |
|||
"我爱Java编程", |
|||
"SpringBoot是最流行的后端框架", |
|||
"人工智能改变世界", |
|||
"大模型应用开发", |
|||
"我喜欢学习编程", |
|||
"向量数据库用于存储Embedding", |
|||
"SpringAI简化大模型开发" |
|||
); |
|||
|
|||
/** |
|||
* 查找最相似的文本 |
|||
* @param queryText 查询文本 |
|||
* @param topN 返回前N条结果 |
|||
* @return 相似文本+相似度 |
|||
*/ |
|||
|
|||
public Map<String, Double> 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<String, float[]> textVectorMap = new HashMap<>(); |
|||
for (String text : TEXT_LIBRARY) { |
|||
float[] textVector = embeddingModel.embed(text); |
|||
//put是将文本和向量存储到Map中 |
|||
textVectorMap.put(text, textVector); |
|||
} |
|||
|
|||
// 3. 计算相似度并排序 |
|||
Map<String, Double> similarityMap = new HashMap<>(); |
|||
for (Map.Entry<String, float[]> 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)); |
|||
} |
|||
} |
|||
} |
|||
@ -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<String> 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<String> chunks = new ArrayList<>(); |
|||
for (String chunk : content.split("---")) { |
|||
String trimmed = chunk.trim(); |
|||
if (!trimmed.isEmpty()) chunks.add(trimmed); |
|||
} |
|||
return chunks; |
|||
} |
|||
} |
|||
@ -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 |
|||
|
|||
|
|||
|
|||
|
|||
|
|||
@ -0,0 +1,8 @@ |
|||
--- |
|||
Spring AI 是一个用于简化大模型应用开发的框架,支持智谱AI、OpenAI 等多种大模型厂商。 |
|||
--- |
|||
智谱AI Embedding 模型可将文本转换为向量,用于语义检索、相似匹配等场景。 |
|||
--- |
|||
RAG(检索增强生成)通过检索本地知识库内容,辅助大模型生成更准确的回答,避免幻觉。 |
|||
--- |
|||
Spring AI 提供 EmbeddingModel 和 ChatModel 接口,让开发者快速接入大模型能力。 |
|||
@ -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() { |
|||
} |
|||
|
|||
} |
|||
@ -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/ |
|||
@ -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 |
|||
@ -0,0 +1,104 @@ |
|||
<?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>org.springframework.boot</groupId> |
|||
<artifactId>spring-boot-starter-parent</artifactId> |
|||
<version>3.5.13</version> |
|||
<relativePath/> <!-- lookup parent from repository --> |
|||
</parent> |
|||
<groupId>com.example</groupId> |
|||
<artifactId>SpringAIQuickStart</artifactId> |
|||
<version>0.0.1-SNAPSHOT</version> |
|||
<name>SpringAIQuickStart</name> |
|||
<description>SpringAIQuickStart</description> |
|||
<url/> |
|||
<licenses> |
|||
<license/> |
|||
</licenses> |
|||
<developers> |
|||
<developer/> |
|||
</developers> |
|||
<scm> |
|||
<connection/> |
|||
<developerConnection/> |
|||
<tag/> |
|||
<url/> |
|||
</scm> |
|||
<properties> |
|||
<java.version>17</java.version> |
|||
</properties> |
|||
|
|||
<dependencyManagement> |
|||
<dependencies> |
|||
<dependency> |
|||
<groupId>org.springframework.ai</groupId> |
|||
<artifactId>spring-ai-bom</artifactId> |
|||
<version>1.0.0-SNAPSHOT</version> |
|||
<type>pom</type> |
|||
<scope>import</scope> |
|||
</dependency> |
|||
</dependencies> |
|||
</dependencyManagement> |
|||
|
|||
<dependencies> |
|||
<dependency> |
|||
<groupId>org.springframework.boot</groupId> |
|||
<artifactId>spring-boot-starter</artifactId> |
|||
</dependency> |
|||
|
|||
<dependency> |
|||
<groupId>org.springframework.boot</groupId> |
|||
<artifactId>spring-boot-starter-web</artifactId> |
|||
</dependency> |
|||
|
|||
<!-- Spring AI Deepseek 模型依赖 --> |
|||
<dependency> |
|||
<groupId>org.springframework.ai</groupId> |
|||
<artifactId>spring-ai-starter-model-deepseek</artifactId> |
|||
</dependency> |
|||
|
|||
|
|||
<dependency> |
|||
<groupId>org.springframework.boot</groupId> |
|||
<artifactId>spring-boot-starter-test</artifactId> |
|||
<scope>test</scope> |
|||
</dependency> |
|||
</dependencies> |
|||
|
|||
<!-- 声明仓库,用于获取 Spring AI 以及相关预发布版本 --> |
|||
<repositories> |
|||
<!-- Spring Milestones 仓库 --> |
|||
<repository> |
|||
<id>spring-milestones</id> |
|||
<name>Spring Milestones</name> |
|||
<url>https://repo.spring.io/milestone</url> |
|||
<snapshots> |
|||
<enabled>false</enabled> |
|||
</snapshots> |
|||
</repository> |
|||
<!-- Spring Snapshots 仓库 --> |
|||
<repository> |
|||
<id>spring-snapshots</id> |
|||
<name>Spring Snapshots</name> |
|||
<url>https://repo.spring.io/snapshot</url> |
|||
<releases> |
|||
<enabled>false</enabled> |
|||
</releases> |
|||
<snapshots> |
|||
<enabled>true</enabled> |
|||
</snapshots> |
|||
</repository> |
|||
</repositories> |
|||
|
|||
<build> |
|||
<plugins> |
|||
<plugin> |
|||
<groupId>org.springframework.boot</groupId> |
|||
<artifactId>spring-boot-maven-plugin</artifactId> |
|||
</plugin> |
|||
</plugins> |
|||
</build> |
|||
|
|||
</project> |
|||
@ -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); |
|||
} |
|||
|
|||
} |
|||
@ -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<ChatResponse> generateStream1(@RequestParam(value = "message",defaultValue = "你好,你是谁?") String message) { |
|||
System.out.println("message: " + message); |
|||
|
|||
// 与模型对话,流式返回内容 |
|||
Prompt prompt = new Prompt(message); |
|||
Flux<ChatResponse> stream = chatModel.stream(prompt); |
|||
|
|||
System.out.println("stream: " + stream); |
|||
|
|||
return stream; |
|||
} |
|||
|
|||
// 与模型对话,流式返回内容,转换为字符串流 |
|||
// 解决中文乱码问题 |
|||
// 用lambda表达式简化 |
|||
@GetMapping("/generateStream2") |
|||
public Flux<String> 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<ChatResponse> stream = chatModel.stream(prompt); |
|||
|
|||
// 转换为字符串流,用lambda表达式简化 |
|||
Flux<String> result = stream.map(ChatResponse -> |
|||
ChatResponse.getResult().getOutput().getText() |
|||
); |
|||
// 转换为字符串流,用方法引用简化 |
|||
// Flux<String> result = stream.map(ChatResponse::getResult) |
|||
// .map(Generation::getOutput) |
|||
// .map(AssistantMessage::getText); |
|||
|
|||
|
|||
System.out.println("result: " + result); |
|||
|
|||
return result; |
|||
} |
|||
|
|||
//运行时设置模型参数 |
|||
@GetMapping("/runtimeOptions") |
|||
public Flux<String> 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<Message> messages = Arrays.asList(systemMessage, userMessage); |
|||
|
|||
// 封装成 Prompt,还可以设置模型参数 |
|||
Prompt prompt = new Prompt(messages, |
|||
ChatOptions.builder() |
|||
.temperature(temp) |
|||
.maxTokens(1000) |
|||
.build() |
|||
); |
|||
|
|||
// 流式调用 |
|||
Flux<ChatResponse> stream = chatModel.stream(prompt); |
|||
// 转换为字符串流,用lambda表达式简化 |
|||
Flux<String> result = stream.map(ChatResponse -> |
|||
ChatResponse.getResult().getOutput().getText() |
|||
); |
|||
|
|||
System.out.println("result: " + result); |
|||
return result; |
|||
} |
|||
|
|||
|
|||
} |
|||
@ -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 |
|||
|
|||
|
|||
@ -0,0 +1,224 @@ |
|||
<!DOCTYPE html> |
|||
<html lang="zh-CN"> |
|||
<head> |
|||
<meta charset="UTF-8"> |
|||
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
|||
<title>Spring AI 聊天助手</title> |
|||
<script src="https://cdn.tailwindcss.com"></script> |
|||
<link href="https://cdn.jsdelivr.net/npm/font-awesome@4.7.0/css/font-awesome.min.css" rel="stylesheet"> |
|||
<script> |
|||
tailwind.config = { |
|||
theme: { |
|||
extend: { |
|||
colors: { |
|||
primary: '#3b82f6', |
|||
secondary: '#64748b', |
|||
neutral: '#f8fafc', |
|||
dark: '#1e293b' |
|||
}, |
|||
fontFamily: { |
|||
sans: ['Inter', 'system-ui', 'sans-serif'] |
|||
} |
|||
} |
|||
} |
|||
} |
|||
</script> |
|||
<style type="text/tailwindcss"> |
|||
@layer utilities { |
|||
.content-auto { |
|||
content-visibility: auto; |
|||
} |
|||
.chat-message { |
|||
@apply p-4 rounded-lg mb-4 max-w-[80%]; |
|||
} |
|||
.user-message { |
|||
@apply bg-primary text-white self-end; |
|||
} |
|||
.ai-message { |
|||
@apply bg-neutral border border-gray-200 text-dark self-start; |
|||
} |
|||
} |
|||
</style> |
|||
</head> |
|||
<body class="bg-gray-50 min-h-screen flex flex-col"> |
|||
<!-- 顶部导航栏 --> |
|||
<header class="bg-white shadow-sm"> |
|||
<div class="container mx-auto px-4 py-4 flex justify-between items-center"> |
|||
<div class="flex items-center space-x-2"> |
|||
<i class="fa fa-robot text-primary text-2xl"></i> |
|||
<h1 class="text-xl font-bold text-dark">Spring AI 聊天助手</h1> |
|||
</div> |
|||
<div class="text-sm text-secondary"> |
|||
<span id="status" class="flex items-center"> |
|||
<span class="w-2 h-2 bg-green-500 rounded-full mr-2"></span> |
|||
在线 |
|||
</span> |
|||
</div> |
|||
</div> |
|||
</header> |
|||
|
|||
<!-- 主内容区 --> |
|||
<main class="flex-1 container mx-auto px-4 py-8 max-w-4xl"> |
|||
<!-- 聊天区域 --> |
|||
<div class="bg-white rounded-xl shadow-md overflow-hidden"> |
|||
<!-- 聊天头部 --> |
|||
<div class="bg-primary text-white p-4"> |
|||
<h2 class="text-lg font-semibold">AI 助手</h2> |
|||
<p class="text-sm opacity-80">基于 DeepSeek 模型</p> |
|||
</div> |
|||
|
|||
<!-- 聊天消息区 --> |
|||
<div id="chat-messages" class="p-4 h-[500px] overflow-y-auto flex flex-col space-y-4"> |
|||
<!-- 欢迎消息 --> |
|||
<div class="chat-message ai-message"> |
|||
<div class="flex items-start"> |
|||
<div class="w-8 h-8 rounded-full bg-primary text-white flex items-center justify-center mr-3 flex-shrink-0"> |
|||
<i class="fa fa-robot"></i> |
|||
</div> |
|||
<div> |
|||
<p class="font-semibold mb-1">AI 助手</p> |
|||
<p>你好!我是基于 DeepSeek 模型的 AI 助手,有什么可以帮助你的吗?</p> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
<!-- 输入区域 --> |
|||
<div class="p-4 border-t"> |
|||
<form id="chat-form" class="flex space-x-2"> |
|||
<input |
|||
type="text" |
|||
id="message-input" |
|||
placeholder="输入消息..." |
|||
class="flex-1 border border-gray-300 rounded-lg px-4 py-2 focus:outline-none focus:ring-2 focus:ring-primary focus:border-transparent" |
|||
> |
|||
<button |
|||
type="submit" |
|||
id="send-button" |
|||
class="bg-primary text-white rounded-lg px-6 py-2 hover:bg-primary/90 transition-colors flex items-center space-x-2" |
|||
> |
|||
<span>发送</span> |
|||
<i class="fa fa-paper-plane"></i> |
|||
</button> |
|||
</form> |
|||
</div> |
|||
</div> |
|||
|
|||
<!-- 功能说明 --> |
|||
<div class="mt-8 bg-white rounded-xl shadow-md p-6"> |
|||
<h3 class="text-lg font-semibold mb-4">功能说明</h3> |
|||
<ul class="space-y-2 text-secondary"> |
|||
<li class="flex items-center"> |
|||
<i class="fa fa-check-circle text-green-500 mr-2"></i> |
|||
支持自然语言对话 |
|||
</li> |
|||
<li class="flex items-center"> |
|||
<i class="fa fa-check-circle text-green-500 mr-2"></i> |
|||
基于 DeepSeek 大语言模型 |
|||
</li> |
|||
<li class="flex items-center"> |
|||
<i class="fa fa-check-circle text-green-500 mr-2"></i> |
|||
实时响应生成 |
|||
</li> |
|||
<li class="flex items-center"> |
|||
<i class="fa fa-check-circle text-green-500 mr-2"></i> |
|||
简洁美观的用户界面 |
|||
</li> |
|||
</ul> |
|||
</div> |
|||
</main> |
|||
|
|||
<!-- 页脚 --> |
|||
<footer class="bg-white shadow-sm mt-8"> |
|||
<div class="container mx-auto px-4 py-4 text-center text-secondary text-sm"> |
|||
<p>© 2026 Spring AI 聊天助手 | 基于 DeepSeek 模型</p> |
|||
</div> |
|||
</footer> |
|||
|
|||
<script> |
|||
document.addEventListener('DOMContentLoaded', function() { |
|||
const chatForm = document.getElementById('chat-form'); |
|||
const messageInput = document.getElementById('message-input'); |
|||
const chatMessages = document.getElementById('chat-messages'); |
|||
const sendButton = document.getElementById('send-button'); |
|||
const status = document.getElementById('status'); |
|||
|
|||
// 发送消息 |
|||
chatForm.addEventListener('submit', function(e) { |
|||
e.preventDefault(); |
|||
const message = messageInput.value.trim(); |
|||
if (message) { |
|||
// 添加用户消息到聊天区 |
|||
addMessage('user', message); |
|||
messageInput.value = ''; |
|||
|
|||
// 禁用发送按钮 |
|||
sendButton.disabled = true; |
|||
sendButton.classList.add('opacity-50'); |
|||
status.innerHTML = '<span class="w-2 h-2 bg-yellow-500 rounded-full mr-2"></span>处理中...'; |
|||
|
|||
// 调用后端 API |
|||
fetch(`/ai/generate?message=${encodeURIComponent(message)}`) |
|||
.then(response => response.text()) |
|||
.then(data => { |
|||
// 添加 AI 响应到聊天区 |
|||
addMessage('ai', data); |
|||
}) |
|||
.catch(error => { |
|||
console.error('Error:', error); |
|||
addMessage('ai', '抱歉,处理请求时出错,请稍后再试。'); |
|||
}) |
|||
.finally(() => { |
|||
// 恢复发送按钮状态 |
|||
sendButton.disabled = false; |
|||
sendButton.classList.remove('opacity-50'); |
|||
status.innerHTML = '<span class="w-2 h-2 bg-green-500 rounded-full mr-2"></span>在线'; |
|||
}); |
|||
} |
|||
}); |
|||
|
|||
// 添加消息到聊天区 |
|||
function addMessage(type, content) { |
|||
const messageDiv = document.createElement('div'); |
|||
messageDiv.className = `chat-message ${type === 'user' ? 'user-message' : 'ai-message'} self-${type === 'user' ? 'end' : 'start'}`; |
|||
|
|||
if (type === 'user') { |
|||
messageDiv.innerHTML = ` |
|||
<div class="flex items-start justify-end"> |
|||
<div> |
|||
<p class="font-semibold mb-1 text-right">你</p> |
|||
<p>${content}</p> |
|||
</div> |
|||
<div class="w-8 h-8 rounded-full bg-gray-300 text-dark flex items-center justify-center ml-3 flex-shrink-0"> |
|||
<i class="fa fa-user"></i> |
|||
</div> |
|||
</div> |
|||
`; |
|||
} else { |
|||
messageDiv.innerHTML = ` |
|||
<div class="flex items-start"> |
|||
<div class="w-8 h-8 rounded-full bg-primary text-white flex items-center justify-center mr-3 flex-shrink-0"> |
|||
<i class="fa fa-robot"></i> |
|||
</div> |
|||
<div> |
|||
<p class="font-semibold mb-1">AI 助手</p> |
|||
<p>${content}</p> |
|||
</div> |
|||
</div> |
|||
`; |
|||
} |
|||
|
|||
chatMessages.appendChild(messageDiv); |
|||
chatMessages.scrollTop = chatMessages.scrollHeight; |
|||
} |
|||
|
|||
// 回车键发送消息 |
|||
messageInput.addEventListener('keypress', function(e) { |
|||
if (e.key === 'Enter') { |
|||
chatForm.dispatchEvent(new Event('submit')); |
|||
} |
|||
}); |
|||
}); |
|||
</script> |
|||
</body> |
|||
</html> |
|||
@ -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() { |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,8 @@ |
|||
<?xml version="1.0" encoding="UTF-8"?> |
|||
<module type="GENERAL_MODULE" version="4"> |
|||
<component name="NewModuleRootManager" inherit-compiler-output="true"> |
|||
<exclude-output /> |
|||
<content url="file://$MODULE_DIR$" /> |
|||
<orderEntry type="sourceFolder" forTests="false" /> |
|||
</component> |
|||
</module> |
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue