ThoughtSpot AgentQL:SQL形态意图,不直接生成SQL的工程路线
DataHot 速览
ThoughtSpot 工程博客介绍了 AgentQL——一种以 SQL 语法表达分析意图、但不用 LLM 直接生成 SQL 的能力。其背景是 Spotter 的搜索令牌(token)存在表达力缺口,无法覆盖多步对比、层级计算等复杂查询。AgentQL 选择 SQL 语法作为意图语言,同时拒绝 SQL 执行模型,以统一查询引擎执行。文章详述了背后的工程取舍与设计原则。
为什么值得关注:数据从业者可从中了解 ChatBI/Text-to-SQL 的一种架构思路:用 SQL 形状的中间意图而非直接生成 SQL,兼顾表达力与可执行性,对构建分析 Agent 有借鉴价值。
译文
AI 逐段翻译我们的CEO最近撰文重申了ThoughtSpot在LLM出现之初做出的架构决策:我们不使用LLM直接生成SQL。
我的团队花了近一年时间构建AgentQL:这一能力强化了我们的决策。
下面我将解释我们实际构建了什么,为什么它不仅尊重这一架构决策,而且依赖于它,以及底层的工程选择。
我们面临的问题:表达性差距
Spotter通过搜索令牌回答大多数问题:这是一种结构化、可读的意图表示,业务用户无需了解任何查询语言即可检查和纠正。令牌是合适的默认选择,它们仍然是主要路径。
但我们不断遇到一道我们称之为表达性差距的墙。我们看到诸如“显示本财年支出较去年下降的客户,按下降幅度排序”和“比较各地区的边际贡献与公司平均水平”等查询。多步比较、粒度级别的计算、队列逻辑。
我们的查询引擎可以回答每一个这样的问题,并且多年来一直是如此。令牌语法根本无法表述它们。每当我们扩展语法并重新训练Spotter以生成它时,新的分析功能就会出现,差距又会重新出现。
我们需要一种更丰富的语言来表达意图,这就是我们构建AgentQL的原因。我们不需要——也拒绝构建——第二条执行路径。
设计决策:SQL形态的意图
我们选择SQL语法作为意图语言,有两个实际的工程原因:
- 它是迄今为止为分析问题创建的最精确、最广泛理解的表示法。
- 它是LLM训练最密集的语言。我们发明的任何定制DSL在这两方面都会更差。
但我们采用了SQL的语法而拒绝了SQL的执行模型。这就是整个设计。这也是AgentQL背后的第一原则:意图和执行是分离的层。
AgentQL语句是针对ThoughtSpot模型编写的:即分析师策划的业务名称。不是物理表或仓库列。
它永远不会,在任何情况下,都不会对您的数据库执行。没有代码路径可以做到这一点。我们知道,因为我们构建了代码路径。
实际发生的情况:AgentQL语句被解析为查询规范,即每个ThoughtSpot查询变成的相同内部表示。
该规范交给我们的确定性查询生成引擎,该引擎生成ThoughtSpot中的每个查询,无论是从Liveboard、搜索还是Spotter对话开始。该引擎编写要运行的SQL。LLM的输出是编译器的输入,而不是数据库的查询。
我们称之为SQL形态的意图。
与文本到SQL的区别并不微妙:
| 文本到SQL | ThoughtSpot AgentQL | |
|---|---|---|
| LLM编写的内容 | 运行的查询 | 对所需内容的描述 |
| 它引用的内容 | 物理表和列 | 模型的业务名称 |
| 执行的内容 | LLM的输出,逐字逐句 | 由我们的专有引擎编译的确定性SQL |
| 谁强制执行安全、连接、指标、日历 | LLM(希望,每次查询) | 引擎(始终,按构造) |
一个具体的例子。“按产品类别的总销售额和总退货额”可能表达为:
SELECT "t1"."Product Category",
SUM("t1"."Sales Amount") AS "Total Sales",
SUM("t1"."Return Amount") AS "Total Returns"
FROM "Retail Sales" AS "t1"
GROUP BY "t1"."Product Category"该语句永远不会运行,并且隐藏了一个陷阱。在模型背后,销售和退货位于两个共享产品维度的独立事实表中:一个经典的沟壑陷阱。
手写SQL直接连接它们会使行扇出并静默夸大两个总数。编译器知道得更好,因为模型知道:它先在各自粒度上聚合每个事实,然后再合并,解析声明的连接路径,并注入查询用户的行级安全过滤器。您可以检查编译后的SQL。意图只需几秒钟来表达;它继承的语义我们花了十年时间工程化。
一个值得明确说明的结果:AgentQL故意不是SQL的全部。它是一个受限方言,限制就是契约。语句必须完全可针对模型解析,因此编译器会拒绝任何无法确定性治理的内容,从SELECT *到可能绕过模型语义的结构。
我们限制方言不是因为解析完整SQL很难。我们限制它是因为每个接受的语句都是一个承诺,即编译的查询是受治理的,而我们只接受我们能兑现这一承诺的语句。
为什么我们拒绝让LLM做更多
这是一个合理的问题:LLM正在迅速改进,那么为什么不让前沿LLM直接编写仓库SQL并随后验证呢?
因为我们已经花了多年时间为企业数据的实际情况进行工程化,而其中没有一项是对LLM友好的。真实数据模型不是平面表。它们是具有沟壑陷阱和扇出陷阱的多星型模式,如上面销售和退货的例子:连接模式中错误数字看起来完全合理。
有能力的工程师会弄错这些;我们见过。它们携带PII,必须在每次查询中为每个用户保持安全。它们为每个业务单位运行不同的财务日历。它们定义了半加指标,如库存和账户余额,在这种情况下,简单的跨时间SUM就是错误的答案。
要求LLM在每次问题上概率性地重新推导所有这些,你得到的只有两样东西:不确定性和一笔令牌账单。
我们的引擎已经编码了这些语义。因此工程答案显而易见,它成为第二原则:保持LLM的工作薄弱。理解问题,紧凑地表达,然后停止。所有决定数字是否正确的东西都按构造从模型继承。
LLM无法忘记你的行级安全性,因为它从不编写实际运行的查询。它不会选择错误的连接路径,因为模型的连接由编译器应用。它不会偏离你的收入定义,因为定义只有一个,且每个查询都通过它编译。
失败模式同样重要,这是第三个原则:糟糕的查询是编译错误,而不是事故。 当LLM表达意图错误时,你会得到错误但受管控的答案或清晰的验证错误:语句在编译时被拒绝并附有原因。
你永远不会得到的是未受管控的查询接触你的数据。在text-to-SQL系统中,幻觉是实时查询。在我们的系统中,它是编译错误。
这些原则还带来另一项好处,这也是客户最关心的一点:信任。确定性编译器意味着相同的问题产生相同的查询规范、相同的编译SQL和相同的数字,无论哪个LLM表达意图或如何措辞。许多text-to-SQL工具也会显示运行的SQL。
区别在于阅读它给你带来的价值。在那里,你在审计LLM的即兴发挥,逐个查询:它是否选择了正确的连接,应用了正确的安全性,并使用了正确的收入定义?
在这里,SQL是从模型的受管控定义编译的,因此这些问题已经得到解答。验证答案意味着确认意图被理解,而不是手动重新推导正确性。
结果:不按令牌收费
这种架构的一个副产品体现在账单上而不是演示中。因为LLM的角色刻意很小,繁重的工作由我们的确定性引擎完成,而不是按计量推理。一个问题不会启动代理消耗令牌重新推理你的模式。
这是公司在Spotter 3发布时做出决定背后的工程事实:不按消耗的令牌收费。我们的架构使这一选择成为可能;企业选择将好处传递下去。
没有人想要每次提问都带着后台计费器的体验。有了这种架构,就不必如此。
同样的赌注,加倍下注
业界许多公司押注,通过足够的提示、护栏和重试,LLM将编写可信赖的仓库SQL。我们在LLM出现多年前就做了相反的赌注。AgentQL加倍下注:LLM永远不应编写实际执行的查询。LLM理解意图。受管控的引擎产生确定性的答案。
对Spotter用户来说,回报立竿见影:今天就能回答更广泛的分析问题,无需等待令牌语法增长或Spotter针对新能力进行重新训练。如果引擎能回答,AgentQL现在就能提问。
AgentQL为意图提供了更丰富的语言。信任架构毫不动摇:它存在于引擎中、模型中,并掌握在你手中以验证。这是设计带来的工程,而非妥协——所以你永远不必在速度和信任之间做出选择。
开始您的个性化演示 了解如何实现。
订阅我们的博客
这篇内容对你有用吗?
反馈只用于改善内容筛选,不等同于收藏