目录

持久化聊天记忆 Persistence


 标题

人工智能服务 AIService

package com.lzw.javaailangchain4j.assistant;

import dev.langchain4j.service.spring.AiService;

import static dev.langchain4j.service.spring.AiServiceWiringMode.EXPLICIT;

@AiService(wiringMode = EXPLICIT, chatModel = "qwenChatModel")
public interface Assistant {
    String chat(String userMessage) ;
}

在这个接口中加上AiService 并指定大数据模型就可以利用@Autowired去注入

 @Autowired
    Assistant assistant;
    @Test
    public void testAssistant()
    {
        String anser = assistant.chat("你是谁");
        System.out.println(anser);
    }
AiServices 组装 Assistant 接口以及其他组件 ,并使用反射机制创建一个实现 Assistant 接口的 代理对象 。 这个代理对象会处理输入和输出的所有转换工作。在这个例子中,chat 方法的输入是一个字符串,但是大 模型需要一个 UserMessage 对象。所以,代理对象将这个字符串转换为 UserMessage ,并调用聊天语 言模型。chat 方法的输出类型也是字符串,但是大模型返回的是 AiMessage 对象,代理对象会将其转换为字符串。

实现聊天记忆

@Test
    public void chartMeory(){
        MessageWindowChatMemory chatMemory = MessageWindowChatMemory.withMaxMessages(10);
        Assistant assistant1 = AiServices.builder(Assistant.class)
                .chatModel(qwenChatModel)
                .chatMemory(chatMemory)
                .build();
        String anser1 = assistant1.chat("我是lzw");
        System.out.println(anser1);
        String anser2 = assistant1.chat("我是谁");
        System.out.println(anser2);
    }

我设置的会话记忆只有十层超过之后就不会记住上下文记忆

使用AIsevice实现聊天记忆

package com.lzw.javaailangchain4j.assistant;

import dev.langchain4j.service.spring.AiService;

import static dev.langchain4j.service.spring.AiServiceWiringMode.EXPLICIT;

@AiService(
        wiringMode = EXPLICIT,
        chatModel = "qwenChatModel",
        chatMemory = "chatMemory"
)
public interface MemorychatAssistant {
    String chat(String userMessage) ;
}

然后对chatMemory进行配置Bean

package com.lzw.javaailangchain4j.config;

import dev.langchain4j.memory.ChatMemory;
import dev.langchain4j.memory.chat.MessageWindowChatMemory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class MemorychatAssistantConfig {
    @Bean
    ChatMemory chatMemory() {
//设置聊天记忆记录的message数量
        return MessageWindowChatMemory.withMaxMessages(10);
    }
}
  @Autowired
    private MemorychatAssistant memorychatAssistant;
    @Test
    public void chartMeory1(){

        String anser1 =memorychatAssistant.chat("我是lzw");
        System.out.println(anser1);
        String anser2 = memorychatAssistant.chat("我是谁");
        System.out.println(anser2);
    }

这样就可以简化代码直接使用

进行聊天记忆隔离

为每个用户的新聊天或者不同的用户区分聊天记忆
配置聊天智能体
package com.lzw.javaailangchain4j.assistant;

import dev.langchain4j.service.MemoryId;
import dev.langchain4j.service.UserMessage;
import dev.langchain4j.service.spring.AiService;

import static dev.langchain4j.service.spring.AiServiceWiringMode.EXPLICIT;

@AiService(
        wiringMode = EXPLICIT,
        chatModel = "qwenChatModel",
        chatMemoryProvider = "chatMemoryProvider"
)
public interface SeparateChatAssistant {
    String chat(@MemoryId int memoryId, @UserMessage String userMessage);
}

创建chatMemoryProvider的Bean

package com.lzw.javaailangchain4j.config;

import dev.langchain4j.memory.chat.ChatMemoryProvider;
import dev.langchain4j.memory.chat.MessageWindowChatMemory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class SeparateChatAssistantConfig {
    @Bean
    ChatMemoryProvider chatMemoryProvider() {
        return memoryId -> MessageWindowChatMemory.builder()
                .id(memoryId)
                .maxMessages(10)
                .build();
    }
}

这样就保证了不同的用户有不同的聊天记忆

聊天记忆是怎么实现的呢

在MessageWindowChatMemory里面有个store字段

而ChatMemoryStore里面有两个实现类

