Browse Source

feat: 提交

master
yuanjs@qutke.com 2 months ago
commit
19f147a6e0
  1. 33
      .gitignore
  2. 152
      pom.xml
  3. 13
      src/main/java/cn/ai/springaigu/SpringAiGuApplication.java
  4. 15
      src/main/java/cn/ai/springaigu/asistant/Assistant.java
  5. 17
      src/main/java/cn/ai/springaigu/asistant/MemoryChatAssistant.java
  6. 44
      src/main/java/cn/ai/springaigu/asistant/SeparateChatAssistant.java
  7. 20
      src/main/java/cn/ai/springaigu/asistant/XiaozhiAgent.java
  8. 36
      src/main/java/cn/ai/springaigu/config/EmbeddingStoreConfig.java
  9. 16
      src/main/java/cn/ai/springaigu/config/MemoryChatAssistantConfig.java
  10. 24
      src/main/java/cn/ai/springaigu/config/SeparateChatAssistantConfig.java
  11. 74
      src/main/java/cn/ai/springaigu/config/XiaozhiAgentConfig.java
  12. 49
      src/main/java/cn/ai/springaigu/controller/XiaozhiController.java
  13. 21
      src/main/java/cn/ai/springaigu/domain/Appointment.java
  14. 9
      src/main/java/cn/ai/springaigu/domain/ChatForm.java
  15. 21
      src/main/java/cn/ai/springaigu/domain/ChatMessages.java
  16. 9
      src/main/java/cn/ai/springaigu/mapper/AppointmentMapper.java
  17. 8
      src/main/java/cn/ai/springaigu/service/AppointmentService.java
  18. 27
      src/main/java/cn/ai/springaigu/service/impl/AppointmentServiceImpl.java
  19. 46
      src/main/java/cn/ai/springaigu/store/MongoChatMemoryStore.java
  20. 66
      src/main/java/cn/ai/springaigu/tools/AppointmentTools.java
  21. 36
      src/main/java/cn/ai/springaigu/tools/CalculatorTools.java
  22. 46
      src/main/resources/application.yml
  23. 5
      src/main/resources/mapper/AppointmentMapper.xml
  24. 3
      src/main/resources/my-prompt-template.txt
  25. 3
      src/main/resources/my-prompt-template3.txt
  26. 18
      src/main/resources/zhaozhi-prompt-template.txt
  27. 42
      src/test/java/cn/ai/springaigu/AppointmentServiceTest.java
  28. 94
      src/test/java/cn/ai/springaigu/EmbeddingTest.java
  29. 77
      src/test/java/cn/ai/springaigu/MongoCrudTest.java
  30. 137
      src/test/java/cn/ai/springaigu/RAGTest.java
  31. 196
      src/test/java/cn/ai/springaigu/TestGPT.java
  32. 19
      src/test/java/cn/ai/springaigu/ToolsTest.java

33
.gitignore

@ -0,0 +1,33 @@
ReadMe.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/

152
pom.xml

