GTE+SeqGPT检索增强教程:top-k结果重排序与SeqGPT融合生成策略

GTE+SeqGPT检索增强教程:top-k结果重排序与SeqGPT融合生成策略 GTESeqGPT检索增强教程top-k结果重排序与SeqGPT融合生成策略你是否遇到过这样的场景向一个知识库提问它返回了一堆看似相关的文档但仔细一看要么答非所问要么信息零散需要你自己拼凑答案传统的基于关键词的搜索已经很难满足我们对“智能问答”的期待。今天我们来聊聊如何构建一个更聪明的系统。它不仅能“理解”你的问题从海量资料中精准捞出最相关的信息还能像一位得力的助手把这些信息组织成一段通顺、完整的回答。这背后是两个模型的默契配合GTE-Chinese-Large负责“大海捞针”般的语义检索而SeqGPT-560m则负责“穿针引线”将检索到的信息编织成文。本教程将手把手带你实现这个“检索-增强-生成”的完整流程核心聚焦于两个关键环节如何对初步检索到的多个结果进行智能重排序以及如何让生成模型巧妙地利用这些检索结果来组织答案。无论你是想为自己的项目添加一个智能客服模块还是构建一个垂直领域的知识问答系统这里都有你需要的实战代码和思路。1. 环境准备与项目概览在开始写代码之前我们先快速把环境搭起来并理解整个项目的骨架。1.1 快速启动与验证项目已经为你准备好了三个核心演示脚本我们可以依次运行来感受一下两个模型的基础能力。# 进入项目目录 cd nlp_gte_sentence-embedding # 1. 基础校验验证GTE模型是否能正常加载和计算相似度 python main.py # 2. 语义搜索演示模拟一个智能知识库看AI如何理解问题并找到答案 python vivid_search.py # 3. 文案生成演示测试SeqGPT模型的理解和生成能力 python vivid_gen.py运行main.py你会看到GTE模型计算两个句子相似度的原始分数。运行vivid_search.py会更有趣比如你问“今天天气如何”即使知识库里没有完全相同的句子只有“气象预报显示明日晴朗”它也能通过语义理解找到这条相关信息。1.2 核心模型与工具介绍GTE-Chinese-Large: 这是一个强大的中文语义向量模型。你可以把它想象成一个“句子翻译器”它能把任何一段中文文本转换成一个高维空间中的点向量。语义相近的句子它们的向量点在空间里的距离也会很近。我们就是利用这个特性来做检索的。SeqGPT-560m: 这是一个轻量化的文本生成模型。虽然只有5.6亿参数但在指令微调后它能很好地理解“任务描述”并根据给定的“输入”生成相应的“输出”。我们将用它来消化检索到的信息并生成最终回答。Transformers ModelScope: 我们将主要使用 Hugging Face 的transformers库来加载和运行模型。ModelScope 是另一个优秀的模型仓库这里我们主要用它来获取模型文件。1.3 安装依赖确保你的Python环境在3.8以上然后安装核心依赖pip install torch transformers modelscope如果遇到datasets库的兼容性问题可以指定安装一个稍旧的稳定版本pip install datasets2.16.0模型文件在第一次运行时会自动从 ModelScope 下载可能会比较慢。如果你有加速需求可以按照项目提示使用aria2c等工具预先下载模型权重文件到本地缓存目录通常是~/.cache/modelscope/hub/。2. 基础检索用GTE实现语义搜索我们先来实现最基础的一步给定一个问题如何从一个知识库中找到最相关的文档。2.1 加载GTE模型并生成向量首先我们需要把GTE模型请出来并学会如何用它把文本变成向量。from transformers import AutoTokenizer, AutoModel import torch import torch.nn.functional as F # 1. 指定模型路径使用ModelScope上的模型ID model_name iic/nlp_gte_sentence-embedding_chinese-large # 或者如果你已经下载到本地可以直接指向本地路径 # model_name /path/to/your/local/gte-model # 2. 加载分词器和模型 tokenizer AutoTokenizer.from_pretrained(model_name) model AutoModel.from_pretrained(model_name) model.eval() # 设置为评估模式 def get_embedding(text): 将单条文本转换为向量 # 对文本进行分词和编码 inputs tokenizer(text, paddingTrue, truncationTrue, return_tensorspt, max_length512) # 不计算梯度加快推理速度 with torch.no_grad(): outputs model(**inputs) # GTE模型通常使用[CLS]位置的输出作为句子向量 # 对最后一层隐藏状态进行均值池化这是获取句子向量的常用方法 embeddings outputs.last_hidden_state.mean(dim1) # 对向量进行归一化方便后续计算余弦相似度 embeddings F.normalize(embeddings, p2, dim1) return embeddings.squeeze() # 去掉多余的批次维度 # 试试看 query 如何学习Python编程 query_vec get_embedding(query) print(f查询句子的向量维度{query_vec.shape}) # 应该是 torch.Size([1024]) 之类的2.2 构建示例知识库并进行检索现在我们模拟一个小型知识库并实现最基础的检索功能——返回最相似的一条结果。# 模拟一个简单的知识库 knowledge_base [ Python是一种高级编程语言以简洁易读著称。, 机器学习是人工智能的一个分支让计算机从数据中学习。, 深度学习使用神经网络模型在图像和语音识别上表现突出。, 学习编程需要多写代码从简单的项目开始实践。, TensorFlow和PyTorch是目前流行的深度学习框架。 ] # 为知识库中的所有句子预计算向量在实际应用中这一步通常是离线完成的 print(正在为知识库生成向量...) kb_embeddings [] for sentence in knowledge_base: vec get_embedding(sentence) kb_embeddings.append(vec) # 将列表转换为一个张量方便批量计算 kb_embeddings torch.stack(kb_embeddings) print(f知识库向量集合形状{kb_embeddings.shape}) # torch.Size([5, 向量维度]) def simple_retrieve(query, top_k1): 基础检索返回最相似的top_k个结果 query_vec get_embedding(query) # 计算查询向量与知识库所有向量的余弦相似度 # 余弦相似度 向量点积 / (向量模的乘积)因为向量已归一化所以直接点积即可 similarities torch.matmul(kb_embeddings, query_vec) # 获取相似度最高的top_k个索引 top_k_indices similarities.topk(top_k).indices.tolist() # 获取对应的句子和相似度分数 results [] for idx in top_k_indices: results.append({ text: knowledge_base[idx], score: similarities[idx].item(), index: idx }) return results # 测试检索 query 我想入门编程该怎么做 top_k_results simple_retrieve(query, top_k3) print(f\n查询{query}) print(检索结果) for i, res in enumerate(top_k_results): print(f {i1}. [相似度{res[score]:.4f}] {res[text]})运行这段代码你会发现对于“我想入门编程该怎么做”这个问题系统成功检索到了“学习编程需要多写代码从简单的项目开始实践。”这条知识尽管它们并没有共享任何关键词。这就是语义搜索的魅力。3. 进阶策略Top-K结果重排序基础检索直接返回相似度最高的几条但这可能不够好。比如最相似的文档可能只是一段泛泛而谈而排名第三的文档虽然整体相似度稍低却恰好包含问题需要的具体数字或关键步骤。因此我们需要对初步检索到的Top-K个结果进行“重排序”让最相关、最优质的答案排到最前面。3.1 为什么需要重排序语义相似度 ≠ 答案质量向量相似度衡量的是“意思像不像”但一个“意思像”的文档可能冗长、包含无关信息而另一个则精炼、切中要害。答案覆盖度有时单一文档无法完整回答问题需要综合多个文档的信息。重排序可以考虑文档之间的互补性。关键信息匹配问题中可能包含关键实体如“Python 3.11”或疑问词如“如何”、“为什么”重排序可以强化对这些关键元素的匹配程度。3.2 实现一个简单的重排序器我们来设计一个重排序策略它不仅考虑语义相似度还考虑答案的“信息密度”长度适中和与查询中关键词的匹配度。import re def rerank_results(query, initial_results, knowledge_base_texts): 对初步检索结果进行重排序。 初始结果 initial_results 是包含 index 和 score 的字典列表。 reranked [] for res in initial_results: doc_index res[index] doc_text knowledge_base_texts[doc_index] base_score res[score] # 计算附加分数 additional_score 0.0 # 1. 信息密度奖励避免过长或过短的文档这里假设理想长度在50-200字符之间 doc_len len(doc_text) if 50 doc_len 200: additional_score 0.05 # 长度适中加分 elif doc_len 300: additional_score - 0.02 # 过于冗长减分 # 2. 关键词匹配奖励简单示例匹配名词性词汇 # 这里用一个简单的分词和词性标注思路来模拟实际应用可用jieba等库 query_words set(re.findall(r[\w\u4e00-\u9fff], query)) # 简单提取中文字词 doc_words set(re.findall(r[\w\u4e00-\u9fff], doc_text)) common_words query_words.intersection(doc_words) if common_words: # 有关键词匹配给予奖励奖励值与匹配词数相关 additional_score 0.01 * len(common_words) # 3. 计算最终分数基础相似度分数 附加分数 final_score base_score additional_score reranked.append({ text: doc_text, original_score: base_score, additional_score: additional_score, final_score: final_score, index: doc_index }) # 按最终分数降序排序 reranked.sort(keylambda x: x[final_score], reverseTrue) return reranked # 测试重排序 print( 基础检索结果 ) for res in top_k_results: print(f[原始分数{res[score]:.4f}] {res[text]}) print(\n 重排序后结果 ) reranked_results rerank_results(query, top_k_results, knowledge_base) for res in reranked_results: print(f[最终分数{res[final_score]:.4f} 原始{res[original_score]:.4f} 附加{res[additional_score]:.4f}] {res[text]})这个示例展示了重排序的基本思想。在实际项目中你可以设计更复杂的规则例如使用交叉编码器用一个更精细的模型如MiniLM直接计算查询和每个候选文档的相关性分数这个分数比单纯的向量相似度更准确但计算成本也更高。考虑答案类型如果问题是“是什么”定义类文档优先如果是“怎么做”步骤类文档优先。去重与多样性避免返回多个意思重复的文档确保Top-K结果覆盖不同的信息侧面。4. 生成答案用SeqGPT融合检索结果检索到了相关的文档接下来就是生成答案的环节。我们不可能把好几段文档直接扔给用户而是需要让SeqGPT模型来消化、整合这些信息生成一段连贯的回答。4.1 加载SeqGPT模型首先我们把SeqGPT模型加载进来。from transformers import AutoTokenizer, AutoModelForCausalLM # 加载SeqGPT模型和分词器 seqgpt_model_name iic/nlp_seqgpt-560m seqgpt_tokenizer AutoTokenizer.from_pretrained(seqgpt_model_name) seqgpt_model AutoModelForCausalLM.from_pretrained(seqgpt_model_name) seqgpt_model.eval() # 注意SeqGPT可能使用与GTE不同的分词器需要特别处理。 # 有些对话模型需要设置 pad_token if seqgpt_tokenizer.pad_token is None: seqgpt_tokenizer.pad_token seqgpt_tokenizer.eos_token4.2 设计提示词模板要让生成模型利用检索结果关键在于如何构建提示词Prompt。我们需要把“任务指令”、“检索到的上下文”和“用户问题”清晰地组织起来。def build_prompt_with_context(query, retrieved_docs, max_context_length500): 构建包含检索上下文的提示词。 retrieved_docs: 经过重排序后的文档列表。 max_context_length: 限制上下文的文本长度避免超出模型处理能力。 # 将检索到的文档拼接成上下文 context_parts [] total_len 0 for doc in retrieved_docs[:3]: # 假设只使用前3个最相关的文档 doc_text doc[text] if total_len len(doc_text) max_context_length: context_parts.append(doc_text) total_len len(doc_text) else: break context \n.join(context_parts) # 构建提示词模板。这是一个指令微调模型常用的格式。 prompt_template f你是一个有帮助的AI助手。请根据以下提供的相关背景信息回答用户的问题。 如果信息不足以回答问题请根据你的知识如实回答并说明信息不足。 相关背景信息 {context} 用户问题{query} 请给出回答 return prompt_template # 测试提示词构建 retrieved_for_gen reranked_results[:2] # 取重排序后的前两个文档 prompt build_prompt_with_context(query, retrieved_for_gen) print(生成的提示词\n) print(prompt)4.3 生成最终答案现在我们可以将构建好的提示词送给SeqGPT让它生成答案了。def generate_answer(prompt, max_new_tokens150): 使用SeqGPT模型生成回答 # 编码输入 inputs seqgpt_tokenizer(prompt, return_tensorspt, truncationTrue, max_length512) # 生成 with torch.no_grad(): outputs seqgpt_model.generate( inputs.input_ids, attention_maskinputs.attention_mask, max_new_tokensmax_new_tokens, # 控制生成答案的最大长度 do_sampleTrue, # 使用采样使生成结果更多样 temperature0.7, # 温度参数控制随机性 top_p0.9, # Nucleus sampling 参数 pad_token_idseqgpt_tokenizer.pad_token_id, eos_token_idseqgpt_tokenizer.eos_token_id ) # 解码生成结果 # 跳过输入部分即提示词只取新生成的token generated_ids outputs[0][inputs.input_ids.shape[1]:] answer seqgpt_tokenizer.decode(generated_ids, skip_special_tokensTrue) return answer.strip() # 测试生成答案 answer generate_answer(prompt) print(\n *50) print(用户问题, query) print(检索到的上下文) for doc in retrieved_for_gen: print(f - {doc[text]}) print(\nSeqGPT生成的答案) print(answer) print(*50)运行这段代码你将看到SeqGPT模型基于我们提供的“学习编程需要多实践”等上下文生成了一段关于如何入门编程的建议性回答。它不再是简单地复述原文而是进行了整合与转述。5. 完整流程实战与优化建议让我们把检索、重排序、生成这三个步骤串联起来形成一个完整的问答流水线。5.1 构建完整问答流水线class RetrievalAugmentedQASystem: 一个简单的检索增强问答系统 def __init__(self, knowledge_base_texts): self.kb_texts knowledge_base_texts self.kb_embeddings None print(系统初始化中...) print(1. 加载GTE模型...) self.gte_tokenizer, self.gte_model self._load_gte_model() print(2. 加载SeqGPT模型...) self.seqgpt_tokenizer, self.seqgpt_model self._load_seqgpt_model() print(3. 为知识库生成向量...) self._precompute_kb_embeddings() print(系统初始化完成) def _load_gte_model(self): 加载GTE模型 model_name iic/nlp_gte_sentence-embedding_chinese-large tokenizer AutoTokenizer.from_pretrained(model_name) model AutoModel.from_pretrained(model_name) model.eval() return tokenizer, model def _load_seqgpt_model(self): 加载SeqGPT模型 model_name iic/nlp_seqgpt-560m tokenizer AutoTokenizer.from_pretrained(model_name) model AutoModelForCausalLM.from_pretrained(model_name) model.eval() if tokenizer.pad_token is None: tokenizer.pad_token tokenizer.eos_token return tokenizer, model def _precompute_kb_embeddings(self): 预计算知识库所有文本的向量 embeddings [] for text in self.kb_texts: vec self._get_embedding(text) embeddings.append(vec) self.kb_embeddings torch.stack(embeddings) def _get_embedding(self, text): 使用GTE获取文本向量 inputs self.gte_tokenizer(text, paddingTrue, truncationTrue, return_tensorspt, max_length512) with torch.no_grad(): outputs self.gte_model(**inputs) embeddings outputs.last_hidden_state.mean(dim1) embeddings F.normalize(embeddings, p2, dim1) return embeddings.squeeze() def retrieve(self, query, top_k5): 检索返回最相似的top_k个结果原始 query_vec self._get_embedding(query) similarities torch.matmul(self.kb_embeddings, query_vec) top_k_indices similarities.topk(top_k).indices.tolist() results [] for idx in top_k_indices: results.append({ text: self.kb_texts[idx], score: similarities[idx].item(), index: idx }) return results def answer(self, query, use_rerankTrue, top_k_retrieve5, top_k_use3): 回答问题的完整流程 print(f\n[用户问题] {query}) # 1. 检索 print(f[步骤1] 语义检索 (top_k{top_k_retrieve})...) retrieved self.retrieve(query, top_ktop_k_retrieve) # 2. (可选)重排序 if use_rerank: print(f[步骤2] 对 {len(retrieved)} 个结果进行重排序...) final_docs self.rerank(query, retrieved) else: final_docs retrieved print(f[步骤2] 跳过重排序使用原始排序。) # 3. 构建提示词并生成 print(f[步骤3] 使用前 {top_k_use} 个文档构建提示词并生成答案...) prompt self.build_prompt(query, final_docs[:top_k_use]) answer self.generate(prompt) # 4. 返回结果 return { answer: answer, retrieved_docs: final_docs[:top_k_use], prompt: prompt } def rerank(self, query, initial_results): 重排序使用一个简化策略 reranked [] for res in initial_results: doc_text res[text] base_score res[score] final_score base_score # 简单的长度惩罚/奖励 doc_len len(doc_text) if doc_len 300: # 太长可能包含冗余 final_score * 0.95 elif doc_len 30: # 太短可能信息不足 final_score * 0.98 reranked.append({ text: doc_text, score: final_score, original_score: base_score, index: res[index] }) reranked.sort(keylambda x: x[score], reverseTrue) return reranked def build_prompt(self, query, docs): 构建提示词 context \n.join([doc[text] for doc in docs]) prompt f基于以下信息回答问题。如果信息不足请根据你的知识回答。 信息 {context} 问题{query} 回答 return prompt def generate(self, prompt, max_length200): 使用SeqGPT生成 inputs self.seqgpt_tokenizer(prompt, return_tensorspt, truncationTrue, max_length512) with torch.no_grad(): outputs self.seqgpt_model.generate( inputs.input_ids, attention_maskinputs.attention_mask, max_new_tokensmax_length, do_sampleTrue, temperature0.8, pad_token_idself.seqgpt_tokenizer.pad_token_id, eos_token_idself.seqgpt_tokenizer.eos_token_id ) generated outputs[0][inputs.input_ids.shape[1]:] return self.seqgpt_tokenizer.decode(generated, skip_special_tokensTrue).strip() # 初始化系统 print(*60) print(初始化检索增强问答系统...) qa_system RetrievalAugmentedQASystem(knowledge_base) # 进行问答 questions [ 推荐一个深度学习框架。, 作为新手如何学习人工智能 ] for q in questions: result qa_system.answer(q, use_rerankTrue) print(f\n[最终答案] {result[answer]}) print(f[参考文档]) for i, doc in enumerate(result[retrieved_docs]): print(f 文档{i1}: {doc[text]}) print(*60)5.2 优化建议与扩展思路你现在已经拥有了一个可工作的检索增强生成RAG系统原型。要让它在实际项目中表现更好可以考虑以下优化方向知识库构建与索引分块策略对于长文档不要整篇存入。使用滑动窗口或按段落/章节进行智能分块保证每个文本块信息完整且长度适中。元数据存储为每个文本块存储来源、标题、章节等元数据方便在答案中引用。使用向量数据库当知识库很大时如上万条使用ChromaDB,FAISS,Milvus等专业向量数据库来管理向量和进行高效检索。检索环节优化混合检索结合语义检索GTE和关键词检索如BM25。先用关键词检索快速召回再用语义检索进行精排兼顾准确性和召回率。更优的重排序模型用一个小型的交叉编码器模型如bge-reranker对Top-K结果进行精排它比向量相似度计算更准确。生成环节优化提示词工程精心设计提示词模板明确告诉模型如何利用上下文如“请严格依据以下信息回答”、“如果信息中没有请说不知道”。引用溯源让模型在生成答案时注明信息来源于哪个文档块如用[1]标注增加可信度。处理“未知”问题当检索到的文档与问题相关性都很低时应让模型拒绝回答或转向其内部知识避免“胡编乱造”。系统性能缓存对常见问题的检索结果或生成的答案进行缓存加快响应速度。异步处理将耗时的模型推理过程异步化提升系统吞吐量。6. 总结通过本教程我们完成了一个从语义检索到文本生成的完整链路实践。核心在于GTE与SeqGPT的协同GTE像一位高效的图书管理员它不关心关键词是否匹配而是理解问题的“意图”从知识库的“语义地图”中快速定位相关区域。重排序则像一位经验丰富的审稿人对这些初步找到的资料进行二次筛选和排序挑出最切题、质量最高的部分。SeqGPT最后登场扮演一位专业的撰稿人它消化这些筛选后的材料用自己的语言组织成一段流畅、直接回答用户问题的文本。这个“检索-排序-生成”的范式是当前构建可靠、可信AI问答系统的主流方法。它既利用了外部知识库的准确性和时效性又发挥了大型语言模型的强大理解和生成能力。你可以以此项目为起点将其应用到你的专业领域知识库上比如法律条文问答、医疗知识咨询、产品手册查询等构建属于你自己的智能助手。记住一个好的RAG系统三分在模型七分在数据知识库和工程细节检索与提示策略。多迭代多测试效果会越来越好。获取更多AI镜像想探索更多AI镜像和应用场景访问 CSDN星图镜像广场提供丰富的预置镜像覆盖大模型推理、图像生成、视频生成、模型微调等多个领域支持一键部署。