目标:基于本体推理结果 + 映射,用「模板 + 参数填充」生成 SQL,并做口径校验。LLM 不写 SQL,只填参数。
LLM 不写 SQL,只填参数。 SQL 结构由指标模板固化,口径固化在模板里。
public class MetricSqlTemplate {
// GMV 模板:口径固化(status='paid' AND refunded=false)
public static final String GMV =
"SELECT {region_col}, SUM(bill_amount) AS gmv " +
"FROM dwd_bill " +
"WHERE status='paid' AND refunded=false " + // ← 口径固化
"AND dt BETWEEN '{start_dt}' AND '{end_dt}' " +
"{region_filter} " +
"GROUP BY {region_col}";
// 订单数模板
public static final String ORDER_COUNT =
"SELECT SUM(bill_count) AS cnt FROM dwd_bill " +
"WHERE status='paid' AND dt BETWEEN '{start_dt}' AND '{end_dt}' {region_filter}";
}
① 本体推理结果:
- 指标:GMV(等价识别后)
- 维度展开:华东区 → [上海,江苏,浙江,安徽,福建,江西,山东]
- 时间:上个月 → 2026-07-01 ~ 2026-07-31
② 选模板:根据指标选 GMV 模板
③ 填参数:
region_filter = "AND region_id IN (SELECT region_id FROM dim_region WHERE region_name IN ('上海','江苏',...))"
start_dt = "2026-07-01", end_dt = "2026-07-31"
④ 生成 SQL(确定性拼接,无 LLM 参与)
public String generateSql(String metric, List<String> regions, TimeRange time) {
String template = selectTemplate(metric);
String regionFilter = regions.isEmpty() ? "" :
"AND region_id IN (SELECT region_id FROM dim_region WHERE region_name IN (" +
regions.stream().map(r -> "'" + r + "'").collect(Collectors.joining(",")) + "))";
return template
.replace("{region_col}", "region_id")
.replace("{region_filter}", regionFilter)
.replace("{start_dt}", time.start())
.replace("{end_dt}", time.end());
}
public class CaliberValidator {
/** 校验生成的 SQL 是否满足指标口径 */
public boolean validate(String metric, String sql) {
// 从本体读口径公理(whereClause 数据属性)
String requiredClause = ontologyService.getWhereClause(metric);
// GMV 的 requiredClause = "status='paid' AND refunded=false"
// 校验 SQL 是否包含必需的过滤条件
for (String clause : requiredClause.split(" AND ")) {
if (!sql.contains(clause)) return false;
}
return true;
}
}
@Service
public class MetricQueryService {
// ① 等价识别
String metric = ontologyService.resolveEquivalentMetric("成交额"); // → GMV
// ② 维度展开
List<String> regions = ontologyService.expandRegion("华东区"); // → 7省
// ③ 生成 SQL
String sql = sqlGenerator.generateSql(metric, regions, time);
// ④ 口径校验
if (!caliberValidator.validate(metric, sql)) {
throw new CaliberException("SQL 违背指标口径");
}
// ⑤ 执行(复用 MaxComputeQueryService)
return maxComputeQueryService.query(sql);
}
WHERE status='paid' AND refunded=false 就是口径,LLM 碰不到