SaaS · 查询引擎 · LLM 应用复盘

低代码/无代码的黄昏,与 AI 交互的黎明
——一套多表查询引擎的两代演进

从点选式的分步配置,到接入大模型的一句话查询

发布时间:2026-09-03 17:02

TL;DR

本文完整复盘一个 SaaS 低代码平台上「多表复合查询」功能的两次演进——第一代用单表查询 + ID 集合回灌(拆掉 JOIN)在多租户下实现可控的跨表查询,却在半年后被验证「能力到位、门槛过高」;第二代引入大模型做自然语言入口,复用原有引擎,并通过两段式路由、JSON 修复重试、KV 双向转换等一组工程护栏,让 80% 以上的多表查询一句话跑通。

文中所有表名、字段均已脱敏为通用中性名词(订单、客户、商品、仓库…),聚焦方法论、设计权衡与工程经验,不依赖任何具体行业,可自由迁移适用。

关键词低代码 / 无代码LLM 应用自然语言查询多表查询提示词工程SaaS 多租户从 SQL 到 AI

READING GUIDE

阅读指引

本文适合以下读者,并期望你至少带走其中一点:

后端 / 架构工程师

关注「在一个成熟 SQL 底座上,如何用单表查询组合出跨表能力」,以及两段式路由如何化解超长提示词;可重点看第 2、5 章。

AI 应用工程师

关心「如何给不可靠的 LLM 输出套护栏」,以及与大模型反复打磨的实战细节;可重点看第 4、6 章。

产品 / 平台负责人

想理解「为什么点选式低代码门槛高、以及 AI 交互为何能拉平普惠」;可重点看第 3、7、8 章。

如果你时间有限,只看「TL;DR」+ 第 6 章(十个坑)即可掌握这篇文章最硬核、最可复用的一部分。

PROLOGUE

一、写在前面:我们到底要解决什么问题

我们在超兔一体云平台上,开发低代码交互的查询工具。支撑业务方跨表分析的核心组件,就是我们围绕多表复合查询引擎做的两代开发:从点选式的分步配置,到接入大模型的自然语言查询。作为研发该引擎的技术人员,我们在前一代沉淀了一套成熟的查询底座,又在后一代把这层交互整个交给了大模型。业务方在平台上搭表、搭表单、搭流程,最终沉淀出一套几十张相互关联的业务表。随着业务跑起来,一个高频诉求浮出水面:

「把(条件A)的(订单),关联上(条件B)的(客户),看看谁…」

这类查询有几个共同特征:

  1. 跨表:要同时看订单、客户、商品等多张表的数据;
  2. 条件不固定:今天是「近 90 天 + 某区域」,明天是「满 2 年没下单 + 有库存」,条件组合千变万化;
  3. 主从关系可嵌套:订单挂在客户下,明细挂在订单下,还可能经过中间表间接关联。

在关系型数据库里,这种需求最「标准」的答案当然是 JOIN。但在「一套库养几百上千个租户」的 SaaS 架构里,JOIN 并没有想象中美好。这正是我们第一代方案动刀的起点。

而站在更大的时间轴上看,这两代方案恰好把过去几年两条最受关注的技术路径——低代码/无代码的「点选式」与大模型的「一句话式」——在同一个功能上完整地各走了一遍。行业里围绕二者的争论有个尖锐的落点:低代码/无代码本就不是给终端用户用的,它帮开发者把「写代码」降级成「点界面」,却把「点界面」这件靠人手工、因硬编码在前端而难以被工具自动复制的事留了下来,因而在大模型时代几乎找不到立足之地。我们这个发生在普通用户身上的小切面,恰恰能在最贴近业务的一层印证这个判断——这是第八章要展开的核心观点,这里先按下不表。

GENERATION ONE

二、第一代:用「拆 JOIN」换跨表查询

2.1为什么不敢直接用 JOIN

对单租户自建系统,JOIN 是家常便饭;但对 SaaS 多租户,我们反复权衡后决定避免把多表 JOIN 直接下推到数据库,理由有三个:

顾虑说明
资源占用条件不固定、跨多表 + 大字段(如明细、富文本)JOIN,很容易把库压垮;一个租户的「极端查询」可能拖垮同实例上的其他租户。
慢查询/死锁高并发写多的系统里,复杂 JOIN 和主从复制之间存在锁竞争风险。
隔离与公平多租户场景更在意「把负载控制在查询维度、按租户隔离」,而不是让数据库引擎去赌索引和连接策略。

一句话概括我们的取舍哲学:用计算时间换数据库负载,把复杂的关联从 SQL 层抬到业务逻辑层。这在多租户下是更「温和」、更可控的做法。

2.2核心思想:ID 单表查询的逻辑组合

不去 JOIN,却要获得等价的跨表结果,秘诀是用多张表的单表查询 + 内存中的 ID 集合拼接。核心套路三步走:

  1. 先在主表上用主条件查询出目标记录的主键集合;
  2. 对每一张需要参与关联的子表/关联表,单独用它的条件单表查询,取出「能和主表对上」的关联字段值(通常是主表的外键 ID)集合;
  3. 把子表的结果以 关联ID IN (集合) 的形式回灌成主表的主键过滤条件,逐级缩小主表结果。

这样每一层查询都是标准单表查询,数据库可以正常走索引、分页、缓存;复杂的关联分析全部发生在应用层,且天然按租户隔离。

我们把它形象地叫做 「IN 管道」:上一级的 ID 集合变成下一级的 IN 条件,一级一级传下去,直到回到主表。

2.3配套的元数据:表档案

要做到「程序化地点选、无关代码地组合」,光有查询引擎还不够,还需要一份描述「这张表能提供什么」的元数据,我们叫它表档案(Table Profile)。它是一份结构化 JSON,记录了:

表档案是整个体系的「唯一事实来源」(Single Source of Truth)。查询引擎本身是通用的、不感知业务;一切「这张表有哪些字段、字段是枚举还是人名」都从档案里读。这意味着加一张新表,只需要新增一份档案,不碰引擎代码

2.4一次点选查询的内部时序

用户在界面上的每一次点选,背后都走这样一条链路:

低代码/无代码的黄昏,与AI交互的黎明——一套多表查询引擎的两代演进

2.5它做到了什么,又留下了什么

做到的:一个通用引擎,能覆盖非常多条件不固定的多表组合查询,且每层都是单表查询,负载可控、租户隔离清晰、可加表不改引擎。我们当时对它很满意。

留下的:它把「能力」做到了,却把「门槛」也留下了——这个门槛最终决定了一代产品的命运,见下一章。

THE COLD WATER

三、半年的冷水:功能「能」做,但没人用

功能在 3、4 月上线。到 7、8 月回看数据,我们得到一个冷静的事实:

这个功能只有一些「深度用户」和「粉丝级用户」在用。

3.1谁在用,谁不用

用的人:理解表之间关系的老用户,愿意啃界面、一层层配关系。这批人是真正的受益者。

不用的人:占绝大多数的普通用户。不是功能跑不起来,而是他们一眼看不懂、也不想学

3.2门槛到底在哪里

我们复盘出,门槛由三层「认知税」构成:

低代码/无代码的黄昏,与AI交互的黎明——一套多表查询引擎的两代演进

更扎心的对比是一句话:普通用户要表达的诉求,一两句话就能说清楚;但用点选界面把它配出来,可能要花 2 分钟左右,还要夹带上面那些认知。价值(能查复杂数据)是真的,但获取价值的成本把绝大多数人挡在了门外。

3.3大模型交互为什么能破局

回看大模型之所以能快速普及,本质不是它逻辑多强,而在于两点交互特质:

  1. 自然语言输入:用户用「说话」代替「配置」;
  2. 复杂逻辑输出:把脑子里那句含混的话,翻译成严谨的结构化逻辑。

即便我们清楚大模型是靠「概率生成」而非「严格推理」,但用户认可的是交互本身——低门槛、说人话、即时反馈。对我们的启示非常直接:

既然查询引擎(第一代)已经成熟可靠,那 AI 方案不需要再造一个引擎,只需要在前面加一个「听得懂人话的大脑」,把自然语言翻译成引擎能吃的结构化查询要求。复用已踩过坑的成熟引擎,用 LLM 替换「点选」这层输入

GENERATION TWO

四、第二代:AI 复合查询,一句话的事

