Files
hermes-skills/skills/legal/contract-portfolio-analysis/scripts/kl-separation-check.py
T

96 lines
3.8 KiB
Python

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
K/L 分工自检 / K-L Column Separation Check
==========================================
校区梳理表 Step5 终审必跑:验证 K 列(法律风险) 与 L 列(模版差异) 职责分离。
· K 列(第 11 列)= 独立法律审查,从合同条款本身起笔,**绝不引模版**
→ 不得出现 "模版 / 07模版 / 07-房屋 / 07标准 / 相对模版 / 被放宽 / 被删除 / 被改为"
· L 列(第 12 列)= 纯客观文本对比,只陈述 "模版表述为X;本合同表述为Y"
→ 不得出现 "风险 / 建议 / 不利 / 详见"(判断词与向 K 列导流的话)
⚠️ K 列匹配 "07模版 / 07-房屋 / 07标准" 而非裸 "07"——裸 07 会误命中金额数字
(如租金 377,507.80 里的 "507"),2026-06-24 跃龙路实证误报,已修正。
判据·遮模版测试:把"模版怎么写"整个遮住,K 列那条风险论述仍完整成立才算独立审查;
遮住就垮 = L 列逻辑混进了 K 列。
用法:
python3 kl-separation-check.py <校区梳理表.xlsx>
退出码:0 = 通过;1 = 有违规(需回去拆分 K/L);2 = 用法错误
注:本脚本只查 K(11)/L(12) 两列的数据行。整体段(合并 A:L,在第 1 列)里
"【与07标准模版的文本差异】" 小节合法含"模版/07",不在本脚本检查范围——
那是整体段按设计单独成段的客观对比,不是 K 列。
"""
import sys
import re
import openpyxl
K_FORBIDDEN_LITERAL = ["模版", "相对模版", "被放宽", "被删除", "被改为"]
K_FORBIDDEN_REGEX = r"07模版|07-?房屋|07标准"
L_FORBIDDEN_LITERAL = ["风险", "建议", "不利", "详见"]
def cell_text(cell):
"""取单元格纯文本,兼容 CellRichText(可迭代) 与标量。"""
v = cell.value
if v is None:
return ""
if isinstance(v, str):
return v
try:
return "".join(getattr(t, "text", str(t)) for t in v)
except TypeError:
return str(v)
def is_header(text):
"""表头行启发式:K 表头='法律风险…'/'风险点…';L 表头='与…模版差异'"""
head = text[:10]
if ("法律风险" in head or "风险点" in head) and len(text) < 40:
return True
if text.startswith("") and "模版差异" in text and len(text) < 40:
return True
return False
def check(path):
wb = openpyxl.load_workbook(path)
violations = []
for ws in wb.worksheets:
for r in range(1, ws.max_row + 1):
k = cell_text(ws.cell(r, 11))
l = cell_text(ws.cell(r, 12))
if k and not is_header(k):
kb = [w for w in K_FORBIDDEN_LITERAL if w in k]
kb += re.findall(K_FORBIDDEN_REGEX, k)
if kb:
violations.append((ws.title, f"K{r}", "独立审查不得引模版", kb))
if l and not is_header(l):
lb = [w for w in L_FORBIDDEN_LITERAL if w in l]
if lb:
violations.append((ws.title, f"L{r}", "纯客观对比不得下判断", lb))
return violations
def main():
if len(sys.argv) < 2:
print("用法: python3 kl-separation-check.py <校区梳理表.xlsx>")
sys.exit(2)
v = check(sys.argv[1])
if not v:
print("✅ K/L 分工自检通过:K 列零模版引用、L 列零判断词。")
sys.exit(0)
print("❌ K/L 分工自检发现违规(需回去拆分):")
for sheet, cell, why, hits in v:
print(f" [{sheet}] {cell}: {why} — 命中 {hits}")
print("\n判据·遮模版测试:把'模版怎么写'遮住,K 列风险论述仍完整成立才算独立。")
print("L 列只陈述'模版表述为X;本合同表述为Y',判断与建议归 K 列。")
sys.exit(1)
if __name__ == "__main__":
main()