DeepSeek R1在首次推出时就展现了出色的推理能力。本文将详细分享我们使用DeepSeek R1构建法律文件检索增强生成(RAG)系统的经验。

我们选择法律文件作为研究重点,是因为法律专业人士经常会面临一个挑战:需要浏览大量的案例、法规及非正式法律评论。即使是最认真的研究人员,在检索合适的文档时也可能遇到困难,更不用说准确总结这些文档了。因此,RAG在这个领域展现出巨大的潜力。

我们在一个庞大的法律文件数据集上构建了RAG系统,技术架构包括:

1. 利用Qwen2进行嵌入检索;

2. 使用ChromaDB作为向量存储,负责嵌入的存储和查询;

3. 通过DeepSeek R1生成最终答案。

通过将专用检索器与强大的推理大模型结合,我们实现了“三全其美”的效果:

1. 高相关性的文档检索;

2. 丰富的推理文本生成;

3. 通过直接引用减少生成内容的幻觉。

我们把RAG构建的整个流程开源(地址见文末),并分享了一些宝贵的经验——哪些方法有效,哪些方法无效。

来之不易的教训:该做和不该做的事情

1. 不要使用 DeepSeek R1 进行检索

尽管DeepSeek R1在推理能力上表现出色,但它目前并不适合用于生成嵌入。

我们发现了一些例子,表明DeepSeek R1生成的嵌入与专门的嵌入模型Alibaba-NLP/gte-Qwen2-7B-instruct相比存在很大差距,而Qwen 2则是当前MTEB排行榜上表现最好的嵌入模型。

我们分别使用这两个模型为数据集生成嵌入,并建立了两个向量数据库。接着,我们对这两个模型进行了相同的查询,找出了各自向量数据库中最相似的前五个嵌入。

以下是关于解除租约的一些问答。

打开网易新闻 查看精彩图片

简单翻译下

打开网易新闻 查看精彩图片

关于小额赔偿

打开网易新闻 查看精彩图片
简单翻译下
打开网易新闻 查看精彩图片

在上述表格中,DeepSeek R1的检索结果明显劣于其他模型。这其中的根本原因在于DeepSeek R1的训练方式。该模型被设计为推理引擎,注重顺序思维和逻辑关系,因此它并不将文档映射到语义空间。

相对而言,Qwen2模型(gte-Qwen2-7B-instruct变体)则专门针对语义相似性进行了训练,构建了一个高维空间,使得概念相近的文档能够紧密聚集,尽管其具体措辞可能不同。

这种训练方式的差异使得Qwen2能够更好地捕捉查询背后的意图,而DeepSeek R1有时则会依据推理的逻辑路径,导致返回的结果在主题上相关但实际上并不相关。

除非对DeepSeek-R1进行了针对嵌入的微调,否则不应将其用作RAG中的检索嵌入模型。

2. 务必使用 R1 进行生成:推理令人印象深刻

尽管R1在生成嵌入方面存在困难,但其生成能力却非常出色。通过采用R1的思维链方法,我们观察到以下几点:

1.更高的连贯性:该模型能够整合多个文档中的见解,并清晰地引用相关段落。

2.幻觉减少:R1在生成过程中会进行“大声思考”,通过数据的视角来验证每一个结论。

接下来,让我们来看一些具体的例子:

打开网易新闻 查看精彩图片
打开网易新闻 查看精彩图片

通过这些实例,我们发现DeepSeek R1展现了卓越的推理能力。它的思考过程清晰地说明了如何从源法律文件中得出结论:

R1首先构建了一个连贯的法律问题模型,其思维过程详尽无遗。例如,它记得关于提前终止罚款的相关内容,并引用了文件1中的信息。这种推理优先的方法使得模型能够在检索之前有条不紊地整合多个来源的概念。

在处理复杂的场景,如租约终止或小额索赔法庭问题时,我们观察到R1能准确理解每份文件内容,并将所有信息结合在一起,避免了幻觉的产生。

最终,推理大模型通过精确的引用来阐释其推理过程,将结论与源材料紧密联系。这确保了从问题到推理再到答案的明确关联,体现了严谨性和可用性。

我们使用多种法律查询测试了该模型,它始终表现出既能从源文件中提取信息,又能进行学习和推理的能力。

要点:对于问答和总结任务,R1是逐步推理法律逻辑的重要资源。在生成答案的阶段,使用它是绝对可以的。

3. 提示工程仍然很重要

高级推理并不能完全取代对精心设计提示的需求。我们的研究表明,提示中的明确指示对以下几个方面非常重要:

1. 促使生成的回答中包含文献引用。

2. 通过“引用资料或表示不知道”的方式来避免错误信息。

