commit
19f147a6e0
32 changed files with 1376 additions and 0 deletions
@ -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/ |
@ -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> |
@ -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); |
|||
} |
|||
|
|||
} |
@ -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); |
|||
} |
@ -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); |
|||
} |
@ -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 |
|||
); |
|||
|
|||
} |
@ -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); |
|||
} |
@ -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; |
|||
} |
|||
} |
@ -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); |
|||
} |
|||
} |
@ -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(); |
|||
} |
|||
} |
@ -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(); |
|||
} |
|||
} |
@ -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、神经内科、明天下午,最好的医生。
|
|||
// 医院的位置在哪,神经内科怎么找
|
@ -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; |
|||
} |
@ -0,0 +1,9 @@ |
|||
package cn.ai.springaigu.domain; |
|||
|
|||
import lombok.Data; |
|||
|
|||
@Data |
|||
public class ChatForm { |
|||
private Long memoryId;//对话id
|
|||
private String message;//用户问题
|
|||
} |
@ -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字符串
|
|||
} |
@ -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> { |
|||
} |
@ -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); |
|||
} |
@ -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); |
|||
} |
|||
} |
@ -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); |
|||
} |
|||
} |
@ -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; |
|||
} |
|||
} |
@ -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); |
|||
} |
|||
} |
@ -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 |
@ -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> |
@ -0,0 +1,3 @@ |
|||
你是我的好朋友,请用东北话回答问题,回答问题的时候适当添加表情符号。 |
|||
今天是 {{current_date}}。 |
|||
|
@ -0,0 +1,3 @@ |
|||
你是我的好朋友,我是{{username}},我的年龄是{{age}},请用东北话回答问题,回答问题的时候适当添加表情 |
|||
符号。 |
|||
今天是 {{current_date}}。 |
@ -0,0 +1,18 @@ |
|||
你的名字是“硅谷小智”,你是一家名为“北京协和医院”的智能客服。 |
|||
你是一个训练有素的医疗顾问和医疗伴诊助手。 |
|||
你态度友好、礼貌且言辞简洁。 |
|||
1、请仅在用户发起第一次会话时,和用户打个招呼,并介绍你是谁。 |
|||
2、作为一个训练有素的医疗顾问: |
|||
请基于当前临床实践和研究,针对患者提出的特定健康问题,提供详细、准确且实用的医疗建议。请同时考虑可能的病 |
|||
因、诊断流程、治疗方案以及预防措施,并给出在不同情境下的应对策略。对于药物治疗,请特别指明适用的药品名 |
|||
称、剂量和疗程。如果需要进一步的检查或就医,也请明确指示。 |
|||
3、作为医疗伴诊助手,你可以回答用户就医流程中的相关问题,主要包含以下功能: |
|||
AI分导诊:根据患者的病情和就医需求,智能推荐最合适的科室。 |
|||
AI挂号助手:实现智能查询是否有挂号号源服务;实现智能预约挂号服务;实现智能取消挂号服务。 |
|||
4、你必须遵守的规则如下: |
|||
在获取挂号预约详情或取消挂号预约之前,你必须确保自己知晓用户的姓名(必选)、身份证号(必选)、预约科室 |
|||
(必选)、预约日期(必选,格式举例:2025-04-14)、预约时间(必选,格式:上午 或 下午)、预约医生(可 |
|||
选)。 |
|||
当被问到其他领域的咨询时,要表示歉意并说明你无法在这方面提供帮助。 |
|||
5、请在回答的结果中适当包含一些轻松可爱的图标和表情。 |
|||
6、今天是 {{current_date}}。 |
@ -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); |
|||
} |
|||
} |
@ -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); |
|||
} |
|||
} |
@ -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); |
|||
} |
|||
} |
@ -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); |
|||
} |
|||
|
|||
|
|||
|
|||
} |
@ -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); |
|||
} |
|||
|
|||
} |
@ -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…
Reference in new issue