检索增强生成RAG系列6--RAG提升之查询结构内容(Query Construction)

系列5中讲到会讲解3个方面RAG的提升,它们可能与RAG的准确率有关系,但是更多的它们是有其它用途。本期来讲解第二部分:查询结构化(Query Construction)。在系列3文档处理中,我们着重讲解了文档解析,但是我们说的文档都是大部分是非结构化的文档或者说它就是以一个文档的形式存储。而现实中我们很多有价值的数据可能以结构化(关系型数据库、图形数据库等)或者半结构(关系型数据库、文档数据库等)的形式存储中,并且这些数据一般都是存储于特定数据库,那么如果数据存储在结构化或者半结构化中,我们RAG又该如何与之配合。这一章就着重来讲讲查询结构化内容(Query Construction)

1 查询结构内容(Query Construction)

现实中我们很多有价值的数据可能以结构化(关系型数据库、图形数据库等)或者半结构化(关系型数据库、文档数据库等)的形式存储中,并且这些数据一般都是存储于特定数据库,同时数据库提供结构化的查询功能,使用其结构化查询(比如SQL等)比使用向量化查询更为准确。那这时候如果我们RAG想要去查询这些有结构的数据,就不是将数据向量化后再去查询,而是利用大模型将用户问题转换为结构化查询,再通过结构化查询去数据库查询结果,最后利用结果回答用户问题。下图流程可以让你明显感受不同之处:

在这里插入图片描述

我们将不同结构化或者半结构化查询归纳如下表,下面也是逐一讲解各个方法

方法数据源
Text-to-metadata-filter向量数据库
Text-to-SQL关系型数据库
Text-to-Cypher图形数据库
Text-to-SQL+ Semantic关系型+向量混合数据库

2 Text-to-metadata-filter

第一个要利用大模型将用户问题转换为结构化查询的依旧是向量数据库,但是这里并非做向量查询,而是很多向量存储其实都配备了元数据过滤功能,这些元数据其实就是结构化存储,因此需要过滤的是存储在向量数据库的元数据

这里以查询ChromaDB的metadata为例,做一个demo代码演示。在运行代码之前我们需要做以下前置条件

  • 这里采用智谱AI的API接口,因此可以先去申请一个API KEY(当然你使用其它模型也可以,目前智谱AI的GLM4送token,就拿它来试验吧)
  • 下载m3e-base的embedding模型
  • 给一个文档目录,里面放入一个文档即可,文档内容不限
import os
from typing import List
from langchain_openai import ChatOpenAI
from langchain_core.documents import Document
from langchain_community.vectorstores import Chroma
from langchain_core.prompts import ChatPromptTemplate
from langchain_huggingface import HuggingFaceEmbeddings
from langchain_core.pydantic_v1 import BaseModel, Field
from langchain_core.runnables import RunnablePassthrough
from langchain_community.document_loaders import DirectoryLoader


# 前置工作1:文档存储,给文档设置author属性
encode_kwargs = {"normalize_embeddings": False}
model_kwargs = {"device": "cuda:0"}
embeddings = HuggingFaceEmbeddings(
    model_name='/root/autodl-tmp/model/AI-ModelScope/m3e-base',  # 换成自己的embedding模型路径
    model_kwargs=model_kwargs,
    encode_kwargs=encode_kwargs
)
if os.path.exists('VectorStore'):
    db = Chroma(persist_directory='VectorStore', embedding_function=embeddings)
loader = DirectoryLoader("/root/autodl-tmp/doc")  # 换成自己的文档路径
documents = loader.load()
# 这里假设设置一个author作者的属性
documents[0].metadata["author"] = "刘震云"
database = Chroma.from_documents(documents, embeddings, persist_directory="VectorStore")
database.persist()

# 前置工作2:创建llm
llm = ChatOpenAI(
    temperature=0.95,
    model="glm-4",
    openai_api_key="你的API KEY",
    openai_api_base="https://open.bigmodel.cn/api/paas/v4/"
)


# 前置工作3:为了将用户问题转换为结构化查询,使用LangChain可以通过Pydantic类轻松指定所需的function call schema:
# 这里假设书籍有2个字段匹配,一个是内容content_search,一个是作者author_search
class BookSearch(BaseModel):
    content_search: str = Field(
        ...,
        description=(
            "书籍文本内容进行相似性搜索"
        ),
    )

    author_search: str = Field(
        ...,
        description=(
            "书籍作者,仅在使用人名查询时,才进行关键字匹配,其它情况不使用"
        ),
    )


