Query Construction

查询构建

7分钟阅读

关键链接

将自然语言与各种类型的数据(结构化、非结构化和半结构化)无缝连接具有巨大的潜力。但是,这种新兴的“LUI”(语言用户界面)对于每种数据类型都存在特定的挑战/考虑因素。

  • 结构化数据:主要存储在 SQL 或图数据库中,结构化数据的特点是预定义模式,并按表或关系进行组织,使其易于进行精确的查询操作。
  • 半结构化数据:半结构化数据将结构化元素(例如,文档中的表或关系数据库)与非结构化元素(例如,文本或关系数据库中的嵌入列)混合在一起。
  • 非结构化数据:通常存储在向量数据库中,非结构化数据由没有预定义模型的信息组成,并且经常伴随允许过滤的结构化元数据。

为了应对这些挑战,LLM 在查询构建方面具有很强的能力,即从自然语言转换为每种数据类型的特定查询语法。在本篇关于高级检索的博客系列的第三部分中,我们将介绍各种查询构建策略(有关更多信息,请参阅我们关于多表示索引查询转换的博客文章)。

什么是查询构建?

在使用典型的检索增强生成(RAG)时,用户查询会被转换为向量表示。然后,该向量与源文档的向量表示进行比较,以找到最相似的文档。这对于非结构化数据效果相当好(请参阅我们关于多表示索引查询转换的博客文章),但对于结构化数据呢?

世界上大部分数据都具有某种结构。其中许多数据存在于关系型(例如 SQL)或图数据库中。即使是非结构化数据,也经常关联结构化元数据(例如,作者、流派、发布日期等)。

💡
许多用户查询的最佳答案不仅仅是通过查找嵌入空间中相似的文档或数据来获得,而是通过利用数据固有的结构以及在用户查询中表达的结构。

例如,考虑查询 1980 年关于外星人的电影。其中一部分(外星人)我们可能想进行语义查找,但还有一部分(“年份 == 1980”)我们想进行精确查找。

💡
查询构建是将自然语言查询转换为你正在交互的数据库的查询语言。

下面我们将重点介绍几种查询构建的示例,并为每种示例提供相关的食谱、模板和文档。

示例

数据源

参考资料

文本转元数据过滤器

向量存储

文档

文本转 SQL

SQL 数据库

文档, 博客, 博客

文本转 SQL+语义

PGVector 支持的 SQL 数据库 

食谱

文本转 Cypher

图数据库 

博客, 博客, 文档

文本转元数据过滤器

配备元数据过滤的向量存储使结构化查询能够过滤嵌入的非结构化文档。自查询检索器可以通过几个步骤将自然语言查询转换为这些结构化查询。

  • 数据源定义:自查询检索器的核心是清晰地指定相关元数据文件(例如,在歌曲检索的上下文中,这可能是艺术家、时长和流派)。
  • 用户查询解释:给定一个自然语言问题,自查询检索器将分离查询(用于语义检索)和用于元数据过滤的过滤器。例如,查询 泰勒·斯威夫特或凯蒂·佩里的关于少女恋情的歌曲,时长不到 3 分钟,属于舞曲流派 被分解为过滤器和查询。
  • 逻辑条件提取:过滤器本身由向量存储定义的比较器和运算符(如等于的 eq 或小于的 lt)构成。
  • 结构化请求生成:最后,自查询检索器组装结构化请求,将语义搜索词(查询)与逻辑条件(过滤器)分开,从而简化文档检索过程。

我们可以定义一个链来执行上述步骤,该链接受用户问题并返回一个封装了必要过滤器的 StructuredQuery 对象。

# Generate a prompt and parse output
prompt = get_query_constructor_prompt(document_content_description, metadata_field_info)
output_parser = StructuredQueryOutputParser.from_components()
query_constructor = prompt | llm | output_parser

# Invoke the query constructor with a sample query
query_constructor.invoke({
 "query": "Songs by Taylor Swift or Katy Perry about teenage romance under 3 minutes long in the dance pop genre"
})

结构化请求将如下所示:

{
 "query": "teenager love",
 "filter": "and(or(eq(\"artist\", \"Taylor Swift\"), eq(\"artist\", \"Katy Perry\")), lt(\"length\", 180), eq(\"genre\", \"pop\"))"
}

这种方法可以通过直接从用户问题中推断出的逻辑过滤条件来改进与向量数据库交互时的 RAG 答案质量,从而引导传递给 LLM 进行最终答案综合的文本块。

阅读更多

  • 文档:自查询检索器
  • 模板:自查询 Supabase 向量数据库
  • 模板:自查询 Elastic 向量数据库

文本转 SQL

在数据连续体的另一端,SQL/关系数据库是大量结构化数据的重要来源。已经付出了相当大的努力来将自然语言翻译成 SQL 请求,但也存在一些显著的挑战,例如:

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

