从 40% 提升到 100% 的 SQL 生成准确率:为什么本地 AI 需要自我纠正,而不是完美提示
Source: Dev.to
概览
自我纠正循环胜过“完美一次”方法,适用于本地 AI。构建一个完全在笔记本电脑上运行的零售分析副驾驶(使用量化的 24B 模型)听起来对隐私很友好,但在可靠性上却是噩梦。与托管的 GPT 不同,后者能像资深工程师一样遵循指令,本地模型更像热情的实习生:他们努力尝试,却会出现语法幻觉、忘记模式细节,并且在应该写代码时喜欢聊天。
基线
我的最初基线表现糟糕:只有 40 % 的生成 SQL 查询能够实际执行。其余的要么语法错误,要么出现幻觉列,要么充满对话式的废话(“Here is your query …”)。
转变:预期错误,然后修复
我没有尝试阻止错误,而是构建了一个 预期 错误并自动修复的系统。
修复循环工作流
- 生成 – 模型编写查询。
- 执行 – 在 SQLite 上运行。
- 捕获 – 如果失败,获取错误信息(例如
no such column: 'Price')。 - 反馈 – 将完整的错误信息反馈给模型:“前一个查询因错误 X 失败。请修复它。”
这种模式可以推广到除 SQL 之外的场景。只要你使用概率系统、可能失败的外部 API,或模糊的用户输入,都应为优雅降级而设计。
来自失败的训练数据
每个 (failed_query, error_message, corrected_query) 三元组都可以成为未来优化的 few‑shot 示例。你不仅在修复 bug;你在构建一个自我改进的系统。
修复循环实现(Python)
def sql_execution_node(state: AgentState) -> AgentState:
"""Execute SQL and handle errors gracefully."""
query = state["sql_query"]
try:
cursor.execute(query)
state["sql_results"] = cursor.fetchall()
state["errors"] = []
except sqlite3.OperationalError as e:
# Don't crash—capture and route to repair
state["sql_results"] = []
state["errors"].append(str(e))
state["feedback"] = f"SQL execution failed: {e}. Fix the query."
state["repair_count"] = state.get("repair_count", 0) + 1
return state
def should_repair(state: AgentState) -> str:
"""Conditional edge: repair or continue?"""
if state["errors"] and state["repair_count"] str:
match = re.search(r"(SELECT\s+.*)", text, re.IGNORECASE)
return match.group(1).strip() if match else ""
更好的是:强制结构化输出(例如 JSON),这样就可以完全避免自由形式的解析。
从 Prompt Engineering 转向 DSPy
我停止手写提示,转而使用 DSPy,它把提示优化视为可学习的问题。
把传统的提示工程想象成手动调超参数;DSPy 则像反向传播,使用梯度自由优化在你定义的度量上自动搜索最佳提示。
定义度量
“如果查询 能够执行 并且返回非空结果,则认为它是好的。”
使用 BootstrapFewShot 优化器,DSPy 生成多个候选 SQL 查询,执行它们,并仅保留通过度量的作为 few‑shot 示例。
度量改进
| 度量 | 基线 | 优化后 | 修复循环后 |
|---|---|---|---|
| 有效 SQL (%) | 40 % | 85 % | 100 % |
| 正确格式 (%) | 30 % | 60 % | 95 % |
| 端到端成功 (%) | 12 % | 51 % | 66 % |
*100 % 有效 SQL 与 66 % 端到端成功之间的差距表明,优化单一组件会在其他环节(编排逻辑,而非模型质量)产生新的瓶颈。
实践中的修复模式
在 LLM 状态中添加 feedback 字段。失败时注入错误信息并重试。这大约只会产生 一次额外的 LLM 调用,但可靠性可以提升一个数量级。
“ELECT” 测试
assert clean_sql_output("SQL: SELECT * FROM orders") == "SELECT * FROM orders"
assert clean_sql_output("SELECT * FROM orders") == "SELECT * FROM orders"
如果这些断言失败,说明你误用了字符串方法。
DSPy 入门模板
- 将任务定义为
(inputs, output, metric)。 - 让优化器发现有效的 few‑shot 示例,而不是手动编写它们。
为什么这很重要(超出本项目)
AI 领域正出现两极分化:
| 方法 | 特点 |
|---|---|
| 云优先 (GPT‑5.1、Claude 等) | 强大但昂贵;隐私风险更高。 |
| 边缘优先 (本地模型) | 更便宜、私密,但更难驾驭。 |
能够掌握本地 AI 的公司将在受监管行业(医疗、金融、政府)中占据主导,因为云端 LLM 在这些领域是不可行的。瓶颈不在模型权重,而在 可靠性工程。
如果你能通过巧妙的编排让 7B 模型表现得像 70B 模型,你就在解决一个 1000 亿美元 的问题。这不仅是技术练习,更是战略护城河。
关键技能
- 系统思维 – 理解失效模式并围绕它们设计。
- 优化 – 将提示视为可学习的参数,而非艺术。
- 防御性工程 – 为概率世界构建系统。
掌握这些,你竞争的将不是提示工程师,而是 AI 原生公司的基础设施工程师。
项目仓库: