
我愿意让模型解释一段复杂 SQL,但不会让它在“顺手加注释”时改连接、补条件或重写分母。注释是说明文字,查询才是计算契约;二者混在一个任务里,审查会变得困难。
10 月 22 日的 Langfuse/ClickHouse 实践介绍了把模型调用、工具执行和评测放在同一条追踪链上。对 SQL 注释同样有用:保存输入查询、模型输出、比较结果,出了问题才能定位究竟是哪次改写。ClickHouse 与 Langfuse 的 Agent 追踪实践
好注释解释业务选择
SELECT order_id — 查询订单号 帮助不大。“按支付日统计;退款金额在原订单渠道回溯;保留没有退款的订单”才解释了维护者容易误解的选择。但这些定义必须来自指标文档或业务确认,不能由模型猜测。

图中计划树是概念示意,不是真实 EXPLAIN 输出。计划形态变化应触发复核,不能单凭它判定语义改变;计划相同也不是等价性证明。
用冻结样本检查一个最低限度
下面故意只用一张表。原始与带注释查询应返回相同结果,另放一条偷偷加过滤条件的坏样本,验证比较器确实会发现差异。
import sqlite3
original = 'SELECT channel, SUM(amount) FROM orders GROUP BY channel ORDER BY channel'
commented = ('SELECT channel, SUM(amount) — amount is in cents' + chr(10)
+ 'FROM orders GROUP BY channel ORDER BY channel')
changed = "SELECT channel, SUM(amount) FROM orders WHERE channel = 'app' GROUP BY channel ORDER BY channel"
fixtures = [[('app', 100), ('mini', 50)], [('app', 0), ('app', 200)], []]
def run(sql, rows):
con = sqlite3.connect(':memory:')
con.execute('CREATE TABLE orders(channel TEXT, amount INTEGER)')
con.executemany('INSERT INTO orders VALUES (?, ?)', rows)
try:
return con.execute(sql).fetchall()
finally:
con.close()
assert all(run(original, data) == run(commented, data) for data in fixtures)
assert run(original, fixtures[0]) != run(changed, fixtures[0])
print('3 equivalent fixtures; changed filter rejected')
这证明三份样本上的结果一致,不是对所有输入的等价性证明。实际还应使用目标方言解析器比较去掉注释后的 AST,保留类型、排序与窗口定义;解析器版本也要锁定。只用正则删除注释可能破坏字符串里合法的 — 或 /*。
解释层不能取得额外权限
模型可以返回注释草稿,但最终展示的 SQL 应是经过核验的版本。若解释中出现新表名、新业务状态或“系统保证不会出错”,把它标成待确认,不要混进执行结果。生产字段含个人信息时,传给远端模型前还应明确数据范围。
这里没有调用模型或 SQLGlot,没有测量注释生成速度。自包含的标准库例子只验证结果比较确实有成功和失败路径;接到真实数仓后,要用真实方言和只读数据副本重做验证。
让代码更好读的前提,是读到的仍然是同一段计算。
延伸阅读
- ClickHouse 与 Langfuse 的 Agent 追踪实践
- Python sqlite3 文档
- DuckDB 2.0 迁移指南