# 第一步:让模型将用户问题转换成与查询字段匹配的格式
system = """你是将用户问题转换为数据库查询的专家。
你可以访问关于的书籍数据库。
给定一个问题,返回一个优化为检索最相关结果的数据库查询。"""
prompt = ChatPromptTemplate.from_messages(
    [
        ("system", system),
        ("human", "{question}"),
    ]
)

# 绑定前面定义的输出数据格式,意思就是要求模型根据类结构返回数据。
structured_llm = llm.with_structured_output(BookSearch)
# 定义chain
query_analyzer = {"question": RunnablePassthrough()} | prompt | structured_llm
print(query_analyzer.invoke("作家刘震云的书"))


# 第二步:转换匹配,拼接向量查询条件查询Chroma
def retrieval(search: BookSearch) -> List[Document]:
    if search.author_search is not None:
        _filter = {"author": {"$eq": search.author_search}}
    else:
        _filter = None
    return database.similarity_search(search.content_search, filter=_filter)


retrieval_chain = query_analyzer | retrieval
# 未查询到结果
print(retrieval_chain.invoke("作家莫言的书"))
# 查询到结果
print(retrieval_chain.invoke("作家刘震云的书"))

3 Text-to-SQL

关于关系型数据库就不用在累赘了,但是使用大模型生成SQL可能会出现以下问题:

  • 幻觉:大模型容易对虚构的表或字段产生“幻觉”,从而产生无效的查询。方法必须将这些大模型 建立在现实中,确保它们生成与实际数据库模式一致的有效 SQL。
  • 用户错误:文本转 SQL 方法应能够防止用户拼写错误或用户输入中可能导致无效查询的其他不规范情况。

对于以上问题,有不少的解决方案供参考:

  • 数据库描述:要生成SQL查询,必须向大模型提供数据库的准确描述。常见的做法:为大模型提供每张表的CREATE TABLE描述,包括列名、类型等,然后再给出几个SELECT语句的示例。
  • Few-shot样例:在prompt中添加question-query的几个样例可以提高query生成的准确性。在prompt中简单的添加标准的静态示例指导大模型如何基于question创建query。
  • 错误处理:当遇到错误时,利用工具(例如SQL Agent)修复错误。
  • 查找专有名词中的拼写错误:当查询名称等专有名词时,用户可能会不小心写错。我们允许大模型Agent根据向量库搜索正确的名称,向量库在SQL数据库中存储相关专有名词的正确拼写。

下面以查询sqlite数据库为例子,,做一个demo代码演示。在运行代码之前我们需要做以下前置条件

  • 这里采用智谱AI的API接口,因此可以先去申请一个API KEY(当然你使用其它模型也可以,目前智谱AI的GLM4送token,就拿它来试验吧)
  • 准备一个本地sqlite数据库,并创建数据库test.db,以及表和存入数据,脚本如下

sqllite3 test.db
create table student(id Integer,name char,score Integer);
insert into student values(1,“student1”,100);
insert into student values(2,“student2”,80);
insert into student values(3,“student3”,50);

代码如下:


from langchain_openai import ChatOpenAI
from langchain_core.prompts import PromptTemplate
from langchain.chains import create_sql_query_chain
from langchain_community.utilities import SQLDatabase


# 前置工作1:创建llm
llm = ChatOpenAI(
    temperature=0.01,
    model="glm-4",
    openai_api_key="你的API KEY",
    openai_api_base="https://open.bigmodel.cn/api/paas/v4/"
)

# 前置工作2:改写了一下prompt(因为默认的prompt似乎不适合智谱AI)
_sqlite_prompt = """你是一个SQLite专家。给定一个输入问题,首先创建一个语法正确的SQLite查询来运行,然后查看查询的结果并返回输入问题的答案。
除非用户在问题中指定要获取的特定数量的示例,否则根据SQLite使用LIMIT子句查询最多{top_k}个结果。您可以对结果进行排序,以返回数据库中信息量最大的数据。
永远不要查询表中的所有列。您必须只查询回答问题所需的列。将每个列名用双引号(")括起来,以表示它们为分隔符。
注意,只使用您可以在下面的表中看到的列名。注意不要查询不存在的列。另外,要注意哪个列在哪个表中。
如果问题涉及“今天”,注意使用date('now')函数来获取当前日期
使用以下格式:

问题:问题
SQLQuery:要运行的SQL查询
答案:最终答案

只使用以下表格:
{table_info}

问题: {input}
"""

SQLITE_PROMPT = PromptTemplate(
    input_variables=["input", "table_info", "top_k"],
    template=_sqlite_prompt,
)

