1. 别被“AI万能论”带偏:从图灵奖得主的冷水说起
最近技术圈里流传着一个观点,来自数据库领域的图灵奖得主 Michael Stonebraker。大意是:别太高估大语言模型了,它跟数据库的结合远没有想象中那么顺滑,智能体也不是万能钥匙。这话乍一听有点“反潮流”,但如果你真正在生产环境里折腾过 LLM 和数据库的集成,就会明白这不是唱衰,而是一盆让人清醒的冷水。
我自己在过去一年多里,陆续做过几个把 LLM 接进数据库查询、报表生成、运维问答的小项目,踩过的坑能写满一个笔记本。所以看到这个标题的时候,第一反应不是“大佬又出来泼冷水了”,而是“终于有人把大家心照不宣的问题摆到台面上了”。这篇文章我想聊的不是复述他的观点,而是结合我自己在LLM、智能体、数据库、DBOS、Text-to-SQL这些方向上的实操经验,把“为什么不能高估”这件事拆开讲透,顺便给正在做类似项目的朋友一些能直接抄作业的思路。
先说清楚这篇文章适合谁看。如果你是把 LLM 当成一个“输入问题就出答案”的黑盒来用,那可能收获有限;但如果你是正在做Text-to-SQL、智能体开发、数据库运维自动化,或者正在评估“要不要把 LLM 塞进数据链路”的工程师、架构师、技术负责人,那这篇内容应该能帮你省下不少试错成本。核心关键词会围绕 LLM、智能体、数据库、DBOS、Text-to-SQL 展开,但我会尽量说人话,不堆术语。
Stonebraker 的核心担忧其实可以归纳成一句话:LLM 擅长的是语言层面的模糊匹配,而数据库要的是精确、可验证、可回滚的操作,这两者的气质天生不合。很多人做 Text-to-SQL 的时候,demo 阶段惊艳,上线之后翻车,根本原因就在这里。下面我分几个层面,把这件事掰开揉碎。
2. 为什么 LLM 和数据库“气质不合”:核心矛盾拆解
2.1 概率生成 vs 确定性执行的根本冲突
LLM 的本质是概率模型,它输出的每一个 token 都是“最可能的下一个词”,而不是“逻辑上必然正确的答案”。数据库恰恰相反,一条 SQL 要么执行成功返回精确结果,要么报错,没有“大概对”这种中间状态。这个矛盾在 Text-to-SQL 场景里体现得淋漓尽致。
我做过一个实验:给同一个业务库,让几个主流 LLM 生成“查询上个月销售额环比增长超过 10% 的门店”这样的 SQL。结果十次里有三次把“环比”理解成了“同比”,两次把日期边界算错,还有一次 JOIN 条件写反了。单看每一条 SQL 都“像模像样”,语法完全正确,但语义全错。这就是概率模型的典型表现——它生成的是“看起来最像正确答案的文本”,而不是“经过验证的正确逻辑”。
提示:判断一个 Text-to-SQL 方案能不能上生产,不要看它 demo 时答对了几道题,要看它在“边界条件、多表关联、时间窗口、聚合嵌套”这四类问题上的错误率。这四类才是真实业务的常态。
2.2 Schema 理解的“最后一公里”问题
数据库的 schema 是高度结构化的,表名、字段名、外键关系、枚举值、注释,这些信息对人是清晰的,但对 LLM 来说,它看到的只是一堆文本。问题在于,真实业务库的字段命名往往极其“反人类”:flag_a、type_cd、amt1、dt,光看名字根本猜不出含义。LLM 只能靠字段类型和少量注释去猜,猜错的概率非常高。
我见过一个典型案例:某系统里有个字段叫status,取值是 0/1/2/3,但没有任何注释。LLM 想当然地认为 0 是“无效”、1 是“有效”,结果实际业务里 0 是“待审核”、1 是“已通过”、2 是“已拒绝”、3 是“已撤销”。生成的 SQL 里WHERE status = 1直接把“已拒绝”的记录也漏掉了。这种错误在 demo 里根本发现不了,因为 demo 数据往往很干净。
解决这个问题的常见做法是引入语义层(Semantic Layer)或者本体(Ontology),把字段的业务含义显式地喂给 LLM。这也是为什么最近llm ontology、rag graphrag llm wiki 本体rag这些词热度很高——大家都在试图给 LLM 补上“业务语义”这一课。但语义层的维护成本很高,业务一变就得同步更新,这又引出了下一个问题。
2.3 智能体不是银弹:多步推理的误差累积
很多人觉得,单次 Text-to-SQL 不准,那就上智能体,让它多步推理、自我纠错不就行了?理论上没错,但实际做下来你会发现,智能体的每一步推理都会引入新的误差,误差是累积的,不是抵消的。
假设一个智能体完成一次查询需要 5 步:理解意图、检索 schema、生成 SQL、执行验证、格式化结果。如果每一步的准确率是 90%,那整体成功率只有 0.9 的 5 次方,约 59%。这还没算上工具调用失败、上下文超长、幻觉字段这些常见问题。我实测过一个 7 步的智能体流程,单步准确率看着都不错,端到端成功率却只有 40% 出头。
所以 Stonebraker 说“别太高估”,本质是在提醒:智能体把问题从“一次生成”变成了“多次生成”,而每一次生成都是概率事件,链条越长越脆弱。这也是为什么agentdojo测试智能体方法、2026年智能体应用owasp top 10这类测试和风险清单开始流行——大家开始意识到,智能体的可靠性需要专门的工程手段来保障,而不是靠“模型更强”就能解决。
3. Text-to-SQL 落地实操:从 demo 到生产的完整路径
3.1 第一步:把 schema 喂对,比换模型更重要
我踩过最大的坑就是一开始迷信“换个更强的模型就好了”。后来发现,在 schema 描述上多花一小时,比换模型带来的提升大得多。具体怎么做?我总结了一套“三层 schema 描述法”。
第一层是物理层,就是数据库原生的表结构、字段类型、主外键,这部分直接导出即可。第二层是语义层,给每个字段补上业务含义、取值范围、单位。第三层是关系层,描述表与表之间的业务关系,比如“订单表通过 user_id 关联用户表,一个用户可以有多个订单”。
-- 物理层:直接导出 -- 语义层:手工补充,建议用 YAML 维护 tables: orders: description: "订单主表,记录所有交易订单" columns: status: type: int description: "订单状态" enum: 0: "待审核" 1: "已通过" 2: "已拒绝" 3: "已撤销" amount: type: decimal description: "订单金额,单位:元,含税"这套描述维护起来确实有成本,但它是 Text-to-SQL 准确率的“地基”。我实测下来,补全语义层之后,同一个模型在复杂查询上的准确率能从 50% 出头提升到 75% 以上。这个投入产出比,比换模型划算太多。
3.2 第二步:用“检索增强”缩小 LLM 的搜索空间
真实业务库动辄几百张表,把全部 schema 塞进 prompt 既不现实也不经济。这时候就需要RAG(检索增强生成)的思路:先根据用户问题检索出最相关的几张表,再让 LLM 在这几张表的范围内生成 SQL。
具体做法是:把每张表的描述、字段说明、典型查询做成向量,存进向量库。用户提问时,先做一次相似度检索,召回 Top-K 张表,再拼进 prompt。这样既控制了 token 消耗,又降低了 LLM “看花眼”选错表的概率。
注意:检索的粒度很关键。按“表”检索太粗,按“字段”检索太细,我一般按“表 + 核心字段组合”来切分,效果比较平衡。另外,检索召回率要单独监控,召回错了后面全错。
3.3 第三步:执行验证与自我纠错闭环
生成 SQL 之后,千万别直接返回给用户。我的做法是加一道“执行验证”关卡:先在只读副本上跑一遍,如果报错,把错误信息回传给 LLM 让它修正,最多重试 2 次。这个闭环能拦下相当一部分语法错误和字段名错误。
def generate_and_validate(question, schema_context, max_retry=2): sql = llm_generate(question, schema_context) for i in range(max_retry): try: result = execute_readonly(sql) return sql, result except SQLExecutionError as e: # 把错误信息回传,让模型修正 sql = llm_fix(sql, str(e), schema_context) return None, "生成失败,请人工介入"这里有个经验:只读副本是必须的,而且要给 SQL 加超时和行数限制。我见过 LLM 生成一个没有 LIMIT 的全表扫描,直接把副本拖垮的情况。另外,重试次数不要太多,2 次足够,再多就是在浪费 token 和时间,而且模型往往会“越改越错”。
3.4 第四步:把“不确定”显式暴露给用户
这是最容易被忽略但最重要的一步。LLM 生成 SQL 时,如果它对某个字段的含义不确定,应该主动说出来,而不是硬猜。我现在的做法是让模型在生成 SQL 的同时,输出一个“置信度说明”,列出它不确定的地方。
比如:“我理解status=1表示已通过,如果实际含义不同请指正。”这样用户看到结果时,能快速判断这次查询是否可信。把不确定性显式化,比假装确定要负责任得多。这也是我在多个项目里验证过的、提升用户信任度的有效手段。
4. 智能体与数据库结合:哪些场景真能用,哪些是坑
4.1 适合智能体的场景:运维问答与异常排查
不是所有场景都适合上智能体。我实践下来,数据库运维问答是比较靠谱的落地场景。比如“昨天凌晨的慢查询有哪些”“这个库的连接池是不是快满了”“最近一周哪张表增长最快”。这类问题的特点是:查询相对固定、有明确的监控数据源、容错率高(答错了人工再查一遍即可)。
这类场景我一般用coze+智能体或者自研的轻量智能体框架来搭,核心是把监控 API、慢日志、连接池状态这些工具封装成智能体可调用的函数。智能体负责理解问题、选择工具、汇总结果。因为工具返回的是确定性数据,智能体只做“调度”和“表达”,不直接生成 SQL,可靠性就高很多。
4.2 不适合智能体的场景:复杂业务查询与写操作
反过来,涉及多表复杂关联的业务查询,以及任何写操作,我强烈建议不要交给智能体自动执行。写操作一旦出错,可能是数据污染甚至资损,这个风险担不起。复杂查询则是因为误差累积问题,前面算过,步骤一多成功率就断崖式下跌。
我现在的原则是:智能体可以“建议”,但不能“执行”。它可以生成 SQL 草稿、解释查询逻辑、提示潜在风险,但最终执行必须由人确认。这个边界划清楚之后,很多焦虑就消失了——你不再指望智能体“全自动”,而是把它当成一个能力不错但需要复核的助手。
4.3 DBOS 思路的启发:把状态管理交给数据库
Stonebraker 提到的DBOS(Database-Oriented Operating System)思路很有意思。传统上我们用操作系统管理进程、调度、状态,而 DBOS 的想法是:既然数据库已经擅长持久化、事务、恢复,为什么不把这些能力直接用来管理应用状态?
这个思路对智能体开发很有启发。智能体的多步推理本质上是一个“有状态的工作流”,每一步的中间结果、工具调用记录、上下文,都可以存进数据库。这样即使智能体执行到一半崩了,也能从数据库里恢复现场,而不是从头再来。我在一个长流程智能体项目里试过这个做法,把每步状态写进一张agent_steps表,重启后能续跑,体验提升明显。
提示:用数据库管理智能体状态时,注意给每步操作加幂等标记,避免恢复时重复执行有副作用的工具调用。这个坑我踩过,重复发了两封通知邮件。
5. 常见问题与排查技巧实录
5.1 Text-to-SQL 高频问题速查表
| 问题现象 | 可能原因 | 排查思路 | 解决方向 |
|---|---|---|---|
| SQL 语法正确但结果为空 | 字段枚举值理解错误 | 检查 WHERE 条件里的枚举 | 补全语义层枚举说明 |
| 多表查询 JOIN 错乱 | 外键关系未提供 | 核对 schema 关系描述 | 补充关系层描述 |
| 时间范围查询偏差 | 时区或日期边界理解错 | 对比预期与实际日期 | 明确时区与边界规则 |
| 聚合结果对不上 | 分组维度理解偏差 | 拆解 GROUP BY 逻辑 | 提供典型查询示例 |
| 生成 SQL 超长超时 | 未加 LIMIT 或全表扫描 | 查看执行计划 | 强制加行数与超时限制 |
| 模型反复改错 | 重试次数过多 | 观察每轮修正内容 | 限制重试次数,人工兜底 |
5.2 几个只有踩过才知道的避坑经验
第一个坑是别用生产库做验证。我早期图省事,直接在生产只读副本上跑生成的 SQL,结果有一次一个没加索引的查询把副本 IO 打满,影响了线上报表。后来我专门搭了一个结构相同但数据量小的“影子库”做验证,安全多了。
第二个坑是prompt 里的示例要精选。很多人喜欢往 prompt 里塞一堆 few-shot 示例,觉得越多越好。实际上示例太多会稀释关键信息,而且如果示例本身有偏差,模型会跟着偏。我的经验是每个查询类型给 1-2 个高质量示例就够,宁缺毋滥。
第三个坑是别忽视中文分词的坑。做中文 Text-to-SQL 时,用户问题里的“上个月”“近一周”“环比”这些词,模型理解起来经常出偏差。我后来在预处理阶段加了一层“时间表达归一化”,把自然语言时间统一转成明确的日期区间再喂给模型,准确率提升明显。
5.3 关于“模型选型”的实在建议
经常有人问我该用哪个 LLM 做 Text-to-SQL。我的回答是:先别纠结模型,先把 schema 描述和验证闭环做好。这两件事做到位,中等能力的模型也能有不错的表现;这两件事不做,再强的模型也白搭。
如果非要说选型,我的经验是:复杂 SQL 生成优先选推理能力强的模型,简单查询可以用更轻量的模型降本。另外,llm as judge这种用模型评估模型输出的做法,在 SQL 质量评估上可以辅助,但不能全信,最终还是要靠执行结果说话。至于open llm leaderboard这类榜单,参考一下就行,榜单分数和你的实际业务表现往往不是一回事。
6. 我对“LLM + 数据库”这件事的真实判断
折腾了这么久,我越来越认同 Stonebraker 的那盆冷水。LLM 和智能体确实给数据库交互带来了新的可能,Text-to-SQL 让不懂 SQL 的人也能查数据,智能体让运维问答变得自然。但这些都是“锦上添花”,不是“颠覆重来”。数据库的确定性、事务性、可验证性,是几十年工程实践沉淀下来的底线,不能因为 LLM 火了就丢掉。
我的实际做法是:把 LLM 放在“人”和“数据库”之间,做一个聪明的翻译官和助手,而不是让它直接掌控数据。它能帮我更快地写出 SQL 草稿、更快地定位问题、更自然地解释数据,但最终的执行权和判断权,还在人手里。这个定位想清楚了,很多技术选型和架构设计就顺了。
最后分享一个我一直在用的小技巧:每次 LLM 生成的 SQL 被人工修正后,把“原始问题 + 错误 SQL + 修正后 SQL”存下来,定期拿这些数据做 few-shot 示例或者微调。这个“错误案例库”是提升系统准确率最实在的资产,比任何榜单和论文都管用。数据库这东西,终究是要靠数据和实践说话的。