针对 Text-to-SQL 场景,Prompt 优化的核心目标是消除歧义、减少模型试错、让 SQL 一步生成到位 ,从根源解决 max iterations 超限问题,同时大幅提升查询准确率。
下面按优先级排序,结合你的公交维修业务场景,讲透可直接落地的优化方法,最后给出完整的优化版模板。
一、Prompt 优化的 6 个核心维度
1. 结构化 Schema 信息:消除字段歧义(最基础)
模型 80% 的试错都来自「字段含义不清、同名不同义」。不要只扔表名,要把每张表的字段、类型、业务含义、枚举值都写清楚。
优化要点:
- 分表罗列,核心字段标注业务注释
- 重名字段必须明确区分(比如车辆状态和工单状态都叫
status) - 枚举值直接写出编码对应的业务含义
示例(你的业务场景):
【可用表及核心字段】
1. bus_vehicle(车辆基础表)
- vehicle_id INT 车辆唯一ID(主键)
- bus_no VARCHAR 车辆自编号(内部运营编号)
- plate_no VARCHAR 车牌号
- model VARCHAR 车型
- fleet_id INT 所属车队ID(外键)
- buy_date DATE 购置日期
- status TINYINT 车辆状态:1=运营中,2=维修中,3=停运
2. bus_fleet(车队组织表)
- fleet_id INT 车队ID(主键)
- fleet_name VARCHAR 车队名称,如"119车队"
- company_name VARCHAR 分公司名称,如"第一运营分公司"
3. repair_order(维修工单表)
- order_id INT 工单ID(主键)
- vehicle_id INT 关联车辆ID(外键)
- fault_type VARCHAR 故障类型:电机系统/底盘系统/电气系统/车身系统
- repair_content TEXT 维修内容
- order_time DATETIME 报修时间
- status TINYINT 工单状态:1=待修,2=维修中,3=已完成
2. 显式声明表关联关系:避免模型瞎猜
多表查询时,模型会浪费大量迭代去猜测用哪个字段关联。直接把所有关联关系写死,一步到位。
示例:
【表关联关系(必须严格遵守)】
- 车辆归属车队:bus_vehicle.fleet_id = bus_fleet.fleet_id
- 工单对应车辆:repair_order.vehicle_id = bus_vehicle.vehicle_id
3. 业务术语字典:打通自然语言与数据库编码
业务口语和数据库编码的映射,是场景化准确率的核心。模型不知道「一公司」「坏车」「上月」对应什么,就会反复试错。
示例:
【业务字典(必须严格匹配)】
- "一公司" / "第一分公司" = company_name = "第一运营分公司"
- "X车队" = fleet_name = "X车队"
- "坏车" / "待修车" = 车辆status = 2(维修中)
- "上月" = 时间范围为上一个自然月的第一天到最后一天
- "近30天" = order_time >= DATE_SUB(CURDATE(), INTERVAL 30 DAY)
4. Few-shot 少样本示例(性价比最高的优化)
给模型 3~5 个「业务问题 → 正确 SQL」的真实示例,模型会直接模仿格式和逻辑,迭代次数通常能从 5+ 次降到 1~2 次。
示例要点:
- 覆盖常见查询类型:清单查询、统计计数、时间筛选、多表关联
- SQL 写法就是你期望的标准写法
- 示例越贴近真实业务,效果越好
示例:
【参考示例】
问:一公司119车队的车辆清单
答:SELECT v.bus_no, v.plate_no, v.model, v.status
FROM bus_vehicle v
JOIN bus_fleet f ON v.fleet_id = f.fleet_id
WHERE f.company_name='第一运营分公司' AND f.fleet_name='119车队'
问:119车队上个月有多少条维修工单
答:SELECT COUNT(*) as 工单数量
FROM repair_order r
JOIN bus_vehicle v ON r.vehicle_id = v.vehicle_id
JOIN bus_fleet f ON v.fleet_id = f.fleet_id
WHERE f.fleet_name='119车队'
AND r.order_time BETWEEN '2026-08-01 00:00:00' AND '2026-08-31 23:59:59'
问:运营中车辆最多的是哪个车队
答:SELECT f.fleet_name, COUNT(*) as 运营车辆数
FROM bus_vehicle v
JOIN bus_fleet f ON v.fleet_id = f.fleet_id
WHERE v.status = 1
GROUP BY f.fleet_name
ORDER BY 运营车辆数 DESC
LIMIT 1
5. 硬性约束规则:划定边界,避免无效操作
明确告诉模型什么能做、什么不能做、输出格式是什么,减少不必要的推理和返工。
示例:
【严格规则】
1. 只能生成 SELECT 查询语句,绝对禁止 DELETE/UPDATE/INSERT/DROP/ALTER 等任何修改语句
2. 禁止使用 SELECT *,必须明确列出需要的字段
3. 必须使用表别名(如 v 代表 bus_vehicle),提升SQL可读性
4. 如果问题缺少必要条件或语义模糊,请直接反问用户,不要猜测编造
5. 回答格式:先给出自然语言结论,再附上数据表格
6. 如果查询无结果,明确告知用户"未查询到符合条件的数据"
6. 思维链(CoT)引导:先思考再写 SQL
让模型先理清楚逻辑再写代码,看似多了一步,实则减少了后续反复修正的次数,整体迭代更少、准确率更高。
示例指令(加在Prompt末尾):
【答题步骤】
请先按以下步骤思考,再输出最终答案:
1. 用户的核心需求是什么
2. 需要用到哪几张表
3. 表之间的关联条件是什么
4. 筛选条件有哪些
5. 需要返回哪些字段
思考完成后,直接写出完整的SQL语句并执行。
二、进阶优化技巧(适合复杂场景)
1. 表召回前置
如果你的数据库有十几张甚至几十张表,不要全塞给模型。先做一步「表召回」:用关键词匹配或向量检索,只把和当前问题相关的 2~3 张表的 Schema 传给模型,减少信息干扰。
2. 错误自修正提示
把模型常犯的错误直接写在提示里,让它自己检查。
【常见错误自查】
写完SQL后,请先自查:
- 有没有用 SELECT *
- 表关联条件是否正确
- 状态枚举值有没有用错
- 时间范围是否完整
有错误就自行修正,再执行。
3. 动态字段裁剪
根据问题类型只返回相关字段。比如问「数量」就只给统计相关字段,问「清单」就给基础信息字段,避免冗余信息干扰。
三、完整优化版 Prompt(直接替换到代码里)
把上面所有优化点整合,针对你的公交维修场景,最终版如下:
python
custom_prompt = """
你是公交机务数据查询专家,只能根据提供的数据库表结构回答问题。
【可用表及核心字段】
1. bus_vehicle(车辆基础表)
- vehicle_id INT 车辆唯一ID(主键)
- bus_no VARCHAR 车辆自编号(内部运营编号)
- plate_no VARCHAR 车牌号
- model VARCHAR 车型
- fleet_id INT 所属车队ID(外键)
- buy_date DATE 购置日期
- status TINYINT 车辆状态:1=运营中,2=维修中,3=停运
2. bus_fleet(车队组织表)
- fleet_id INT 车队ID(主键)
- fleet_name VARCHAR 车队名称,如"119车队"
- company_name VARCHAR 分公司名称,如"第一运营分公司"
3. repair_order(维修工单表)
- order_id INT 工单ID(主键)
- vehicle_id INT 关联车辆ID(外键)
- fault_type VARCHAR 故障类型:电机系统/底盘系统/电气系统/车身系统
- repair_content TEXT 维修内容
- order_time DATETIME 报修时间
- status TINYINT 工单状态:1=待修,2=维修中,3=已完成
【表关联关系(必须严格遵守)】
- 车辆归属车队:bus_vehicle.fleet_id = bus_fleet.fleet_id
- 工单对应车辆:repair_order.vehicle_id = bus_vehicle.vehicle_id
【业务字典(必须严格匹配)】
- "一公司" / "第一分公司" = company_name = "第一运营分公司"
- "X车队" = fleet_name = "X车队"
- "坏车" / "待修车" = 车辆status = 2(维修中)
- "上月" = 时间范围为上一个自然月的第一天0点到最后一天23:59:59
- "近30天" = order_time >= DATE_SUB(CURDATE(), INTERVAL 30 DAY)
【严格规则】
1. 只能生成 SELECT 查询语句,绝对禁止 DELETE/UPDATE/INSERT/DROP/ALTER 等任何修改语句
2. 禁止使用 SELECT *,必须明确列出需要的字段
3. 必须使用表别名(如 v 代表 bus_vehicle),提升SQL可读性
4. 如果问题缺少必要条件或语义模糊,请直接反问用户,不要猜测编造
5. 回答格式:先给出自然语言结论,再附上数据表格
6. 如果查询无结果,明确告知用户"未查询到符合条件的数据"
【参考示例】
问:一公司119车队的车辆清单
答:SELECT v.bus_no, v.plate_no, v.model, v.status
FROM bus_vehicle v
JOIN bus_fleet f ON v.fleet_id = f.fleet_id
WHERE f.company_name='第一运营分公司' AND f.fleet_name='119车队'
问:119车队上个月有多少条维修工单
答:SELECT COUNT(*) as 工单数量
FROM repair_order r
JOIN bus_vehicle v ON r.vehicle_id = v.vehicle_id
JOIN bus_fleet f ON v.fleet_id = f.fleet_id
WHERE f.fleet_name='119车队'
AND r.order_time BETWEEN '2026-08-01 00:00:00' AND '2026-08-31 23:59:59'
【答题步骤】
请先按以下步骤思考,再输出最终答案:
1. 用户的核心需求是什么
2. 需要用到哪几张表
3. 表之间的关联条件是什么
4. 筛选条件有哪些
5. 需要返回哪些字段
思考完成后,直接写出完整的SQL语句并执行。
"""
四、优化效果验证方法
- 开启 verbose 日志 :把
verbose=True打开,观察 Agent 迭代次数- 优化前:复杂问题可能 5~8 步还没做完
- 优化后:绝大多数问题 1~2 步就能生成正确SQL并执行
- 校验 SQL 正确性:看生成的 SQL 语法、关联条件、筛选值是否符合业务预期
- Bad Case 迭代:把答错的问题和正确 SQL 补充到 Few-shot 示例里,持续迭代
五、常见避坑点
- 不要信息过载:只开放必要的表和字段,表越多、字段越多,模型越容易出错
- 不要模糊表述:所有业务术语都要有明确映射,不要让模型「意会」
- 示例不要太简单:示例要覆盖真实的复杂场景,只用单表查询示例没用
- 不要频繁改结构:数据库表结构变更时,同步更新 Prompt 里的