使用RAG、Milvus和Ollama简化法律研究

2024-11-29

By Stephen Batifol

使用RAG、Milvus和Ollama简化法律研究

在这篇博客中,我们将探讨如何将RAG应用于法律数据。

法律研究可能非常耗时。您通常需要审查大量文档以找到所需的答案。检索增强生成(RAG)可以帮助简化您的研究过程。

什么是RAG?

检索增强生成(RAG)是一种通过整合额外数据源来增强LLM的技术。典型的RAG应用包括:

  • 索引 - 从源摄取数据并进行索引的管道,通常包括在Milvus中加载、分割和排序数据。
  • 检索和生成 - 在运行时,RAG处理用户的查询,从存储在Milvus中的索引中提取相关数据,LLM根据这个丰富的上下文生成响应。

在这篇动手指南中,我们将探索如何使用Ollama构建检索增强生成(RAG)系统,重点关注法律数据,并利用Milvus作为我们的向量数据库。

工具:

  • Ollama:将LLM的强大功能带到您的笔记本电脑,简化本地操作。
  • Milvus:我们用来高效存储和检索数据的向量数据库。
  • Llama 3:Meta最新版本的大型语言模型。
  • Voyage AI - 提供专门针对特定领域的嵌入模型。我们使用他们的法律嵌入模型,优化用于法律和长上下文检索。

准备工作

依赖项和数据

首先,安装所需的依赖项:

    ! pip install --upgrade pymilvus ollama tqdm pypdf voyageai openai wget

导入API密钥

如果您在.env文件中设置了API密钥,可以使用dotenv加载它,以避免在推送笔记本时泄露密钥。

    import os
    from dotenv import load_dotenv

    load_dotenv()

    VOYAGE_API_KEY = os.getenv('VOYAGE_API_KEY')

如果您没有.env文件,只需跳过dotenv导入。

准备数据

对于本教程,我们将使用来自伦敦皇家法院的数据。使用wget下载并保存在本地:

    ! wget https://www.judiciary.uk/wp-content/uploads/2024/07/Final-Judgment-CA-2023-001978-BBC-v-BBC-Pension-Trust-another.pdf

接下来,读取PDF文件并提取其内容以供我们的RAG应用使用:

    from pypdf import PdfReader

    reader = PdfReader("Final-Judgment-CA-2023-001978-BBC-v-BBC-Pension-Trust-another.pdf")

    pages = [page.extract_text() for page in reader.pages]

    print(pages[0])

这应该给您以下输出:

    Neutral Citation Number  [2024] EWCA Civ 767   
     
    Case No:  CA-2023 -001978   
    IN THE COURT OF APPEAL ( CIVIL  DIVISION)  
    ON APPEAL FROM  THE HIGH COURT OF JUSTICE  
    BUSINESS AND PROPERTY COURTS OF ENGLAND AND WALES  
    BUSINESS LIST : PENSIONS (ChD)        
    The Hon Mr Justice Adam Johnson  
    [2023] EWHC 1965  (Ch)  
    Royal Courts of Justice  
    Strand, London, WC2A 2LL  
     
    Date:  09/07 /2024   
    Before :  
     
    LORD JUSTICE LEWISON  
    LADY JUSTICE FALK  
    and 
    SIR CHRISTOPHER FLOYD

嵌入您的文档

我们将使用voyage-law-2,这是一个专门针对法律领域的嵌入模型。

首先,定义一个函数,使用Voyage AI API生成文本嵌入:

    import voyageai

    voyage_client = voyageai.Client()

    def embed_text(text: str) -> str:
        return voyage_client.embed([text], model="voyage-law-2").embeddings[0]

生成一个测试嵌入并打印其维度和前几个元素。

    result = voyage_client.embed(["hello world"], model="voyage-law-2")
    embedding_dim = len(result.embeddings[0])
    print(embedding_dim)
    print(result.embeddings[0][:10])


    1024
    [0.000756315013859421, -0.02162403240799904, 0.0052010356448590755, -0.02917512319982052, -0.00796651840209961, -0.03238343447446823, 0.0660339742898941, 0.03845587745308876, -0.01913367211818695, 0.05562642216682434]

将数据加载到Milvus中

在Milvus中创建集合

    from pymilvus import MilvusClient

    milvus_client = MilvusClient(uri="./milvus_legal.db")

    collection_name = "my_rag_collection"

将URI设置为本地文件,例如./milvus_legal.db,方便使用,因为它会自动使用Milvus Lite将所有数据存储在此文件中。对于大规模数据,您可以在Docker或Kubernetes上设置一个性能更好的Milvus服务器。

如果集合已经存在,则删除该集合,然后创建一个新的:

    if milvus_client.has_collection(collection_name):
        milvus_client.drop_collection(collection_name)

    milvus_client.create_collection(
        collection_name=collection_name,
        dimension=embedding_dim,
        metric_type="IP",  # Inner product distance
        consistency_level="Strong",  # Strong consistency level
    )

如果我们不指定任何字段信息,Milvus将自动为主键创建一个默认的id字段,并创建一个用于存储向量数据的向量字段。一个保留的JSON字段用于存储未定义模式的字段及其值。

插入数据

遍历我们文档的页面,创建嵌入,然后将数据插入Milvus。

