今天通过RAG来实现失物查询功能
一、RAG相关配置
1、定义向量数据库
java
// 配置向量数据库, 这里使用内存数据库
@Bean
public EmbeddingStore<TextSegment> embeddingStore() {
return new InMemoryEmbeddingStore<>();
}
2、定义向量模型
java
// 配置向量模型
@Bean
public EmbeddingModel embeddingModel() {
return QwenEmbeddingModel.builder()
.apiKey(System.getenv("ALI_AI_KEY"))
.modelName("text-embedding-v3")
.build();
}
3、定义内容检索器
java
// 配置内容检索器
@Bean
public ContentRetriever contentRetriever(EmbeddingStore<TextSegment> embeddingStore, EmbeddingModel embeddingModel) {
return EmbeddingStoreContentRetriever.builder()
.embeddingStore(embeddingStore)
.embeddingModel(embeddingModel)
.maxResults(2)
.minScore(0.5)
.build();
}
二、向量数据初始化
1、初始化
要查询向量数据,需要先通过向量模型把数据先存入到向量数据库中,我这边先定义个接口初始化一下向量数据。
java
@GetMapping(value = "/embedding-index")
public String embeddingIndex() {
return aiChatService.embeddingIndex();
}
初始化代码如下:
java
public String embeddingIndex() {
String path = "./data/data.txt";
try {
// 将丢失物品数据写入文件
FileUtil.writeString("", path, StandardCharsets.UTF_8);
List<String> collect = StreamSupport.stream(lostPropertyRepository.findAll().spliterator(), false)
.map(JsonUtils::toJson)
.toList();
FileUtil.appendLines(collect, path, StandardCharsets.UTF_8);
log.info("write file success");
} catch (Exception e) {
log.error("write file error", e);
}
// 加载并解析文档内容
DocumentParser documentParser = new TextDocumentParser();
Document document = FileSystemDocumentLoader.loadDocument(FileUtil.getAbsolutePath(path), documentParser);
// 按行分割文档为指定长度的文本片段
DocumentSplitter splitter = new DocumentByLineSplitter(200, 100);
List<TextSegment> documents = splitter.split(document);
// 生成文本片段的嵌入向量并存储
List<Embedding> embeddings = embeddingModel.embedAll(documents).content();
embeddingStore.addAll(embeddings, documents);
return "success";
}
2、测试
查询测试:
java
@GetMapping(value = "/embedding-query")
public List<String> embeddingQuery(String message) {
return aiChatService.embeddingQuery(message);
}
java
public List<String> embeddingQuery(String message) {
EmbeddingSearchRequest embeddingSearchRequest = EmbeddingSearchRequest.builder()
.queryEmbedding(embeddingModel.embed(message).content())
.minScore(0.5)
.maxResults(2)
.build();
EmbeddingSearchResult<TextSegment> search = embeddingStore.search(embeddingSearchRequest);
return search.matches().stream().map(x -> x.embedded().text()).collect(Collectors.toList());
}
我这边先通过DeepSeek帮我生成了一些失物数据,如下:

启动服务后,先初始化一下向量数据:

然后测试一下:


三、向量数据查询
1、增加失物查询工具
java
@Tool("根据物品及物品特征查询物品登记信息")
public List<String> queryLostProperty(@P(value = "物品名称和特征") String lostProperty) {
log.info("根据物品及物品特征查询物品登记信息,物品名称和特征: {}", lostProperty);
List<String> list = contentRetriever.retrieve(new Query(lostProperty))
.stream()
.map(x -> x.textSegment().text())
.toList();
log.info("根据物品及物品特征查询物品登记信息,查询结果: {}", list);
return list;
}
2、定义AiServices
java
/**
* 查询失物信息
*/
@SystemMessage(fromResource = "/message/system/queryLostProperty.txt")
@UserMessage("当前sessionId:{{sessionId}};用户当前消息:{{message}}")
Flux<String> queryLostProperty(@V("sessionId") String id, @V("message") String message);
提示词:
plain
# 角色
你是一位专业的失物管理员,负责处理失物招领相关工作。根据用户输入的丢失物品信息,仔细与系统中已录入的失物信息进行比对,并准确告知用户拾取到人的信息。
## 技能
### 技能 1: 比对丢失物品信息
- 每次对话,需要调用getChatHistory函数获取用户对话历史。
- 主动询问用户的姓名和手机号。
- 当用户提供失物相关信息时,从中提取物品信息和相关特征。
- 如果用户已经输入了手机号,调用queryLostRegisterByPhone函数查询用户丢失物品的信息。若能查询到数据,可能有多条数据,将其与用户已输入的物品信息进行匹配(比较失物名称和失物特征是否是能匹配),最多返回一条已经匹配的数据,如果没有查询到数据,需要引导用户进入登记流程。
- 如果已经查询到用户登记的丢失信息,获取丢失的失物信息和相关失物特征,调用queryLostProperty函数,获取系统中已经录入的失物信息,将其与系统中录入的失物信息进行详细比对。
- 若找到匹配信息,准确提取并整理拾取到人的相关信息。
- 以清晰易懂的方式向用户反馈拾取到人的信息。
## 限制:
- 仅围绕失物招领工作展开交流,拒绝回答无关话题。
- 反馈信息应基于系统中已录入的信息,确保准确性。
- 不要调用其他无关函数。
3、修改业务调用
java
private String queryLostProperty(String userId, String message) {
StringBuilder sb = new StringBuilder();
lostPropertyAssistant.queryLostProperty(userId, message)
.doOnNext(sb::append)
.blockLast();
log.info("queryLostProperty:{}", sb);
return sb.toString();
}
4、测试
数据库中数据如下:

测试一下:

over。。。