在这个图中就可以清晰的看出默认的是使用Single这个实现类去实现的

而在single实现类中

聊天记忆是存储来List集合中的,当去查询聊天记忆时会先去调用checkMemoryId去检查id是否匹配,添加新的聊天记忆也是如此

具体的就是这样使用key value存储记忆不同的用户之间直接隔离

对于一种类的实现是将内存信息放在hashmap中

持久化聊天记忆 Persistence

默认情况下,聊天记忆存储在内存中。如果需要持久化存储,可以实现一个自定义的聊天记忆存储类, 以便将聊天消息存储在你选择的任何持久化存储介质中。

我们使用的是MongoDB

配置MongoDB,先去创建实体类映射MongoDB

@Data
@AllArgsConstructor
@NoArgsConstructor
@Document("chat_messages")
public class ChatMessages {
    //唯一标识,映射到 MongoDB 文档的 _id 字段
    @Id
    private ObjectId messageId;
    //private Long messageId;
    private String content; //存储当前聊天记录列表的json字符串
}

先进行MonfoDB插入数据测试

 @Autowired
    MongoTemplate mongoTemplate;
    @Test
    public void testInsert2() {
        ChatMessages chatMessages = new ChatMessages();
        chatMessages.setContent("聊天记录");
        mongoTemplate.insert(chatMessages);
    }

得到结果

更新和插入

 @Test
    public void testUpdate() {
        ObjectId id = new ObjectId("69c38b95e7a9443bf03b0db7");
        Criteria criteria = Criteria.where("_id").is(id);
        Query query = new Query(criteria);
        Update update = new Update();
        update.set("content", "hello world");

        // 修改或新增
        mongoTemplate.upsert(query, update, ChatMessages.class);
    }

当你给的id不存在的话就会去创建而不是去修改了

删除操作

 @Test
    public void testDelete() {
        Criteria criteria = Criteria.where("_id").is("69c38b95e7a9443bf03b0db7");
        Query query = new Query(criteria);
        mongoTemplate.remove(query, ChatMessages.class);
    }

聊天记忆的持久化配置

我们首先会去创建记忆持久化的类去实现chatmemoryStore的接口重写里面的三个方法

package com.lzw.javaailangchain4j.store;

import com.lzw.javaailangchain4j.Bean.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) {
            //因为信息是存储再linkedlist的
            return new LinkedList<>();
        }
        String contentJson = chatMessages.getContent();
        return ChatMessageDeserializer.messagesFromJson(contentJson);
    }

    @Override
    public void updateMessages(Object memoryId, List<ChatMessage> list) {
        Criteria criteria = Criteria.where("memoryId").is(memoryId);
        Query query = new Query(criteria);
        Update update = new Update();
        //将聊天记忆存储起来,将list转成json
        update.set("content", ChatMessageSerializer.messagesToJson(list));

        // 修改或新增
        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);

    }
}

在这个持久化记忆里面我们是用memoryid去查询的

然后去配置这个类

@Configuration
public class SeparateChatAssistantConfig {
    @Autowired
    private MongoChatMemoryStore  mongoChatMemoryStore;
    @Bean
    ChatMemoryProvider chatMemoryProvider() {
        return memoryId -> MessageWindowChatMemory.builder()
                .id(memoryId)
                .maxMessages(10)
                .chatMemoryStore(mongoChatMemoryStore)
                .build();
    }
}
@AiService(
        wiringMode = EXPLICIT,
        chatModel = "qwenChatModel",
        chatMemoryProvider = "chatMemoryProvider"
)
public interface SeparateChatAssistant {
    String chat(@MemoryId int memoryId, @UserMessage String userMessage);
}

这样就实现了聊天记忆的持久化

使用提示词

public interface SeparateChatAssistant {
    @SystemMessage("你是我的好朋友,请用东北话回答问题。今天是{{current_date}}")
    String chat(@MemoryId int memoryId, @UserMessage String userMessage);
}

上面加上SystemMessage就可以了,在MongoDB中的显示是

比原来多了一个system的回答在开头。当你将提示词换了之后你再使用3id去聊天他就会重置记忆

使用UserMessage

public interface MemorychatAssistant {
    @UserMessage("你是我的好朋友,请用上海话回答问题,并且添加一些表情符号。 {{it}}")
    String chat(String userMessage) ;
}