牢记这些挑战,出现了一些技巧:

  • 数据库描述:为了约束 SQL 查询,必须向 LLM 提供数据库的准确描述。一种常见的文本转 SQL 提示采用了几篇论文报道的理念:为 LLM 提供每个表的 CREATE TABLE 描述,其中包含列名、类型等,然后是 SELECT 语句中的三个示例行。 
  • 少样本示例:在提示中提供问题-查询匹配的少样本示例可以提高查询生成准确性。这可以通过简单地在提示中附加标准的静态示例来实现,以指导代理如何根据问题构建查询。 
  • 错误处理:当面临错误时,数据分析师不会放弃——他们会迭代。我们可以使用 SQL 代理(此处)等工具来从错误中恢复。
  • 查找专有名词中的拼写错误:在查询专有名词(如姓名)时,用户可能会无意中拼写错误(例如,Franc Sinatra)。我们可以允许代理搜索向量存储中的专有名词,向量存储中包含 SQL 数据库中相关专有名词的正确拼写。

阅读更多

  • 文档:关于结构化数据的 QA
  • 博客:LLM 和 SQL
  • 博客:将领域特定知识整合到 SQL-LLM 解决方案中

文本转 SQL+语义

在数据连续体的中间,混合类型(结构化和非结构化)数据存储日益普遍。关系数据库添加向量支持是支持混合检索的方法的关键推动因素(请参阅 AI Engineer Summit 最近的视频)。特别是,PostgreSQL 的开源 pgvector 扩展将 SQL 的表达能力与语义搜索提供的细致入微的语义理解相结合。与Pinecone 等向量存储相比,Pgvector 在性能和成本方面都表现良好。

Pgvector 使得通过 <-> 运算符在嵌入向量列上执行相似性搜索(例如,余弦、L2 距离、内积)成为可能。

SELECT * FROM tracks ORDER BY "name_embedding" <-> {sadness_embedding}

根据上述查询,我们可以使用 LIMIT 3 来获取“最悲伤的 3 首歌曲”(类似于标准 kNN 的 top_k 值),还可以进行更复杂的操作,例如选择最悲伤的一首,以及出于某种原因选择第 90 和第 50 百分位数。这解锁了两个重要的新功能:

  • 我们可以执行使用向量存储无法实现的语义搜索。
  • 我们可以通过了解语义运算符来增强文本转 SQL。例如,它支持文本到语义搜索(例如,查找传达特定情感的歌曲标题)以及 SQL 查询(例如,按流派过滤)。

遵循专辑-歌曲的示例,通过这种方法,我们可以找到包含最多符合特定情感的歌曲的专辑(使用语义相似性作为表格数据的过滤器或计数),或者找到标题带有“可爱”的专辑中的悲伤歌曲(在一个查询中组合两个 语义搜索,这在使用具有元数据过滤的向量数据库时是不可能的)。

阅读更多

文本转 Cypher 

虽然向量存储可以很好地处理非结构化数据,但它们不理解向量之间的关系。虽然 SQL 数据库可以建模关系,但模式更改可能会造成干扰和高成本。 知识图谱可以通过建模数据之间的关系并扩展关系类型而不进行大规模的改造来解决这些挑战。它们对于具有难以在表格形式中表示的多对多关系或层次结构的数据很有吸引力。

就像关系数据库经常使用 SQL 一样,图数据库通常使用一种名为Cypher 的特定查询语言,它旨在以视觉方式匹配模式和关系。它依赖于以下类似的 ASCII 艺术语法:

(:Person {name:"Tomaz"})-[:LIVES_IN]->(:Country {name:"Slovenia"})

此模式描述了一个标签为 Person、名称属性为 Tomaz 的节点,该节点与 Slovenia 的国家节点存在 LIVES_IN 关系。与前面的示例一样,文本转 Cypher 可以将自然语言转换为 Cypher 查询。

from langchain.chains import GraphCypherQAChain

graph.refresh_schema()

cypher_chain = GraphCypherQAChain.from_llm(
    cypher_llm = ChatOpenAI(temperature=0, model_name='gpt-4'),
    qa_llm = ChatOpenAI(temperature=0), graph=graph, verbose=True,
)

由于生成有效的 Cypher 可能是一项复杂的任务,因此建议使用 GPT-4 等高性能 LLM 来生成 Cypher 语句作为 cypher_llm。如上所示,我们可以然后用自然语言提问。

cypher_chain.run(
    "How many open tickets there are?"
)

查看文档

  • 博客:使用知识图谱实现 DevOps RAG 应用程序
  • 博客:使用 Neo4j 实现高级 RAG 策略
  • 文档:Neo4j 数据库 QA 链

结论

无缝地跨各种数据源检索结构化和非结构化数据对于释放 LLM 的潜力至关重要。我们总结了为不同类型数据存储而出现的四种流行的“自然语言到结构化查询”管道,并提供了用户入门的模板和食谱。

© . This site is unofficial and not affiliated with LangChain, Inc.