3. 以用户友好的方式呈现最终回答。

在整个实验过程中,我们制定了以下提示:

You are a helpful AI assistant analyzing legal documents and related content. When responding, please follow these guidelines:- In the search results provided, each document is formatted as [Document X begin]...[Document X end], where X represents the numerical index of each document.- Cite your documents using [citation:X] format where X is the document number, placing citations immediately after the relevant information.- Include citations throughout your response, not just at the end.- If information comes from multiple documents, use multiple citations like [citation:1][citation:2].- Not all search results may be relevant - evaluate and use only pertinent information.- Structure longer responses into clear paragraphs or sections for readability.- If you cannot find the answer in the provided documents, say so - do not make up information.- Some documents may be informal discussions or reddit posts - adjust your interpretation accordingly.- Put citation as much as possible in your response. First, explain your thinking process between <think> tags.Then provide your final answer after the thinking process.
打开网易新闻 查看精彩图片

4. 文档分块

此外,我们发现,将文档进行有效分块对于准确检索文档至关重要。文档分块能够更清晰地表达特定主题,同时减少每个嵌入生成时需要处理的标记数量。

我们通过NLTK使用句子感知的方式对文档进行分块,并确保每个块的开头和结尾部分与相邻块有重叠内容。这种做法有助于模型更好地理解部分引用,而不至于失去整体的上下文。以下是文档分块的代码:

def chunk_document(document, chunk_size=2048, overlap=512): """Split document into overlapping chunks using sentence-aware splitting.""" text = document['text'] chunks = []  Split into sentences first sentences = nltk.sent_tokenize(text) current_chunk = [] current_length = 0 for sentence in sentences: sentence_len = len(sentence)  If adding this sentence would exceed chunk size, save current chunk if current_length + sentence_len > chunk_size and current_chunk: chunk_text = ' '.join(current_chunk) chunks.append({ 'id': document['id'], 'name': document['name'], 'content': document['text'], 'chunk_start': len(' '.join(current_chunk[:-(2 if overlap > 0 else 0)])) if overlap > 0 else 0,  Additional metadata fields... })  Keep last few sentences for overlap overlap_text = ' '.join(current_chunk[-2:])  Keep last 2 sentences current_chunk = [overlap_text] if overlap > 0 else [] current_length = len(overlap_text) if overlap > 0 else 0 current_chunk.append(sentence) current_length += sentence_len + 1  +1 for space

要点:

1. 采用NLTK进行句子感知的分词,而非依赖字符级的分块。

2. 通过在块之间重叠句子来保留文档的上下文信息。

5. vLLM 高效快速

由于法律文件的数据量庞大,生成RAG的嵌入可能会消耗较长时间。

起初,我们使用HuggingFace库中的默认sentence_transformer进行操作。最初在典型的Nvidia L4 GPU上运行时,我们遇到了常见的错误:CUDA内存不足。在尝试在Nvidia A100上运行后,我们发现要完整加载Alibaba-NLP/gte-Qwen2-7B-instruct模型,sentence_transformer需要57GB的DRAM。

打开网易新闻 查看精彩图片

我们转向使用vLLM,这是一种高吞吐量且内存效率高的LLM推理与服务引擎。通过采用vLLM,我们可以在标准的Nvidia L4 GPU上运行模型,而vLLM大约需要24GB的GPU内存。此外,L4的成本也远低于A100:在GCP上,Nvidia L4每小时的费用超过0.7美元,而Nvidia A100每小时的费用至少为2.9美元。

在搭载80GB DRAM的Nvidia A100上比较vLLM和句子转换器时,我们发现使用vLLM生成Qwen2模型的嵌入速度比句子转换器快了5.5倍。在处理包含15000个块的10000份法律文件的语料库时,所需的时间为:标准的sentence_transformer大约需要5.5小时,而vLLM的实施则只需约1小时。

打开网易新闻 查看精彩图片

结论

构建针对法律文件的DeepSeek R1 RAG使我们获得了一些重要的经验教训。首先,利用专门的嵌入模型(如Qwen2)能够实现更加稳健的检索。其次,在生成阶段,R1的推理能力对解决复杂的法律查询非常有效。此外,提示工程在管理引用和构建内容方面仍然至关重要。最后,借助vLLM加速推理显著提升了效率和速度。

https://github.com/skypilot-org/skypilot/tree/master/llm/rag

https://blog.skypilot.co/deepseek-rag/

我们相信人工智能为普通人提供了一种“增强工具”,并致力于分享全方位的AI知识。在这里,您可以找到最新的AI科普文章、工具评测、提升效率的秘籍以及行业洞察。

欢迎关注“福大大架构师每日一题”,让AI助力您的未来发展。