搭建一个简单的Rag系统

LLM的广泛使用使得快速地为项目编写文档成为可能,恰巧最近想为自己的服务器以及项目编写文档,便于未来对于系统和程序进行持续地维护,然后又转念一想,为什么不再搭建一个RAG系统来与文档进行交互呢?

简介

RAG(Retrieval-Augmented Generation)一般用于对于长文档或者知识库的检索,针对用户的prompt,获取其他有关的信息,并附加在正文的后面,再提交给LLM。本文演示的是对于长文本的索引。 搭建一个RAG系统需要以下的部分:

  1. 语言模型LLM,用于处理prompt。
  2. RAG Pipeline: 包含文本分割,创建Embedding,合并prompt等操作。
  3. 向量数据库,存储所需的数据,并用Embedding对其进行索引,并且在用户请求时计算获取最符合要求的记录。
  4. 文档或者知识库

前置步骤

获取gemini的api key

Gemini是Google的招牌模型,能够免费使用,并且可以在google ai studio获取到api key,

进入google ai studio,点击Get API Key

进入ai studio

接着点击Create API Key创建API key,接下来就可以在Python中通过api调用gemini了。

获取API Key

获取文档

将wikipedia “汉语” 词条作为测试,通过python wikipedia包来获取其内容

import wikipedia

def get_wikipedia_page_content(page_title: str, language_code: str) -> str:
    wikipedia.set_lang(language_code)  # Set the desired language
    page = wikipedia.page(page_title)
    return page.content

page_title = "汉语"
content = get_wikipedia_page_content(page_title, "zh")

输出结果:

漢語又稱華語,是来自汉民族的语言。汉语拥有声调,是分析语。
汉语是汉藏语系中最大的一支语族,若把整个汉语族視為單一語言,则汉语为世界上母语使用者人数最多的语言,目前全世界有五分之一人口将其作为母語或第二語言。此外,漢語是中华人民共和国、中華民國和新加坡共和國的官方语言,也是海外华人地区、果敢族、东干族和塔兹族等少数民族社区的通用语,也是多个国家官方承认的少数民族语言,在国际事务上是联合国官方语言之一,並被上海合作组织、金砖国家等國際組織採用為官方工作语言。
......

搭建RAG系统

文本分割

用langchain对文本进行分割,使每个chunk的大小不超过500

# 文本分割
from langchain.text_splitter import RecursiveCharacterTextSplitter

text_splitter = RecursiveCharacterTextSplitter(
    chunk_size = 500,
    chunk_overlap = 0
)

chunks = text_splitter.split_text(content)

print(f'Chunks length: {len(chunks)}')
print(chunks[0])

输出

chunks: 56
漢語又稱華語,是来自汉民族的语言。汉语拥有声调,是分析语。
汉语是汉藏语系中最大的一支语族,若把整个汉语族視為單一語言,则汉语为世界上母语使用者人数最多的语言,目前全世界有五分之一人口将其作为母語或第二語言。此外,漢語是中华人民共和国、中華民國和新加坡共和國的官方语言,也是海外华人地区、果敢族、东干族和塔兹族等少数民族社区的通用语,也是多个国家官方承认的少数民族语言,在国际事务上是联合国官方语言之一,並被上海合作组织、金砖国家等國際組織採用為官方工作语言。
漢語在以其為母語的地区有不同通稱,且有多種方言变体,其中以中国北方汉语为基础的北京官話最為流行,其衍生而來的現代標準漢語(有國語、普通話、新加坡标准华语等變體)是漢語圈的主要通用语。在许多语境中,所谓“汉语”实际上指现代标准汉语。

创建embedding

使用sentence transformer来为每一个chunk都创建embedding

# 构建索引
from sentence_transformers import SentenceTransformer

model = SentenceTransformer('paraphrase-MiniLM-L6-v2')

chunks_2 = chunks
embeddings = model.encode(chunks)
print(f"Embeddings shape: {embeddings.shape}")

输出:

Embeddings shape: (56, 384)

向量存储

Faiss是一个内存向量存储,支持各种类型的向量索引方式。但是在面向生产环境的部署中,会使用向量数据库如milvus。

将embedding存入faiss,并创建一个函数用于搜索最相近的K个结果,这里用欧几里得距离来衡量句子的相似性。

import faiss

d = embeddings.shape[1]

index = faiss.IndexFlatL2(d)

index.add(embeddings)

helper function:

def get_familiar(query, k=5):
    query_embedding = model.encode([query])

    k = 5
    distances, indices = index.search(query_embedding, k)

    result = []

    for i, idx in enumerate(indices[0]):
        result.append(chunks[idx])

    return result

创建用户prompt

将get_familiar获取到的结果拼接到用户prompt的后面

def create_prompt(query, context):
    prompt = "Question: " + user_query + "\n" + "Contexts: " + "\n"

    for context in rag_context:
        prompt = prompt  + "[context]" + context + "\n"

    return prompt

连接gemini

设置api key,选择模型

import google.generativeai as genai

api_key = os.environ['GOOGLE_API_KEY']
genai.configure(api_key=api_key)
gemini_model = genai.GenerativeModel(
    "models/gemini-exp-1206",
    system_instruction="你是一个乐于助人的助手,按照给出的context来回答问题"
)

还可以通过以下代码列出所有模型

for m in genai.list_models():
    if "generateContent" in m.supported_generation_methods:
        print(m.name)

输出:

models/gemini-1.0-pro-latest
models/gemini-1.0-pro
models/gemini-pro
......

进行提问

user_query = "我想要了解一些关于吴语的知识"
rag_context = get_familiar(user_query, 5)
prompt = create_prompt(user_query, rag_context)

response = gemini_model.generate_content(prompt)

if response:
    print(response.text)

输出:

吴语是中国南方的一种汉语方言,在过去,吴语也曾被用来教授中文课。例如在“[context]在過去,則於不同地區,有使用該地主流漢語(例如吳語、客家話等)教授中文課的現象。”这个句子中就提到了吴语。

后记

  • 本文所介绍的RAG系统只是一个基于向量索引的简单RAG系统,也没用进行相关性检查,可能会出现获取到无关信息的情况,如果想要更近一步获取到更符合语义的星系,应该使用GraphRAG或混合RAG(同时使用GraphRAG和RAG)。

  • 如果需要对支持多模态,比如自动提交图像,可以选择Clip或者其他的embedding模型。

  • RAG系统还有另一种用法,不仅能够用于搭建知识库,可以对于系统日志进行索引,再配合LLM进行流式分析,能够更为主动的了解系统的状态,降低运维的成本,提高可以维护的集群的规模,但是持续使用LLM的成本还是相当高昂的,并且流式分析对于单个设备的性能和集群的规模都有很高的要求,所有笔者认为还需要找到办法来解决这些问题。

有关资源

  1. langchain文本分割: medium
  2. gemini api的使用方法: github
  3. sentence transformer: huggingface
  4. 使用faiss和sentence transformer来创建索引: stephendiehl
  5. langchain官方rag教程: langchain
  6. google ai studio: ai studio