feat(check): 文件要在、扫描件兜底(C10);响应方式存进清单并可改(C12)

- check/presence.py:「提供《授权委托书》」「须提交投标函」这类要求按标题里有没有这个文件名判断,不调模型。
- 扫描件兜底:要附证明材料的要求没找到、投标文件里有扫描件页时,改为提示级「可能在扫描件里,请人工核对」。
- 响应方式解析时识别、带依据句子存进 checklist.response_modes(迁移 0008);清单页显示、可改(写审计,
  重新解析不覆盖人工改过的);查标用存下来的。新接口 PUT /projects/{id}/checklist/response-modes/{category}。
- check_item_result.layer 加 presence(迁移 0007);契约同步默认接受的两种结论。

开发集 17 份:规矩投标文件的致命问题 120 → 51,致命级召回不变(22 / 32)(docs/architecture/check-eval.md §5)。

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
YANG JIANKUAN
2026-11-04 15:53:45 +08:00
parent cb0c2fcafe
commit 47c6a1258e
21 changed files with 529 additions and 24 deletions

View File

@@ -128,9 +128,23 @@
**一处要说明的**:调响应方式识别时,打印结果里顺带出现了留出集 S03、S06、S09(招标文件本身,不是投标文件)的识别结果,其中 S03、S09 和规范 §2 的记录不一致。没有据此改任何规则,留到留出集正式评测时再看。
## 5. 下一步
## 5. 第三轮:扫描件兜底、文件要在、响应方式存进清单(2026-11-04)
1. 证明材料和格式文件:扫描件兜底(C10)、文件要在(presence)按文件在不在判断。
2. 响应方式存进清单、清单页显示并可改(C12 的人工确认点)。
3. 第 2 步(投标侧结构识别、规则、C12 响应方式抽取)和第 3 步(整行上下文、偏离表直取、presence 判断)按 §2.2 的顺序改,每改一轮在开发集 14 份上重跑对比。
**改了什么**:
- **文件要在**(`check/presence.py`):「提供《授权委托书》」「须提交投标函」这类要求,从原文取出文件名,在投标文件里找写着它的标题;找到就算已响应(出处是那个标题),不调模型。
- **扫描件兜底**(C10):要附证明材料的要求没找到、而投标文件里有扫描件页时,不报「未找到响应」,改成提示级「可能在扫描件里,请人工核对」。没有 OCR 时,扫描件里有没有这份材料分不出来。
- **响应方式存进清单**(迁移 0008):解析时识别、带依据句子存下来;清单页显示,可以改(人工确认点,写审计,重新解析不覆盖人工改过的);查标用存下来的。
| 指标(17 份) | 上一轮(mode) | 本轮(scan) |
|---|---|---|
| 查标召回 | 98 / 150(65%) | 92 / 150(61%) |
| 致命级召回 | 22 / 32 | 22 / 32 |
| 规矩的投标文件报出的问题 | 229(致命 120、重要 58、提示 51) | 235(**致命 51**、重要 14、提示 170) |
| 规矩响应的要求上的误报 | 12.4 / 100 | 13.5 / 100 |
**读法**:问题的个数没变,变的是级别:规矩的投标文件上的致命问题少了一半多(120 → 51),多出来的是「请到扫描件里核对」的提示,致命级召回不变。总召回掉了 4 个点,主要是批次变化带来的波动(文件要在的条目不再进模型,同一批里的组合变了),以及几条漏响应被「标题里出现了文件名」误判为已响应——要盯着。评测集里证明材料全是扫描件页,所以证明材料缺失(D02)照旧查不出、扫描件兜底也分不出缺没缺;真正查出来要靠 OCR / 多模态(S5)。
## 6. 下一步
1. 第 2 步(投标侧结构识别、规则、C12 响应方式抽取)和第 3 步(整行上下文、偏离表直取、presence 判断)按 §2.2 的顺序改,每改一轮在开发集 14 份上重跑对比。
3. 留出集(S03、S06、S09,风格 B + C,封存的词库)和真实国动投标文件,在第 3 步冻结代码后只跑一次。