it就是占位符就是chat的参数userMessage每次对话都会带着这句话

两次都能够带上这句话,依据他去回答

使用@V注解替换默认的it,他们两个的作用是一样的

public interface MemorychatAssistant {
    @UserMessage("你是我的好朋友,请用上海话回答问题,并且添加一些表情符号。 {{userMessage}}")
    String chat(@V("userMessage") String userMessage) ;
}

有两个及两个以上的参数就必须使用@V注解了

@Autowired
private MemoryChatAssistant memoryChatAssistant;
@Test
public void testUserMessage() {
String answer = memoryChatAssistant.chat("我是环环");
System.out.println(answer);
}
@UserMessage("你是我的好朋友,请用上海话回答问题,并且添加一些表情符号。{{message}}")
String chat(@V("message") String userMessage);
@UserMessage("你是我的好朋友,请用粤语回答问题。{{message}}")
String chat2(@MemoryId int memoryId, @V("message") String userMessage);
@SystemMessage @V共同使用
@SystemMessage(fromResource = "lzwPropt.txt")
    String chat3(
            @MemoryId int memoryId,
            @UserMessage String userMessage,
            @V("username") String username,
            @V("age") int age
    );

system的文件是我们自己写的

你是我的好朋友,请用东北话回答问题,回答问题的时候适当添加表情符号。
今天是 {{current_date}}。
@Autowired
    private SeparateChatAssistant separateChatAssistant;
    @Test
    public void testSeparateChatAssistant() {
        //假如说是从数据库获取用户姓名跟年龄
        String name="埃特曼";
        int age=14;
        String anser = separateChatAssistant.chat3(3, "我是谁,我多大了", name, age);
        System.out.println(anser);
    }

这个的作用就是给系统提示词让他按照我们的提示词去回答

正式开始

首先要去创建小智的接口

@AiService(
        wiringMode = EXPLICIT,
        chatModel = "qwenChatModel",
        chatMemoryProvider = "chatMemoryProviderXiaozhi"
)
public interface XiaozhiAssistant {

    @SystemMessage(fromResource = "zhaozhi-prompt-template.txt")
    String chat(@MemoryId Long memoryId, @UserMessage String userMessage);
}

提示词我们已经保存

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

去实现记忆的持久化

@Configuration
public class XiaozhiAssistanConfig {
    @Autowired
    private MongoChatMemoryStore mongoChatMemoryStore;
    @Bean
    ChatMemoryProvider chatMemoryProviderXiaozhi() {
        return memoryId -> MessageWindowChatMemory.builder()
                .id(memoryId)
                .maxMessages(20)
                .chatMemoryStore(mongoChatMemoryStore)
                .build();
    }
}

去封装一个会话对象

package com.lzw.javaailangchain4j.config;

import lombok.Data;

@Data
public class ChatForm {
    private Long memoryId;//对话id
    private String message;//用户问题
}

然后创建controller实现会话

@RestController
@RequestMapping("/xiaozhi")
@Tag(name="硅谷小智")

public class XiaozhiController {
    @Autowired
    private XiaozhiAssistant xiaozhiAssistant;
    @Operation(summary = "对话")
    @PostMapping("/chat")
    public String chat(@RequestBody ChatForm chatForm) {
        return xiaozhiAssistant.chat(chatForm.getMemoryId(), chatForm.getMessage());
    }
}

调试接口

添加tools工具

@Component
public class CalculatorTools {
    @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);
    }

}

如果你不配置的话有的时候ai会出现幻觉(计算出的结果并不对)@Tool是给方法进行说明的以防ai看不懂,@P是给参数说明的里面的required是默认为true代表你必须传入该参数,@ToolMemoryId是为了有些需求需要展示用户的id(那个用户调用的方法)

测试用例

@Autowired
    private SeparateChatAssistant separateChatAssistant;
    @Test
    public void testCalculatorTools() {
            String answer =separateChatAssistant.chat(99,"65006500的平方根为多少?");
        String anser1 = separateChatAssistant.chat(99, "3+5等于多少");
        System.out.println(answer);
        System.out.println(anser1);

    }

结果

做一个优化,将预约信息跟查询预约放入数据库

首先就需要创建数据库