4.1总设计:复用引擎,新增大脑

第二代在整个系统里只做了两个大的环节:

低代码/无代码的黄昏,与AI交互的黎明——一套多表查询引擎的两代演进

环节一唯一的新东西

分析用户自然语言描述的多表查询逻辑,结构化为查询要求。

环节二直接复用

第一代已经打磨成熟的复合查询引擎,原封不动接上。

这避免了「为 AI 重造轮子」,是第二代能快速落地、且一上来就有 80% 命中率的关键。

4.2端到端链路

低代码/无代码的黄昏,与AI交互的黎明——一套多表查询引擎的两代演进

4.3环节一:自然语言 → 结构化查询要求

这是整个 AI 方案的「翻译官」,也是和大模型博弈最激烈的地方。它要产出类似这样的结构化查询要求(已脱敏):

json{
  "mainTable": "商品",
  "conditions": [
    { "field": "库存", "op": "gt", "value": 0 }
  ],
  "steps": [
    {
      "relationTable": "订单",
      "via": "商品ID",
      "condition": {
        "field": "下单日期",
        "op": "within",
        "value": "近90天"
      }
    }
  ]
}

翻译官不是一次就能做好的。它和大模型的每一次握手都可能失败,于是我们在其后挂了 修复 + 校验 + 重试 三层保险(详见第六章)。关键词:不要把大模型的「一次输出」当成可靠输入,而要把它当成一个需要持续对齐、校验、纠偏的子系统

4.4环节二:结构化查询要求 → 引擎执行

有了结构化查询要求,剩下就是第一代引擎的主场:

这一点特别重要:LLM 只是把话说清楚,它一次 SELECT 都不会执行;真正干活、兜底正确性的,仍是那套验证过的查询引擎。

TWO-STAGE ROUTING

五、关键的架构改造:两段式路由

这是在我们认为第二代「已经完成 80%」时,被迫做、也做得最值的一次改造。

5.1触发改造的导火索:提示词超限

第二代初期,做法相对朴素:把所有表的档案一股脑塞进提示词,让大模型自己去挑。平心而论,这套做法在体验上是合格的——我们选用的是非思考的快速模型,单次生成在 5~8 秒之间,响应速度没有问题。真正的导火索出现在表数量到 25 张左右时:提示词体积逼近 3 万 token,在智能体平台侧触及了上下文限额。超限带来两个问题:

  1. 输出漂移:提示词越长,大模型越容易注意力涣散,输出开始不稳定、前后不一致;
  2. 扩展性被锁死:如果就这样下去,往后扩到 40、50 张表根本走不通——这直接堵死了产品的增长预期。

我们意识到:「把所有表都喂给模型」这条路,是条死路。

5.2两段式的本质:从「喂全部」到「喂相关的」

改造后的核心动作,是把一次问答拆成两段

低代码/无代码的黄昏,与AI交互的黎明——一套多表查询引擎的两代演进

第一段表路由

只给大模型一张「表名 + 一句话说明」的轻量索引 + 表关系图,让它召回相关表。这里我们刻意采用「宁多勿少(recall more than necessary)」策略——多召回不致命,漏召才是致命的。

第二段计划生成

只把第一段选中的那几张表的完整档案注入提示词,生成精确的查询计划。

这段逻辑把「表的数据」和「大模型的分析结构」彻底解耦了。改造后,智能体人设稳定在 8000 字出头且不再随表数量增长;无论未来是 40 张、50 张还是 100 张表,第一段都只看一层轻量索引,提示词长度基本恒定;第二段则永远只关心「和本次问题相关的那几张」。扩表从此不再受提示词长度约束,同时也因为单次注入的档案变短,降低了长提示词带来的输出漂移。

5.3路径闭包:自动补齐中间表

两段式有个隐患:LLM 按直觉选表,但表之间可能不是直接关联,需要中间表搭桥。例如用户问「近 90 天下单的客户」,模型第一段可能只召回「客户」和「订单」,但两者实际要通过「订单_明细」甚至「订单_商品」间接关联。

于是我们在表路由后加了一个 闭合(closure) 步骤:拿到召回表集合后,基于预生成的「表关系图」,用 BFS / 最短路径 自动把缺失的中间表补进集合,保证路由结果在关系上「自洽」。