db = SQLDatabase.from_uri("sqlite:///sqlite-autoconf-3460000/test.db")  # 你的数据库地址
# 使用create_sql_query_chain的方式,langchain还有agent方式,大家可以去探索使用
chain = create_sql_query_chain(llm, db, SQLITE_PROMPT)
response = chain.invoke({"question": "总共有多少学生"})
print(response)

4 Text-to-Cypher

对于图形数据库的作用,一般就是表示实体之间的关系,适合于知识图谱、人际关系等应用场景。
下面以查询neo4j图数据库为例子,,做一个demo代码演示。在运行代码之前我们需要做以下前置条件

  • 这里采用智谱AI的API接口,因此可以先去申请一个API KEY(当然你使用其它模型也可以,目前智谱AI的GLM4送token,就拿它来试验吧)
  • 准备一个本地neo4j数据库,并创建数据库test_relationship,录入一些作家、书籍以及相关关系
from langchain.chains import GraphCypherQAChain
from langchain_community.graphs import Neo4jGraph
from langchain_openai import ChatOpenAI


# 前置工作1:创建llm
llm = ChatOpenAI(
    temperature=0.95,
    model="glm-4",
    openai_api_key="你的API KEY",
    openai_api_base="https://open.bigmodel.cn/api/paas/v4/"
)

# 前置工作2:准备好图形数据库
graph = Neo4jGraph(
    url="bolt://localhost:7687", username="test_relationship", password="***"
)
graph.refresh_schema()

# 使用langchain的GraphCypherQAChain进行封装,这里面的提示效果对于智谱AI来说一般般,没调试出来最终结果
chain = GraphCypherQAChain.from_llm(llm, graph=graph, verbose=True)

print(chain.run("谁是一地鸡毛的作者?"))

5 Text-to-SQL+ Semantic

现在混合类型(结构化和非结构化)数据存储越来越普遍。向关系数据库添加向量支持是支持混合检索方法的关键推动因素。比如PostgreSQL 的开源 pgvector 扩展将 SQL 的表现力与语义搜索提供的对语义的细致入微的理解相结合。那么在如何将用户问题转换为这种混合类型数据库就提出了更高的挑战。
在对这种类型数据库,更加复杂的query生成,需要创建few-shot prompt或者增加query-checking等环节来提升准确度,这里就不举例子,有兴趣深入的同学可以搜索研究。

### 关于GNN-RAG架构图或流程图 GNN-RAG(Graph Neural Network Retrieval-Augmented Generation)是一种结合了图神经网络(GNNs)和RAG模型的方法,旨在通过利用结构化数据增强检索生成能力。其核心理念在于引入图结构表示以捕捉实体之间的复杂关系,从而改进传统RAG模型的信息检索效率和上下文理解能力。 #### GNN-RAG的主要组成部分 1. **图构建阶段** 在此阶段,输入数据被转换为图形式,其中节点代表实体,边则表示它们的关系。这种图可以基于预定义的知识库或者动态生成的语义关联[^2]。 2. **嵌入学习阶段** 使用GNN对图中的节点和边进行编码,生成低维向量表示。这些嵌入能够更好地反映实体间的依赖性和层次结构,有助于后续的检索操作[^3]。 3. **检索阶段** 基于查询文本及其对应的图嵌入,在知识库中找到最相关的文档片段或子图作为候选集。这一过程通常会考虑图拓扑特征以及文本相似度指标[^4]。 4. **生成阶段** 将检索到的内容与原始查询相结合,送入大型语言模型(LLM),完成最终的回答生成任务。值得注意的是,这里的生成不仅依赖于文本信息,还融合了来自图结构的重要线索[^5]。 以下是简化版的GNN-RAG工作流伪代码实现: ```python def gnn_rag(query, knowledge_graph): # Step 1: Graph Construction and Embedding Learning graph_embeddings = compute_gnn_embeddings(knowledge_graph) # Step 2: Retrieve Relevant Subgraphs or Documents retrieved_subgraphs = retrieve_relevant_subgraphs( query_embedding=encode_query(query), graph_embeddings=graph_embeddings ) # Step 3: Generate Response with LLM Integration response = generate_response_with_llm( input=query, context=retrieved_subgraphs ) return response ``` 尽管目前公开可用的具体GNN-RAG架构图表较少,但可以根据上述描述自行绘制逻辑框图。一般而言,这类流程图应包含以下几个部分:输入层(用户提问)、图处理模块、检索机制以及生成组件,并清晰标注各环节间的数据流动方向。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

linmoo1986

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值