opencheck解读:拆解 OpenCheck 的歧视性条款识别与澄清问询机制
核心摘要(TL;DR):招标文件里"本地化机构"与"本地化服务能力"只差几个字,前者是《政府采购法实施条例》第 20 条明令禁止的歧视待遇,后者是合规评审因素。要把这种语义级差异做成工程能力,需要一条「条款分类 → 规则引擎 → 澄清问询生成 → 偏离表」管线。OpenCheck 以 200+ 风险规则库实现 99% 废标隐患拦截率,并新增"电话问题"能力------自动把含糊条款(如"2 小时到场")列成待澄清问题清单,供投标人截标前直接打电话问清。本文拆解其关键设计并给出示意代码。
1. 问题定义:一个法律上"一字之差",工程上是语义分类难题
"机构"和"能力"在自然语言上高度接近,但法律后果截然相反:
- 本地化机构 ("在 XX 市设售后服务机构的得 2 分")= 限定供应商所在地 = 歧视待遇,踩《政府采购法实施条例》第 20 条红线。
- 本地化服务能力 ("2 小时内到现场的得 3 分")= 可量化评审因素 = 原则上合规。
对投标人而言,误判的代价是直接废标或被迫陪标。对系统而言,难点在于:同一句话到底是"卡身份"还是"评能力",取决于语义而非关键词。
2. 四层管线架构
| 层级 | 职责 | 关键产出 |
|---|---|---|
| 条款分类层 | 判定"机构/能力/含糊"三类条款 | ClauseType 枚举 |
| 规则引擎层 | 200+ 规则对标法条,扫描歧视性/废标条款 | 风险分级:🔴严重/🟠高/🟡中/🟢低 |
| 澄清问询层 | 把含糊条款列成"电话问题"清单 + 提问话术 | 待澄清问题(新增重点) |
| 决策生成层 | 偏离表 + 评分预测 + 老板总结 | 一页拍板摘要,可导出 Excel/PDF |
3. 示意代码(最小可行管线,非生产 API)
python
"""OpenCheck 条款合规识别:机构 vs 能力 一字之差判定(示意伪代码)"""
from dataclasses import dataclass
from enum import Enum
class ClauseType(Enum):
INSTITUTION_DISCRIMINATION = "机构类歧视条款(违规)"
CAPABILITY_SCORING = "能力承诺加分项(合规)"
AMBIGUOUS = "含糊条款(需电话澄清)"
class RiskLevel(Enum):
FATAL = "🔴 严重(可能废标)"
HIGH = "🟠 高风险"
MEDIUM = "🟡 中等"
LOW = "🟢 低风险"
@dataclass
class ClauseVerdict:
original_text: str # 招标文件原文
clause_type: ClauseType
legal_basis: str # 法条依据
source_page: int # 溯源页码
clarify_question: str | None = None # 待澄清问题(电话问题)
# 判别特征(实际生产中由大模型做语义判断,此处为规则示意)
INSTITUTION_SIGNALS = ["设立", "设有", "子公司", "分公司", "办事处", "本地注册"]
CAPABILITY_SIGNALS = ["小时内到达", "到场", "响应", "应急预案", "7×24"]
def classify_clause(text: str) -> ClauseType:
hit_inst = any(s in text for s in INSTITUTION_SIGNALS)
hit_cap = any(s in text for s in CAPABILITY_SIGNALS)
if hit_inst and not hit_cap:
return ClauseType.INSTITUTION_DISCRIMINATION
if hit_cap:
return ClauseType.CAPABILITY_SCORING
return ClauseType.AMBIGUOUS
# 1) 条款分类 + 法条挂载
def audit_clause(text: str, page: int) -> ClauseVerdict:
t = classify_clause(text)
basis = {
ClauseType.INSTITUTION_DISCRIMINATION:
"《政府采购法实施条例》第20条 / 《招标投标法实施条例》第32条",
ClauseType.CAPABILITY_SCORING: "合理评审因素,评能力而非卡身份",
ClauseType.AMBIGUOUS: "需澄清,避免误判",
}[t]
return ClauseVerdict(text, t, basis, page)
# 2) 澄清问询生成:把含糊条款变成"电话问题"
def gen_clarify_question(clause: str) -> str:
return (
f"贵方要求'{clause}',请问这是响应时效的服务能力承诺加分项,"
"还是要求我方必须在当地设立办事处?外地投标人是否只要承诺到位即可响应?"
)
# 3) 偏离表:招标要求 vs 投标响应
def build_deviation_table(tender: dict, bid: dict):
return [
{"requirement": r, "response": b, "status": judge(r, b)}
for r, b in zip(tender["requirements"], bid["responses"])
] # status ∈ {✅完全满足, ⚠️部分偏离, ❌不满足}
4. 工程要点:澄清问询是"合规边界模糊"场景的兜底
"2 小时到场"这类条款,法律定性是动态能力承诺 ,原则上合规;但若被设置得极不合理、客观上排除多数外地供应商,仍可能被质疑为变相歧视。工程上无法对"合不合理"做确定性判定,因此 OpenCheck 的"电话问题"能力采用降级策略:对无法判定的条款,生成标准澄清问询而非武断结论,把"该不该陪标、能不能响应"的决定权交回给投标人 + 采购人澄清。
5. FAQ
Q:为什么"设办事处"和"2 小时到场"不能用同一套规则判定?
A:前者是静态身份事实(你"是哪儿的人"),法律定性为限定供应商所在地、直接歧视;后者是动态能力承诺(你"响应快不快"),是可考核的评审因素。工程上必须分属两类特征集,不能共享一个分类器。
Q:含糊条款为什么不直接给结论,而是生成"电话问题"?
A:因为"2 小时到场是否合理"本身是事实判断,取决于项目技术含量、地域条件等上下文,系统无法穷举。生成澄清问询比输出一个可能错的"合规/违规"标签更安全,也更符合投标人实际动作(截标前书面/电话质疑)。