低代码/无代码的黄昏,与AI交互的黎明——一套多表查询引擎的两代演进

5.4两段式完整时序

完整的「用户提问 → 结构化 → 执行」时序如下(标注了每一步的兜底/重试):

低代码/无代码的黄昏,与AI交互的黎明——一套多表查询引擎的两代演进

TEN PITFALLS

六、与大模型缠斗的过程:十个坑和它们的解法

如果说第一代的技术主旋律是「架构权衡」,那第二代的主题就是「和概率生成做纠错」。这一章集中复盘我们踩过的坑和对应的工程化解法。

先给出全貌:LLM 输出从「不可靠」到「可用」,中间隔着一条完整的护栏流水线——

低代码/无代码的黄昏,与AI交互的黎明——一套多表查询引擎的两代演进

后面的十个坑,本质上都是这条流水线上某一段的具体实现细节。

6.1JSON 不稳定:全角符号与尾逗号

:大模型输出的 JSON 时常携带全角标点 )、或结构末尾多一个逗号,导致 JSON.parse 直接崩。

解法:在解析前增加一个 _repairJson:把全角标点转半角、去掉字符串外的尾逗号。这能静默修复掉很多小毛病,把「偶发失败」挡在解析的最前面。

js// 示意:先用宽松手段修复,不行再进入重试路径
function repairJson(raw) {
  return raw
    .replace(/[\uFF0C\uFF1B\uFF1A]/g, (c) => ({
      '\uFF0C': ',', '\uFF1B': ';', '\uFF1A': ':',
    }[c]))
    .replace(/,\s*([}\]])/g, '$1'); // 去尾逗号
}

6.2修不好就重试:带上错误现场

:修复后仍然解析失败,或者字段非法。

解法:不再盲目整体重试,而是把错误位置 + 前后 40 字上下文回传给大模型,让它「看着现场改」。带现场的重试比无脑重说一遍的命中率高得多。

6.3相对时间漂移:日期锚点 + 白名单

:用户说「近 90 天」,模型可能把它解析成错误的起始日期,或干脆编造一个不存在的时间 token(如 last_30_days),导致查错范围。

解法(两招并用):

  1. 日期锚点:调用大模型前,在用户消息前注入 [当前系统日期:YYYY-MM-DD],让模型能正确地算相对日期;
  2. token 白名单:所有相对时间 token 必须先过白名单校验;验证到「编造出来的 token」就刻意抛错,反馈给模型要求改用绝对日期,而不是悄悄默认成「今天」——悄悄默认才是真正的隐患。

6.4KV 双向转换:WHERE 侧与结果侧两面镜像

:枚举/人员/选择类字段库里存的是数字 K(如 上海≈25),但用户说的是中文 V(上海),且查询(WHERE)和结果展示(结果侧)两个方向都要转,方向正好相反。

解法:做成双向转换,并且规则完全对称:

方向目的手段
WHERE 侧 V→K 把用户/计划里的中文筛选项转成库里的存储值再打库 走字典/名称接口取到 {key, value},把 V 映射成 K
结果侧 K→V 把库里返回的数字/ID 翻译成用户看得懂的展示值 主表取数后用字典翻译枚举/人员/选择ID,多选按逗号拆分、用「、」重新拼接

这个转换是查询正确性展示可读性最后的护城河,做了以后,细节瑕疵(如个别值没映射)就地保留原值并打日志,不阻塞整条查询。

6.5主表取数要「原始值」再翻译

:如果直接取翻译后的展示值回来做过滤,会把 K→VV→K 搞乱。

解法:主表取数要求带原始存储值(接口 flag=1),返回后才能统一在结果侧做 K→V 翻译。取数用原值,展示才做二次翻译,方向清晰不打架。

6.6「id」字段:能聚合、不能筛选

:为了让模型能做「计数」类统计,把所有表都注入了 id 字段,但模型可能误把它当普通筛选条件用,造成错误查询。

解法:在档案里对 id 明确标注 「仅用于聚合 count,不可作为筛选条件」,并把这条规则也写进人设,从源头约束。

6.7单占位符约束:一次档案双注入的教训

