用 DeepSeek R1 做法律行业的 RAG:哪些有效,哪些无效

2025年3月10日

DeepSeek R1 在首次发布时展现了出色的推理能力。在这篇博客文章中,我们详细介绍了使用 DeepSeek R1 构建针对法律文件的检索增强生成(RAG)系统的经验。

我们选择法律文件是因为法律专业人士常常面临一项艰巨的任务:在案例、法规和非正式法律评论的库中进行导航。

即使是最有意图的研究,也可能在检索正确文件时陷入困境,更不用说准确地总结它们了。这是一个非常适合 RAG 的领域(另见 Harvey AI)!

我们在大量法律文件数据集的基础上构建了 RAG,使用了 DeepSeek R1 和 Qwen2 嵌入。

我们将整个构建 RAG 的流程开源(由 vLLM 和 SkyPilot 提供支持),并分享了一些来之不易的经验——哪些有效,哪些无效。

想看看这个如何运作 🔥? 请在 这里 尝试我们的托管演示。

我们在 这里 开源了构建这个 RAG 的流程。欢迎探索我们如何结合 vLLM + SkyPilot 实现快速、可靠和可扩展的性能。

RAG 系统网站

为什么要在法律文件上构建 RAG?

检索增强生成在准确性和可追溯性至关重要的领域(如法律)中至关重要。通过将专门的检索器与强大的 LLM 生成器连接,我们可以获得两全其美的效果:

  1. 高相关性的文档 检索
  2. 富有推理的文本 生成
  3. 通过直接引用 减少幻觉

为了打造这个 RAG 系统,我们用到了:

  • pile-of-law/pile-of-law 数据集的一个子集(聚焦于法律建议),
  • ChromaDB 作为向量存储,用于保存和查询嵌入向量,
  • Qwen2 嵌入模型用于检索,
  • DeepSeek R1 用于生成最终答案。
    整个系统运行在 vLLM 上,并通过 SkyPilot 无缝部署到任意云端,以获得最佳 GPU 性能和成本效益。我们的开源流水线可以在这里找到。

来之不易的经验教训:该做与不该做

1. 别用 DeepSeek R1 做检索

尽管 DeepSeek R1 的推理能力很强,但它并不适合生成嵌入向量——至少现在还不行。

我们发现了一些例子,证明 DeepSeek R1 生成的嵌入向量跟专门的嵌入模型 Alibaba-NLP/gte-Qwen2-7B-instruct(目前 MTEB 排行榜上最好的嵌入模型)相比有多逊色。

我们用这两个模型为数据集生成嵌入向量,分别构建了两个向量数据库。然后,对两个模型使用相同的查询,在各自生成的向量数据库中找出前 5 个最相似的嵌入向量进行比较。

Qwen 的结果 DeepSeek-R1 的结果
租赁代理说我必须再住一个月,因为需要提前30天通知? 100% 责任在我,导致车祸,现在保险公司将我的案件转到“诉讼部门”
搬进新公寓,但我的一个室友不能在租约上,因为她和前男友还有几个月的租约。 房东告诉租户我们必须支付她的烟囱清扫费,必须使用她选择的清扫工
房东要求签署新租约 前女友偷了我的车
亚利桑那州房东要求我们在房屋展示期间搬出。 1/5 的室友没有支付租金
[MO] 我们的名字都在租约上 - 如果我想把我的男朋友赶出去,最好的做法是什么? 我发生了车祸,之后我还遭到了攻击。

为什么会有差距?

在上表的对比中,DeepSeek R1 的检索结果明显更差。为什么会这样?

我们认为,根源在于 DeepSeek R1 的训练方式。

DeepSeek R1 的设计主要是作为一个推理引擎,专注于顺序思考和逻辑联系。这意味着它并不会把文档映射到一个语义空间中。

相比之下,Qwen2 的变体模型(gte-Qwen2-7B-instruct)是专门为语义相似性任务训练的,它能构建一个高维空间,让概念上相似的文档紧密聚集在一起,不管具体用词如何。

这种训练过程上的差异导致 Qwen2 擅长捕捉查询背后的意图,而 DeepSeek R1 有时会沿着推理路径跑偏,得出主题相关但实际没啥用的结果。

除非 DeepSeek R1 被专门微调用于生成嵌入向量,否则它不应该被用作 RAG 的检索嵌入模型。