我们引入一个名为text的新字段,这是集合模式中未定义的字段。它将自动添加到保留的JSON动态字段中,可以在高级别上视为正常字段。

    from tqdm import tqdm

    data = []

    for i, page in enumerate(tqdm(pages, desc="Creating embeddings")):
        data.append({"id": i, "vector": embed_text(page), "text": page})

    milvus_client.insert(collection_name=collection_name, data=data)
    Creating embeddings: 100%|███████████████████████████████████████████████████████████████████████████████████████| 20/20 [00:08<00:00,  2.45it/s]
    {'insert_count': 20,
     'ids': [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19],
     'cost': 0}

构建基本RAG系统

接下来,让我们定义一个关于法庭听证内容的查询:

    question = "who are the lawyers?"

我们将使用Milvus搜索我们的索引数据。稍后,我们将与LLM集成。

    search_res = milvus_client.search(
        collection_name=collection_name,
        data=[
            embed_text(question)
        ],  # Use the `embed_text` function to convert the question to an embedding vector
        limit=3,  # Return top 3 results
        search_params={"metric_type": "IP", "params": {}},  # Inner product distance
        output_fields=["text"],  # Return the text field
    )

打印检索到的行及其距离:

    import json
    retrieved_lines_with_distances = [
        (res["entity"]["text"], res["distance"]) for res in search_res[0]
    ]
    print(json.dumps(retrieved_lines_with_distances, indent=4))

与我们的嵌入相关的文本相当长,但以下是Milvus返回的示例:

["  n nNeutral Citation Number  [2024] EWCA Civ 767   n nCase No:  CA-2023 -001978   nIN THE COURT OF APPEAL ( CIVIL  DIVISION)  nON APPEAL FROM  THE HIGH COURT OF JUSTICE  nBUSINESS AND PROPERTY COURTS OF ENGLAND AND WALES  nBUSINESS LIST : PENSIONS (ChD)        nThe Hon Mr Justice Adam Johnson  n[2023] EWHC 1965  (Ch)  nRoyal Courts of Justice  nStrand, London, WC2A 2LL  n nDate:  09/07 /2024   nBefore :  n nLORD JUSTICE LEWISON  nLADY JUSTICE FALK  nand nSIR CHRISTOPHER FLOYD  n n- - - - - - - - - - - - - - - - - - - - - n nBetween :  n n BRITISH BROADCASTING CORPORATION  Appellant  n - and -  n (1) BBC P ENSION TRUST LIMITED  n(2) CHRISTINA BURNS   nRespondent s n n- - - - - - - - - - - - - - - - - - - - - n n     Michael Tennet KC and Edward Sawyer  (instructed by  Linklaters LLP ) nfor the Appellant       n      Brian Green KC and Joseph Steadman (instructed by  Slaughter and May  Solicitors ) nfor the  First Respondent  nAndrew Spink KC and Saul Margo  (instructed by Stephenson Harwood LLP ) nfor the Second Respondent     n nHearing dates: 25, 26 & 27/06/2024       n- - - - - - - - - - - - - - - - - - - - - n nApproved Judgment  n nThis judgment was handed down remotely at 11.00am on 09/07/2024 by circulation to the nparties or their representatives by e -mail and by release to the Nat ional Archives.  n n.............................  n",
        0.1101425364613533
    ],

使用LLM获取RAG响应

将检索到的文档合并为上下文,并为语言模型定义系统和用户提示:

    context = "n".join(
        [line_with_distance[0] for line_with_distance in retrieved_lines_with_distances]
    )

    SYSTEM_PROMPT = """
    Human: You are an AI assistant. You are able to find answers to the questions from the contextual passage snippets provided.
    """

    USER_PROMPT = f"""
        Use the following pieces of information enclosed in <context> tags to provide an answer to the question enclosed in <question> tags.
        <context>
        {context}
        </context>
        <question>
        {question}
        </question>
    """

Ollama有一个完全兼容的OpenAI API,这意味着我们可以使用OpenAI Python SDK调用Ollama:

from openai import OpenAI

client = OpenAI(
    base_url = 'http://localhost:11434/v1',
    api_key='ollama', # required, but unused
)

response = client.chat.completions.create(
    model="llama3",
    messages=[
        {"role": "system", "content": SYSTEM_PROMPT},
        {"role": "user", "content": USER_PROMPT},
    ],
)
print(response.choices[0].message.content)
    According to the text, the lawyers involved in this case are:

    * Andrew Spink KC (instructed by Stephenson Harwood LLP) for the First Respondent
    * Saul Margo (also instructed by Stephenson Harwood LLP) for the First Respondent
    * Mr. Tennet (representing the Second Respondent)
    * Mr. Spink KC (also representing the Second Respondent)
    * Arden LJ (mentioned as having given a previous judgment in Stena Line)

    Note that "KC" stands for King's Counsel, which is a title of distinction conferred upon certain senior barristers in England and Wales.

结论

使用Milvus和Ollama为法律数据设置RAG可以使法律研究变得更容易、更高效。

欢迎查看Milvus、Github上的代码,并通过加入我们的Discord与社区分享您的经历。

注:本文为AI翻译,查看原文

  • Stephen Batifol

    Stephen Batifol

    Developer Advocate

    准备好开始了吗?

    立刻创建 Zilliz Cloud 集群,存储和检索您的向量。

    免费试用 Zilliz Cloud