:提示词模板里曾出现两个替换占位符指向同一份表档案,结果档案被双份注入,token 直接翻倍(曾观察到 2.7 万+ token 的超大提示词,定位修复后回落到 1.5 万左右),既浪费又加重漂移。

解法:收敛模板,全篇只保留文末一个档案占位符,从根上杜绝双份注入。token 用量显著回落。

6.8把人设和档案掰开:一致性优先

:如果档案内联在人设里,每次加表都要重排人设,且不同入口容易不一致。

解法人设常量、档案动态注入。智能体人设只写固定的行为规则与输出约束,表档案作为每次请求的变量注入;加表只改档案、不改人设,天然保持一致。这也让「人设」从一串会随业务膨胀的长文本,固化成一个稳定、可维护的常量。

6.9别让超时变成谜语

:推理型(思考型)模型单次出计划可能要几十秒甚至更久,普通用户等得不耐烦;且超时/网络错误如果不处理,就是一段看不懂的报错。

解法:给请求设置较长且可调的超时时间(推理模型需要在 60~120 秒量级),并把超时、网络错误统一包装成中文、清晰、常驻(不随操作消失)的提示,告诉用户「正在等模型,或已超时」,而不是甩一段 stack trace。一个附带经验:如果对延迟敏感,尽量选非思考的快速模型——我们后来就为此从思考模型切换到了去思考的快速模型。

6.10区分两类失败:语法错误 vs 档案校验失败

:初版把所有失败都叫「解析失败」,用户和模型都分不清问题到底出在「JSON 格式」还是「表/字段本身不存在」,纠偏没有方向。

解法:把错误明确分成两类——「JSON 语法错误」「表档案校验失败」,让系统向模型分别回传不同的反馈提示,纠错更有方向。

IMPACT

七、效果与价值

把两代做下来,简单算一笔账:

第一代(点选式)第二代(一句话)
输入方式逐层选表、配关系、加条件一句自然语言
表达成本约 2 分钟的连续点选 + 认知税10 秒内说清诉求
能力上限支持任意复杂条件组合80%+ 的常用复合查询直达
用户构成深度用户 / 粉丝级用户全量普通用户
扩展性加表不改引擎加表不改引擎、不改人设

第一代把「复杂多表查询」从「要写 SQL」降级成「点选配置」,能力到了,但对多数人门槛仍在;第二代把「点选配置」再降级成「说一句话」。实测下来,至少 80% 的多表复合查询,通过自然语言描述就能直接跑通

80%+

多表复合查询,一句话直接跑通

5~10 倍

门槛拉平后的潜在普惠人群

而这 80% 还只是「存量用户」口径下的统计。如果把 LLM 交互带来的用户增量算进去,这个功能的潜在普惠人群可能提升 5~10 倍——因为门槛被拉平,普通用户第一次敢用了;而且随着口碑扩散,使用数据会呈现扩散式增长。

从项目价值的角度,我们认为第二代是一次成功且很有意义的升级探索:它没有推翻第一代的引擎,而是在一个已验证的底座上,用 LLM 替换了「门槛最高、价值最薄」的交互层,把一项「高手专属」能力变成了「人人可用」能力——这正是「以降门槛换取普惠」的典型样本。

THE ERA VERDICT

八、从点选到一句话:低代码/无代码的时代命运

第一章末尾我们埋了个伏笔——这两代方案不是一次普通的升级,而是把两条时代路径在同一个小切面上各走了一遍。现在回到这个视角展开。

如果只把这两代方案放在「一个查询功能」的尺度里看,它只是一次成功的升级。但把「点选配置」和「一句话查询」这两类交互方式放到更大的时间轴上,它恰好构成了过去几年低代码/无代码浪潮与今天AI 浪潮的一个缩影。

8.1两种交互,两个时代

先把术语对齐:

前者强调「配置」,后者强调「表达」。表面看只是操作方式不同,本质上是把人的心智负担,从「理解机器逻辑」转移回了「用人的语言说话」

8.2为什么开发者判定低代码/无代码没有未来

最近很多开发者唱衰低代码/无代码,理由并不是「能力不够」,而恰恰是它的交互方式注定的。归纳下来有三点:

症结说明
受众错位低代码/无代码严格说不是给终端用户用的,它是帮开发者/搭建者快速实现能力的工具。一旦交给「不懂表结构的普通用户」,仍然要求对方理解领域模型,门槛并没有消失。
不可自动复制点选操作本质是「人手工点击界面」,而很多界面级逻辑都硬编码在前端,很难像普通 API 那样被 Cursor 这类 AI 编程工具或 MCP 这类标准协议抽象、接管、批量复制。
重度依赖人工哪怕你脑子里想得再快再清晰,也必须一步不落地点一遍。这种「人肉点按」无法随需求扩张而自动化,也扛不住规模化复制。

一句话:低代码/无代码把「写代码」降级成了「点界面」,但「点界面」这件事本身,恰恰是 AI 最难以高效复制的那种劳动。

低代码/无代码的黄昏,与AI交互的黎明——一套多表查询引擎的两代演进

8.3在 AI 编程的时代,它退守到哪

当 AI 编程(Vibe Coding)可以用自然语言直接生成、修改、调用代码时,低代码/无代码「降低编码门槛」的价值几乎被覆盖。绝大多数过去非用低代码/无代码不可的场景,现在都能被 AI 编程彻底颠覆

它真正残留的空间,大概率只有很小范围、做配置升级/配置优化的局部——而且即便如此,也更可能以「被 AI 驱动」的方式存在,而不是以「人手工点选」的方式存在。

8.4我们的案例恰恰补上了最关键的证据

有意思的地方在于:行业里这场争论大多围绕开发者展开——低代码/无代码对开发者失去了吸引力。而我们的案例发生在终端用户层

即便不是开发者、只是普通业务用户,「点选配置」和「一句话查询」之间的差距依然大到天壤之别。

第一代把 SQL 降级成点选,对「非开发者」门槛依然高;第二代用一句话平掉门槛,才真正普惠了终端用户。这补上了两件关键证据:

  1. 低代码/无代码在最贴近业务的一层——终端用户这里,同样无力,而不只是对开发者无力;
  2. 大模型的「一句话交互」不是一个领域的小改良,而是一种覆盖范围更广、更强的交互范式换代

所以,如果给这两代方案找一个时代注脚,它可以浓缩成一句话:

低代码/无代码改变了「开发者怎么搭」,而大模型改变了「每个人怎么用」——前者是工具升级,后者是交互范式的换代,也就是技术的时代进步。

EPILOGUE

九、总结与展望

把第 8 章放回工程语境,两代演进可以浓缩成三步棋:「拆掉 JOIN、换掉入口、再加上大脑」

  1. 第一代(点选式)回答了「多表查询怎么在 SaaS 多租户下体面地做」——用 IN 管道拆掉 JOIN,用表档案做唯一事实来源。
  2. 第二代(AI 式)回答了「怎么让它人人可用」——复用成熟引擎,用自然语言做输入,用两段式路由解决扩展性与漂移。
  3. 贯穿始终的是一条工程态度:永远不要把 LLM 的一次输出当可靠输入,系统要在其周围建起 校验、修复、重试、闭环 的护栏

展望上,我们下一步会往三个方向走:支持更多表的更广业务域、沉淀高频查询为「常用查询」注入、探索让 LLM 不只是翻译查询、还能辅助解释结果。而这些,都已站在两代方案共同打下的地基上。

如果你也在做低代码平台的查询、或正想把 LLM 接入某个成熟功能,希望本文的「两个阶段、两次架构决策、十个工程坑」能给你一个可复制的心智模型:

先问自己:要新增的是「引擎」还是「入口」?如果想降的是门槛,那大概率是后者——复用你的成熟底座,只把交互层交给大模型。

BOUNDARY

附:适用边界与局限

把经验讲清之外,也把边界交代清楚,避免误用:

END

这篇复盘来自超兔一体云查询引擎的研发一线——两个阶段、两次架构决策、十个工程坑,都来自真实项目的反复打磨。如果你觉得有收获,欢迎把它分享给正在做低代码平台或 LLM 应用的朋友。

文中涉及的内部表名、字段名、部署细节均已脱敏处理;所有业务均为通用中性命名。

下一篇:上一篇:线索不多,所以一条都输不起:工业工贸企业的线索管理 Know-How

注册试用