@ -0,0 +1,152 @@
<?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.2.6</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>cn.ai</groupId>
<artifactId>Spring-Ai-Gu</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>Spring-Ai-Gu</name>
<description>Spring-Ai-Gu</description>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<spring-boot.version>3.2.6</spring-boot.version>
<knife4j.version>4.3.0</knife4j.version>
<langchain4j.version>1.0.0-beta3</langchain4j.version>
<mybatis-plus.version>3.5.11</mybatis-plus.version>
</properties>
<dependencies>
<!-- web应用程序核心依赖 -->
<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>
<!-- 前后端分离中的后端接口测试工具 -->
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId>
<version>${knife4j.version}</version>
</dependency>
<!-- 基于open-ai的langchain4j接口:ChatGPT、deepseek都是open-ai标准下的大模型 -->
<!-- <dependency>-->
<!-- <groupId>dev.langchain4j</groupId>-->
<!-- <artifactId>langchain4j-open-ai</artifactId>-->
<!-- </dependency>-->
<!-- spring整合openai-->
<!-- <dependency>-->
<!-- <groupId>dev.langchain4j</groupId>-->
<!-- <artifactId>langchain4j-open-ai-spring-boot-starter</artifactId>-->
<!-- </dependency>-->
<!-- 接入阿里云百炼平台 -->
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-community-dashscope-spring-boot-starter</artifactId>
</dependency>
<!--langchain4j高级功能-->
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-spring-boot-starter</artifactId>
</dependency>
<!-- Spring Boot Starter Data mongodb -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
<!-- Mysql Connector -->
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
</dependency>
<!--mybatis-plus 持久层-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-spring-boot3-starter</artifactId>
<version>${mybatis-plus.version}</version>
</dependency>
<!--解析pdf文档-->
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-document-parser-apache-pdfbox</artifactId>
</dependency>
<!--简单的rag实现-->
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-easy-rag</artifactId>
</dependency>
<!--向量存储 -->
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-pinecone</artifactId>
</dependency>
<!--流式输出-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-reactor</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<!--引入SpringBoot依赖管理清单-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!--引入langchain4j依赖管理清单-->
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-bom</artifactId>
<version>${langchain4j.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!--引入百炼依赖管理清单-->
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-community-bom</artifactId>
<version>${langchain4j.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

13
src/main/java/cn/ai/springaigu/SpringAiGuApplication.java

@ -0,0 +1,13 @@
package cn.ai.springaigu;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class SpringAiGuApplication {
public static void main(String[] args) {
SpringApplication.run(SpringAiGuApplication.class, args);
}
}

15
src/main/java/cn/ai/springaigu/asistant/Assistant.java

@ -0,0 +1,15 @@
package cn.ai.springaigu.asistant;
import dev.langchain4j.service.spring.AiService;
import static dev.langchain4j.service.spring.AiServiceWiringMode.EXPLICIT;
/**
* 创建接
*/
//因为我们在配置文件中同时配置了多个大语言模型,所以需要在这里明确指定(EXPLICIT)模型的beanName(qwenChatModel)
@AiService(wiringMode = EXPLICIT, chatModel = "qwenChatModel")
public interface Assistant {
String chat(String userMessage);
}

17
src/main/java/cn/ai/springaigu/asistant/MemoryChatAssistant.java

@ -0,0 +1,17 @@
package cn.ai.springaigu.asistant;
import dev.langchain4j.service.UserMessage;
import dev.langchain4j.service.spring.AiService;
import static dev.langchain4j.service.spring.AiServiceWiringMode.EXPLICIT;
// 使用AIService实现聊天记忆
@AiService(
wiringMode = EXPLICIT,
chatModel = "qwenChatModel",
chatMemory = "chatMemory"
)
public interface MemoryChatAssistant {
@UserMessage("你是我的好朋友,请用上海话回答问题,并且添加一些表情符号。 {{it}}") //{{it}}表示这里唯一的参数的占位符
String chat(String message);
}

44
src/main/java/cn/ai/springaigu/asistant/SeparateChatAssistant.java

@ -0,0 +1,44 @@
package cn.ai.springaigu.asistant;
import dev.langchain4j.service.MemoryId;
import dev.langchain4j.service.SystemMessage;
import dev.langchain4j.service.UserMessage;
import dev.langchain4j.service.V;
import dev.langchain4j.service.spring.AiService;
import static dev.langchain4j.service.spring.AiServiceWiringMode.EXPLICIT;
// 创建记忆隔离对话智能体
@AiService(
wiringMode = EXPLICIT,
chatModel = "qwenChatModel",
chatMemory = "chatMemory",
chatMemoryProvider = "chatMemoryProvider",
tools = "calculatorTools" //配置tools
)
public interface SeparateChatAssistant {
/**
* 分离聊天记录
*
* @param memoryId 聊天id
* @param userMessage 用户消息
*/
// @SystemMessage("你是我的好朋友,请用东北话回答问题。")//系统消息提示词
//@SystemMessage("你是我的好朋友,请用东北话回答问题。今天是{{current_date}}")//系统消息提示词,在提示词中添加当前日期的占位符{{current_date}}
@SystemMessage(fromResource = "my-prompt-template.txt")
String chat(@MemoryId int memoryId, @UserMessage String userMessage);
@UserMessage("你是我的好朋友,请用粤语回答问题。{{message}}")
String chat2(@MemoryId int memoryId, @V("message") String userMessage);
@SystemMessage(fromResource = "my-prompt-template3.txt")
String chat3(
@MemoryId int memoryId,
@UserMessage String userMessage,
@V("username") String username,
@V("age") int age
);
}

20
src/main/java/cn/ai/springaigu/asistant/XiaozhiAgent.java

@ -0,0 +1,20 @@
package cn.ai.springaigu.asistant;
import dev.langchain4j.service.*;
import dev.langchain4j.service.spring.AiService;
import reactor.core.publisher.Flux;
import static dev.langchain4j.service.spring.AiServiceWiringMode.EXPLICIT;
@AiService(
wiringMode = EXPLICIT,
//chatModel = "qwenChatModel",
streamingChatModel = "qwenStreamingChatModel",
chatMemoryProvider = "chatMemoryProviderXiaozhi",
tools = "appointmentTools", //tools配置
contentRetriever = "contentRetrieverXiaozhiPincone") //配置向量存储
public interface XiaozhiAgent {
@SystemMessage(fromResource = "zhaozhi-prompt-template.txt")
Flux<String> chat(@MemoryId Long memoryId, @UserMessage String userMessage);
}

36
src/main/java/cn/ai/springaigu/config/EmbeddingStoreConfig.java

@ -0,0 +1,36 @@
package cn.ai.springaigu.config;
import dev.langchain4j.data.segment.TextSegment;
import dev.langchain4j.model.embedding.EmbeddingModel;
import dev.langchain4j.store.embedding.EmbeddingStore;
import dev.langchain4j.store.embedding.pinecone.PineconeEmbeddingStore;
import dev.langchain4j.store.embedding.pinecone.PineconeServerlessIndexConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class EmbeddingStoreConfig {
@Value("${pinecone.api-key}")
private String apiKey;
@Autowired
private EmbeddingModel embeddingModel;
@Bean
public EmbeddingStore<TextSegment> embeddingStore() {
//创建向量存储
EmbeddingStore<TextSegment> embeddingStore = PineconeEmbeddingStore.builder()
.apiKey(apiKey)
.index("ai-study-index")//如果指定的索引不存在,将创建一个新的索引
.nameSpace("ai-study-namespace") //如果指定的名称空间不存在,将创建一个新的名称 空间
.createIndex(PineconeServerlessIndexConfig.builder()
.cloud("AWS") //指定索引部署在 AWS 云服务上。
.region("us-east-1") //指定索引所在的 AWS 区域为 us-east-1。
.dimension(embeddingModel.dimension()) //指定索引的向量维度,该维度与 embeddedModel 生成的向量维度相同。
.build())
.build();
return embeddingStore;
}
}

16
src/main/java/cn/ai/springaigu/config/MemoryChatAssistantConfig.java

@ -0,0 +1,16 @@
package cn.ai.springaigu.config;
import dev.langchain4j.memory.ChatMemory;
import dev.langchain4j.memory.chat.MessageWindowChatMemory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
// 配置ChatMemory
@Configuration
public class MemoryChatAssistantConfig {
@Bean
ChatMemory chatMemory() {
//设置聊天记忆记录的message数量
return MessageWindowChatMemory.withMaxMessages(10);
}
}

24
src/main/java/cn/ai/springaigu/config/SeparateChatAssistantConfig.java

@ -0,0 +1,24 @@
package cn.ai.springaigu.config;
import cn.ai.springaigu.store.MongoChatMemoryStore;
import dev.langchain4j.memory.chat.ChatMemoryProvider;
import dev.langchain4j.memory.chat.MessageWindowChatMemory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class SeparateChatAssistantConfig {
@Autowired
private MongoChatMemoryStore mongoChatMemoryStore;
@Bean
ChatMemoryProvider chatMemoryProvider() {
return memoryId -> MessageWindowChatMemory.builder()
.id(memoryId)
.maxMessages(10)
.chatMemoryStore(mongoChatMemoryStore)//配置持久化对象
.build();
}
}

74
src/main/java/cn/ai/springaigu/config/XiaozhiAgentConfig.java

@ -0,0 +1,74 @@
package cn.ai.springaigu.config;
import cn.ai.springaigu.store.MongoChatMemoryStore;
import dev.langchain4j.data.document.Document;
import dev.langchain4j.data.document.loader.FileSystemDocumentLoader;
import dev.langchain4j.data.segment.TextSegment;
import dev.langchain4j.memory.chat.ChatMemoryProvider;
import dev.langchain4j.memory.chat.MessageWindowChatMemory;
import dev.langchain4j.model.embedding.EmbeddingModel;
import dev.langchain4j.rag.content.retriever.ContentRetriever;
import dev.langchain4j.rag.content.retriever.EmbeddingStoreContentRetriever;
import dev.langchain4j.store.embedding.EmbeddingStore;
import dev.langchain4j.store.embedding.EmbeddingStoreIngestor;
import dev.langchain4j.store.embedding.inmemory.InMemoryEmbeddingStore;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.Arrays;
import java.util.List;
@Configuration
public class XiaozhiAgentConfig {
@Autowired
private MongoChatMemoryStore mongoChatMemoryStore;
@Bean
ChatMemoryProvider chatMemoryProviderXiaozhi() {
return memoryId -> MessageWindowChatMemory.builder()
.id(memoryId)
.maxMessages(20)
.chatMemoryStore(mongoChatMemoryStore)
.build();
}
@Bean
ContentRetriever contentRetrieverXiaozhi() {
//使用FileSystemDocumentLoader读取指定目录下的知识库文档
//并使用默认的文档解析器对文档进行解析
Document document1 = FileSystemDocumentLoader.loadDocument("D:/学习/课件/knowledge/医院信息.md");
Document document2 = FileSystemDocumentLoader.loadDocument("D:/学习/课件/knowledge/科室信息.md");
Document document3 = FileSystemDocumentLoader.loadDocument("D:/学习/课件/knowledge/神经内科.md");
List<Document> documents = Arrays.asList(document1, document2, document3);
//使用内存向量存储 生产不建议使用
InMemoryEmbeddingStore<TextSegment> embeddingStore = new InMemoryEmbeddingStore<>();
//使用默认的文档分割器
EmbeddingStoreIngestor.ingest(documents, embeddingStore);
//从嵌入存储(EmbeddingStore)里检索和查询内容相关的信息
return EmbeddingStoreContentRetriever.from(embeddingStore);
}
@Autowired
private EmbeddingStore embeddingStore;
@Autowired
private EmbeddingModel embeddingModel;
@Bean
ContentRetriever contentRetrieverXiaozhiPincone() {
// 创建一个 EmbeddingStoreContentRetriever 对象,用于从嵌入存储中检索内容
return EmbeddingStoreContentRetriever
.builder()
// 设置用于生成嵌入向量的嵌入模型
.embeddingModel(embeddingModel)
// 指定要使用的嵌入存储
.embeddingStore(embeddingStore)
// 设置最大检索结果数量,这里表示最多返回 1 条匹配结果
.maxResults(1)
// 设置最小得分阈值,只有得分大于等于 0.8 的结果才会被返回
.minScore(0.8)
// 构建最终的 EmbeddingStoreContentRetriever 实例
.build();
}
}

49
src/main/java/cn/ai/springaigu/controller/XiaozhiController.java

@ -0,0 +1,49 @@
package cn.ai.springaigu.controller;
import cn.ai.springaigu.asistant.XiaozhiAgent;
import cn.ai.springaigu.domain.ChatForm;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;
@Tag(name = "硅谷小智")
@RestController
@RequestMapping("/xiaozhi")
public class XiaozhiController {
@Autowired
private XiaozhiAgent xiaozhiAgent;
// @Operation(summary = "对话")
// @PostMapping("/chat")
// public String chat(@RequestBody ChatForm chatForm) {
// return xiaozhiAgent.chat(chatForm.getMemoryId(), chatForm.getMessage());
// }
@Operation(summary = "对话")
@PostMapping(value = "/chat", produces = "text/stream;charset=utf-8")
public Flux<String> chat(@RequestBody ChatForm chatForm) {
return xiaozhiAgent.chat(chatForm.getMemoryId(), chatForm.getMessage());
}
}
// 测试:
//{
// "memoryId": 0,
// "message": "我想预约明天上午的神经内科"
//}
// {
// "memoryId": 1,
// "message": "帮我预约一下后天的皮肤科,信息是:翠花,523921199204234816,刘医生"
//}
//{
// "memoryId": 1,
// "message": "对不起,临时有事,帮我取消一下预约"
//}
// 我头疼应该怎么处理
// 姓名:姚翠花、230104190000000000、神经内科、明天下午,最好的医生。
// 医院的位置在哪,神经内科怎么找

21
src/main/java/cn/ai/springaigu/domain/Appointment.java

@ -0,0 +1,21 @@
package cn.ai.springaigu.domain;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Appointment {
@TableId(type = IdType.AUTO)
private Long id;
private String username;
private String idCard;
private String department;
private String date;
private String time;
private String doctorName;
}

9
src/main/java/cn/ai/springaigu/domain/ChatForm.java

@ -0,0 +1,9 @@
package cn.ai.springaigu.domain;
import lombok.Data;
@Data
public class ChatForm {
private Long memoryId;//对话id
private String message;//用户问题
}

21
src/main/java/cn/ai/springaigu/domain/ChatMessages.java

@ -0,0 +1,21 @@
package cn.ai.springaigu.domain;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.bson.types.ObjectId;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;
@Data
@AllArgsConstructor
@NoArgsConstructor
@Document("chat_messages")
public class ChatMessages {
//唯一标识,映射到 MongoDB 文档的 _id 字段
@Id
private ObjectId id;
private Long messageId;
private String content; //存储当前聊天记录列表的json字符串
}

9
src/main/java/cn/ai/springaigu/mapper/AppointmentMapper.java

@ -0,0 +1,9 @@
package cn.ai.springaigu.mapper;
import cn.ai.springaigu.domain.Appointment;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface AppointmentMapper extends BaseMapper<Appointment> {
}

8
src/main/java/cn/ai/springaigu/service/AppointmentService.java

@ -0,0 +1,8 @@
package cn.ai.springaigu.service;
import cn.ai.springaigu.domain.Appointment;
import com.baomidou.mybatisplus.extension.service.IService;
public interface AppointmentService extends IService<Appointment> {
Appointment getOne(Appointment appointment);
}

27
src/main/java/cn/ai/springaigu/service/impl/AppointmentServiceImpl.java

@ -0,0 +1,27 @@
package cn.ai.springaigu.service.impl;
import cn.ai.springaigu.domain.Appointment;
import cn.ai.springaigu.mapper.AppointmentMapper;
import cn.ai.springaigu.service.AppointmentService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
@Service
public class AppointmentServiceImpl extends ServiceImpl<AppointmentMapper, Appointment> implements AppointmentService {
/**
* 查询订单是否存在
*
* @param appointment
*/
@Override
public Appointment getOne(Appointment appointment) {
LambdaQueryWrapper<Appointment> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(Appointment::getUsername, appointment.getUsername());
queryWrapper.eq(Appointment::getIdCard, appointment.getIdCard());
queryWrapper.eq(Appointment::getDepartment, appointment.getDepartment());
queryWrapper.eq(Appointment::getDate, appointment.getDate());
queryWrapper.eq(Appointment::getTime, appointment.getTime());
return baseMapper.selectOne(queryWrapper);
}
}

46
src/main/java/cn/ai/springaigu/store/MongoChatMemoryStore.java

@ -0,0 +1,46 @@
package cn.ai.springaigu.store;
import cn.ai.springaigu.domain.ChatMessages;
import dev.langchain4j.data.message.ChatMessage;
import dev.langchain4j.data.message.ChatMessageDeserializer;
import dev.langchain4j.data.message.ChatMessageSerializer;
import dev.langchain4j.store.memory.chat.ChatMemoryStore;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.stereotype.Component;
import java.util.LinkedList;
import java.util.List;
@Component
public class MongoChatMemoryStore implements ChatMemoryStore {
@Autowired
private MongoTemplate mongoTemplate;
@Override
public List<ChatMessage> getMessages(Object memoryId) {
Criteria criteria = Criteria.where("memoryId").is(memoryId);
Query query = new Query(criteria);
ChatMessages chatMessages = mongoTemplate.findOne(query, ChatMessages.class);
if(chatMessages == null) return new LinkedList<>();
return ChatMessageDeserializer.messagesFromJson(chatMessages.getContent());
}
@Override
public void updateMessages(Object memoryId, List<ChatMessage> messages) {
Criteria criteria = Criteria.where("memoryId").is(memoryId);
Query query = new Query(criteria);
Update update = new Update();
update.set("content", ChatMessageSerializer.messagesToJson(messages));
//根据query条件能查询出文档,则修改文档;否则新增文档
mongoTemplate.upsert(query, update, ChatMessages.class);
}
@Override
public void deleteMessages(Object memoryId) {
Criteria criteria = Criteria.where("memoryId").is(memoryId);
Query query = new Query(criteria);
mongoTemplate.remove(query, ChatMessages.class);
}
}

66
src/main/java/cn/ai/springaigu/tools/AppointmentTools.java

@ -0,0 +1,66 @@
package cn.ai.springaigu.tools;
import cn.ai.springaigu.domain.Appointment;
import cn.ai.springaigu.service.AppointmentService;
import dev.langchain4j.agent.tool.P;
import dev.langchain4j.agent.tool.Tool;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class AppointmentTools {
@Autowired
private AppointmentService appointmentService;
// @Tool(name = "预约挂号", value = "根据参数,先执行工具方法queryDepartment查询是否可预约,并直接给用户回答是否可预约,并让用户确认所有预约信息,用户确认后再进行预约。")
@Tool(name="预约挂号", value = "根据参数,先执行工具方法queryDepartment查询是否可预约,并直接给用户回答是否可预约,并让用户确认所有预约信息,用户确认后再进行预约。如果用户没有提供具体的医生姓名,请从向量存储中找到一位医生。")
public String bookAppointment(Appointment appointment) {
//查找数据库中是否包含对应的预约记录
Appointment appointmentDB = appointmentService.getOne(appointment);
if (appointmentDB == null) {
appointment.setId(null);//防止大模型幻觉设置了id
if (appointmentService.save(appointment)) {
return "预约成功,并返回预约详情";
} else {
return "预约失败";
}
}
return "您在相同的科室和时间已有预约";
}
@Tool(name = "取消预约挂号", value = "根据参数,查询预约是否存在,如果存在则删除预约记录并返回取消预约成功,否则返回取消预约失败")
public String cancelAppointment(Appointment appointment) {
Appointment appointmentDB = appointmentService.getOne(appointment);
if (appointmentDB != null) {
//删除预约记录
if (appointmentService.removeById(appointmentDB.getId())) {
return "取消预约成功";
} else {
return "取消预约失败";
}
}
//取消失败
return "您没有预约记录,请核对预约科室和时间";
}
@Tool(name = "查询是否有号源", value = "根据科室名称,日期,时间和医生查询是否有号源,并返回给用户")
public boolean queryDepartment(
@P(value = "科室名称") String name,
@P(value = "日期") String date,
@P(value = "时间,可选值:上午、下午") String time,
@P(value = "医生名称", required = false) String doctorName
) {
System.out.println("查询是否有号源");
System.out.println("科室名称:" + name);
System.out.println("日期:" + date);
System.out.println("时间:" + time);
System.out.println("医生名称:" + doctorName);
//TODO 维护医生的排班信息:
//如果没有指定医生名字,则根据其他条件查询是否有可以预约的医生(有返回true,否则返回false);
//如果指定了医生名字,则判断医生是否有排班(没有排版返回false)
//如果有排班,则判断医生排班时间段是否已约满(约满返回false,有空闲时间返回true)
return true;
}
}

36
src/main/java/cn/ai/springaigu/tools/CalculatorTools.java

@ -0,0 +1,36 @@
package cn.ai.springaigu.tools;
import dev.langchain4j.agent.tool.P;
import dev.langchain4j.agent.tool.Tool;
import dev.langchain4j.agent.tool.ToolMemoryId;
import org.springframework.stereotype.Component;
@Component
public class CalculatorTools {
// @Tool
// double sum(double a, double b) {
// System.out.println("调用加法运算");
// return a + b;
// }
// @Tool
// double squareRoot(double x) {
// System.out.println("调用平方根运算");
// return Math.sqrt(x);
// }
@Tool(name = "加法", value = "返回两个参数相加之和")
double sum(
@ToolMemoryId int memoryId,
@P(value="加数1", required = true) double a,
@P(value="加数2", required = true) double b) {
System.out.println("调用加法运算 " + memoryId);
return a + b;
}
@Tool(name = "平方根", value = "返回给定参数的平方根")
double squareRoot(
@ToolMemoryId int memoryId, double x) {
System.out.println("调用平方根运算 " + memoryId);
return Math.sqrt(x);
}
}

46
src/main/resources/application.yml

@ -0,0 +1,46 @@
# langchain4j测试模型
langchain4j:
open-ai:
chat-model:
base-url: https://api.deepseek.com
api-key: sk-d2c693bbb4c64956809a03d7f40e3e70
# 推理模型 deepseek-reasoner
model-name: deepseek-chat
log-requests: true
log-responses: true
community:
dashscope:
chat-model:
api-key: sk-10a57fb7b31e4806ab5c4ea6ffa02f5a
model-name: qwen-max
embedding-model:
api-key: sk-10a57fb7b31e4806ab5c4ea6ffa02f5a
model-name: text-embedding-v3
streaming-chat-model:
api-key: sk-10a57fb7b31e4806ab5c4ea6ffa02f5a
model-name: qwen-plus
spring:
data:
mongodb:
uri: mongodb://localhost:27017/chat_memory_db
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://117.72.43.105:3306/ai_study?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&useSSL=false
username: root
password: Yuan625621105.
pinecone:
api-key: pcsk_5qjBiY_FrVVSC7NHrMem43SrCJ4CK6EGXduToZKZmJ4dGFDonGJPmVux2mQNiyqypTvkVx
# 开启 SQL 日志打印
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
# 启用日志debug级别
logging:
level:
root: info

5
src/main/resources/mapper/AppointmentMapper.xml

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.ai.springaigu.mapper.AppointmentMapper">
</mapper>

3
src/main/resources/my-prompt-template.txt

@ -0,0 +1,3 @@
你是我的好朋友,请用东北话回答问题,回答问题的时候适当添加表情符号。
今天是 {{current_date}}。

3
src/main/resources/my-prompt-template3.txt

@ -0,0 +1,3 @@
你是我的好朋友,我是{{username}},我的年龄是{{age}},请用东北话回答问题,回答问题的时候适当添加表情
符号。
今天是 {{current_date}}。

18
src/main/resources/zhaozhi-prompt-template.txt

@ -0,0 +1,18 @@
你的名字是“硅谷小智”,你是一家名为“北京协和医院”的智能客服。
你是一个训练有素的医疗顾问和医疗伴诊助手。
你态度友好、礼貌且言辞简洁。
1、请仅在用户发起第一次会话时,和用户打个招呼,并介绍你是谁。
2、作为一个训练有素的医疗顾问:
请基于当前临床实践和研究,针对患者提出的特定健康问题,提供详细、准确且实用的医疗建议。请同时考虑可能的病
因、诊断流程、治疗方案以及预防措施,并给出在不同情境下的应对策略。对于药物治疗,请特别指明适用的药品名
称、剂量和疗程。如果需要进一步的检查或就医,也请明确指示。
3、作为医疗伴诊助手,你可以回答用户就医流程中的相关问题,主要包含以下功能:
AI分导诊:根据患者的病情和就医需求,智能推荐最合适的科室。
AI挂号助手:实现智能查询是否有挂号号源服务;实现智能预约挂号服务;实现智能取消挂号服务。
4、你必须遵守的规则如下:
在获取挂号预约详情或取消挂号预约之前,你必须确保自己知晓用户的姓名(必选)、身份证号(必选)、预约科室
(必选)、预约日期(必选,格式举例:2025-04-14)、预约时间(必选,格式:上午 或 下午)、预约医生(可
选)。
当被问到其他领域的咨询时,要表示歉意并说明你无法在这方面提供帮助。
5、请在回答的结果中适当包含一些轻松可爱的图标和表情。
6、今天是 {{current_date}}。

42
src/test/java/cn/ai/springaigu/AppointmentServiceTest.java

@ -0,0 +1,42 @@
package cn.ai.springaigu;
import cn.ai.springaigu.domain.Appointment;
import cn.ai.springaigu.service.AppointmentService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class AppointmentServiceTest {
@Autowired
private AppointmentService appointmentService;
@Test
void testGetOne() {
Appointment appointment = new Appointment();
appointment.setUsername("张三");
appointment.setIdCard("123456789012345678");
appointment.setDepartment("内科");
appointment.setDate("2025-04-14");
appointment.setTime("上午");
Appointment appointmentDB = appointmentService.getOne(appointment);
System.out.println(appointmentDB);
}
@Test
void testSave() {
Appointment appointment = new Appointment();
appointment.setUsername("张三");
appointment.setIdCard("123456789012345678");
appointment.setDepartment("内科");
appointment.setDate("2025-04-14");
appointment.setTime("上午");
appointment.setDoctorName("张医生");
appointmentService.save(appointment);
}
@Test
void testRemoveById() {
appointmentService.removeById(1L);
}
}

94
src/test/java/cn/ai/springaigu/EmbeddingTest.java

@ -0,0 +1,94 @@
package cn.ai.springaigu;
import dev.langchain4j.data.document.Document;
import dev.langchain4j.data.document.loader.FileSystemDocumentLoader;
import dev.langchain4j.data.embedding.Embedding;
import dev.langchain4j.data.segment.TextSegment;
import dev.langchain4j.model.embedding.EmbeddingModel;
import dev.langchain4j.model.output.Response;
import dev.langchain4j.store.embedding.*;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.Arrays;
import java.util.List;
@SpringBootTest
public class EmbeddingTest {
@Autowired
private EmbeddingModel embeddingModel;
@Test
public void testEmbeddingModel() {
Response<Embedding> embed = embeddingModel.embed("你好");
System.out.println("向量维度:" + embed.content().vector().length);
System.out.println("向量输出:" + embed.toString());
}
// 测试向量存储
@Autowired
private EmbeddingStore embeddingStore;
/**
* 将文本转换成向量然后存储到pinecone中
* <p>
* 参考
* https://docs.langchain4j.dev/tutorials/embedding-stores
*/
@Test
public void testPineconeEmbeded() {
//将文本转换成向量
TextSegment segment1 = TextSegment.from("我喜欢羽毛球");
Embedding embedding1 = embeddingModel.embed(segment1).content();
//存入向量数据库
embeddingStore.add(embedding1, segment1);
TextSegment segment2 = TextSegment.from("今天天气很好");
Embedding embedding2 = embeddingModel.embed(segment2).content();
embeddingStore.add(embedding2, segment2);
}
/**
* Pinecone-相似度匹配
*/
@Test
public void embeddingSearch() {
//提问,并将问题转成向量数据
Embedding queryEmbedding = embeddingModel.embed("你最喜欢的运动是什么?").content();
//创建搜索请求对象
EmbeddingSearchRequest searchRequest = EmbeddingSearchRequest.builder()
.queryEmbedding(queryEmbedding)
.maxResults(1) //匹配最相似的一条记录
//.minScore(0.8)
.build();
//根据搜索请求 searchRequest 在向量存储中进行相似度搜索
EmbeddingSearchResult<TextSegment> searchResult = embeddingStore.search(searchRequest);
//searchResult.matches():获取搜索结果中的匹配项列表。
//.get(0):从匹配项列表中获取第一个匹配项
EmbeddingMatch<TextSegment> embeddingMatch = searchResult.matches().get(0);
//获取匹配项的相似度得分
System.out.println(embeddingMatch.score()); // 0.8144288515898701
//返回文本结果
System.out.println(embeddingMatch.embedded().text());
}
// 上传知识库到Pinecone
@Test
public void testUploadKnowledgeLibrary() {
//使用FileSystemDocumentLoader读取指定目录下的知识库文档
//并使用默认的文档解析器对文档进行解析
Document document1 = FileSystemDocumentLoader.loadDocument("D:/学习/课件/knowledge/医院信息.md");
Document document2 = FileSystemDocumentLoader.loadDocument("D:/学习/课件/knowledge/科室信息.md");
Document document3 = FileSystemDocumentLoader.loadDocument("D:/学习/课件/knowledge/神经内科.md");
List<Document> documents = Arrays.asList(document1, document2, document3);
//文本向量化并存入向量数据库:将每个片段进行向量化,得到一个嵌入向量
EmbeddingStoreIngestor
.builder()
.embeddingStore(embeddingStore)
.embeddingModel(embeddingModel)
.build()
.ingest(documents);
}
}

77
src/test/java/cn/ai/springaigu/MongoCrudTest.java

@ -0,0 +1,77 @@
package cn.ai.springaigu;
import cn.ai.springaigu.domain.ChatMessages;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
@SpringBootTest
public class MongoCrudTest {
@Autowired
private MongoTemplate mongoTemplate;
/**
* 插入文档
*/
/*
@Test
public void testInsert() {
mongoTemplate.insert(new ChatMessages(1L, "聊天记录"));
}*/
/**
* 插入文档
*/
@Test
public void testInsert2() {
ChatMessages chatMessages = new ChatMessages();
chatMessages.setContent("聊天记录列表");
mongoTemplate.insert(chatMessages);
}
/**
* 根据id查询文档
*/
@Test
public void testFindById() {
ChatMessages chatMessages = mongoTemplate.findById("6801ead733ba9c4a0d9b6c7b", ChatMessages.class);
System.out.println(chatMessages);
}
/**
* 修改文档
*/
@Test
public void testUpdate() {
Criteria criteria = Criteria.where("_id").is("6801ead733ba9c4a0d9b6c7b");
Query query = new Query(criteria);
Update update = new Update();
update.set("content", "新的聊天记录列表");
//修改或新增
mongoTemplate.upsert(query, update, ChatMessages.class);
}
/**
* 新增或修改文档
*/
@Test
public void testUpdate2() {
Criteria criteria = Criteria.where("_id").is("100");
Query query = new Query(criteria);
Update update = new Update();
update.set("content", "新的聊天记录列表");
//修改或新增
mongoTemplate.upsert(query, update, ChatMessages.class);
}
/**
* 删除文档
*/
@Test
public void testDelete() {
Criteria criteria = Criteria.where("_id").is("100");
Query query = new Query(criteria);
mongoTemplate.remove(query, ChatMessages.class);
}
}

137
src/test/java/cn/ai/springaigu/RAGTest.java

@ -0,0 +1,137 @@
package cn.ai.springaigu;
import dev.langchain4j.data.document.Document;
import dev.langchain4j.data.document.loader.FileSystemDocumentLoader;
import dev.langchain4j.data.document.parser.TextDocumentParser;
import dev.langchain4j.data.document.parser.apache.pdfbox.ApachePdfBoxDocumentParser;
import dev.langchain4j.data.document.splitter.DocumentByParagraphSplitter;
import dev.langchain4j.data.embedding.Embedding;
import dev.langchain4j.data.message.UserMessage;
import dev.langchain4j.data.segment.TextSegment;
import dev.langchain4j.model.embedding.EmbeddingModel;
import dev.langchain4j.model.embedding.onnx.HuggingFaceTokenizer;
import dev.langchain4j.store.embedding.*;
import dev.langchain4j.store.embedding.inmemory.InMemoryEmbeddingStore;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.nio.file.FileSystems;
import java.nio.file.PathMatcher;
import java.util.Arrays;
import java.util.List;
@SpringBootTest
public class RAGTest {
// 文档加载
@Test
public void testReadDocument() {
// // 加载单个文档
// Document document = FileSystemDocumentLoader.loadDocument("E:/knowledge/file.txt", new TextDocumentParser());
// // 从一个目录中加载所有文档
// List<Document> documents = FileSystemDocumentLoader.loadDocuments("E:/knowledge", new TextDocumentParser());
// // 从一个目录中加载所有的.txt文档
// PathMatcher pathMatcher = FileSystems.getDefault().getPathMatcher("glob:*.txt");
// List<Document> documents = FileSystemDocumentLoader.loadDocuments("E:/knowledge",pathMatcher, new TextDocumentParser());
// // 从一个目录及其子目录中加载所有文档
// List<Document> documents = FileSystemDocumentLoader.loadDocumentsRecursively("E:/knowledge", new TextDocumentParser());
//使用FileSystemDocumentLoader读取指定目录下的知识库文档
//并使用默认的文档解析器TextDocumentParser对文档进行解析
Document document = FileSystemDocumentLoader.loadDocument("D:\\IdeaProjects\\learn\\Spring-Ai-Gu\\src\\main\\resources\\my-prompt-template.txt");
System.out.println(document.text());
}
// 来自 langchain4j 模块的文本文档解析器(TextDocumentParser),它能够解析纯文本格式的文件(例如 TXT、HTML、MD 等)。
// ApachePdfBoxDocumentParser 解析PDF
// ApachePoiDocumentParser 解析微软办公软件的文件格式(例如 DOC、DOCX、PPT、PPTX、XLS、XLSX 等)
// ApacheTikaDocumentParser 以自动检测并解析几乎所有现有的文件格式
/**
* 解析PDF
*/
@Test
public void testParsePDF() {
Document document = FileSystemDocumentLoader.loadDocument("C:\\Users\\admin\\Desktop\\工作\\幼儿园E-portfolio\\Biannual Portfolios\\N1:认知、语言.pdf", new ApachePdfBoxDocumentParser());
System.out.println(document);
}
// 文档分割
// 按段落文档分割器(DocumentByParagraphSplitter)
// 按行文档分割器(DocumentByLineSplitter)
// 按句子文档分割器(DocumentBySentenceSplitter)
// 按单词文档分割器(DocumentByWordSplitter)
// 按字符文档分割器(DocumentByCharacterSplitter)
// 按正则表达式文档分割器(DocumentByRegexSplitter)
// 递归分割:DocumentSplitters.recursive (...)
// 默认情况下每个文本片段最多不能超过300个token
/**
* 加载文档并存入向量数据库
*/
@Test
public void testReadDocumentAndStore() {
//使用FileSystemDocumentLoader读取指定目录下的知识库文档
//并使用默认的文档解析器对文档进行解析(TextDocumentParser)
Document document = FileSystemDocumentLoader.loadDocument("C:\\Users\\admin\\Desktop\\评估报告.md");
//为了简单起见,我们暂时使用基于内存的向量存储
InMemoryEmbeddingStore<TextSegment> embeddingStore = new InMemoryEmbeddingStore<>();
//ingest
//1、分割文档:默认使用递归分割器,将文档分割为多个文本片段,每个片段包含不超过 300个token,并且 有 30 个token的重叠部分保证连贯性
//DocumentByParagraphSplitter(DocumentByLineSplitter(DocumentBySentenceSplitter(DocumentByWordSplitter)))
//2、文本向量化:使用一个LangChain4j内置的轻量化向量模型对每个文本片段进行向量化
//3、将原始文本和向量存储到向量数据库中(InMemoryEmbeddingStore)
EmbeddingStoreIngestor.ingest(document, embeddingStore);
//查看向量数据库内容
System.out.println(embeddingStore);
}
/**
* 文档分割
*/
@Test
public void testDocumentSplitter() {
//使用FileSystemDocumentLoader读取指定目录下的知识库文档
//并使用默认的文档解析器对文档进行解析(TextDocumentParser)
Document document = FileSystemDocumentLoader.loadDocument("C:\\Users\\admin\\Desktop\\评估报告.md");
//为了简单起见,我们暂时使用基于内存的向量存储
InMemoryEmbeddingStore<TextSegment> embeddingStore = new InMemoryEmbeddingStore<>();
//自定义文档分割器
//按段落分割文档:每个片段包含不超过 300个token,并且有 30个token的重叠部分保证连贯性
//注意:当段落长度总和小于设定的最大长度时,就不会有重叠的必要。
DocumentByParagraphSplitter documentSplitter = new DocumentByParagraphSplitter(
300,
30,
//token分词器:按token计算
new HuggingFaceTokenizer());
//按字符计算
//DocumentByParagraphSplitter documentSplitter = new DocumentByParagraphSplitter(300, 30);
EmbeddingStoreIngestor
.builder()
.embeddingStore(embeddingStore)
.documentSplitter(documentSplitter)
.build()
.ingest(document);
}
/**
* 计算 token 长度
*/
@Test
public void testTokenCount() {
String text = "这是一个示例文本,用于测试 token 长度的计算。";
UserMessage userMessage = UserMessage.userMessage(text);
//计算 token 长度
//QwenTokenizer tokenizer = new QwenTokenizer(System.getenv("DASH_SCOPE_API_KEY"),"qwen-max");
HuggingFaceTokenizer tokenizer = new HuggingFaceTokenizer();
int count = tokenizer.estimateTokenCountInMessage(userMessage);
System.out.println("token长度:" + count);
}
}

196
src/test/java/cn/ai/springaigu/TestGPT.java

@ -0,0 +1,196 @@
package cn.ai.springaigu;
import cn.ai.springaigu.asistant.Assistant;
import cn.ai.springaigu.asistant.MemoryChatAssistant;
import cn.ai.springaigu.asistant.SeparateChatAssistant;
import dev.langchain4j.community.model.dashscope.QwenChatModel;
//import dev.langchain4j.model.openai.OpenAiChatModel;
import dev.langchain4j.community.model.dashscope.WanxImageModel;
import dev.langchain4j.data.image.Image;
import dev.langchain4j.data.message.AiMessage;
import dev.langchain4j.data.message.UserMessage;
import dev.langchain4j.memory.chat.MessageWindowChatMemory;
import dev.langchain4j.model.chat.response.ChatResponse;
import dev.langchain4j.model.output.Response;
import dev.langchain4j.service.AiServices;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.Arrays;
@SpringBootTest
public class TestGPT {
// /**
// * 整合SpringBoot
// */
// @Autowired
// private OpenAiChatModel openAiChatModel;
@Value("${langchain4j.community.dashscope.chat-model.api-key}")
private String apiKey;
/**
* 通义千问大模型
*/
@Autowired
private QwenChatModel qwenChatModel;
@Autowired
private Assistant assistant;
/**
* gpt-4o-mini语言模型接入测试
* 如果你暂时没有密钥也可以使用LangChain4j 提供的演示密钥这个密钥是免费的有使用配额限制且仅限于 gpt-4o-mini 模型
*/
// @Test
// public void testGPTDemo() {
// //初始化模型
// OpenAiChatModel model = OpenAiChatModel.builder()
// //LangChain4j提供的代理服务器,该代理服务器会将演示密钥替换成真实密钥, 再将请求转发给OpenAI API
// .baseUrl("http://langchain4j.dev/demo/openai/v1") //设置模型api地址(如果apiKey = "demo",则可省略baseUrl的配置)
// .apiKey("demo") //设置模型apiKey
// .modelName("gpt-4o-mini") //设置模型名称
// .build();
// //向模型提问
// String answer = model.chat("你好");
// //输出结果
// System.out.println(answer);
// }
//
// @Test
// public void testSpringBoot() {
// //向模型提问
// String answer = openAiChatModel.chat("你好");
// //输出结果
// System.out.println(answer);
// }
@Test
public void testDashScopeQwen() {
//向模型提问
String answer = qwenChatModel.chat("你好");
//输出结果
System.out.println(answer);
}
@Test
public void testDashScopeWanx() {
WanxImageModel wanxImageModel = WanxImageModel.builder()
.modelName("wanx2.1-t2i-plus")
.apiKey(apiKey)
.build();
Response<Image> response = wanxImageModel.generate("奇幻森林精灵:在一片弥漫着轻柔薄雾的 古老森林深处,阳光透过茂密枝叶洒下金色光斑。一位身材娇小、长着透明薄翼的精灵少女站在一朵硕大的蘑菇上。她 有着海藻般的绿色长发,发间点缀着蓝色的小花,皮肤泛着珍珠般的微光。身上穿着由翠绿树叶和白色藤蔓编织而成的 连衣裙,手中捧着一颗散发着柔和光芒的水晶球,周围环绕着五彩斑斓的蝴蝶,脚下是铺满苔藓的地面,蘑菇和蕨类植物丛生,营造出神秘而梦幻的氛围。");
System.out.println(response.content().url());
}
@Test
public void testChat() {
//创建AIService
Assistant assistant = AiServices.create(Assistant.class, qwenChatModel);
//调用service的接口
String answer = assistant.chat("Hello");
System.out.println(answer);
}
//也可以在 Assistant 接口上添加 @AiService 注解
@Test
public void testAssistant() {
String answer = assistant.chat("Hello");
System.out.println(answer);
}
// 测试对话是否有记忆
@Test
public void testChatMemory() {
String answer1 = assistant.chat("我是环环");
System.out.println(answer1);
String answer2 = assistant.chat("我是谁");
System.out.println(answer2);
}
// 聊天记忆的简单实现
@Test
public void testChatMemory2() {
//第一轮对话
UserMessage userMessage1 = UserMessage.userMessage("我是环环");
ChatResponse chatResponse1 = qwenChatModel.chat(userMessage1);
AiMessage aiMessage1 = chatResponse1.aiMessage();
//输出大语言模型的回复
System.out.println(aiMessage1.text());
//第二轮对话
UserMessage userMessage2 = UserMessage.userMessage("你知道我是谁吗");
ChatResponse chatResponse2 = qwenChatModel.chat(Arrays.asList(userMessage1,
aiMessage1, userMessage2));
AiMessage aiMessage2 = chatResponse2.aiMessage();
//输出大语言模型的回复
System.out.println(aiMessage2.text());
}
// 使用ChatMemory实现聊天记忆
@Test
public void testChatMemory3() {
//创建chatMemory
MessageWindowChatMemory chatMemory = MessageWindowChatMemory.withMaxMessages(10);
//创建AIService
Assistant assistant = AiServices
.builder(Assistant.class)
.chatLanguageModel(qwenChatModel)
.chatMemory(chatMemory)
.build();
//调用service的接口
String answer1 = assistant.chat("我是环环");
System.out.println(answer1);
String answer2 = assistant.chat("我是谁");
System.out.println(answer2);
}
@Autowired
private MemoryChatAssistant memoryChatAssistant;
@Test
public void testChatMemory4() {
String answer1 = memoryChatAssistant.chat("我是环环");
System.out.println(answer1);
String answer2 = memoryChatAssistant.chat("我是谁");
System.out.println(answer2);
}
@Autowired
private SeparateChatAssistant separateChatAssistant;
@Test
public void testChatMemory5() {
String answer1 = separateChatAssistant.chat(1,"我是环环");
System.out.println(answer1);
String answer2 = separateChatAssistant.chat(1,"我是谁");
System.out.println(answer2);
String answer3 = separateChatAssistant.chat(2,"我是谁");
System.out.println(answer3);
}
//系统提示词@SystemMessage
@Test
public void testSystemMessage() {
String answer = separateChatAssistant.chat(3,"今天几号");
System.out.println(answer);
}
@Test
public void testUserMessage() {
String answer = memoryChatAssistant.chat("我是环环");
System.out.println(answer);
}
@Test
public void testV() {
String answer1 = separateChatAssistant.chat2(1, "我是环环");
System.out.println(answer1);
String answer2 = separateChatAssistant.chat2(1, "我是谁");
System.out.println(answer2);
}
@Test
public void testUserInfo() {
String answer = separateChatAssistant.chat3(1, "我是谁,我多大了", "翠花", 18);
System.out.println(answer);
}
}

19
src/test/java/cn/ai/springaigu/ToolsTest.java

@ -0,0 +1,19 @@
package cn.ai.springaigu;
import cn.ai.springaigu.asistant.SeparateChatAssistant;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
public class ToolsTest {
@Autowired
private SeparateChatAssistant separateChatAssistant;
@Test
public void testCalculatorTools() {
String answer = separateChatAssistant.chat(1, "1+2等于几,475695037565的平方根是多 少?");
//答案:3,689706.4865
System.out.println(answer);
}
}
Loading…
Cancel
Save