lzf_0626
84bf51c0d0
NL2SQL:5 位产品代码被丢弃导致"查询成功但数据错"(演示产品 15911 正是 5 位)
## 现象
通用自然语言查询里问「查询15911的净值」,返回的是**别的产品**的数据:
```
问:查询15911的净值 → 返回 511810 货币ETF南方 的净值
问:查询基金代码15911最近30天净值 → 同上
```
而且 `status = success`、`message = 查询成功` —— **不报错,只是数据是错的**。
## 根因
`financial_nl2sql_service._filters()` 只认 6 位数字:
```python
re.findall(r"(?<!\d)\d{6}(?!\d)", question) # 15911 只有 5 位 ⇒ 被静默丢弃
```
过滤条件为空 ⇒ 生成的 SQL **没有产品过滤**(`WHERE 1=1 ... GROUP BY ... LIMIT 50`)
⇒ 返回一批无关于问题的产品净值。现库实测:**6 位代码 25 个、5 位代码 1 个**
(后者就是演示产品 `15911`)—— 所以这个缺陷精确地打在了演示产品上。
`tests/unit/service/test_financial_nl2sql_golden_cases.py` 里 30 条黄金用例**全是 6 位代码**
(159511/588890/159948),因此从未暴露。
## 修法
`app/service/financial_nl2sql_service.py`:
- 产品代码模式放宽为 **5–6 位**(`(?<!\d)\d{5,6}(?!\d)`);
- 5 位数字更容易与"金额/数量/天数"撞车(6 位不会),因此加单位守卫:
紧跟 `元/万元/万/亿/份/股/手/天/日/周/个月/年/次/笔` 的一律**不当**产品代码
(例:「申购金额50000元」里的 50000);
- 抽成 `_product_codes()` 便于单测。
## 验证
- `query("查询15911的净值")` → SQL 含 `p.product_code = :filter_0`、参数 `15911`、
返回行 `{"product_code": "15911", "product_name": "15911 自定义产品", "nav": "1.074596"}`;
三种问法("查询15911的净值"/"查询基金代码 15911 最近 30 天的净值"/"15911 的最新净值是多少")全部命中 15911;
- 新增 5 条单测:5 位代码成为过滤条件、6 位仍正常、金额不当代码、
年份/天数不被误认、多代码并存;
- `pytest tests/unit tests/contract` → **1458 passed, 2 skipped, 0 failed**;
- `ruff` 干净。
## 说明(未改,供你决定)
问「最新净值」时返回的是该产品的 50 行净值明细(SQL 无 `ORDER BY`),
不是"最新的那一条"。要的话我可以补成按日期倒序、单点取值。
2026-09-14 22:32:26 +08:00
..
2026-09-14 21:40:05 +08:00
2026-09-09 21:55:37 +08:00
2026-09-13 22:08:04 +08:00
2026-09-11 20:28:21 +08:00
2026-09-14 18:13:10 +08:00
2026-09-11 22:31:51 +08:00
2026-09-11 15:08:37 +08:00
2026-09-11 14:12:56 +08:00
2026-09-14 18:22:56 +08:00
2026-09-11 20:50:41 +08:00
2026-09-14 20:14:50 +08:00
2026-09-11 17:32:47 +08:00
2026-09-11 16:57:47 +08:00
2026-09-12 13:14:57 +08:00
2026-09-11 14:46:40 +08:00
2026-09-11 16:11:30 +08:00
2026-09-11 16:11:30 +08:00
2026-09-11 16:11:30 +08:00
2026-09-11 14:46:40 +08:00
2026-09-11 14:44:29 +08:00
2026-09-14 22:32:26 +08:00
2026-09-11 16:57:47 +08:00
2026-09-11 16:12:38 +08:00
2026-09-10 21:43:39 +08:00
2026-09-12 12:50:14 +08:00
2026-09-09 21:55:37 +08:00
2026-09-11 16:57:47 +08:00
2026-09-14 18:22:56 +08:00
2026-09-10 18:42:44 +08:00
2026-09-11 22:31:51 +08:00
2026-09-11 14:46:40 +08:00
2026-09-11 14:46:40 +08:00
2026-09-10 22:08:15 +08:00
2026-09-12 11:15:24 +08:00
2026-09-11 18:42:42 +08:00
2026-09-11 16:57:47 +08:00
2026-09-10 20:22:42 +08:00
2026-09-14 00:30:06 +08:00
2026-09-13 20:39:36 +08:00
2026-09-12 14:11:42 +08:00
2026-09-11 16:57:47 +08:00
2026-09-14 21:26:03 +08:00
2026-09-11 22:31:51 +08:00
2026-09-11 16:57:47 +08:00
2026-09-12 12:50:14 +08:00
2026-09-09 21:55:37 +08:00
2026-09-14 01:07:43 +08:00
2026-09-14 01:07:43 +08:00
2026-09-14 22:16:29 +08:00
2026-09-11 16:57:47 +08:00
2026-09-11 16:57:47 +08:00
2026-09-10 09:23:22 +08:00
2026-09-09 21:55:37 +08:00
2026-09-14 18:22:56 +08:00
2026-09-11 19:20:32 +08:00
2026-09-11 12:47:01 +08:00
2026-09-12 12:10:10 +08:00
2026-09-11 12:47:01 +08:00
2026-09-14 18:22:56 +08:00
2026-09-10 21:58:42 +08:00
2026-09-12 10:45:40 +08:00
2026-09-11 16:42:31 +08:00
2026-09-10 21:55:03 +08:00
2026-09-14 21:26:03 +08:00
2026-09-09 21:55:37 +08:00
2026-09-11 16:57:47 +08:00
2026-09-14 18:13:10 +08:00
2026-09-14 18:13:10 +08:00
2026-09-14 18:13:10 +08:00
2026-09-11 16:57:47 +08:00
2026-09-14 18:13:10 +08:00
2026-09-14 18:13:10 +08:00
2026-09-14 18:13:10 +08:00
2026-09-14 20:35:46 +08:00
2026-09-14 00:30:06 +08:00
2026-09-11 21:22:56 +08:00
2026-09-11 14:14:50 +08:00
2026-09-10 21:03:44 +08:00
2026-09-10 21:03:44 +08:00
2026-09-13 19:17:56 +08:00
2026-09-12 15:56:50 +08:00
2026-09-11 14:16:07 +08:00
2026-09-11 11:42:53 +08:00
2026-09-11 14:24:45 +08:00
2026-09-10 21:03:44 +08:00
2026-09-12 15:56:50 +08:00
2026-09-13 23:51:45 +08:00
2026-09-14 01:41:02 +08:00
2026-09-11 09:49:21 +08:00
2026-09-12 16:30:56 +08:00
2026-09-12 15:56:50 +08:00
2026-09-11 19:37:26 +08:00
2026-09-14 18:13:10 +08:00
2026-09-11 16:57:47 +08:00
2026-09-11 21:03:58 +08:00
2026-09-11 16:57:47 +08:00
2026-09-14 20:27:35 +08:00