2.一定要用 R1 做生成:推理能力真牛

虽然 R1 在生成嵌入向量上不给力,但我们发现它的生成能力简直惊艳。通过利用 R1 的“思维链”方法,我们观察到:

  • 更强的连贯性:这个模型能从多个文档中综合出洞见,引用相关段落时清晰明了。
  • 减少胡编乱造:R1 会在内部“自言自语”地思考,通过你的数据验证每个结论。

我们来看几个例子:

示例 1:解除租约

问题: I want to break my lease. My landlord doesn't allow me to do that.

查看 DeepSeek R1 的思维过程

好的,我想要解除租约,但我的房东不允许我这样做。我不确定我的选择是什么。让我根据提供的文件来试着弄清楚这个问题。

首先,我记得看到过关于提前终止租约的罚款。在文件1中,有人询问是否可以在15个月后解除18个月的租约。答案是可以,但前提是房东同意或者找到新租户。因此,如果我想提前解除租约,房东可能会让我承担责任,除非他们同意或者能迅速重新出租。这似乎是一个可行的途径,但我不确定我的房东是否愿意谈判。

然后,查看文件2,有一种情况是有人因为结婚想要解除租约。答案建议租约可能允许支付费用解除,或者他们可能需要等到租约结束。因此,也许我可以检查一下我的租约是否有允许解除的条件,比如转租或转让条款。如果没有,我可能需要支付罚款或耐心等到租约到期。

文件3讨论了因服务犬而解除租约的情况。选项包括支付罚款、转租、寻找其他服务动物,或等待租约到期。这对我来说并不直接适用,但它表明解除租约通常会涉及一些后果,除非有特定的理由。

在文件4中,房东提高了租金,用户质疑短通知期。答案提到,技术上应该有30天的通知,但房东可能会通过同意在下一个租期之前支付旧租金来提供一个解决方案。如果我的房东拒绝让我解除租约,也许提出支付增加的租金可以是一个选项,但我不知道这在经济上对我是否可行。 文档5提到了一位房东因猫而拒绝续租。答案指出,房东没有法律义务续租,因此可以选择不续租。这让我想到我的房东可能也有类似的自由裁量权,但我不知道这是否是他们拒绝续租的原因。

文档6讨论了带有新条款的续租。答案表示,房东必须遵守当前的租约,直到租约到期。因此,如果我的房东拒绝让我提前解约,可能他们必须等到租约期满,除非有特定条款允许他们不续租。

文档7和8提到租赁办公室使续租变得困难。他们提到房东可能因为差评或其他原因不想续租,但从法律上讲,房东可以选择不续租。这表明房东有一定的权力,但除非有特定原因,他们可能没有法律义务让我提前解约。

文档9谈到搬出需要提前30天通知。即使我在入住时没有签署任何文件,房东仍可能要求提前通知。因此,如果我想在不通知的情况下搬出,可能会面临罚款或需要支付整个租期的租金。

最后,文档10讨论了一位室友离开,租户需要承担租金的租约。答案建议可以进行转租以帮助分担费用。如果我要离开,也许可以尝试转租我的租约部分,但这取决于租约条款。

综合这些信息,我认为我需要检查我的租约,看看是否有关于提前终止的条款。如果没有具体规定,我可能需要支付罚款,除非房东同意让我解约。如果我有正当理由,比如搬家,我可以尝试与房东协商。 转租可能会有所帮助,但我需要查看租约的规定。如果找不到其他安排,我也应该考虑等到租约结束。 查看 DeepSeek R1 的回应 根据对提供文件的分析,以下是当房东拒绝允许解除租约时,解除租约的结构化方法:

1. 审查您的租约

  • 提前终止条款:检查您的租约中是否有关于提前终止的条款。如果存在这样的条款,您可能在法律上有义务支付罚金或提前通知房东,如果您希望解除租约 [1]。
  • 转租或转让:确定您的租约是否允许转租或将租约转让给他人。这可能帮助您避免罚款或法律诉讼 [2]。

2. 理解房东的自由裁量权

  • 自由裁量续租:房东可能有权拒绝续租,特别是如果您的租约中没有允许提前终止的具体条款。在这种情况下,您可能需要履行租约直到其期限结束 [6][8]。

3. 考虑财务后果

  • 罚款:如果您在没有房东同意的情况下解除租约,请准备好可能面临的财务罚款。这可能包括额外的租金支付或押金 [1][9]。

4. 探索谈判

  • 谈判条款:如果您有合理的理由解除租约,例如搬家,您可以与房东谈判,以减少罚款或允许提前终止 [2]。

5. 将转租作为解决方案

  • 转租您的空间:如果可能,考虑将您租约的一部分转租给他人,以帮助承担您的财务责任 [10]。

6. 为未来做好计划

  • 等待租约结束:如果解除租约不可行,计划在租约期限结束之前继续居住。这可以避免任何罚款和法律问题 [6][8]。

结论:

在采取任何行动之前,请仔细审查您的租赁条款。如果不允许提前终止,考虑转租或等待租约结束。如果您可以与房东协商,这可能是最佳的行动方案。始终咨询法律专业人士,以获取针对您情况的具体建议。

示例 2:小额索赔法庭

问题: I received a summons for small claims court regarding a disputed debt. How should I prepare?

查看 DeepSeek R1 的思维过程

好的,我刚收到了一份关于争议债务的小额索赔法庭传票。我不太确定从哪里开始,但我需要弄清楚如何为此做好准备。从搜索结果来看,有几个人经历过类似的情况,也许我可以从他们的经验中学习。

首先,我可能应该了解债务的性质。在文档5中,有人收到了他们认为已经在偿还的债务的传票。他们对为什么再次被传唤感到困惑。因此,我需要验证这笔债务是否有效。也许我应该检查我的记录或向债权人索要债务的证明。根据文档10,拒绝投诉并要求提供证据是个好主意。这样,我可以质疑债务的可信度。

接下来,我应该准备我的案件。在文档1中,有人处理了类似的问题,建议确保案件可以在10分钟内呈现。这意味着我需要简明扼要,并将所有证据整理好。我应该收集任何证明我不欠这笔债务的文件,比如付款收据、债权人发来的表明和解的信件,或文档2中的保险文件。

我还需要考虑证人。在文档4中,一个室友威胁要因兽医账单将此人告上法庭,建议是忽略这个威胁,除非真的被起诉。但如果我被起诉,我应该准备好证人来支持我。也许是一个在我尝试付款时和我在一起的朋友,或者一个可以验证房产状况的前租户,如文档7所示。

理解法庭程序至关重要。从文档7来看,在赢得小额索赔案件后,房东上诉意味着需要再次出庭。这个人不得不再次经历相同的过程。 所以,我应该知道即使我赢了,可能还会有上诉程序。如果需要,我应该准备好再次陈述我的案件。

我还需要考虑传票和诉状。在文件5中,某人必须对诉状作出答复。如果我不这样做,可能会被判缺席判决。因此,我必须及时回应,并清楚地陈述我的辩护理由。

提交答复是下一步。

文件10提到,即使有提议,我也应该始终拒绝并要求提供证据。这将为我后续谈判更好的和解提供筹码。我应该在答复中写下我所有的观点和证据。

考虑我的财务状况也很重要。在文件6中,有人担心因无法支付而被监禁,但已澄清债务人监狱并不存在。不过,我仍然应该沟通我的财务状况。如果我无法支付,我需要表明我已做出合理的努力,并提供一个付款计划,如文件5所示。

如果我对法律问题不确定,也许我应该咨询律师。

在文件3中,某人正在搬家并有一个法庭日期。咨询债务和解律师可能会有所帮助,特别是如果我有其他债务或复杂情况。

最后,我应该为法庭听证会做好准备。准时到达、穿着得体以及准备好所有文件是至关重要的。

在文件7中,某人因准备充分且有证据而获胜,所以我应该效仿这个例子。

因此,综合考虑,我需要:核实债务、准备证据、了解法庭程序、妥善回应传票、可能咨询律师,并为听证会做好组织准备。引用相关文件将有助于我支持我的决定。

查看 DeepSeek R1 的回应

根据对提供的法律文件的分析,以下是针对争议债务的小额索赔法庭传票准备的结构化和有序的计划:

  1. 验证债务

  • 检查债权人的证明:首先审查债权人提供的任何文件,以确认债务的有效性和细节。利用这些信息与您自己的记录或任何先前的沟通进行交叉核对。

  • 请求文件:联系债权人请求债务的正式证明,例如法院判决文件或和解协议。此步骤对于质疑债务的可信度至关重要,如文件10所建议的。

  1. 准备您的案件

  • 收集证据:收集所有相关证据,例如付款收据、债权人表示和解的信件或保险文件,如文件2和文件8中提到的。

  • 组织和呈现证据:确保所有证据都已组织好,并准备在法庭上呈现,遵循文件1中的建议。如有必要,考虑包括证人证言,例如在付款尝试期间在场的朋友或关于物业状况的前租户。

  1. 理解法庭程序

  • 为多次听证会做好准备:如果您赢得案件,请注意可能会有上诉程序,如文件7中详细说明的。如果需要,准备再次呈现您的案件。

  • 熟悉法庭程序:了解小额索赔法庭的基本知识,包括听证会的结构和法官的角色。

  1. 对传票和投诉作出回应

  • 提交答复:确保及时回应传票,以避免缺席判决。 明确在您的答辩中陈述您的辩护理由,并要求提供债务的证明,如文档10所建议的那样。

  • 否认投诉:始终否认投诉,以利用谈判机会,如文档10中提到的。

  1. 考虑财务状况

  • 沟通财务状况:如果您无法偿还债务,请展示您的财务状况并提供还款计划,如文档5中所述。表明您已做出合理努力来解决债务问题。

  1. 如有必要,咨询律师

  • 寻求法律建议:如果情况复杂或您不确定自己的法律地位,请咨询债务和解律师,特别是当涉及其他债务或复杂因素时,如文档3所建议的。

  1. 为法庭听证会做好准备

  • 准时并做好准备:提前到达,穿着得体,并携带所有必要文件。参考文档7中的示例,准备充分的案件会导致成功的结果。

  1. 考虑额外步骤

  • 通知室友或证人:如果债务与共享财产有关,请考虑通知任何可以提供支持的证人或室友,如文档4中提到的。

通过遵循这些步骤,您可以自信地参加小额索赔法庭听证会,确保您的案件得到有效呈现,您的权利得到保护。

从这些例子中,我们观察到 DeepSeek R1 的卓越推理能力。它的思维过程清晰地展示了如何从源法律文件中得出

结论:

  • R1 首先构建了一个 连贯的模型 来处理法律问题,这在其详细的思维过程中得到了证明:First, I remember reading about early termination penalties... Document 1 mentions...。这种以推理为先的方式使模型能够在检索之前系统地连接多个来源的概念。
  • 在处理复杂场景,如租赁终止或小额索赔法庭问题时,我们观察到 R1 明确地分析了对每个文件的理解(Putting this all together...),没有幻觉
  • 最后,生成器通过精确的引用解释其推理,将结论与来源联系起来。这建立了从问题到推理再到答案的清晰连接,确保了严谨性和可访问性。

我们尝试用多样的法律查询来测试模型,模型始终展示了不仅能够从源文件中提取信息,还能从中学习和推理的能力。

总结: 对于问答和总结,R1 是一步一步推理的宝藏。保持在生成器阶段,你几乎不会后悔。

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.
你是一个分析法律文件及相关内容的得力 AI 助手。在回复时,请遵循以下指南:  
- 在提供的搜索结果中,每个文档格式为 [Document X begin]...[Document X end],其中 X 表示文档的数字索引。  
- 引用文档时使用 [citation:X] 格式,其中 X 是文档编号,引用需紧跟在相关信息后。  
- 在整个回答中穿插引用,而不仅仅在结尾处。  
- 如果信息来自多个文档,使用多个引用,如 [citation:1][citation:2]。  
- 并非所有搜索结果都相关——请评估并仅使用相关信息。  
- 对于较长的回答,按清晰的段落或部分结构化,便于阅读。  
- 如果在提供的文档中找不到答案,请说明——不要编造信息。  
- 某些文档可能是非正式讨论或 Reddit 帖子,需相应调整解读方式。  
- 在回答中尽量多使用引用。  

首先,在 <think> 标签中解释你的思考过程。  
然后在思考过程后给出最终答案。

文档分块

此外,我们发现 有效的文档分块对于准确的文档检索非常重要

对文档进行分块有助于使每个嵌入更简洁地表示特定主题,并减少每次生成嵌入所需处理的令牌数量。

我们使用了基于句子的分割(通过 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. 在块之间保留重叠句子以保持文档上下文

4. vLLM 高效且快速

由于法律文档包含大量数据,为 RAG 生成嵌入可能需要很长时间。

最初,我们使用了默认的 HuggingFace 库 sentence_transformer

我们首先在典型的 Nvidia L4 GPU 上运行,但遇到了我们“最喜欢”的错误:CUDA 内存不足。在尝试 Nvidia A100 后,我们发现 sentence_transformer 加载完整的 Alibaba-NLP/gte-Qwen2-7B-instruct 模型需要 57GB 的 DRAM。

sentence_transformer_oom

我们切换到 vLLM,这是一个高吞吐量和内存高效的 LLM 推理和服务引擎。

使用 vLLM,我们可以在标准的 Nvidia L4 GPU 上运行模型,这大约需要 24G 的 DRAM GPU。

L4 的成本也比 A100 便宜得多:在 GCP 上,Nvidia L4 的费用超过 $0.7/hour while Nvidia A100 is at least $2.9/小时。 在对比Nvidia A100(80GB DRAM)上的vllm和句子变换器时,我们发现使用vLLM生成Qwen2模型的嵌入速度比句子变换器快了多达5.5倍

对于包含10,000份法律文件和15,000个片段的语料库,处理时间如下:

  • 标准句子变换器:约5.5小时
  • vLLM实现:约1小时

vllm对比

以下是我们使用vLLM进行向量化的SkyPilot YAML配置:

用于vLLM嵌入生成的SkyPilot YAML:

name: compute-legal-embeddings

resources:
    # this needs ~ 24G DRAM GPU for vllm, 57G with SentenceTransformer
    accelerators: {L4:1, A100:1} 

envs:
    START_IDX: ${START_IDX}
    END_IDX: ${END_IDX}

file_mounts:
    /output:
        name: my-bucket-for-embedding-output
        mode: MOUNT

setup: |
    pip install torch==2.5.1 vllm==0.6.6.post
    ...
    
envs: 
    MODEL_NAME: "Alibaba-NLP/gte-Qwen2-7B-instruct"
    
run: |
    python -m vllm.entrypoints.openai.api_server \
    --host 0.0.0.0 \
    --model $MODEL_NAME \
    --max-model-len 3072 \
    --task embed &

    python scripts/compute_embeddings.py \
    --start-idx $START_IDX \
    --end-idx $END_IDX \
    --chunk-size 2048 \
    --chunk-overlap 512 \
    --vllm-endpoint http://localhost:8000 

5. 不要让向量化耗时超过100小时

使用单个L4 GPU生成法律文件向量化耗时过长——最长可达100小时。我们可以考虑使用临时机器,这可以显著减少向量化的时间和成本:

名称 原始 100个临时工作节点
时间 100小时 1小时(快100倍)
成本 $70.1 $28.2(节省2.4倍)

因此,我们通过SkyPilot托管作业扩展向量化,支持以下功能:

  1. 大规模并行作业: 我们将数据拆分为多个分区,并在多个云中启动多个嵌入作业。
  2. 使用带自动恢复的临时实例: 如果临时实例或节点失败,SkyPilot会自动重试,从中断的地方继续。
  3. 统一存储: 所有嵌入直接流入云存储桶(采用Apache Parquet格式)。不再丢失工作或处理部分输出。

下图展示了并行运行10个以上的临时作业进行向量化,SkyPilot在不同区域之间寻找最佳GPU可用性:

仪表盘 结果: 我们不再需要等待几天才能获得嵌入。我们的工作动态扩展,使我们能够快速从原型转向生产——即使面对庞大的数据集。

结论

构建 DeepSeek R1 RAG 用于法律文件让我们学到了几个关键教训:

  1. 利用专门的嵌入模型(如 Qwen2)进行强大的检索。
  2. 在生成阶段使用 R1 的推理能力来处理复杂的法律查询。
  3. (提示)工程仍然是控制引用和结构内容的关键。
  4. 使用 vLLM 加速推理,以实现显著的效率和速度提升。
  5. 通过 SkyPilot 在云端和您自己的基础设施上扩展 AI,以保持计算时间短且预算可控。

想了解更多?

注意:该 RAG 系统应被视为法律研究和分析的强大工具,而不是合格法律专业人士的替代品。输出内容不应被视为法律建议。

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

入群交流请联系:a52947593