DeepSeek R1 在首次发布时就展现出了强大的推理能力。在这篇博客文章中,我们详细分享了使用 DeepSeek R1 构建检索增强生成(Retrieval-Augmented Generation,RAG)系统的经验,专门针对法律文档领域。
我们选择法律文档是因为法律专业人士通常面临一项艰巨的任务:在大量案例、法规和法律评论中进行检索。即使是最用心的研究,也可能因为难以找到正确的文档而受到阻碍,更不用说准确地总结内容了。这正是 RAG 能大显身手的领域(另请参见 Harvey AI)。
我们基于庞大的法律文档数据集构建了 RAG,采用了 DeepSeek R1 和 Qwen2 作为嵌入模型。我们开源了整个 RAG 构建流程(由 vLLM 和 SkyPilot 提供支持),并分享了一些宝贵的经验——哪些方法有效,哪些无效。
想要亲自体验 🔥? 试试我们的在线演示:点击这里。
我们已开源该 RAG 构建流程 点击这里查看。欢迎探索我们如何结合 vLLM + SkyPilot 以实现快速、稳定且可扩展的性能。
为什么要在法律文档上构建 RAG?
在法律等对准确性和可追溯性要求极高的领域,检索增强生成(RAG)至关重要。通过将专门的检索器与强大的大语言模型(LLM)生成器相结合,我们可以获得最佳效果:
- 高相关性文档的检索
- 具有推理能力的文本生成
- 通过直接引用减少幻觉
为构建此 RAG,我们使用了:
- pile-of-law/pile-of-law 数据集的一个子集(专注于法律建议);
- ChromaDB 作为向量存储库,用于存储和查询嵌入;
- Qwen2 作为检索嵌入模型;
- DeepSeek R1 作为最终答案生成模型。
整个系统运行在 vLLM 之上,并可通过 SkyPilot 无缝部署到任何云端,以确保最佳 GPU 可用性和成本效益。开源的完整 RAG 构建流程可在此处获取。
宝贵经验:注意事项与禁忌
1. 不要使用 DeepSeek R1 进行检索
尽管 DeepSeek R1 具有卓越的推理能力,但它不适合用于生成嵌入——至少目前不适合。
我们发现,与专门的嵌入模型 Alibaba-NLP/gte-Qwen2-7B-instruct
(目前在 MTEB 排行榜 上排名最高的嵌入模型)相比,DeepSeek R1 生成的嵌入效果较差。
我们使用这两个模型分别为数据集生成嵌入,并构建两个向量数据库。然后,对这两个模型使用相同的查询,并检索各自向量数据库中最相似的前 5 个嵌入。
查询: 我收到了一张关于债务纠纷的小额索赔法庭传票,我应该如何准备?
Qwen2 的检索结果 | DeepSeek-R1 的检索结果 |
---|---|
[CA] 一个关于小额索赔的问题 | 我可以起诉雇佣电话推销员并且无视法律不停给我打电话的公司吗? |
[CO] 刚收到法院传票,是关于一笔保险已经支付的医院账单……我有证明,但我仍然需要请律师来解决这个问题吗? | 前女友的女儿因入店行窃被捕,我们该怎么办? |
我因拖欠 3,500 美元贷款被起诉 | 深夜下班后,你走到几乎空无一人的停车场,看到两个人正在撬你的车门。你让他们住手,但他们无视你。你持有隐蔽手枪,你可以怎么做? |
室友的猫吃了些坚果混合零食,不得不去看兽医,账单超过 1,500 美元。她威胁要把我告上小额索赔法庭让我支付费用,但没有证据表明零食是我的。我应该担心吗? | 密苏里州持有大麻的指控,但没有实物证据。 |
我最近收到了一张关于一笔我以为已经支付的债务的民事传票。 | “放狗咬人”合法吗? |
为什么会有差异?
在上表中,DeepSeek R1 的检索结果明显较差。这是为什么呢?
我们认为,根本问题在于 DeepSeek R1 的训练方式。DeepSeek R1 的架构主要侧重于推理,强调顺序思维和逻辑连接。这意味着 DeepSeek R1 并不会将文档映射到一个语义空间中。
相比之下,Qwen2 变体 (gte-Qwen2-7B-instruct
) 专门针对语义相似性任务进行训练,能够创建一个高维空间,使概念上相似的文档紧密聚类在一起,而不受具体措辞的影响。
这种训练方式的差异意味着 Qwen2 在捕捉查询意图方面表现出色,而 DeepSeek R1 有时会遵循推理路径,导致检索到的结果在主题上相关但实际上无关紧要。
除非对 DeepSeek R1 进行嵌入微调,否则不应将其用作 RAG 的检索嵌入模型。
2. 使用 R1 进行生成:推理能力令人印象深刻
尽管 R1 在嵌入方面存在一定困难,但我们发现它的 生成 能力非常出色。通过利用 R1 的链式思维(chain-of-thought)方法,我们观察到:
- 更强的连贯性: 该模型能够综合多个文档的信息,并清晰地引用相关段落。
- 减少幻觉: R1 在内部“自言自语”进行推理,通过你的数据验证每一个结论。
让我们来看几个示例:
示例 1:解除租约
问题: 我想解除租约,但房东不允许我这样做。
查看 DeepSeek R1 的思维过程
好的,我想要终止我的租约,但房东不允许。我不确定自己有哪些选项。我想根据提供的文件来理清思路。
首先,我记得在文件 1 中读到关于提前终止租约的罚款。有个人问是否可以在 18 个月的租期满 15 个月后解约,答案是可以,但前提是房东同意或找到新租客。因此,如果我想提前终止租约,房东可能会让我承担责任,除非他们同意或能迅速重新出租。这似乎是一个可能的途径,但我不确定我的房东是否愿意谈判。
接着,在文件 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: 小额索赔法院
问题: 我收到了一张关于争议债务的小额索赔法院传票。我应该如何准备?
查看 DeepSeek R1 的思考过程
好的,我刚收到了一份关于争议债务的小额法庭传票。我不太确定从哪里开始,但我需要弄清楚如何为此做好准备。从搜索结果来看,有几个人经历过类似的情况,也许我可以从他们的经验中学到一些东西。
首先,我可能应该了解债务的性质。在文档 5 中,有人收到了他们认为已经在支付的债务的传票。他们不明白为什么再次收到传票。所以,我需要验证这个债务是否有效。也许我应该检查我的记录或向债权人索要债务证明。根据文档 10,否认投诉并要求提供证据是个好主意。这样,我可以质疑债务的可信度。
接下来,我应该准备我的案件。在文档 1 中,有人处理过类似的问题,建议是确保案件能在 10 分钟内或更短时间内呈现。也就是说,我需要简明扼要,并将所有证据整理好。我应该收集任何证明我不欠债的文件,比如付款收据、债权人表明已结算的信件,或者像文档 2 中那样的保险文件。
我还需要考虑证人问题。在文档 4 中,一个室友威胁要因兽医账单将此人告上法庭,建议是除非被起诉,否则忽略威胁。但如果我被起诉,我应该准备好有证人支持我。也许是当时在我尝试付款时陪伴我的朋友,或者是能够证明房屋状况的前租户,像文档 7 中所示。
了解法庭程序至关重要。从文档 7 看,在小额法庭案件胜诉后,房东提出上诉意味着需要再次出庭。此人不得不再次经历相同的过程。所以,我应该知道即使我赢了,可能仍然会有上诉过程。如果需要,我应该准备再次陈述我的案件。
我还需要考虑传票和诉状。在文档 5 中,某人必须对诉状作出回应。如果我不这么做,可能会被判默认判决。所以,我必须按时回应并清晰地陈述我的辩护理由。
提交答辩书是下一步。文档 10 提到,即使有提议,我也应该始终拒绝并要求提供证据。这可以让我在后续谈判中拥有更多筹码。我要在答辩书中列出所有我的观点和证据。
考虑我的财务状况也很重要。在文档 6 中,有人担心因为无法支付而被监禁,但已澄清债务人监狱并不存在。不过,我还是应该与法庭沟通我的财务状况。如果我无法支付,我需要表明我已做出合理努力并提供分期付款计划,就像文档 5 中所示。
如果我对法律问题不确定,或许我应该咨询律师。在文档 3 中,此人正在搬家并且有庭审日期。咨询债务解决律师可能有帮助,尤其是当我有其他债务或复杂情况时。
最后,我应该为庭审做好准备。准时到场、穿着得体并准备好所有文件是至关重要的。在文档 7 中,此人通过充分准备证据赢得了案件,所以我应该效仿。
总而言之,我需要:验证债务、准备证据、了解法庭程序、正确回应传票、可能咨询律师,并为庭审做好组织准备。引用相关文档将有助于我支持我的决策。
查看 DeepSeek R1 的回复
根据提供的法律文件分析,以下是为准备有关争议债务的小额法庭传票的结构化和组织化计划:
核实债务
检查债权人的证明:首先审查债权人提供的任何文件,以确认债务的有效性和细节。利用这些信息与您自己的记录或任何先前的通讯进行交叉核对。
请求文件:联系债权人请求债务的正式证明,如法院判决文件或和解协议。根据文件 10 的建议,这一步对于质疑债务的可信度至关重要。
准备您的案件
收集证据:收集所有相关的证据,如支付收据、债权人表示和解的信件或保险文件,正如文件 2 和文件 8 中所提到的。
组织和呈现证据:确保所有证据已组织好并准备在法庭上呈现,遵循文件 1 中的建议。如果需要,考虑包括证人陈述,例如曾在支付尝试期间在场的朋友或有关物业状况的前租户。
了解法庭程序
为多次听证会做好准备:如果您赢得案件,要意识到可能会有上诉程序,如文件 7 所述。如果有必要,请准备再次陈述您的案件。
熟悉法庭程序:了解小额法庭的基本情况,包括听证会的结构和法官的角色。
回应传票和起诉状
提交答辩书:确保及时回应传票,以避免缺席判决。在您的答辩书中清楚地陈述您的辩护理由,并请求债务的证明,如文件 10 所建议。
否认起诉状:始终否认起诉状,以便利用谈判机会,如文件 10 中提到的。
考虑财务状况
沟通财务状况:如果您无法偿还债务,展示您的财务状况并提供分期付款计划,正如文件 5 中所概述的那样。表明您已做出合理的努力来解决债务问题。
必要时咨询律师
寻求法律建议:如果情况复杂或您不确定自己的法律地位,请咨询债务和解律师,尤其是当涉及其他债务或复杂因素时,如文件 3 中的建议。
为法庭听证做准备
准时到场并做好准备:提前到达,穿着得体,携带所有必要的文件。参考文件 7 中的示例,充分准备的案件取得了成功的结果。
考虑额外步骤
通知室友或证人:如果债务涉及共享财产,考虑通知任何可以提供支持的证人或室友,如文件 4 中提到的那样。通过遵循这些步骤,您可以自信地应对小额法庭听证会,确保您的案件得到有效陈述,并保护您的权利。
从这些示例中,我们观察到 DeepSeek R1 具有卓越的推理能力。其思维过程清晰地展示了如何从源法律文件中得出结论:
- R1 首先构建了一个 连贯的模型 来理解法律问题,这体现在其详细的推理过程中:
首先,我记得关于提前终止罚款的内容……文档 1 提到……
这种“先推理后检索”的方法使模型能够在检索之前系统地连接多个来源的概念。 - 在处理租赁终止或小额诉讼法院等复杂情境时,我们观察到 R1 不会产生幻觉 ,而是明确地梳理其对每个文档的理解(
综合来看……
)。 - 最后,生成器会使用精确的引用来解释其推理过程,将结论与来源联系起来。这确保了从问题到推理再到答案的清晰链条,保证了严谨性和可访问性。
我们尝试了各种法律查询,发现模型不仅能够从源文件中提取信息,还能学习并推理其内容。
结论: 在问答和摘要任务中,R1 是逐步法律推理的“金矿”。将其用于生成阶段,几乎不会让你失望。
3. 工程仍然重要
提示词设计
高级推理能力并不会消除对精心设计的提示词的需求。我们发现,在提示词中明确指引对于以下方面至关重要:
- 促使 文档引用 贯穿整个生成答案。
- 防止幻觉 ,采用“引用来源或说明不知道”的方式。
- 将最终答案结构化,使其更易读 。
在实验过程中,我们构建了以下提示词:
你是一个帮助分析法律文件及相关内容的 AI 助手。在回复时,请遵循以下准则:
- 在提供的搜索结果中,每个文档的格式为 [Document X begin]...[Document X end],其中 X 表示文档的编号。
- 使用 [citation:X] 形式的引用格式,其中 X 是文档编号,并将引用放在相关信息之后。
- 在整个回答中加入引用,而不仅仅是结尾处。
- 如果信息来自多个文档,请使用多个引用,例如 [citation:1][citation:2]。
- 不是所有搜索结果都相关——请评估并仅使用相关信息。
- 对于较长的回答,请将内容结构化,以段落或部分的形式呈现,以提高可读性。
- 如果在提供的文档中找不到答案,请直接说明,而不要编造信息。
- 一些文档可能是非正式讨论或 Reddit 帖子——请根据内容调整解读方式。
- 让回答尽可能多地包含引用。
首先,在 <think> 标签中解释你的思维过程。
然后,在思维过程之后提供最终答案。
文档分块
此外,我们发现 有效的文档分块对于准确的文档检索至关重要。对文档进行分块有助于使每个嵌入更精确地表示特定主题,并减少每次嵌入生成所需的 Token 数量。
我们使用基于句子的分割(通过 NLTK)来对文档进行分块。同时,每个分块的开头和结尾都会包含与相邻分块重叠的内容。这有助于模型更好地理解部分引用,而不会丢失整体语境。以下是用于文档分块的代码:
def chunk_document(document, chunk_size=2048, overlap=512):
"""使用基于句子的分割方法将文档拆分为重叠的块。"""
text = document['text']
chunks = []
# 先按句子分割
sentences = nltk.sent_tokenize(text)
current_chunk = []
current_length = 0
for sentence in sentences:
sentence_len = len(sentence)
# 如果加入此句子后会超出块大小,则保存当前块
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,
# 其他元数据字段...
})
# 保留最后几句话作为重叠部分
overlap_text = ' '.join(current_chunk[-2:]) # 保留最后 2 句
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 代表空格
要点总结:
- 使用 NLTK 进行基于句子的 Token 分割,而非基于字符的分割。
- 通过在块之间保留重叠句子来保持文档上下文。
4. vLLM 高效且快速
由于法律文档数据量庞大,为 RAG 生成嵌入可能需要较长时间。
最初,我们使用了 HuggingFace 的 sentence_transformer
。我们先在 Nvidia L4 GPU 上运行,但遇到了常见的“CUDA 内存不足”错误。之后,我们尝试了 Nvidia A100,发现 sentence_transformer
需要 57GB DRAM 来加载完整的 Alibaba-NLP/gte-Qwen2-7B-instruct
模型。
我们改用 vLLM,这是一种高吞吐量、节省内存的 LLM 推理与服务引擎。
使用 vLLM 后,我们可以在标准 Nvidia L4 GPU(约 24GB DRAM)上运行模型。相比之下,L4 的价格远低于 A100:在 GCP 上,Nvidia L4 约 $0.7/小时,而 Nvidia A100 至少 $2.9/小时。
在 Nvidia A100(80GB DRAM)上比较 vLLM 和 sentence transformer,我们发现 vLLM 生成 Qwen2 嵌入的速度提高了 5.5 倍。
在处理 10,000 份法律文档(共 15,000 个分块)时:
- 标准 sentence transformer 处理时间:约 5.5 小时
- vLLM 处理时间:约 1 小时
以下是用于在 vLLM 上运行 Alibaba-NLP/gte-Qwen2-7B-instruct
以生成嵌入的 SkyPilot YAML 配置:
name: compute-legal-embeddings
resources:
# vLLM 需要 ~24GB DRAM GPU,SentenceTransformer 需要 57GB
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+ 小时 —— 使用 SkyPilot 并行处理
在单个 L4 GPU 上生成法律文档的嵌入需要 100 小时。我们可以利用 Spot 实例大幅降低时间和成本:
名称 | 原始时间 | 100 个 Spot 实例 |
---|---|---|
时间 | 100 小时 | 1 小时(100 倍加速) |
成本 | $70.1 | $28.2(2.4 倍节省) |
结果: 我们不再需要等待数天来生成嵌入。我们的任务可以动态扩展,从原型迅速转向生产,即使面对庞大数据集。
结论
构建 DeepSeek R1 RAG 让我们学到了几个关键点:
- 利用专门的嵌入模型(如 Qwen2)提升检索能力。
- 在生成阶段利用 R1 的推理能力处理复杂法律查询。
- (提示词)工程仍然是控制引用和结构化内容的关键。
- 使用 vLLM 提升推理效率,实现显著加速。
- 在云端和自有基础设施上扩展 AI 计算,缩短计算时间并降低成本。
想了解更多?
- RAG 构建指南:GitHub Repo
- 使用 SkyPilot 扩展 AI 任务:SkyPilot Docs
注意: 该 RAG 系统是法律研究和分析的强大工具,但不能替代合格的法律专业人士。其输出不应被视为法律建议。