CREATE DATABASE `guiguxiaozhi`;
USE `guiguxiaozhi`;
CREATE TABLE `appointment` (
`id` BIGINT NOT NULL AUTO_INCREMENT,
`username` VARCHAR(50) NOT NULL,
`id_card` VARCHAR(18) NOT NULL,
`department` VARCHAR(50) NOT NULL,
`date` VARCHAR(10) NOT NULL,
`time` VARCHAR(10) NOT NULL,
`doctor_name` VARCHAR(50) DEFAULT NULL,
PRIMARY KEY (`id`)
);

创建他的实体类

package com.lzw.javaailangchain4j.entity;

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;
}

然后创建Sevice和Mapper各种东西

进行测试

@SpringBootTest
public class AppointmentSevicesTest {
    @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);
    }

}

配置Tools

@Component
public class AppointmentTools {
    @Autowired
    private AppointmentService appointmentService;
    @Tool(name="预约挂号", value ="根据参数,先执行工具方法queryDepartment查询是否可预约,并直\n" +
            "接给用户回答是否可预约,并让用户确认所有预约信息,用户确认后再进行预约")
    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 = "根据参数,查询预约是否存在,如果存在则删除预约记录并返回取\n" +
            "消预约成功,否则返回取消预约失败")
    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);
        return true;
    }

//如果没有指定医生名字,则根据其他条件查询是否有可以预约的医生(有返回true,否则返回

//如果指定了医生名字,则判断医生是否有排班(没有排版返回false)
//如果有排班,则判断医生排班时间段是否已约满(约满返回false,有空闲时间返回true)




}

根据用户给的ai发的信息我们去查询是否有号源,给用户预约和取消预约

进行一下测试

对话框

增强RAG

RAG有两个过程一个是检索一个是索引

首先索引

加载知识库文档 ==> 将文档中的 文本分段 ==> 利用 向量大模型 将分段后的 文本转换成向量 ==> 将向量
入向量数据库

为什么要进行分段,就是为了减少不必要的信息污染大模型导致幻读,提供的上下文是有限的整个知识库并不能全部放入其中,提供的信息越多花费越多而且反应时间越长

检索

通过向量模型 将用户查询转换成向量 ==> 在向量数据库中根据用户查询进行 相似度匹配 ==> 将用户查询
和向量数据库中匹配到的相关内容一起交给 LLM 处理

通过向量模型将用户查询转换成向量文本,然后去向量知识库中去做相似度匹配得出分数最高的(就是相似度最高的)结合原本的系统提示词和查询信息去发送给大模型

文本加载器

public class RAGTest {
@Test
public void testReadDocument() {
//使用FileSystemDocumentLoader读取指定目录下的知识库文档
//并使用默认的文档解析器TextDocumentParser对文档进行解析
    Document document = FileSystemDocumentLoader.loadDocument("D:/java/knowledge/测试.txt");
    System.out.println(document);

}
}

使用默认的解析器