View File

@@ -1171,6 +1171,34 @@ paths:
description: '`checklist_empty` 或 `project_read_only`'
content: { application/problem+json: { schema: { $ref: '#/components/schemas/Problem' } } }
/projects/{project_id}/checklist/response-modes/{category}:
parameters:
- $ref: '#/components/parameters/ProjectId'
- name: category
in: path
required: true
schema: { type: string, enum: [technical, commercial] }
put:
tags: [checklist]
operationId: setResponseMode
summary: 改招标文件某一类要求的响应方式(C12 的人工确认点)
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [mode]
properties:
mode: { type: string, enum: [explicit, implicit] }
responses:
'200':
description: 返回清单概况
content:
application/json:
schema: { $ref: '#/components/schemas/Checklist' }
'409': { $ref: '#/components/responses/ProjectReadOnly' }
/projects/{project_id}/checklist/versions:
parameters:
- $ref: '#/components/parameters/ProjectId'
@@ -2469,6 +2497,10 @@ components:
type: object
required: [status, version_no, counts, marking_rules]
properties:
response_modes:
type: array
description: 招标文件对技术、商务要求的响应方式(C12)。识别出来的(origin = rule)可以改(origin = human,人工确认点);查标按它判没找到响应算不算问题
items: { $ref: '#/components/schemas/ResponseMode' }
status: { type: string, enum: [draft, confirmed] }
version_no: { type: integer, description: 最近一次确认的版本号;没确认过为 0 }
confirmed_by:
@@ -2863,6 +2895,25 @@ components:
oneOf:
- $ref: '#/components/schemas/Timestamp'
- type: 'null'
ResponseMode:
type: object
required: [category, mode, status, origin, citations]
properties:
category: { type: string, enum: [technical, commercial] }
mode: { type: string, enum: [explicit, implicit], description: 逐条响应 / 默认接受(只列偏离) }
status: { type: string, enum: [found, conflict, unknown], description: 写明了 / 两处写法冲突(按逐条处理)/ 没写明(按逐条处理) }
origin: { type: string, enum: [rule, human] }
citations:
type: array
items: { $ref: '#/components/schemas/Citation' }
set_by:
oneOf:
- $ref: '#/components/schemas/UserRef'
- type: 'null'
set_at:
oneOf:
- $ref: '#/components/schemas/Timestamp'
- type: 'null'
CheckItemResult:
type: object
required: [checklist_item_id, verdict, layer, evidence_status, bid_citations, detail, reused]
@@ -2871,11 +2922,12 @@ components:
title: { type: [string, 'null'] }
verdict:
type: string
enum: [responded, not_found, negative_deviation, suspected_unmet, no_value, undetermined, scanned]
enum: [responded, not_found, negative_deviation, suspected_unmet, no_value, undetermined, scanned, implicit_accepted, implicit_review]
description: |
已响应 / 未找到 / 负偏离未说明 / 疑似不满足 / 未给出具体值 / 未判定(模型漏答或暂时不可用)/ 扫描件请人工核对。
已响应 / 未找到 / 负偏离未说明 / 疑似不满足 / 未给出具体值 / 未判定(模型漏答或暂时不可用)/ 扫描件请人工核对 /
按招标文件默认接受(未列入偏离表的视为接受,不出问题)/ 默认接受的实质性要求、请人工核对(C12)。
没有校验通过的投标侧证据,不会是 responded(架构 §3.6)
layer: { type: string, enum: [deviation_table, rule_compare, model, retrieval], description: 结论来自哪一层(m3-plan §2.1) }
layer: { type: string, enum: [deviation_table, rule_compare, model, retrieval, presence], description: 结论来自哪一层(m3-plan §2.1;presence 是按文件在不在判断) }
evidence_status: { type: string, enum: [verified, unverified, none] }
reason_code: { type: [string, 'null'], description: 证据没通过、漏答、模型错误等的原因码 }
bid_citations: