今年四月初,Pinterest 工程团队分享了他们如何在内部实现 Text-to-SQL 解决方案,以使数据用户无需编写 SQL 即可检索数据;他们还分享了该解决方案为用户带来的惊人投资回报率 (ROI),引述如下。
在我们的实际数据中(重要提示:这不控制任务差异),我们发现使用 AI 辅助编写 SQL 查询的任务完成速度提高了 35%。
Wren AI 团队已在 Text-to-SQL 挑战上投入了数月时间,并从这篇博文中获得了许多新的见解和学习;我们想与社区分享我们从这篇博文中学到的以及在开发 Wren AI 过程中获得的经验,以加速将 LLM 的理解能力应用于数据的创新。
在开始之前,如果您还没有了解 Wren AI,请在 GitHub 上查看我们!如果您喜欢我们正在做的事情,别忘了给我们一个 ⭐ 星!
首先,让我们探讨一下 Pinterest Text-to-SQL 和 Wren AI 架构之间的相似性。
以下是 Pinterest 在其博文中分享的架构。
让我们阶段性地分解这个架构;在进行 Text-to-SQL 实现时,有三个关键阶段: 索引、检索以及增强与生成。
将这三个阶段映射到下面的架构中,并深入探讨每个阶段。
在该架构中,他们应用了一个离线作业,根据表的摘要和针对它们的历史查询来生成向量存储的索引。目前,他们在向量索引过程中依赖于两种类型的文档嵌入:**表摘要和查询摘要。**
索引阶段通常与 Text-to-SQL 如何在数据库中搜索最相关的表密切相关;您可以为向量存储提供更多上下文,最终结果就会越好。
在检索阶段,如果用户未指定表,其问题将被转换为嵌入向量。应用了几种方法来执行检索阶段,包括 **NLP 表搜索和表重新选择**。表重新选择的结果将返回给用户进行验证,然后再生成为 SQL。
我们通常使用检索后方法来根据附加信息过滤掉低相关上下文,或者对结果应用不同的权重。
在博文中,他们没有详细提及增强和生成阶段的这一部分。
以下是 Wren AI Text-to-SQL 架构的样子。
Wren AI 由三个核心服务组成
让我们看看 **Wren AI 如何在我们的 Text-to-SQL 架构中设计和实现** 这三个阶段,以便您可以与我们刚刚提到的 Pinterest Text-to-SQL 架构进行比较。
在 Wren AI 中,我们实现了语义引擎架构来为 LLM 提供您的业务上下文;您可以轻松地在数据模式上建立一个逻辑表示层,帮助 LLM 更好地理解您的业务上下文。
我们的索引包含哪些内容
收入
、 利润
时,您希望 LLM 理解您的公司如何定义这些术语;这就是 计算
的作用,您可以在语义建模中轻松定义计算,LLM 可以根据您的定义生成正确的 SQL,而不会产生幻觉。相比之下,Pinterest 目前仅依赖表元数据和查询日志,但他们已经实现了**表摘要和查询摘要方法**来改进上下文。
经验教训:我们的团队计划在 Wren AI 项目中实现“表摘要”和“查询摘要”。
在 Wren AI 中,在检索阶段,用户问题被转换为嵌入向量,并对向量索引进行相似性搜索,以推断出最适合的前 K 个表,如下所示。
from typing import Any
from haystack_integrations.components.retrievers.qdrant import QdrantEmbeddingRetriever
def init_retriever(document_store: Any, top_k: int = 10):
return QdrantEmbeddingRetriever(document_store=document_store, top_k=top_k)
在 Pinterest 的案例中,他们的 Text-to-SQL 实现采用了上述表搜索方法和 **表重新选择。**
表重新选择
从向量索引中检索出前 N 个表后,我们将利用 LLM 通过评估问题和表摘要来选择最相关的 K 个表。
经验教训:从向量存储中选择表后进行表重新选择有助于提高性能。
在 Wren AI 中,我们拥有独特的设计和技术,帮助 LLM 更好地理解业务上下文,以在提示词中增强信息。在**生成**阶段,我们还提供 SQL 验证和自动更正功能。
增强
Wren AI 的关键设计之一是语义引擎,即 Wren Engine。该引擎负责通过“分析即代码”的设计方法,定义和映射元数据、模式、术语、数据关系以及计算和聚合背后的逻辑等复杂处理。
借助 Wren Engine,您可以通过“建模定义语言”(MDL)定义语义。此定义封装了计算公式、指标定义和语义关系,减少了重复编码和数据连接处理。
这里有一个示例;以下是“customers”数据模型的定义。
{
"name": "customers",
"refSql": "select * from main.customers",
"columns": [
{
"name": "City",
"type": "VARCHAR",
"isCalculated": false,
"notNull": false,
"properties": {
"description": "The Customer City, where the customer company is located. Also called 'customer segment'."
}
},
{
"name": "Id",
"type": "VARCHAR",
"isCalculated": false,
"notNull": false,
"properties": {
"description": "A unique identifier for each customer in the data model."
}
},
{
"name": "State",
"type": "VARCHAR",
"isCalculated": false,
"notNull": false,
"properties": {
"description": "A field indicating the state where the customer is located."
}
},
{
"name": "orders",
"type": "orders",
"relationship": "CustomersOrders",
"isCalculated": false,
"notNull": false,
"properties": {}
},
{
"name": "LatestRecord",
"type": "DATE",
"isCalculated": true,
"expression": "max(orders.PurchaseTimestamp)",
"notNull": false,
"properties": {}
},
{
"name": "FirstRecord",
"type": "DATE",
"isCalculated": true,
"expression": "min(orders.PurchaseTimestamp)",
"notNull": false,
"properties": {}
},
{
"name": "VIP",
"type": "BOOLEAN",
"isCalculated": true,
"expression": "sum(orders.Size) > 2",
"notNull": false,
"properties": {}
},
{
"name": "OrderCount",
"type": "BIGINT",
"isCalculated": true,
"expression": "count(orders.OrderId)",
"notNull": false,
"properties": {}
},
{
"name": "Debit",
"type": "DOUBLE",
"isCalculated": true,
"expression": "sum(orders.OrderBalance)",
"notNull": false,
"properties": {}
},
{
"name": "ReviewRate",
"type": "DOUBLE",
"isCalculated": true,
"expression": "count(orders.IsReviewed = TRUE) / count(DISTINCT orders.OrderId)",
"notNull": false,
"properties": {}
}
],
"primaryKey": "Id",
"cached": false,
"refreshTime": "30.00m",
"properties": {
"schema": "main",
"catalog": "memory",
"description": "A table of customers who have made purchases, including their city"
}
}
在生成提示词时,我们将用文件中定义的语义来增强数据定义语言(DDL),如下所示。
/* {"schema": "main", "catalog": "memory", "description": "A table of customers who have made purchases, including their city"} */
CREATE TABLE customers (
-- {"description": "The Customer City, where the customer company is located. Also called \'customer segment\'."}
City VARCHAR,
-- {"description": "A unique identifier for each customer in the data model."}
Id VARCHAR PRIMARY KEY,
-- {"description": "A field indicating the state where the customer is located."}
State VARCHAR,
-- This column is a Calculated Field
-- column expression: max(orders.PurchaseTimestamp)
LatestRecord DATE,
-- This column is a Calculated Field
-- column expression: min(orders.PurchaseTimestamp)
FirstRecord DATE,
-- This column is a Calculated Field
-- column expression: sum(orders.Size) > 2
VIP BOOLEAN,
-- This column is a Calculated Field
-- column expression: count(orders.OrderId)
OrderCount BIGINT,
-- This column is a Calculated Field
-- column expression: sum(orders.OrderBalance)
Debit DOUBLE,
-- This column is a Calculated Field
-- column expression: count(orders.IsReviewed = TRUE) / count(DISTINCT orders.OrderId)
ReviewRate DOUBLE
)
生成
以下是 Wren AI 如何处理生成阶段,我们将其分为两种不同的场景
在 Pinterest 的案例中,SQL 生成的最后阶段相对简单;他们在博客文章中目前没有详细提及这一部分。
在博文中,他们提到了他们在路线图中正在研究和工作的几个领域;其中一些主题已在 Wren AI 中实现,有些正在进行中或计划中。让我们看看下面:
在元数据管理方面,我们已在 Wren AI 中实现了名为“建模定义语言”(MDL)的功能,供 LLM 使用,以包含语义关系、领域上下文、标签、描述等;这显著提高了检索阶段的过滤准确性。
在 Wren AI 中,当您通过我们的用户界面进行建模时,索引会自动更新;点击“部署”按钮,系统将自动在向量存储和语义引擎之间同步。
当前 Wren AI 的评分策略也相当基础;未来,我们计划对其进行微调,以提高检索结果的相关性。
查询验证过程已在 Wren AI 中实现;我们将在 Wren Engine 中对查询进行空运行(dry run),以确保查询可执行,验证 SQL,并在 SQL 格式无效时请求 LLM 进行修正。
这是我们在 Wren AI 中设计的最重要的开发领域之一;我们将很快发布包含用户反馈的新版本!发布后我们将分享更多细节,请务必订阅我们的 Medium 以获取最新信息!
这也是我们在 Wren AI 中设计的最重要的开发领域之一。我们将很快在 Wren AI 中发布端到端评估流程!发布后我们将分享更多细节;请务必订阅我们的 Medium 以获取最新信息!
基本就是这样了!最后,我们感谢 Pinterest 工程团队分享了如此精彩和富有启发性的博文。我们希望更多工程团队能够分享他们在公司内部实现的知识!继续前进!
如果您还没有了解 Wren AI,请查看我们!
👉 GitHub: https://github.com/Canner/WrenAI
👉 X: https://twitter.com/getwrenai
立即使用 AI 提升您的数据能力?!