|
|
@@ -0,0 +1,347 @@
|
|
|
+# 本地规范检索服务设计
|
|
|
+
|
|
|
+## 1. 文档目的
|
|
|
+
|
|
|
+本文档记录本地规范检索服务当前已经达成共识的需求和接口边界,作为后续讨论匹配算法、阈值、日志及实现方案的基础。
|
|
|
+
|
|
|
+本文档同时记录第一版匹配算法、初始阈值和测试用例。后续可根据真实查询日志调整参数,但不得改变服务的职责边界。
|
|
|
+
|
|
|
+## 2. 建设目标
|
|
|
+
|
|
|
+施工方案审查 Agent(智能体)需要检查方案中引用规范的时效性。为减少 Web Search(网页搜索)的调用和等待时间,建设一个基于本地人工维护规范库的检索服务。
|
|
|
+
|
|
|
+服务接收规范查询关键词,从本地规范库中检索相关规范,并返回最多 3 条候选。Agent(智能体)结合候选内容,自行判断引用规范的时效性。
|
|
|
+
|
|
|
+本地规范库由人工维护,默认其中的数据准确可信。
|
|
|
+
|
|
|
+## 3. 服务职责边界
|
|
|
+
|
|
|
+### 3.1 服务负责
|
|
|
+
|
|
|
+- 接收一批规范查询关键词。
|
|
|
+- 支持按规范名称、规范编号、名称加编号进行查询。
|
|
|
+- 根据规范名称和规范编号执行本地匹配。
|
|
|
+- 每条查询最多返回 3 条候选规范。
|
|
|
+- 按匹配度从高到低排列候选规范。
|
|
|
+- 返回候选规范的状态、实施日期和替代情况。
|
|
|
+- 记录查询日志,支持后续分析、清洗和补充本地规范库。
|
|
|
+
|
|
|
+### 3.2 服务不负责
|
|
|
+
|
|
|
+- 不直接判断引用规范是否有效或是否过期。
|
|
|
+- 不替 Agent(智能体)选择最终采用的规范。
|
|
|
+- 不声明查询结果是否唯一或是否存在歧义。
|
|
|
+- 不使用替代情况参与检索、召回或排序。
|
|
|
+- 不根据替代情况自动关联、扩展其他规范。
|
|
|
+- 不返回 `data_trust(数据可信度)`。
|
|
|
+- 不进行 Web Search(网页搜索)兜底。
|
|
|
+
|
|
|
+## 4. 接口设计
|
|
|
+
|
|
|
+服务只提供一个批量查询业务接口:
|
|
|
+
|
|
|
+```text
|
|
|
+POST /v1/search
|
|
|
+```
|
|
|
+
|
|
|
+### 4.1 请求结构
|
|
|
+
|
|
|
+```json
|
|
|
+{
|
|
|
+ "keywords": [
|
|
|
+ "GB 50330-2013 建筑边坡工程技术规范",
|
|
|
+ "混凝土结构设计规范",
|
|
|
+ "GB 50330"
|
|
|
+ ]
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+请求说明:
|
|
|
+
|
|
|
+- `keywords(关键词列表)`为必填数组,第一版每批允许 1~100 条。
|
|
|
+- 每个元素代表一条独立的规范查询。
|
|
|
+- 查询内容允许为规范名称、规范编号,或者名称加编号。
|
|
|
+
|
|
|
+### 4.2 响应结构
|
|
|
+
|
|
|
+```json
|
|
|
+{
|
|
|
+ "results": [
|
|
|
+ {
|
|
|
+ "query": "GB 50330-2013 建筑边坡工程技术规范",
|
|
|
+ "candidates": [
|
|
|
+ {
|
|
|
+ "code": "GB 50330-2013",
|
|
|
+ "title": "建筑边坡工程技术规范",
|
|
|
+ "status": "现行",
|
|
|
+ "relation_text": "替代 GB 50330-2002",
|
|
|
+ "implement_date": "2014-06-01",
|
|
|
+ "match_score": 1.0
|
|
|
+ }
|
|
|
+ ]
|
|
|
+ },
|
|
|
+ {
|
|
|
+ "query": "本地规范库中不存在的规范",
|
|
|
+ "candidates": []
|
|
|
+ }
|
|
|
+ ]
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+响应说明:
|
|
|
+
|
|
|
+- `results(查询结果列表)`与请求中的查询顺序一致。
|
|
|
+- `query(原始查询)`原样返回,方便调用方建立对应关系。
|
|
|
+- `candidates(候选列表)`包含 0~3 条规范。
|
|
|
+- 候选列表为空表示本地没有达到返回条件的候选规范。
|
|
|
+- 服务不返回 `resolution(解析状态)`或 `matched(是否命中)`。
|
|
|
+
|
|
|
+## 5. 候选规范字段
|
|
|
+
|
|
|
+每条候选规范当前返回以下字段:
|
|
|
+
|
|
|
+| 字段 | 含义 |
|
|
|
+|---|---|
|
|
|
+| `code` | 规范编号 |
|
|
|
+| `title` | 规范名称 |
|
|
|
+| `status` | 规范状态,例如现行、已作废、已废止 |
|
|
|
+| `relation_text` | 本地库中的替代情况原文 |
|
|
|
+| `implement_date` | 实施日期 |
|
|
|
+| `match_score` | 查询关键词与候选规范的匹配度 |
|
|
|
+
|
|
|
+替代情况仅作为候选规范的附带信息原样返回,不参与匹配计算。
|
|
|
+
|
|
|
+## 6. 匹配范围
|
|
|
+
|
|
|
+服务只使用以下内容进行匹配:
|
|
|
+
|
|
|
+- 规范编号。
|
|
|
+- 规范名称。
|
|
|
+
|
|
|
+允许处理的典型查询包括:
|
|
|
+
|
|
|
+- 完整规范编号,例如 `GB 50330-2013`。
|
|
|
+- 不含年份的规范编号,例如 `GB 50330`。
|
|
|
+- 规范名称,例如 `建筑边坡工程技术规范`。
|
|
|
+- 规范编号加名称,例如 `GB 50330-2013 建筑边坡工程技术规范`。
|
|
|
+
|
|
|
+当名称或不含年份的编号匹配到多个规范版本时,服务可以返回多个候选,但最多返回 3 条。
|
|
|
+
|
|
|
+### 6.1 本地索引字段
|
|
|
+
|
|
|
+第一版使用以下字段建立检索索引:
|
|
|
+
|
|
|
+- `csres_code(官方编号)`。
|
|
|
+- `csres_normalized_code(标准化编号)`,作为编号别名使用。
|
|
|
+- `csres_title(规范名称)`。
|
|
|
+
|
|
|
+`query_keyword(原始采集关键词)`不作为规范名称索引,避免采集页面标题、应用指南等噪声影响匹配。
|
|
|
+
|
|
|
+### 6.2 查询解析
|
|
|
+
|
|
|
+每条查询先拆分为“规范编号”和“规范名称”两个部分。例如:
|
|
|
+
|
|
|
+```text
|
|
|
+输入:《建筑边坡工程技术规范》GB 50330—2013
|
|
|
+编号:GB 50330-2013
|
|
|
+名称:建筑边坡工程技术规范
|
|
|
+```
|
|
|
+
|
|
|
+编号解析支持编号出现在文本开头、末尾或括号内。第一版约定每条关键词描述一项规范;如果一条关键词中包含多个规范编号,服务仍按相关度返回最多 3 条候选,但调用方应尽量提前拆分。
|
|
|
+
|
|
|
+### 6.3 编号标准化
|
|
|
+
|
|
|
+编号比较前进行以下处理:
|
|
|
+
|
|
|
+- 统一全角和半角字符。
|
|
|
+- 统一不同样式的横线和冒号。
|
|
|
+- 删除编号内部多余空格。
|
|
|
+- 英文字母统一为大写。
|
|
|
+- 保留编号中的斜杠、点号、冒号和横线。
|
|
|
+
|
|
|
+编号匹配分级如下:
|
|
|
+
|
|
|
+| 编号情况 | 编号匹配度 |
|
|
|
+|---|---:|
|
|
|
+| 标准化后完整编号一致 | 1.00 |
|
|
|
+| 两位年份与四位年份对应 | 0.98 |
|
|
|
+| 编号主体和年份一致,但 `GB/GB/T` 等属性不同 | 0.93 |
|
|
|
+| 编号主体一致,但查询缺少年份 | 0.90 |
|
|
|
+| 查询缺少分册号、分部分号或年份 | 0.80 |
|
|
|
+| 编号疑似存在一个字符错误,且年份一致 | 0.75 |
|
|
|
+| 编号主体一致但年份不同 | 0.70 |
|
|
|
+
|
|
|
+年份不同只降低编号匹配度,不直接淘汰候选。服务返回相关规范及其实际编号,由 Agent(智能体)判断查询年份是否错误。
|
|
|
+
|
|
|
+### 6.4 名称标准化和匹配
|
|
|
+
|
|
|
+名称比较前进行以下处理:
|
|
|
+
|
|
|
+- 统一全角和半角字符。
|
|
|
+- 删除空格、书名号和普通标点。
|
|
|
+- 英文字母统一为小写。
|
|
|
+- 保留“规范”“规程”“标准”等具有区分意义的词语。
|
|
|
+
|
|
|
+名称匹配依次考虑:
|
|
|
+
|
|
|
+1. 标准化后完全一致,匹配度为 1.00。
|
|
|
+2. 官方名称完整包含在查询名称中,根据覆盖比例计算 0.90~0.98 的匹配度。
|
|
|
+3. 查询名称完整包含在官方名称中,根据覆盖比例计算 0.65~0.95 的匹配度。
|
|
|
+4. 使用字符相似度计算模糊匹配度。
|
|
|
+
|
|
|
+查询名称少于 6 个中文字符时,不启用宽松模糊匹配,只允许完全一致或完整包含匹配。短名称包含匹配必须按覆盖比例降低分数,避免“验收规程”等通用短语获得虚高匹配度。
|
|
|
+
|
|
|
+### 6.5 编号和名称的并行匹配
|
|
|
+
|
|
|
+第一版采用编号、名称双通道并行匹配:
|
|
|
+
|
|
|
+1. 对本地库中的每条规范分别计算编号匹配度和名称匹配度。
|
|
|
+2. 编号或名称任一维度达到相关度阈值,该规范即可进入候选池。
|
|
|
+3. 编号、名称相互冲突时不淘汰候选,只降低最终匹配度。
|
|
|
+4. 年份不同不阻止名称高度相关的规范进入候选池。
|
|
|
+5. 所有候选统一评分、去重、排序后返回前 3 条。
|
|
|
+
|
|
|
+这样既能召回类似 `TG/T 5440-2018` 与 `JTG/T 5440-2018` 的单字符编号错误,也能让 `JGJ 18-2022`查询看到名称完全一致的 `JGJ 18-2012`,最终由 Agent(智能体)判断编号冲突。
|
|
|
+
|
|
|
+## 7. 匹配度
|
|
|
+
|
|
|
+`match_score(匹配度)`采用 `0~1`的数值范围:
|
|
|
+
|
|
|
+- 数值越大,表示查询关键词与候选规范越相关。
|
|
|
+- 匹配度只表示文本和编号的相关程度。
|
|
|
+- 匹配度不是候选规范正确的概率。
|
|
|
+- 多个不同年份的规范可能具有相同的高匹配度。
|
|
|
+
|
|
|
+### 7.1 最终匹配度
|
|
|
+
|
|
|
+只有编号时:
|
|
|
+
|
|
|
+```text
|
|
|
+match_score = 编号匹配度
|
|
|
+```
|
|
|
+
|
|
|
+只有名称时:
|
|
|
+
|
|
|
+```text
|
|
|
+match_score = 名称匹配度
|
|
|
+```
|
|
|
+
|
|
|
+同时包含编号和名称时:
|
|
|
+
|
|
|
+```text
|
|
|
+较高分 = max(编号匹配度, 名称匹配度)
|
|
|
+较低分 = min(编号匹配度, 名称匹配度)
|
|
|
+match_score = 较高分 × 70% + 较低分 × 30%
|
|
|
+```
|
|
|
+
|
|
|
+该公式保证编号或名称任一维度高度相关时仍可返回,同时在另一维度冲突时降低最终匹配度。服务不再设置名称兜底分数上限。
|
|
|
+
|
|
|
+### 7.2 候选进入条件
|
|
|
+
|
|
|
+候选满足以下任一条件即可进入排序:
|
|
|
+
|
|
|
+- 查询包含编号,且编号匹配度不低于 0.70。
|
|
|
+- 查询包含名称,且名称匹配度不低于 0.75。
|
|
|
+
|
|
|
+准入条件按两个通道独立判断,不使用最终加权分数淘汰单项高度相关的候选。
|
|
|
+
|
|
|
+### 7.3 排序和截断
|
|
|
+
|
|
|
+- 候选按照最终匹配度降序排列。
|
|
|
+- 分数相同时,编号匹配度更高的候选优先。
|
|
|
+- 同一编号不同年份且前述分数相同时,按年份从新到旧展示。
|
|
|
+- 按官方规范编号去重。
|
|
|
+- 每条查询最多返回 3 条候选。
|
|
|
+- 状态、实施日期和替代情况不参与召回、评分或排序。
|
|
|
+
|
|
|
+## 8. Agent(智能体)使用原则
|
|
|
+
|
|
|
+调用服务的 Agent(智能体)应遵守以下原则:
|
|
|
+
|
|
|
+- 自行核对查询中的规范编号、年份和名称。
|
|
|
+- 不能因为服务只返回一条候选,就认定该候选一定唯一。
|
|
|
+- 当返回多个版本时,根据方案原文和候选信息自行判断。
|
|
|
+- 当无法确定具体版本时,应指出引用信息不足,而不是猜测。
|
|
|
+- 候选列表为空时,不进行规范时效性判断。
|
|
|
+- 根据候选规范的状态、实施日期和替代情况判断时效性。
|
|
|
+
|
|
|
+## 9. 日志要求
|
|
|
+
|
|
|
+服务后台需要记录查询日志,最低包含:
|
|
|
+
|
|
|
+- 查询时间。
|
|
|
+- 原始查询内容。
|
|
|
+- 查询结果。
|
|
|
+
|
|
|
+日志用于:
|
|
|
+
|
|
|
+- 分析未命中的规范。
|
|
|
+- 发现误匹配和低质量匹配。
|
|
|
+- 指导后续补充和清洗本地规范库。
|
|
|
+- 复盘服务返回结果。
|
|
|
+
|
|
|
+第一版使用 JSONL(逐行结构化日志),每条子查询记录一行,至少包含:
|
|
|
+
|
|
|
+- 带时区的查询时间。
|
|
|
+- 原始查询内容。
|
|
|
+- 返回的候选规范及匹配度。
|
|
|
+- 查询耗时。
|
|
|
+
|
|
|
+日志文件使用 UTF-8(统一字符编码)写入。第一版不实现自动轮转,待实际运行后根据日志增长速度确定轮转和保留策略。
|
|
|
+
|
|
|
+## 10. 并发处理
|
|
|
+
|
|
|
+服务支持多个 HTTP(网络请求)并发访问。规范库在服务启动时加载到内存,查询阶段只读本地数据;单进程内日志写入使用线程锁保护。
|
|
|
+
|
|
|
+第一版实现重点保证并发下不出现共享状态写坏的问题。匹配计算仍属于 CPU(计算密集型)工作,因此吞吐能力主要受单机 CPU(中央处理器)、Uvicorn(Python ASGI 服务)进程数和单批关键词数量影响。
|
|
|
+
|
|
|
+当前建议:
|
|
|
+
|
|
|
+- 每批关键词最多 100 条。
|
|
|
+- 如果调用端存在 10 个左右并发请求,建议 HTTP 客户端读取超时不低于 15 秒。
|
|
|
+- 如果部署为多进程服务,需要重新设计日志写入方式,例如按进程分日志文件或接入标准 logging(日志模块)方案。
|
|
|
+
|
|
|
+## 11. 当前已确认事项
|
|
|
+
|
|
|
+- 只提供一个批量查询业务接口。
|
|
|
+- 输入为规范查询关键词列表。
|
|
|
+- 查询支持名称、编号、名称加编号。
|
|
|
+- 只根据名称和编号匹配。
|
|
|
+- 每条查询最多返回 3 条候选。
|
|
|
+- 候选按匹配度降序排列。
|
|
|
+- 返回匹配度,但不返回解析状态或数据可信度。
|
|
|
+- 替代情况仅随候选结果原样返回。
|
|
|
+- Agent(智能体)负责最终时效性判断。
|
|
|
+- 本地未找到合适候选时返回空候选列表,不进行外部搜索。
|
|
|
+- 服务记录查询时间、查询内容和查询结果。
|
|
|
+
|
|
|
+## 12. 后续待讨论事项
|
|
|
+
|
|
|
+- 单条关键词长度限制。
|
|
|
+- 日志轮转、保留时间和维护分析流程。
|
|
|
+- 本地规范库的加载、更新和版本管理方式。
|
|
|
+- 服务进程的部署、守护和开机启动方式。
|
|
|
+- 根据真实日志校准匹配阈值和编号纠错规则。
|
|
|
+
|
|
|
+## 13. 第一版测试用例
|
|
|
+
|
|
|
+| 编号 | 测试场景 | 示例查询 | 预期结果 |
|
|
|
+|---:|---|---|---|
|
|
|
+| 1 | 完整编号和名称 | `GB 50330-2013 建筑边坡工程技术规范` | 2013 版排第一,匹配度为 1.00 |
|
|
|
+| 2 | 全角符号和多余空格 | `GB 50330—2013` | 命中 `GB 50330-2013` |
|
|
|
+| 3 | 两位年份 | `CJJ 69-95` | 命中 `CJJ 69-1995` |
|
|
|
+| 4 | 规范属性差异 | `GB/T 6441-1986` | 可以召回 `GB 6441-1986`,匹配度低于精确编号 |
|
|
|
+| 5 | 编号缺少年份 | `GB 50330` | 返回本地库中相关年份版本,最多 3 条 |
|
|
|
+| 6 | 名称对应多个版本 | `建筑边坡工程技术规范` | 返回同名版本,最多 3 条 |
|
|
|
+| 7 | 名称错别字 | `绕结多孔砖和多孔砌块` | 可以召回“烧结多孔砖和多孔砌块” |
|
|
|
+| 8 | 编号单字符错误且名称一致 | `TG/T 5440-2018 公路隧道加固技术规范` | 召回 `JTG/T 5440-2018`,匹配度低于精确编号 |
|
|
|
+| 9 | 编号年份冲突 | `JTG D70/2-2014 公路隧道设计规范` | 可以返回名称相关的 `JTG D70-2004`,但匹配度应体现编号冲突 |
|
|
|
+| 10 | 替代关系不参与查询 | 只输入一个仅出现在 `relation_text` 中的编号 | 不得因为替代关系召回规范 |
|
|
|
+| 11 | 候选上限 | 名称或编号匹配到 4 条以上记录 | 只返回匹配度最高的 3 条 |
|
|
|
+| 12 | 无匹配结果 | `本地规范库中不存在的规范` | 返回空候选列表 |
|
|
|
+| 13 | 批量查询 | 一次输入多条关键词 | 结果数量和顺序与输入一致 |
|
|
|
+| 14 | 日志记录 | 执行一次批量查询 | 每条子查询都有时间、查询内容、结果和耗时日志 |
|
|
|
+| 15 | 文件编码 | 加载 UTF-8 或 GB18030 编码的规范清单 | 均可正确读取中文内容 |
|
|
|
+| 16 | 名称相同但年份不同 | `钢筋焊接及验收规程JGJ 18-2022` | 返回 `JGJ 18-2012`供 Agent(智能体)判断,匹配度为 0.91 |
|
|
|
+| 17 | 编号分隔符周围有空格 | `GB / T 6441 - 1986` | 正确解析并召回 `GB 6441-1986` |
|
|
|
+| 18 | 通用短名称 | `验收规程` | 可以返回相关候选,但匹配度不得虚高到 0.90 以上 |
|