在构建基于Spring AI的RAG(检索增强生成)应用时,数据的质量 直接决定了回答的智商 。
RAG时,不仅需要原始文本,还需要文本的上下文摘要或核心关键词来辅助检索和生成。
Spring AI提供的ETL Pipeline模块中,KeywordMetadataEnricher和SummaryMetadataEnricher正是为此而生。它们利用大模型的能力,生成文件的核心关键词及摘要信息。
Enricher
KeywordMetadataEnricher:关键词提取专家
它的核心任务是从文档内容中提取出最具代表性的关键词,并将这些关键词存入文档的元数据中。
● 作用:提升检索的召回率。当用户查询使用了同义词或相关术语时,关键词元数据可以帮助匹配到原文未直接出现但语义相关的文档。
● 机制:它调用配置好的ChatModel,通过特定的Prompt模板(默认是{context_str}. Give %s unique keywords for this document. Format as comma separated. Keywords:)让模型提取关键词。
● 输出:生成的关键词会以逗号分隔的字符串形式,存入元数据的excerpt_keywords字段中。
java
public KeywordMetadataEnricher(ChatModel chatModel, int keywordCount) {
Assert.notNull(chatModel, "chatModel must not be null");
Assert.isTrue(keywordCount >= 1, "keywordCount must be >= 1");
this.chatModel = chatModel;
this.keywordsTemplate = new PromptTemplate(String.format("{context_str}. Give %s unique keywords for this\ndocument. Format as comma separated. Keywords:", keywordCount));
}
public List<Document> apply(List<Document> documents) {
for(Document document : documents) {
Prompt prompt = this.keywordsTemplate.create(Map.of("context_str", document.getText()));
String keywords = this.chatModel.call(prompt).getResult().getOutput().getText();
document.getMetadata().put("excerpt_keywords", keywords);
}
return documents;
}
SummaryMetadataEnricher:摘要生成引擎
它的核心任务是为文档生成摘要,并且不仅能生成当前文档的摘要,还能结合上下文生成"上一段"和"下一段"的摘要。
● 作用:解决文档片段(Chunk)上下文丢失的问题。由于文本分割(Splitting)会导致段落被切断,单独的片段可能语义不明。通过注入摘要元数据,可以让模型在检索时快速理解该片段的核心内容及周边上下文。
● 机制:它同样依赖ChatModel,使用默认或自定义的摘要模板(如"Here is the content of the section:\n{context_str}\n\nSummarize the key topics and entities of the section.\n\nSummary:)来生成文本。
● 输出:生成的摘要会存入元数据的section_summary字段。如果配置了PREVIOUS或NEXT,还会生成prev_section_summary和next_section_summary。
java
public List<Document> apply(List<Document> documents) {
List<String> documentSummaries = new ArrayList();
for(Document document : documents) {
String documentContext = document.getFormattedContent(this.metadataMode);
Prompt prompt = (new PromptTemplate(this.summaryTemplate)).create(Map.of("context_str", documentContext));
documentSummaries.add(this.chatModel.call(prompt).getResult().getOutput().getText());
}
for(int i = 0; i < documentSummaries.size(); ++i) {
Map<String, Object> summaryMetadata = this.getSummaryMetadata(i, documentSummaries);
((Document)documents.get(i)).getMetadata().putAll(summaryMetadata);
}
return documents;
}
private Map<String, Object> getSummaryMetadata(int i, List<String> documentSummaries) {
Map<String, Object> summaryMetadata = new HashMap();
if (i > 0 && this.summaryTypes.contains(SummaryMetadataEnricher.SummaryType.PREVIOUS)) {
summaryMetadata.put("prev_section_summary", documentSummaries.get(i - 1));
}
if (i < documentSummaries.size() - 1 && this.summaryTypes.contains(SummaryMetadataEnricher.SummaryType.NEXT)) {
summaryMetadata.put("next_section_summary", documentSummaries.get(i + 1));
}
if (this.summaryTypes.contains(SummaryMetadataEnricher.SummaryType.CURRENT)) {
summaryMetadata.put("section_summary", documentSummaries.get(i));
}
return summaryMetadata;
}
使用示例
KeywordMetadataEnricher
java
KeywordMetadataEnricher enricher = KeywordMetadataEnricher.builder(chatModel)
.keywordsTemplate(new PromptTemplate("Extract 5 important keywords from the following text and separate them with commas:\n{context_str}"))
.build();
Document doc = new Document("This is a document about artificial intelligence and its applications in modern technology.");
List<Document> enrichedDocs = enricher.apply(List.of(doc));
Document enrichedDoc = enrichedDocs.get(0);
String keywords = (String) enrichedDoc.getMetadata().get("excerpt_keywords");
System.out.println("Extracted keywords: " + keywords);
SummaryMetadataEnricher
java
SummaryMetadataEnricher enricher = new SummaryMetadataEnricher(chatModel,
List.of(SummaryType.PREVIOUS, SummaryType.CURRENT, SummaryType.NEXT),
"以下是本节的内容:\n{context_str}\n\n总结本节的主要主题和实体。\n\n简介:",
MetadataMode.ALL);
Document doc1 = new Document("""
自带光环的丑苹果
越丑越甜,越丑越好吃
丑苹果凭什么打败一众苹果,赢得大家的喜爱,高海拔是丑苹果美味的重要因素!
四川盐源,依靠大凉山原始的生态环境,这里是世界公认的7项生态指标都符合苹果生长的优生区域,国家级优质苹果示范区
""");
Document doc2 = new Document("""
这样的生态环境生长出来的苹果,简直自带光环,虽然其貌不扬,但一靠近,就能闻浓郁的到苹果味
不套袋、不打蜡、不催熟,在枝头自由生长的彝家丑苹果,越丑越甜,越丑越好吃,丑苹果是浑然天成的实力派!
""");
List<Document> enrichedDocs = enricher.apply(List.of(doc1, doc2));
// Check the metadata of the enriched documents
for (Document doc : enrichedDocs) {
System.out.println("Current summary: " + doc.getMetadata().get("section_summary"));
System.out.println("Previous summary: " + doc.getMetadata().get("prev_section_summary"));
System.out.println("Next summary: " + doc.getMetadata().get("next_section_summary"));
}
程序输出结果
java
Current summary: ### 总结:
**主要主题**:介绍"丑苹果"因其独特的风味和高品质(越丑越甜)受到欢迎,并强调其产地的高海拔生态环境对品质的关键影响。
**核心实体**:
1. **丑苹果**:外形不美观但口感香甜的苹果品种。
2. **四川盐源**:核心产地,位于大凉山地区,国家级优质苹果示范区。
3. **高海拔/生态环境**:大凉山的高海拔和原始生态条件(符合7项世界公认指标)是苹果优质生长的关键因素。
**亮点**:通过对比"丑"与"甜"的反差,突出自然生态对农产品品质的塑造,强化产地背书。
Previous summary: null
Next summary: ### 主要主题:
1. **自然生态种植**:强调苹果在自然生态环境中生长,不经过人工干预(不套袋、不打蜡、不催熟)。
2. **品质与外观的反差**:突出苹果虽然外观普通("其貌不扬""丑"),但内在品质优异("越丑越甜""实力派")。
3. **感官体验**:通过嗅觉("浓郁的苹果味")和味觉("越甜越好吃")描述苹果的天然魅力。
### 核心实体:
1. **彝家丑苹果**:产品名称,点明产地(彝家)和特点("丑"但好吃)。
2. **自然生长环境**:隐含的实体,指代苹果生长的原始生态环境。
3. **消费者感知**:通过"自带光环""实力派"等拟人化表达,体现产品的市场定位和口碑。
### 总结:
本节通过对比外观与品质,突出彝家丑苹果"天然、健康、美味"的核心卖点,强调其自然种植方式和独特风味。
Current summary: ### 主要主题:
1. **自然生态种植**:强调苹果在自然生态环境中生长,不经过人工干预(不套袋、不打蜡、不催熟)。
2. **品质与外观的反差**:突出苹果虽然外观普通("其貌不扬""丑"),但内在品质优异("越丑越甜""实力派")。
3. **感官体验**:通过嗅觉("浓郁的苹果味")和味觉("越甜越好吃")描述苹果的天然魅力。
### 核心实体:
1. **彝家丑苹果**:产品名称,点明产地(彝家)和特点("丑"但好吃)。
2. **自然生长环境**:隐含的实体,指代苹果生长的原始生态环境。
3. **消费者感知**:通过"自带光环""实力派"等拟人化表达,体现产品的市场定位和口碑。
### 总结:
本节通过对比外观与品质,突出彝家丑苹果"天然、健康、美味"的核心卖点,强调其自然种植方式和独特风味。
Previous summary: ### 总结:
**主要主题**:介绍"丑苹果"因其独特的风味和高品质(越丑越甜)受到欢迎,并强调其产地的高海拔生态环境对品质的关键影响。
**核心实体**:
1. **丑苹果**:外形不美观但口感香甜的苹果品种。
2. **四川盐源**:核心产地,位于大凉山地区,国家级优质苹果示范区。
3. **高海拔/生态环境**:大凉山的高海拔和原始生态条件(符合7项世界公认指标)是苹果优质生长的关键因素。
**亮点**:通过对比"丑"与"甜"的反差,突出自然生态对农产品品质的塑造,强化产地背书。
Next summary: null