@Test
public void testLoadDocument2() {
    // 从一个目录中加载所有的.txt文档
    // 从一个目录中加载所有的.txt文档
    PathMatcher pathMatcher = FileSystems.getDefault().getPathMatcher("glob:*.txt");
    List<Document> documents = FileSystemDocumentLoader.loadDocuments("D:/java/knowledge", pathMatcher, new TextDocumentParser());
    for (Document document : documents) {
        System.out.println(document.metadata());
        System.out.println(document);
    }

这个默认的解析器只能解析txt,md等无法解析pdf所以需要用到另一个解析器

ApachePdfBoxDocumentParser
 @Test
    public void testParsePDF() {
        Document document = FileSystemDocumentLoader.loadDocument(
                "D:/java/knowledge/医院信息.pdf",
                new ApachePdfBoxDocumentParser()
        );
        System.out.println(document);
    }

将知识库文本进行向量转换和存储

    @Test
    public void testReadDocumentAndStore() {
        //使用FileSystemDocumentLoader读取指定目录下的知识库文档
        //并使用默认的文档解析器对文档进行解析(TextDocumentParser)
        Document document = FileSystemDocumentLoader.loadDocument("D:/java/knowledge/人工智能.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);
    }

在小智RAG中实现

在Tools中配置一下

    @Bean
    public ContentRetriever contentRetrieverXiaozhi() {
        // 1. 加载指定目录下的知识库文档(使用 FileSystemDocumentLoader)
        Document document1 = FileSystemDocumentLoader.loadDocument("D:/java/knowledge/医院信息.md");
        Document document2 = FileSystemDocumentLoader.loadDocument("D:/java/knowledge/科室信息.md");
        Document document3 = FileSystemDocumentLoader.loadDocument("D:/java/knowledge/神经内科.md");

        List<Document> documents = Arrays.asList(document1, document2, document3);

        // 2. 创建内存向量存储(InMemoryEmbeddingStore)
        InMemoryEmbeddingStore<TextSegment> embeddingStore = new InMemoryEmbeddingStore<>();

        // 3. 使用默认文档分割器,将文档切分并存入向量存储(EmbeddingStoreIngestor)
        EmbeddingStoreIngestor.ingest(documents, embeddingStore);

        // 4. 基于向量存储构建内容检索器(EmbeddingStoreContentRetriever)
        return EmbeddingStoreContentRetriever.from(embeddingStore);
    }

然后在引入

@AiService(
        wiringMode = EXPLICIT,
        chatModel = "qwenChatModel",
        chatMemoryProvider = "chatMemoryProviderXiaozhi",
        tools = "appointmentTools",
        contentRetriever = "contentRetrieverXiaozhi" //配置向量存储
)
public interface XiaozhiAssistant {

    @SystemMessage(fromResource = "zhaozhi-prompt-template.txt")
    String chat(@MemoryId Long memoryId, @UserMessage String userMessage);
}

这样之后智能回答会更加准确

使用通用文本向量 text-embedding-v4 ,维度 1024 ,维度越多,对事务的描述越精准,信息检索的精度越
#集成阿里通义千问-通用文本向量-v3
langchain4j.community.dashscope.embedding-model.api-key=${DASH_SCOPE_API_KEY}
langchain4j.community.dashscope.embedding-model.model-name=text-embedding-v4
@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());
}
}

结果

在硅谷小智中整合向量数据库

上传知识库到Pinecone

@Test
    public void testUploadKnowledgeLibrary() {
        // 使用 FileSystemDocumentLoader 读取指定目录下的知识库文档
        // 并使用默认的文档解析器对文档进行解析
        Document document1 = FileSystemDocumentLoader.loadDocument("D:/java/knowledge/医院信息.md");
        Document document2 = FileSystemDocumentLoader.loadDocument("D:/java/knowledge/科室信息.md");
        Document document3 = FileSystemDocumentLoader.loadDocument("D:/java/knowledge/神经内科.md");

        List<Document> documents = Arrays.asList(document1, document2, document3);

        // 文本向量化并存入向量数据库:将每个片段进行向量化,得到一个嵌入向量
        EmbeddingStoreIngestor
                .builder()
                .embeddingStore(embeddingStore)
                .embeddingModel(embeddingModel)
                .build()
                .ingest(documents);
    }
修改 XiaozhiAgentConfig
添加基于 Pinecone 的向量数据库配置
 @Autowired
   private EmbeddingStore embeddingStore;
    @Autowired
    private EmbeddingModel embeddingModel;
   @Bean
   public ContentRetriever contentRetrieverXiaozhiPincone() {
       // 创建一个 EmbeddingStoreContentRetriever 对象,用于从嵌入存储中检索内容
       return EmbeddingStoreContentRetriever
               .builder()
               // 设置用于生成嵌入向量的嵌入模型
               .embeddingModel(embeddingModel)
               // 指定要使用的嵌入存储
               .embeddingStore(embeddingStore)
               // 设置最大检索结果数量,这里表示最多返回 1 条匹配结果
               .maxResults(1)
               // 设置最小得分阈值,只有得分大于等于 0.8 的结果才会被返回
               .minScore(0.8)
               // 构建最终的 EmbeddingStoreContentRetriever 实例
               .build();
   }
修改 XiaozhiAgent
修改 contentRetriever 的配置为 contentRetrieverXiaozhiPincone
@AiService(
        wiringMode = EXPLICIT,
        streamingChatModel = "qwenStreamingChatModel",
        chatMemoryProvider = "chatMemoryProviderXiaozhi",
        tools = "appointmentTools",
        contentRetriever = "contentRetrieverXiaozhiPincone" //配置向量存储
)
public interface XiaozhiAssistant {

    @SystemMessage(fromResource = "zhaozhi-prompt-template.txt")
    Flux<String> chat(@MemoryId Long memoryId, @UserMessage String userMessage);
}

然后直接运行

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