feat: export core Hermes skills

This commit is contained in:
2026-07-15 02:45:56 +00:00
parent a028b63eda
commit 54711fee2a
308 changed files with 41310 additions and 1 deletions
@@ -0,0 +1,37 @@
# 批量校区审查执行纪律(2026-07-03 龙信返工总结)
## 根因
连续做6个校区(星月→跃龙路→通州金鹰→小石桥晏园→龙信→海门临时),到第4-5个时执行质量明显衰减:
- 跳过逐字通读(物业20页中间跳了)
- 套用前一校区结论代替独立审查(海门临时照搬龙信结论)
- K列法律风险没做八维框架(凭直觉归纳)
- 不跑物理检查点脚本
- 擅自删除旧文件(没被授权)
## 铁律
### 1. Session容量限制
**连续做3个校区后,必须主动提醒Maggie开新对话。** 不等用户发现,自己数到第3个就提醒。
### 2. 每份合同独立一行
3份合同=3行,不合并。Maggie原话:"3份合同分别提取,分别审阅,做成三行,不要混在一起写"
### 3. 所有租赁合同必须做07模版比对
不论是新东方制式还是甲方制式,租赁合同一律delegate做07比对。L列从比对文件逐条摘。龙信教训:第一版因"非新东方制式"就写了一句"无对应模版"被打回。
### 4. 不擅自删除任何文件
旧版汇总表保留,不清理。Maggie原话:"我没让你清理旧表,请恢复这两个旧表,没有让做的事情不要自己擅自进行"
### 5. 逐字逐句不是口号
被问"你是自己逐字逐句审阅的么?"——如果答案是否定的,诚实说,然后补做。不粉饰。衰减信号:
- 发现自己在写"条款结构与XX类似"→没独立审
- 物业合同20页只grep了关键词→没通读
- K列写完觉得"差不多"但说不出某条依据→没审到位
### 6. H列月租换算写法
行内括号写(如"年340,000元(月租≈28,333元)"),不另起行。
## 解决模式
- 做到第3个校区时:停下来,主动告诉Maggie建议开新对话
- 每份合同做完K列后自问:这条风险的条款号出处是什么?答不出=返工
- 交付前必跑kl-separation-check.py,不凭眼判断
@@ -0,0 +1,64 @@
# 北翼玖玖 / 金飞达纠偏:17份文件先出“按租赁物+时间顺序”的列表
## 触发场景
- 用户说“先把17个文件列表梳理出来”
- 用户强调“根据租赁物,同一租赁物相关合同或文件,按照时间顺序排列”
- 校区项目进入正式H/I/K/L审查前,需要先锁定文件编排顺序
## 本次纠偏结论
在北翼玖玖这类多文件校区任务中,**先交付的不是合同类型清单,而是“按租赁物归组 + 组内按时间顺序”的列表**。
### 具体要求
1. 先按**租赁物**分成主线板块;
2. 同一租赁物下的**租赁合同、补充协议、退租协议、物业合同、物业补充协议、说明文件**一并放入同组;
3. 组内按**签约时间/正文明确生效时间/权利义务转移时间**排序;
4. 如果签章页未载明日期,必须明确写“**签署日未载明**”,并说明采用何种时间线索排序;
5. 在用户要求“先梳理列表”时,**先给列表,不要抢跑到H/I/K/L审查进度汇报**。
## 推荐输出格式
### 租赁物A:……
1. 文件名
- 性质:……
- 时间:……
- 说明:……
### 租赁物B:……
1. 文件名
- 性质:……
- 时间:……
- 说明:……
最后补一句:
- 共几组
- 共几份
- 哪些日期系正文线索排序、哪些日期未载明
## 本次北翼玖玖实际归组方法
### A组:4幢101、102、201室 + 1幢507室
主线顺序:
- 房租主合同
- 物业主合同
- 物业减免补充协议
- 房租减免补充协议
- 物业退租协议
- 物业补充协议二
- 房屋部分退租协议
- 4幢补充协议三(权利义务转移)
- 两份物业电费/水电费收款变更补充协议
- 房租目录下特殊情况说明
### B组:1幢205/206/207/208/209室(核心租赁为206-209)
主线顺序:
- 扩租房屋合同
- 配套物业合同
- 扩租房屋租赁合同补充协议(权利义务转移)
- 扩租物业补充协议
- 扩租物业补充协议(电费教育科技)
- 扩租目录下特殊情况说明
## 操作提醒
- “按文件夹盘点”与“按租赁物输出”是两层动作:
- 盘点时尊重源目录(房租/扩租/物业)
- 输出时重建为租赁物主线
- 同一事项的重复版本/近似版本(尤其电费、水电费收款主体变更协议)**先保留进列表,再在后续审查中判断是否为同一事项不同版本**。
@@ -0,0 +1,50 @@
# 校区审查范围与排序铁律(2026-07-13 金飞达纠偏)
## 适用触发
当用户说:
- “开始某校区的审查和汇总”
- “某校区重新整套做”
- “按租赁物分类输出”
默认这是**校区全部合同/PDF文件**的梳理与审查,不是只抓主租赁合同。
## 范围铁律
必须先盘清该校区**全部相关文件**,至少包括:
- 租赁合同
- 补充协议
- 主体变更协议
- 物业合同
- 物业补充协议
- 授权书
- 其他与该租赁物直接相关的辅助文件
用户未明确排除前,**物业合同也在本轮审查和汇总范围内**。
## 组织铁律
输出结构必须:
1. **先按租赁物分类**建板块
2. 每个租赁物下放入该租赁物的**全部相关文件**
3. 板块内部按**签约时间顺序**排列
排序主键是:
- 第一层:租赁物
- 第二层:签约时间
- 不是文件类型
## 禁止事项
- 禁止把“租赁审查”理解成“只看租赁合同本体”
- 禁止先租赁后物业硬分组
- 禁止把同一租赁物的物业合同甩到别的板块
- 禁止先做主合同,后想起再补物业合同
## 例外
只有确实**无法挂到具体租赁物**的文件,才单列“其他文件”板块,例如:
- 校区层面的总授权书
- 无法判断对应租赁物的通用说明文件
## 与开工闸门的配合
开始前应先在开工确认中显式写出:
- 本校区有哪些文件
- 哪些租赁物板块
- 每个板块包含哪些文件
- 排序是否已按签约时间锁定
@@ -0,0 +1,38 @@
# 中文流程图/图表渲染:matplotlib 坑 + HTML 首选方案
> 2026-06-16 实战教训:给 Maggie 画流程图,matplotlib 反复把中文渲染成"豆腐块"(空心方块 tofu),折腾数轮。根因与可靠方案如下。
## matplotlib 中文豆腐块根因
- 系统装了文泉驿 `wqy-zenhei.ttc`,但 **matplotlib 默认不把 `.ttc` 注册进字体缓存**——`rcParams['font.sans-serif']=['WenQuanYi Zen Hei']` 按"字体名"查找会**静默失败、回退成豆腐块**,不报错。
- `fc-list :lang=zh` 能看到字体 ≠ matplotlib 能按名字用它。
## 可靠修复(若必须用 matplotlib)
**给每个 text 显式传 `fontproperties` 指向字体文件**,不靠字体名查找:
```python
import matplotlib.font_manager as fm
FP = fm.FontProperties(fname="/usr/share/fonts/truetype/droid/DroidSansFallbackFull.ttf")
# Droid Sans Fallback 是 .ttf、已在缓存、支持全中文,比 .ttc 可靠
ax.text(x, y, "租赁合同", fontproperties=FP) # 每处都带 fontproperties
```
验证某字体文件能否渲染中文:渲染单字 '租',统计中心 1/3 区域笔画占比,>0.08 是真字、<0.05 是空心豆腐块。
## ★ 首选方案:用 HTML 而非 matplotlib PNG
**给非技术用户(Maggie,经企微/OnlyOffice 看)的中文图表/流程图,优先做成自包含 HTML**:
1. **中文渲染零风险**——浏览器直接调系统字体,`font-family:"Microsoft YaHei","PingFang SC","WenQuanYi Zen Hei",sans-serif`,绝不豆腐块。
2. **企微能直接打开**,可缩放。
3. **可自检**——`browser_navigate``file://` 后看快照里的文字是否正确,比赌 PNG 字体渲染可靠得多。
4. 用 div + border + 颜色块画节点、`↓`/`→` 字符画箭头,配色用语义色(蓝=主审、绿=subagent、金=交付)。
可复制的起始模板:`templates/lease-review-flowchart.html`
## ⚠️ 语义色点用纯 CSS 圆点,不要用 emoji(🔵🟢🔴)(2026-06-22 workflow流程图实证)
图例/节点里标语义色,**别用 emoji 彩色圆点**——无头浏览器截图环境(Browserbase 等)常缺 emoji 字体,截图里 emoji 渲染成空心方框 `▢`(文本层 emoji 字符其实完好、`browser_console``looksLikeBox:false`,只是那个截图环境字体缺失)。但你**无法保证 Maggie 的企微/设备一定有 emoji 字体**,交付物要万无一失。
- 正解:用**纯 CSS 圆点** `<span class="dot b"></span>``.dot{display:inline-block;width:11px;height:11px;border-radius:50%;border:1.5px solid;}`,各色 `.dot.b{background:#BBD9F0;border-color:#2E6DA4;}`…。任何设备、任何环境稳定显示,不依赖 emoji 字体。
- 节点本身已有语义底色时,标题里的 emoji 色点是冗余,直接删(靠底色表达语义即可)。
- 表意标记 emoji(⚠️📌✅)可保留——渲染更稳,且是真正的语义标记,不是纯装饰色点。
- 自检:`browser_navigate``file://` 后用 `browser_console``.lg` 的 textContent 确认文本层完整;再 `vision_analyze`/`browser_vision` 看截图确认色点真渲染成彩色圆点(不是方框)。
## 图像自检:vision 已配好,是主路径(2026-06-22)
vision_analyze/browser_vision 已由技术支持配好可用——交付 PNG/HTML 截图前**先自己 vision 看一遍**验证版面/配色/截断/乱码,这是主路径(实测能准确读出渲染图的配色、文字截断、版面错位、色点是否方框)。
- **HTML 双层自检**:`browser_navigate``file://` 看快照验证中文/结构(文本层)+ `browser_vision` 看视觉层(配色/排版/emoji 方框)——两层都过才发。
- **兜底(vision 万一又报 "No LLM provider configured for task=vision" 或连不上)**:不把"看不见的 PNG"直接甩用户,改用能推理正确性的格式——HTML(看 `browser_navigate` 快照验证文本层)或纯文字版流程图(方框+箭头字符,零字体依赖),HTML+纯文字兜底一起发。
@@ -0,0 +1,60 @@
# 南通汇总表列归类规则 (Maggie 2026-07-01 确认)
## H列(金额/费用)— 只放纯费用
✅ 属于H列:
- 租金标准(单价+月总额)
- 物业费标准
- 付款推算(各期金额+日期+❗标注)
- 履约保证金
- 供电增容费用归属
- 水电费标准(单价)
- 免费停车位
❌ 不属于H列(常见错误):
- 违约金 → 归I列(核心内容),违约金是合同权利义务安排,不是费用
- 逾期违约金 → 归I列
- 擅自转租违约金 → 归I列
## I列(核心内容)— 客观陈述合同约定
包含:用途限制、转租条件、装修改造权、广告标识、非竞争、维修责任、解除权机制、不可抗力、优先权、续租、消防义务、管辖、通知方式、**违约金条款**。
## K列(法律风险)— 聚焦风险,不放有利条款
K列数据行(r5/r8)只写:
- 整体评价(一段)
- 需注意的风险点(逐条编号)
- 提前退租法律后果分析
❌ "对乙方有利条款"不放K列 → 放整体段(row 10)续签建议里,措辞为"建议保留以下对乙方有利条款"
## K列·提前退租分析框架
### 原则:有约定写约定,无约定写法律规定,不做无锚点推测
### 情况一:合同有明确约定(如凤凰文化第十条第2款)
直接写约定内容,不再延伸:
```
① 单方解除:提前60日通知 + 初年年租金20%违约金 + 租金及其他费用结算至解除日
② 可收回:结算至解除日 + 押金30工作日退还
③ 其他法定/约定解除路径(办学许可证/不可抗力等)
```
**禁止**:"可能主张损失""建议协商规避""不确定责任"等无锚点推测。
### 情况二:合同无明确约定
三层分析(每层都有法律依据,不是推测):
1. **法定解除权**:《民法典》563条(不可抗力致目的不能实现、对方根本违约等)+ 租赁特有法定解除
2. **无法定事由 = 违约**:《民法典》584条——赔偿因违约造成的损失(含履行后可获利益),上限=可预见损失
3. **损失具体构成**:空置期租金(司法实践3-6个月)、押金是否可抵扣、预付租金结算
## 整体段(row 10)结构
```
【整体评价】
【提前退租法律后果】
【其他法律关注点】
【续签建议】
· 建议补充/修改:...
· 建议保留以下对乙方有利条款:①办学许可证免责解约权 ②不可抗力 ③优先权 ...
```
@@ -0,0 +1,70 @@
# 汇总表列内容归类规则(Maggie 2026-07-01 确认)
## H列(金额/费用)—— 只放纯费用信息
✅ 放H列:
- 租金标准+月租金(行内括号写法:如"年191,990元(月租≈15,999元)")+每期合计
- 物业费标准+月物业费
- 付款推算(逐期展开:区间+租金+物业=合计+付款期限+❗标注)
- 履约保证金/押金
- 供电增容费用
- 水电费标准(单价)
- 免费停车位(权益性质但与费用相关)
### 付款推算效率规则(2026-07-03)
付款推算直接从汇总表已有数据推导:
- G列 → 合同起止日期、免租期
- H列 → 租金/期、物业费/月、支付方式(半年/季度/年)、提前天数
无需每次回OCR原文。仅当数据存疑(如附件分期与正文不一致)时才回md核实。
❌ 不放H列:
- 违约金(属合同权利义务安排→归I列)
- 逾期付款违约金
- 擅自转租违约金
- 水电逾期违约金
- 单方解除违约金
## I列(核心内容)—— 客观陈述合同约定的权利义务安排
包含:
- 用途限制、转租条件、装修改造
- 非竞争条款、出租方变更、抵押限制
- 解除权机制(通知期+违约金+结算方式)
- 各类违约金条款(从H列移入)
- 不可抗力、续租、优先权
- 安全责任、消防义务
- 管辖、通知方式
## K列(法律风险)—— 聚焦风险,不列有利条款
结构:
1. 【整体评价·站承租方立场】一段话概述
2. 〇 需注意(逐条列风险点,标条款号)
3. ◎ 提前退租法律后果分析
4. 【续签建议】(改进建议)
### 提前退租分析框架
**核心原则:有约定写约定,无约定写法律规定——都要有依据,不做无锚点推测。**
#### 情形一:合同有明确约定
直接写约定内容,不做额外或然推测。
示例(凤凰文化):
> ① 单方解除(第十条第2款):提前60日书面通知+承担初年年租金20%违约金(约16,411元)+租赁租金及其他费用结算至合同解除日。约定明确,退租成本可控。
❌ 不写:"甲方可能主张实际损失""建议协商规避""不确定责任"
#### 情形二:合同无明确约定
写法律规定的三层分析:
1. 法定解除权(民法典563条+租赁特有法定解除)
2. 无法定事由=违约(民法典584条:赔偿可预见损失)
3. 损失具体构成(空置期3-6个月/押金/预付租金)
### 不总结"对乙方有利条款"
Maggie确认:不总结对客户有利的条款,对客户实际意义不大。
- K列数据行不写
- 整体段不写
- 续签建议也不写"建议保留"
@@ -0,0 +1,585 @@
# 南通汇总表各列规则(Maggie 2026-07-01 校准版)
优先级最高——与 SKILL.md 冲突时以本文件为准。
## 格式参照基准(Maggie 0703 指令)
**跃龙路校区为输出格式唯一参照**("输出格式参照跃龙路,别自己创设")。具体:
- D列:`甲方:XX\n乙方:XX`(简洁,不加括号说明"出租方""承租方")
- E列:地址+用途(`用途:办公`单独一行)
- H列付款推算:用`┃`分隔金额和付款期限(`170,000元 ┃ 2026.9.20前`
- H列物业费:必须逐期列出每期金额+付款截止日期,不得写"按年预收类推"
- K列标题:`【整体评价·站承租方(乙方)立场】`
- K列风险点标题:`〇 需注意`(非"【需注意的风险点】")
- 整体段:`【其他法律关注点】`(非"【法律关注点】")
## 🔴 输出格式铁律(Maggie 2026-07-03 龙信返工教训)
**跃龙路定稿是格式唯一参照物,不得自创格式。** 详见 `references/output-format-canonical-0703.md`
新建校区前必须先 `read_file` 跃龙路汇总表各列,照抄格式框架。关键差异点:
- K列标题:`【整体评价·站承租方(乙方)立场】` + `〇 需注意`(不是`【整体评价】`+`【需注意的风险点】🔴`
- K列风险编号:纯数字不加emoji(不用🔴🟡🟢)
- H列付款推算:`金额 ┃ 付款期限`(┃分隔)
- L列差异:`N.【标签】07模版:XX→本合同:XX`
- 整体段:`【其他法律关注点】`(不是`【法律关注点】`
- D列:`甲方:XX\n乙方:XX`(简洁,不加"出租方""承租方"括号说明)
- 物业行K列:直接"物业合同风险XX。关注点:"开头(不用【整体评价·...】框架)
---
## H列:金额/费用
**原则**:只写乙方正常履约需要支付的确定费用。
- **写什么**:租金标准及总额、物业费、押金、付款推算(各期金额+付款截止日期)等
- **不写什么**:违约金、滞纳金、银行账户信息、供电功率等
- **判断标准**:这笔钱是乙方正常履约就要付的吗?是→写;是出了问题才产生的→不写
- 上面的"写什么""不写什么"均为列举示例,不是穷举清单。按合同实际情况判断。
### ⚠️ H列混入违约金pitfall(小石桥0707教训)
物业合同的"逾期滞纳金X%/日"容易被误写入H列——因为它紧挨着物业费标准出现在同一条款中。**判断标准只看一件事:这笔钱是正常履约就要付的吗?** 逾期滞纳金=出了问题才产生=不写H列。应放K列作为风险项。
常见误入H列的项目(全部应删除):
- ❌ 逾期滞纳金/违约金(0.5%/日、0.1‰/日等)
- ❌ 供电功率(不是费用)
- ❌ 维修费承担方式(属权利义务分配→I列)
- ❌ 押金扣除条件(属违约后果→K列)
### ⚠️ H列条款号引用必须核对附件实际编号(龙信0708教训)
附件一/附件三的条目编号不能凭记忆或推测,必须核对原文实际标号。典型错误:
- 附件一结构为「1.租金 (1)标准 (2)支付期限 / 2.物业管理费 / 3.保证金 / 4.其他费用」
- 错误:把"支付期限"引用为"附件一第2项"(第2项实际是物业管理费)
- 正确:应引用为"附件一第1(2)项"
不同合同的附件结构可能不同(如临时合同无物业管理费项,编号直接跳到第4项)。**每份合同的附件引用必须独立核对该合同自己的编号结构。**
### H列月租换算格式(Maggie 0703 指令)
月租换算一律用**行内括号写法**,写在租金标准那行括号里,不单独另起行。
- ✅ 正确:`年191,990元(月租≈15,999元)`
- ❌ 错误:单独另起一行写"折合月租约15,999元"
### H列付款推算描述规则(通州金鹰0707教训)
**当合同有明确的付款日期表格时(如第三条租金表逐期列出付款日),H列付款推算的描述必须引用表格作为权威来源**,不得仅凭合同文本中的一般规则(如"每期结束前15天付下期")来描述。
- ✅ 正确:`按合同第三条租金表约定的付款日期(各期起始前1个月)`
- ❌ 错误:`每期结束前15天付下期(第三条第2款)`——如果表格实际约定的日期与"15天"不吻合
**如果文本规则与表格约定存在矛盾**(如文本说"15天前",但表格显示各期均为"起始前1个月"),须:
1. H列以表格日期为准(具体约定优先于一般规则)
2. K列补充"付款规则内部矛盾"作为风险项——对方可能援引文本规则主张违约
**首期付款日特殊处理**:如合同表格有明确的首期付款日期(如"2025-03-20"),须同时标注文本规则和表格日期:`合同生效后10个工作日内(合同表格约定2025.3.20)`
### 🔴 H列条款来源归属精准规则(0709悦拾光教训)
H列每项数据后的条款引用必须覆盖**所有实际数据来源**,不能只写主条款号:
- 若机制出自主条款(如3.3条描述"预充值电表由甲方代收"),但具体单价/代收主体出自附件(如交房确认书第4点写明"电费0.9元/度,星展代收"),则必须写"(3.3条+交房确认书第4点)"
- 禁止简化为仅引主条款——Maggie质疑"总结的依据是哪个合同条款"时,引用不全=无法溯源=不合格
- **自检方法**:H列每个带括号引用的数据项,逐一追问"这个数字/这个主体名字,在我引用的条款原文里能不能逐字找到?"——找不到就是漏了来源
- 典型场景:水电费单价(主条款写机制+附件写单价)、物业费发票开具方(主条款约定付款+三方协议约定收款方)、保证金退还条件(合同正文+补充协议修改)
### H列四检(交付前必过——租赁行+物业行都要过)
1. 每个金额都标注了条款号出处(且覆盖全部数据来源,见上条)
2. 做了月租换算——行内括号写法(≈X元/月)
3. 列出了各期具体付款日期
4. 未付期次句首标了❗
### 🔴 H列付款推算必须对应正确费率期(世茂0708教训)
**递增租金合同中,每期推算必须使用该时段对应的费率,不能全部按第1费率计算。**
典型错误:合同有第1费率期(2025.12-2027.2)和第2费率期(2027.3-2029.2),第3/4期付款推算全部按第1费率算→少算了递增部分。
正确做法:
1. 先确定每个付款周期的时间段
2. 检查该时间段落入哪个费率期(可能跨越两个费率期)
3. 跨费率的周期须分段计算:第1费率×N月 + 第2费率×M月
4. 验算:用合同附件三载明的第2期总额反推第2费率含税金额
**附件三有明确载明总额的周期可直接用合同数字**(如"第二期保底租金...金额为人民币401,363.92元"),无需自行推算——反向用它来验证费率是否正确。
### 🔴 合同中"/"=不适用,不提及(世茂0708教训)
合同格式文件中留空或填"/"的条款(如"营业额提成:/%"、"促销服务费:/元/㎡"),意味着该项不适用于本合同。**全表各列均不应提及这些不适用条款**:
- H列不写"营业额提成比例未填"
- I列不写营业额提成取高机制
- K列不写"提成比例未填存在争议风险"
- L列不写"保底+提成取高"作为差异
判断标准:如果合同原文用"/"明确表示空白/不适用,就当它不存在。
**⚠️ 物业行H列同样必须做付款推算**(跃龙路0707审计发现遗漏):
- 物业管理费+公共能耗费=每期总额,按合同约定的结算周期(年/半年/季)逐期列出
- 格式与租金推算一致:`❗第N期(起止日):XX元 ┃ 付款期限YYYY.MM.DD`
- 首期如早于合同起始日,注明"首期随签约支付"
- 电费等按实结算的只写计费标准,不做推算
- **总额必须反映实际合同期限**(含免租期内物业费),不能简单用"年费×整年数"。如合同期为5年4个月,总额=年费÷12×64个月(通州金鹰0707纠正:原写48000×5=240000,实际应为256000)
- **末期不足半年/一年的按比例计算**,单独注明"末期N个月按比例"
### H列付款推算效率(Maggie 0703 确认)
付款推算可以直接从 G列(起止日期)+ H列已有数据(支付方式、租金标准、递增规则、物业费单价)推算,不需要每次回 md 原文重新读取。具体:
1. 从G列取起止日期+免租期
2. 从H列取租金/期、物业费/月、支付方式(半年/季/年)、提前天数
3. 按周期切分区间 → 算出每期金额+付款截止日
只有遇到数据存疑(如附件分期跟正文对不上、免租分摊逻辑不清)才需要回 md 核实。
### 🔴 H列禁止"详见合同""同上"等空壳写法(世茂0708教训)
**每份合同的H列必须完整写明具体金额和付款推算,不得用"详见合同附件约定""同上"等模糊表述代替。**
物业合同尤其容易犯此错——因为金额较小、附件结构复杂、项目多,容易偷懒写"管理费详见附件"。但客户看汇总表就是不想翻原文,必须一目了然。
物业合同H列完整标准:
- 履约保证金具体金额
- 管理费:单价×面积=月费(不含税+税金+含税合计)
- 装修押金+装修管理费(一次性)
- 促销服务费(不适用则标"不适用(/)")
- 水电费计费标准
- 支付方式+完整付款推算(按年/半年/季,逐期列出)
- 合计总额
三层/扩租合同同理——即使条款结构相同,参数(面积、单价、周期)不同就必须独立写完整数字,不能"同上"。
---
## I列:核心内容
按固定类目逐项摘录合同原文,有就写没有就不写。格式统一为 `· [类目] 原文(条款号)`
## I列:核心内容
按固定类目逐项提炼合同核心事实,有就写没有就不写。
### 租赁合同(21个固定类目)
用途/转租/装修改造/广告标识/非竞争/维修责任/保险要求/物业服务联动/配套设施/出租方变更/解除权机制/违约金机制/不可抗力/征收拆迁/房屋抵押查封/政策变化/到期处理/恢复原状/优先权/管辖/备案
### 物业合同(15个固定类目)
物业服务内容/服务标准/公共能耗费/特约服务/共用设施管理/装修管理/安保措施/消防安全/保险要求/联动终止/违约责任/退出交接/免责条款/不可抗力/管辖
### 物业合同I列补充要点(0707审计发现遗漏)
除15个固定类目外,以下条款如合同有约定也应在I列提及(属于权利义务分配的实质内容):
- **违约确认时限**:如物业合同约定"违约侵权须N日内书面告知,逾期视为放弃权利"→必须写(影响维权时效)
- **不动产转让条款**:如约定"转让不影响合同继续履行"→写入[联动终止]类目
- **服务标准引用**:如约定"参照《XX省物业服务N级标准》"→写入[服务标准]类目
- **特约服务收费**:如约定"须事先公布收费标准,按实计付"→写入[特约服务]类目
### 格式规则
- 格式:`· [类目] 关键事实(条款号)`
- 一行一个类目,精简干练,只留核心事实+条款号
- 有就写,没有就不写
- 禁止冗余修饰语和重复描述——能用一句话说清的不用两句
- 违约金属于权利义务安排,放I列(违约金机制)而不是H列
- 同模板多合同时,后续行写"条款结构与XX一致。差异:·面积 ·租期 ·保证金"即可
### 精简标准(Maggie 0703确认)
- ❌ "未经甲方书面同意,乙方对于承租房屋不得以任何形式转租、转让、转借、抵押或其他有损甲方利益的行为"
- ✅ "未经书面同意不得转租/转让/转借/抵押(4.5条)"
- 原则:摘录核心事实≠原文照抄。用最少的字传达最准确的信息。
### ⚠️ 格式陷阱(星月0706发现)
-`【类目】内容(条款号)` —— 方括号用了中文书名号,早期校区残留
- ❌ 详细展开式(每项一大段话,如"【用途】仅限办公(第二条第1款);未经甲方书面同意不得改变用途(第二条第2款)\n【转租】允许部分或全部转租……")——早期跃龙路/通州金鹰残留
-`· [类目] 内容(条款号)` —— 半角方括号+圆点前缀,一行一类目
- 重审旧表时首先检查格式是否符合规范,不符合的一并改正
### I列审计/改写流程(0707跃龙路+通州金鹰+小石桥实践)
当被要求检查已有汇总表的I列时:
1. **先对照21/15类目清单做覆盖检查**——逐个类目看是否已在I列中出现
2. **判断"合同无此条款"vs"合同有但I列遗漏"**——回到合同md原文确认
3. **如果格式不是`· [类目]`式→整列改写**,不是在旧格式末尾追加。全部改为精简类目式一次到位
4. **改写后覆盖率自检**:租赁≥15/21,物业≥10/15(合同无相关条款的不计入分母)
5. **删除非核心条款**:合同份数、现场负责人、反舞弊等(见下方"常见应删除项")
### 汇总表审计完整流程(Maggie 0707 指令模式)
当Maggie说"检查下XX校区的汇总表"时,执行以下标准审计:
### 🔴 I列统筹提炼规则(2026-07-13 金飞达纠正)
**若同一租赁物没有单独物业合同,而物业管理/公共能耗/消防/装修管理/共用设施/安保等内容已经写进租赁合同正文,那么I列不能只按“租赁合同21类”机械提取。** 必须把该租赁物对应文件中的:
- 租赁合同21类要点
- 物业合同15类要点(凡已内嵌在租赁合同正文、附件、补充协议里的)
**一起统筹提炼**
也就是说,判断标准不是“有没有单独物业合同文件”,而是:
> **该租赁物的全部核心权利义务里,是否已经把物业类安排写进租赁合同。**
典型应一并纳入I列的内嵌物业要素:
- 物业运营管理费/物业费的构成与支付
- 公共能耗费、中央空调费、电梯电费、水电费调整机制
- 共用设施管理、公共区域布局调整、营业时间管理
- 装修管理、装修押金、装修管理费、装修验收
- 安保措施、经营管理公约、消防责任书
- 退出交接、物业随租赁联动终止
- 免责条款中与水电中断、设施故障、公共区域管理相关内容
**金飞达案例(2026-07-13)**:没有单独物业合同,但主租赁合同/扩租合同/499合同中已写入物业运营管理费、中央空调费、公共区域管理、经营管理公约、消防责任书、电梯电费、停车位等内容。此时I列应按“租赁+物业混合文本”统筹提炼,而不是误判为“缺物业合同所以只做21类”。
**Step 1: 拉取文件**
- docker cp 从Nextcloud取xlsx + 合同md文件到/tmp/
**Step 2: H列审计**
- 检查是否混入非费用项(违约金/滞纳金/供电功率→应删除)
- 检查物业费是否有逐期付款推算(没有→补充)
- 检查租金付款推算日期是否与合同表格吻合(不吻合→修正描述+K列补充矛盾)
- 检查总额计算是否反映实际合同期限(如5年4个月≠5年)
**Step 3: I列审计**
- 对照21/15类目清单做覆盖率检查
- 判断格式是否为精简类目式(不是→整列改写)
- 补充遗漏类目 + 删除非核心条款
**Step 4: K列审计**
- 逐项回到合同原文核验每条风险是否有据
- 检查是否遗漏明显风险(签约主体错配、合同内部矛盾、支付方向瑕疵等)
- 验证提前退租分析的金额计算
**Step 5: 上传**
- docker cp回Nextcloud + chown + files:scan
**报告格式**:按校区输出结构化检查报告,列明"问题→处理"表格。
### 非固定类目内容的处理(星月0706实践)
旧表中可能存在不属于21/15个固定类目的条目(如"保证金退还""发票""工商迁入""安全责任""甲方维修"等)。修订时处理原则:
- **可归入固定类目的→合并**:甲方维修→并入[维修责任];保证金退还条件→并入[恢复原状];甲方违约→并入[解除权机制]
- **纯信息性/不影响权利义务分配的→删除**:发票开具要求、工商迁入时限、安全第一责任人声明等
- **判断标准**:该信息是否直接影响乙方的权利行使或义务负担?是→找最近的固定类目归入;否→不写
- **格式统一**:旧表若用`【类目】`格式,修订时一律改为`· [类目]`
#### 常见应删除项(小石桥0707补充)
以下内容不属于核心权利义务,不应出现在I列:
- ❌ 合同份数("一式四份各执两份")
- ❌ 现场负责人姓名/电话
- ❌ 反舞弊/举报条款(程序性合规条款)
- ❌ 开票信息(户名/税号/账号)
- ❌ 签字盖章生效条款
- ❌ 补充协议效力条款
这些属于合同程序性/管理性条款,不影响乙方实质权利义务的行使或负担。
### 通读强制机制(悦拾光0703教训)
Step2动作A法律审查必须用read_file从第1行读到最后一行(分批500行/次),不得用grep抽查代替通读。
自检标准:I列类目覆盖数≥阈值(租赁15/21,物业10/15)。
建表后必跑 `scripts/i-column-coverage-check.py` 验证覆盖,不够就核查确认。
> **同模板简写行的⚠️警告是正常的**(桃坞路0703确认):使用"条款结构与XX一致。差异:…"简写的行(如扩租行)会因类目标记少而触发低覆盖警告——这不是遗漏,是规则允许的简写。只需确认首份详细行已达到21/21或15/15即可。
---
---
## J列:当前状态
按实际日期判断:
- 合同期限已过 → "已到期"
- 未到期且正在履行 → "履行中"
- 合同尚未开始(起租日在未来)→ "未开始履行"
不能不看日期直接写"履行中"。
---
## K列:法律风险(站乙方立场)
### 写什么
- 整体评价(简要概括合同对乙方保护程度)
- 需注意的风险点(标条款号,说清楚对乙方的实际影响)
- 提前退租法律后果分析
### 不写什么
- 对乙方有利的条款(对客户无实际意义)
- 续约建议(只放第三部分整体段)
- 引用模版做对比(那是L列的事)
### K列证据纪律(Maggie 0703 纠正)
K列所有判断必须有合同文本直接支撑,不得超出文本能证明的范围做推论。
### K列"用途与实际经营不一致"风险模式(龙信0708)
当合同约定的租赁用途很具体且看上去可能与实际经营不一致时(如约定"新东方学习机"但校区实际做教育培训),K列应提示此风险:
- 引用条款链:用途限定条款(如第二条)+ 禁止改变用途条款(如4.8条)+ 甲方解除条款(如6.2(4)"擅自改变用途")
- 建议:确认甲方是否知情并认可实际经营内容
- 注意:此判断属于**K列**(风险评价),不属于I列(I列只客观摘录约定用途是什么)
### K列"合同内部矛盾"风险模式(0707实践总结)
合同内部条款之间的矛盾是独立的K列风险项。常见模式:
1. **付款规则矛盾**:文本一般规则 vs 表格具体约定(如"15天前"vs表格"1个月前")→对方可择利援引
2. **数值矛盾**:正文 vs 附加条款(如供电"120千瓦"vs"65千瓦")→补充条款效力条款决定优先
3. **甲乙方方向矛盾**:条文写"乙方支付至甲方账户"但收款账户是乙方自己的→模版套用瑕疵
4. **签约主体矛盾**:合同载明乙方名称与实际盖章公章不一致→可能影响效力认定
写法统一为:`N. XX条款矛盾/不一致(第X条):条文A写"...",但条文B/表格/盖章为"..."。[实务判断]。`
**典型错误**
- 合同中物业方联系人与出租方为同一人→直接写"实为关联方""X控制的Y公司"
- 从联系人身份推断股东/法定代表人/实际控制人身份
**正确写法**
- "出租方(范存益)同时为物业方(东德物业)的联系人,电话地址一致,两者**可能**存在关联关系,物业服务质量纠纷时需注意利益一致性。"
- Maggie 0703 审核确认此措辞:提示了关联可能性,但不做无依据的确定性推断
规则:
- "联系人"≠ 股东/法定代表人/实际控制人,不能从联系人身份推断控制关系
- 要证明关联关系需要工商信息,合同文本只能支撑"可能存在关联"
- 用语梯度:合同直接写明→"为";同名同电话同地址→"可能存在关联关系";纯推测→不写
- 同样适用于其他推断(如"实际经营培训"——合同写"办公"就只能说"需确认是否一致")
### 提前退租分析规则
- 合同有明确约定的(通知期+违约金+结算方式),**写约定内容+展开分析"其他损失"**。
- 合同没有明确约定的,回到法律规定做三层分析:
①有无法定解除权(民法典563条)
→ ②无法定事由则单方退租=违约(584条赔偿可预见损失)
→ ③损失构成
- 两种情况都要有依据,不写"甲方可能会主张""建议协商规避"这类没有锚点的推测。
#### 写作风格(Maggie 0708 纠正)
- **语言简单明了,只给条款号,不引用合同原文全文**
- ❌ 错误:引用完整合同原文再做分析("第五条3款:'租赁期间,乙方如需提前解租的,应当提前3个月书面告知甲方。在此情况下……'")
- ✅ 正确:直接写结论+条款号("规范提前解约(第五条3款):提前3个月书面告知 + 没收保证金56,667元 + 30%违约金102,000元,合计约158,667元。")
- 原则:读者是律师,不需要被教条文写了什么,只需要知道后果是什么+出处在哪
#### 区分"规范退出"与"擅自退租"(龙信0708实践)
同一份合同往往有两条退出路径,法律后果不同,必须分别列明:
- **规范提前解约条款**(如5.3条):乙方主动通知+约定违约金→后果确定,有上限
- **擅自退租/违约解除条款**(如4.15条):未经同意中途退出→后果更重,可能无上限("不足弥补损失另行赔偿")
两条不能混写。如果只写5.3条却把4.15条"不足弥补另赔""退还已预付未使用"的后果也算进去,属于张冠李戴。
**写法示例(龙信0708确认格式)**
```
【提前退租法律后果】
一、规范提前解约(第五条3款):提前3个月书面告知 + 没收保证金56,667元 + 30%违约金102,000元,合计约158,667元。
二、擅自退租(第四条15款):甲方可解除 + 没收保证金 + 30%违约金 + 不足弥补损失另行赔偿(无上限)+ 退还已预付未使用租金。
注:5.3条为规范退出,后果封顶;4.15条为擅自退租,后果更重且无上限。
```
#### "其他损失"必须展开分析(Maggie 0706 纠正)
合同写"给守约方造成其他损失,违约方还应进行相应赔偿"或"违约金不足弥补损失的据实赔偿"时,**不能只写违约金数字就停**,必须展开分析甲方可主张的损失范围:
**标准分析结构**
1. 合同约定路径(正常退出):通知期+同意+违约金
2. 甲方可主张的其他损失(逐项列举+估算金额):
- 空置期租金损失(重新招租合理期间,通常3-6个月)
- 重新招商费用(中介佣金、广告支出)
- 免租期租金追溯(如合同有此条款)
- 恢复原状/装修修复费用
- 逾期搬离违约金(如合同约定日租金倍数)
3. 最大风险敞口估算(各项相加=总数字)
4. 擅自退出后果(未获同意时的路径:没收保证金+据实索赔)
**必须有具体金额**——用合同约定的租金标准×期限算出每项的元数。"约XX元"即可,不求精确到分。
#### 同模板多合同须分别分析(Maggie 0706 指出)
同一甲方制式合同、条款结构一致但参数不同(免租期、租金、面积)时,**提前退租法律后果必须逐份单独计算**,不能写"同上"。原因:
- 免租期不同→追溯金额可能翻倍(如90天 vs 6个月)
- 租金基数不同→赔偿金和空置期损失金额不同
- 递增比例不同→后期退出的风险敞口差异更大
写法:1楼K列单独列出完整的提前退租分析(含数字),末尾加"⚠️与2楼对比"说明差异原因。
---
## L列:与07标准模版差异
- **原则**:纯客观描述文本差异,不做风险判断
- **格式**:模版写什么→本合同写什么→差异在哪
- **禁止出现的词**:风险、建议、不利、详见
- **合同原文含禁用词的处理(桃坞路0703教训)**:合同原文本身可能使用"风险"等禁用词(如"经营风险由乙方承担")。L列不可直接引用含禁用词的原文,需改写为客观事实描述。
-`证照风险全归乙方`("风险"触发kl-separation-check)
-`能否获得许可属乙方经营事项,甲方不承担`(客观描述分配结果,不含禁用词)
- 原则:用"甲方不承担/由乙方自行解决/乙方经营事项"等客观描述替代含"风险"的原文引用
- **来源**:必须从子任务生成的比对文件里逐条摘取
- **适用范围(Maggie 0703 纠正)**:**所有租赁合同都必须与07标准模版做对比,不论是否为新东方制式。** 跃龙路(甲方制式)做了55条差异,龙信(甲方制式)做了50条——正因为不是新东方制式,差异才更大、客户才更需要知道缺少了哪些保护。
- **非07制式合同**:L列写明"本合同为XX制式(非新东方制式),与07标准模版差异极大。逐条对比如下:",然后**全部差异逐条列出**。仅物业合同/补充协议可以不做07比对。
### 🔴 L列差异数量诚实原则(龙信0708教训)
**写了"N项差异"就必须列出全部N项,不能只列"主要差异"十几项。**
- ❌ "与07标准模版差异极大(40项差异)。主要差异:1.…2.…(只列18项)"——说40项只列18项=不诚实
- ✅ "与07标准模版差异极大。逐条对比如下:1.…2.…(全部40项逐条列出)"
原因:Maggie会数。写了总数就要能对上。要么全部列出,要么不写总数——不允许"只列主要差异"这种偷工减料。
逐条列出的格式:按07模版条款顺序,分章节标题分组:
```
【第一条·租赁标的】
1. 产权查验:07→XXX;本合同→XXX
2. 供电功率:07→XXX;本合同→无此条款
【第二条·用途与转租】
3. ...
```
---
## 第三部分"整体风险分析与建议"结构
按以下顺序排列(Maggie 0701 确认):
1. **【整体评价】** 几句话概括
2. **【法律关注点】** 逐条列风险(先于退租分析)
3. **【提前退租法律后果】** 按合同约定写
4. **【续签建议】** 具体改进建议(只放这里,不放K列)
---
## 多合同校区规则
### 核心原则:每份合同独占一行(Maggie 0703 明确)
**每份独立的合同文件必须单独一行,不得合并。** 租赁合同、变更协议、物业合同——各自提取、各自审阅、各自一行。
- Maggie原话(0703):"3份合同分别提取,分别审阅,做成三行,不要混在一起写"
- ❌ 错误做法:把租赁合同和主体变更协议合成一行写(即使两者关联密切)
- ✅ 正确做法:租赁合同一行 + 变更协议一行 + 物业合同一行,各有独立的板块标题和表头
### 板块结构
每种合同类型独立一个板块("一、房屋租赁合同""二、主体变更协议""三、物业管理服务合同"),每个板块有自己的段标题行+表头行+数据行。
### 其他排序规则
- **排序**:按签约时间排序,先签的在前
- **同制式合同多份**:详细的K列、L列、I列内容放最早签约的那份行里,后续行写"同上+数据差异"
- **多租赁物**:先按租赁物分类,再按时间排列
- **板块划分听Maggie指令**:Maggie说"按租赁物分类"→板块标题=租赁物描述;说"按原租赁和扩租分类"→板块标题=业务关系。不自作主张选分类方式。
### 多租赁物表结构(Maggie指示"按租赁物分类"时)
板块划分从"按合同类型"变为"按租赁物"。每个租赁物独立板块,内部租赁在前物业在后。
**世茂校区实例(2026-07-03验证通过)**
- 一、二层商铺(商铺编号XXX,968.28㎡)→ 租赁合同 + 物业合同
- 二、三层3023号商铺(347.2㎡)→ 租赁合同 + 物业合同
- 三、校区整体风险分析与建议
**桃坞路校区实例(2026-07-03验证通过,按租赁物分类+时间排序)**
- 一、桃坞服饰城中区201室、中区202室、C区部分房屋(889㎡)→ 租赁合同 + 物业合同
- 二、桃坞服饰城C区二层C-818室部分(80.21㎡)→ 租赁合同 + 物业合同
- 三、校区整体风险分析与建议
> 特征:同一出租方(国企制式)、同一模板,扩租行使用"条款结构与XX一致。差异:…"简写。
> KL分离检查:扩租行K列只补充额外风险,不重复原租赁K列已写内容。
### 按业务关系分类(Maggie指示"按原租赁/扩租分类"时)
板块划分从"物理租赁物"变为"业务关系"。
**悦拾光校区实例(2026-07-03验证通过)**
- 一、原租赁(C204、C205、C206)→ 租赁合同 + 能耗费三方协议
- 二、扩租(C213、C214、C217、C218、C219、C220)→ 租赁合同 + 能耗费三方协议
- 三、校区整体风险分析与建议
### 辅助协议行处理(能耗费/收款变更/补充协议等)
非租赁、非物业的辅助协议(如公共能耗费三方协议、收款账户变更协议):
- 独立一行,不与主合同合并
- K列:简洁写法("收款账户变更协议,风险低。关注点:N."),不用【整体评价·...】框架
- L列:`XX协议,无对应07标准模版。`(不做07比对)
- I列:协议性质+核心约定(如甲方单方解除权等)
- H列:写实质费用变更内容(如收款账户信息)
---
## 文件纪律(Maggie 0703 纠正)
### OCR文本保存规则(Maggie 0703 指令)
**提取并校对好的OCR文本(.md文件)必须保存在同一文件夹下**——即与源PDF同目录。
- 原租赁合同.pdf → 同目录下保存 原租赁合同_全文.md
- 扩租/扩租合同.pdf → 扩租/ 目录下保存 扩租合同_全文.md
- 上传到Nextcloud时用 `docker cp` + `files:scan`,与汇总表一起保存
这些.md文件是审查的工作底稿,客户可对照PDF原件核对。
### 旧版保留规则
**旧版汇总表不得擅自删除/覆盖。** 新版文件命名带新日期(如 `-MJ-20260703.xlsx`),与旧版共存。
规则:
- 生成新汇总表时,保留所有旧版文件原位不动
- 不需要用户明确指示"保留旧表"——默认就是保留
- 用户没有说删除的东西,一律不删
- Maggie原话:"没有让做的事情不要自己擅自进行"
这条规则同样适用于:md文件、pdf原件、任何已存在于Nextcloud的文件。只有cleanup cron和Doro明确说"pass"才允许删除。
---
## 不创设原则(Maggie 2026-07-03 明确)
严格按 workflow 和既定规则执行:
- 不发明新规则、不加额外步骤、不自创标准
- 不擅自做未被指示的操作(如删旧表、改文件名规则、自创格式约定等)
- Maggie原话:"严格按照Workflow和既定规则,不要创设哈"
- Maggie原话:"没有让做的事情不要自己擅自进行"
这包括但不限于:
- 不自行决定清理/删除/覆盖旧文件
- 不自行增加workflow中没有的检查步骤
- 不基于推断创设新规则(如从联系人推断控制关系)
- 不在K列创设合同文本不支撑的判断
---
## OCR铁律(凤凰文化0701 + 悦拾光0703 + 桃坞路0713教训)
**所有OCR文本必须全文vision逐页校对,不只是"关键数据"。**
### 🔴 全文准确性要求(桃坞路0713·Maggie纠正)
**Maggie原话**:「需要核实的不只是核心数据,所有的文字都要准确。比如租赁合同第二条的租赁用途是商业,不是教育培训,你这次有看到么?」
**问题模式**:只核对数字(金额、面积、日期)正确就认为"md准确度没问题",忽略文字内容。OCR会把关键文字变乱码而数字正确。
**典型失败案例**
- 租赁用途"商业"→`Bik`(乱码),数字全对但用途丢失
- 大写金额全错:`会 万武任 硅 佰 建 拾 制 元 伍 角` →实为"叁万贰仟肆佰肆拾捌元伍角"
- "崇川区"→"喧川区","中区201室"→"服4区201"
- 物业费空白→OCR瞎猜成"泣_/元/嘿月"
**正确做法**:逐页vision全文识别→重写md为完整准确版本。每页都要过vision,包括"标准条款"页面。详见`ocr-and-documents` skill的"Vision Full-Text Extraction Workflow"节。
**大批量校区(如金飞达88页)**:分session处理(每session 20-30页),图片预先全部转好(`pdftoppm -png -r 200`),新session无缝衔接。避免context degradation导致后半段质量下降。
不是"看哪里乱就修哪里"——用脚本扫全文自动标红,强制逐条过一遍。
跳过任何一处乱码就可能整段关键条款丢失(凤凰文化10.2:整段"初年年租金20%违约金"丢失→审查结论反转→返工)。
### 🔴 grep抽查 ≠ 逐字通读(悦拾光0703教训·Maggie纠正)
**Maggie原话**:「不只是关键条款,逐字逐句都要核准校对」「准确严谨是一切工作的基础」
**栽点**:OCR断行严重时,一个完整句子散落在3-5行,grep只能命中片段无法还原完整语义。
**典型失败案例**
- 30.3条"每逾期一日甲方有权按拖\n欠金额【3】%\n的标准...逾期支付\n超过【7】日的,甲方有权停止...水、\n电、燃气供应"→grep"停水"零结果→错写"逾期30日停水电"(30日是解除门槛不是停供门槛)
- 第36条"可向该房屋所在地人民法院\n起\n诉"→grep"管辖"零结果→错写"合同未明确约定管辖法院"
**铁律**
1. **必须线性逐页read_file全文**——不能用grep替代通读,grep只是辅助定位
2. **grep无结果 ≠ 合同无约定**——可能是OCR用了不同字词("法院"不含"管辖"、"停止供应"不含"停水")
3. **关键条款必须两份合同交叉核实**——同模版的原租赁和扩租OCR质量不同,一份断行严重的另一份可能完整
4. **争议解决/管辖条款通常在合同末尾(第35-37条区域)**——必须显式确认到具体法院,不可因grep零结果就写"未约定"
5. **违约金条款常含多个阈值**(逾期N日=停供、逾期M日=解除、逾期K日=违约金起算)——必须完整读完整条,区分不同阈值对应的不同后果
---
## 执行诚实性纪律(龙信0703教训)
`references/execution-honesty-0703.md`。核心:
- "看起来差不多/跟上个校区一样/只有2个月风险有限"不是跳过审查的理由
- 每份合同必须独立做八维框架审查(①主体 ②标的 ③期限 ④租金 ⑤违约 ⑥维修 ⑦转租 ⑧争议),不能从另一份合同复制结论
- L列模版比对适用于**所有租赁合同**,不分制式。仅物业合同/补充协议可以不比对
- 速度不是质量的对手——宁可告知"工作量大需要更多时间",不偷偷跳过假装做完
@@ -0,0 +1,50 @@
# Excel汇总表12列结构说明
> ✅ **权威口径(Maggie 2026-06-23 裁定):校区详情 sheet = 12 列,含独立 L 列「与标准模版差异」。** 模版差异(动作B,回 07 原件比对)放 L 列,法律风险(动作A)放 K 列,两者**物理分列**,绝不并入。本文件与 SKILL.md Step3「12列标准结构」、第363行、`independent-legal-review-framework.md`「分列」为同一口径,互为引用。
> 此前一度出现的「11 列、模版差异并入 K 列」旧表述已全部作废(SKILL.md 正文两处、原 Pitfall 19 已于 2026-06-23 同步更正)。
## 列定义
| 列 | 字段 | 宽度 | 内容说明 |
|---|---|---|---|
| A | 序号 | 5 | 校区内连续编号 |
| B | 文件名称 | 24 | 完整PDF文件名 |
| C | 合同类型 | 14 | 如"房屋租赁合同(主合同)""补充协议(租金减免)""物业管理协议" |
| D | 合同当事人 | 26 | 甲方/乙方/丙方全称,换行分隔 |
| E | 租赁标的/服务范围 | 22 | 具体房间号或服务范围 |
| F | 面积(㎡) | 10 | 数值+说明(如"622\n(建筑面积)") |
| G | 合同期限 | 20 | 总期限+起止日期+免租期 |
| H | 金额/费用 | 20 | 租金/物业费/押金明细 |
| I | 核心内容 | 40 | 关键商业和法律条款摘要(不放基础设施规格) |
| J | 当前状态 | 10 | "履行中""已履行""已执行" |
| K | 风险点/备注 | 40 | 风险提示 + 【合同变更与提前解除】小节 |
| L | 与标准模版差异 | 40 | 租赁合同对比结果;物业/补充协议标注"无对应标准模版" |
## 格式规范
### 字体
- 标题行(Row 1): 微软雅黑 14pt 加粗,居中
- 基本信息行(Row 2): 微软雅黑 10pt,左对齐
- 分类标题: 微软雅黑 11pt 加粗,左对齐,底色D6E4F0
- 列标题: 微软雅黑 10pt 加粗,居中,底色E2EFDA
- 数据行: 微软雅黑 10pt,左对齐上对齐,自动换行
### 行高估算
- 标题/分类标题: 30pt
- 列标题: 30pt
- 数据行: 根据内容,K/L列内容多的需要300-400pt
- 风险分析区: 600-750pt(合并单元格必须手动设高度)
### K列【合同变更与提前解除】标准内容
1. 提前解约通知期和违约金
2. 押金退还条件
3. 甲方终止时的赔偿义务
4. 免责解除通道
5. 部分退租先例(如有)
6. 合同变更方式
### L列差异标注分级
- ⚠️ 开头 = 多处重大偏离
- ✅ 开头 = 高度一致
- 编号列举具体差异点
- 物业/补充协议: "物业服务协议,无对应标准模版" 或 "补充协议,非模版对比范围"
@@ -0,0 +1,72 @@
# 统一社会信用代码 OCR 核实配方
## 适用场景
合同首页甲乙方统一社会信用代码 OCR 乱码(如 "MA INAEBX26" 应为 "MA1NAEBX26"),需要核实正确值。
## 核实步骤
### 1. tesseract 重读首页
```bash
# 渲染首页 200 DPI
python3 -c "
import fitz
doc = fitz.open('合同.pdf')
page = doc[0]
mat = fitz.Matrix(200/72, 200/72)
pix = page.get_pixmap(matrix=mat)
pix.save('page1.png')
"
# 裁剪公司名+信用代码区域(通常页面 15-30% 高度)
python3 -c "
from PIL import Image
img = Image.open('page1.png')
w, h = img.size
crop = img.crop((0, int(h*0.15), w, int(h*0.30)))
crop.save('page1_credit.jpg', 'JPEG', quality=75)
"
# tesseract 读取
tesseract page1_credit.jpg stdout -l chi_sim+eng --psm 6
```
### 2. 企查查 web 搜索交叉验证
```
web_search: "公司全称" 统一社会信用代码 企查查
```
企查查结果通常直接显示完整 18 位信用代码。
### 3. 校验位验证(Python)
统一社会信用代码第 18 位是校验位,可用以下脚本验证:
```python
def verify_credit_code(code):
if len(code) != 18:
return False
weights = [1,3,9,27,19,26,16,17,20,29,25,13,8,24,10,30,28]
chars = '0123456789ABCDEFGHJKLMNPQRTUWXY'
code_map = {c:i for i,c in enumerate(chars)}
total = sum(code_map[code[i]] * weights[i] for i in range(17))
check = 31 - (total % 31)
if check == 31: check = 0
expected = chars[check] if check < len(chars) else '?'
return expected == code[17], expected, code[17]
```
## OCR 常见误识模式
- `MA1NAEBX26``MA INAEBX26`(数字0被识别为空格)
- `MADQRFT66P``MADQRFT66P`(通常正确,但需验证末位校验位)
- 数字 `0` 和字母 `O` 混淆
- 数字 `1` 和字母 `I` 混淆
## 判据
- tesseract 重读结果与企查查一致 → 采用
- tesseract 重读结果与企查查不一致 → 以企查查为准(企查查是权威工商数据源)
- 校验位验证不通过 → OCR 有误,回企查查取正确值
## 与 OCR 完整性检查脚本的关系
`ocr-integrity-check.py` 已修复:信用代码中的字母序列(前后有数字的)不再误报为"公司名乱码"。修复方式:检测字母序列前后是否为数字,如是则跳过。
## 2026-06-29 实证
- 解放中路:甲方 "91320600MA1NAEBX26"(企查查确认)
- 通大附:甲方 "91320600MA1NAEBX26"(企查查确认)
- 通大附:乙方 "91320602MADQRFT66P"(校验位 P 验证通过)
@@ -0,0 +1,45 @@
# DeepSeek-OCR 主链路与非交互 session 取 key 规则(2026-07-13)
## 适用场景
- 批量扫描件合同梳理
- 需要把 PDF / 图片先转成 Markdown 再审查/比对/回填汇总表
- 当前环境已把 `SILICONFLOW_API_KEY` 写在 `~/.bashrc`,但 agent 所在 session 不是交互式 shell
## 本次会话固化结论
1. **默认 OCR 主链路改为 DeepSeek-OCR**,脚本:`~/.hermes/scripts/deepseek_ocr.py`
2. **不要默认回到 marker-pdf / surya 本地链路**;本地 OCR 只作显式要求下的备选/对照,不是默认方案。
3. 批量扫描件建议流程:
- 先批量跑 `deepseek_ocr.py` 生成 md
- 再做 grep / 规则扫描,筛出乱码、过短、金额大小写冲突、主体名称异常、日期异常
- 最后只对异常页做 vision 定点校对
4. `deepseek_ocr.py` 已修成:
- 先读当前进程环境变量 `SILICONFLOW_API_KEY`
- 若当前 session 没带 key,则回退到 `bash -ic``~/.bashrc` 读取
- 读取成功后回填到当前进程环境
- 两边都没有才报错:`Missing SILICONFLOW_API_KEY in environment or ~/.bashrc`
## 为什么要这样做
非交互 shell 默认不加载 `~/.bashrc`。如果只在脚本里 `os.environ.get("SILICONFLOW_API_KEY")`,则新 session/批量 terminal 调用会误报“缺 key”,但实际上 `.bashrc` 里已经有 key。
## 执行口径
- 单文件:
```bash
python3 ~/.hermes/scripts/deepseek_ocr.py /path/to/file.pdf -o /path/to/file.md
```
- 批量:按合同目录遍历 `.pdf`,逐个输出同名 `.md`
- 批量产物出来后,优先筛这几类异常:
- 结果过短(只剩标题/前几行)
- 金额小写与大写不一致
- 主体名称错字/缺字
- 日期区间逻辑不通
- 银行账户等长数字串
## 已验证案例
- 北翼玖玖 17 份 PDF:已全量跑完 DeepSeek-OCR 输出 md
- 发现的典型异常:
- 某些物业合同只识别出标题,正文漏识别
- 押金小写金额与大写金额明显冲突
- 主体名称 OCR 错字(如“崇区川”)
## 给后续 agent 的一句话
**扫描件合同默认先跑 DeepSeek-OCR,再筛异常,再 vision 定点校对;不要一上来就切回本地 OCR。**
@@ -0,0 +1,64 @@
# DeepSeek OCR 默认主链路(2026-07-13)
## 结论
当任务目标是**把扫描件 / PDF 稳定识别出来**,默认主链路使用:
- `~/.hermes/scripts/deepseek_ocr.py`
- provider / API: **SiliconFlow**
- model: `deepseek-ai/DeepSeek-OCR`
- env var: `SILICONFLOW_API_KEY`
不默认回退到本地 `marker-pdf / surya`。只有在以下情形才改走本地链路:
1. 用户明确要求离线 / 本地 OCR;
2. DeepSeek OCR 存在不可回避的外部约束(如目标环境无法提供 API key 或必须完全脱网);
3. 任务本身不是“先把内容识别出来”,而是明确在做本地 OCR 工具验证 / 对比实验。
## 本次会话得到的稳定经验
### 1. 先定路线,再谈环境
本次一开始把“当前 Python 环境被 marker-pdf 安装污染”讲得太重,容易把讨论带到本地 OCR 包冲突上;但用户真正要的是:
> 能用 DeepSeek OCR 就直接用,不要默认折回本地方案。
所以遇到 OCR 任务,先回答:
- 目标是**稳定抽取内容**,还是**验证本地 OCR 工具**?
- 如果是前者,先走 DeepSeek OCR 主链路。
### 2. 验证必须是真跑,不是口头判断
本次实际验证命令:
```bash
bash -ic 'python3 ~/.hermes/scripts/deepseek_ocr.py "/home/maggie/contract-review/其他任务/ht_page-1.png" -o /tmp/deepseek_ocr_final.md'
```
成功返回:
- 输入/输出 token 计数
- Markdown 文件落地
- 读回输出文件后可见 OCR 表格内容
因此以后汇报“可用”时,必须至少给出:
1. 实际命令;
2. 实际返回;
3. 输出文件或结果句柄。
### 3. `.bashrc` 中的 key 只对交互式 bash 自动生效
本次确认:
- `SILICONFLOW_API_KEY` 写在 `~/.bashrc`
- 非交互 shell 直接执行时,脚本可能读不到该变量
- `bash -ic '...'` 能加载 `.bashrc`,因此验证通过
因此如果 workflow / 子进程依赖这条 OCR 链路,必须显式保证环境变量可见;不要因为一次 `bash -c` 读不到 key,就误判 DeepSeek OCR 不可用。
### 4. 脚本不得偷偷依赖硬编码 fallback key
本次还暴露出:`deepseek_ocr.py` 曾带硬编码 fallback key。正确做法是:
- 只从 `SILICONFLOW_API_KEY` 读取;
- 没有 key 就直接报错退出;
- 避免“看起来可用,其实在吃脚本里藏着的 key”这种假成功。
## 汇报口径
当用户指定“以后都用这个方案”时,回复应直接、简洁:
- 已切换默认 OCR 方案为 DeepSeek OCR;
- 已确认环境变量位置;
- 已实际跑通;
- 本地 marker/surya 不再作为默认主方案。
不要再把话题绕回“当前主对话模型是不是 DeepSeek”。聊天模型链路 ≠ OCR 链路。
@@ -0,0 +1,31 @@
# 帝奥地产格式合同 — 与07模版核心差异速查(金飞达实证 2026-06-27)
> **适用校区**:金飞达(所有合同均使用帝奥地产格式,与07模版结构完全不同)
> **比对基准**:07-房屋租赁合同.docx(15条+附加条款)
> **帝奥格式**:20条,结构完全重构,偏向保护出租方
## 缺失的07模版核心条款(帝奥格式均无)
1. **第七条 出租方变更**:无通知义务,无新业主继续有效
2. **第九条 优先购买权**:乙方明确放弃(第11.2条)
3. **第十一条 不可抗力**:无疫情/行业治理/政策变更减免权
4. **第十二条第4款 办学许可证**:无无法办证退出机制
5. **第十四条第4款 租赁备案义务**:无
6. **第十四条第5款 竞业限制**:无
7. **附加条款**:无装修配合/标识广告/配套设备
## 帝奥格式特有不利条款
| 条款 | 内容 | 风险等级 |
|---|---|---|
| 第10条 房屋返还 | 恢复原状/逾期7日视为放弃物品/停水停电强制措施 | 🔴 |
| 第11.2条 | 乙方放弃优先购买权 | 🔴 |
| 第11.3条 | 甲方转让后不再承担任何责任 | 🔴 |
| 第14条 免责 | 甲方全面免责(自然灾害/盗窃/设施故障/水电中断) | 🔴 |
| 第13.5条 逾期违约金 | 每日万分之五(07模版0.1‰的5倍) | 🟡 |
| 第13.2条 营业执照注销 | 每日月租金10% | 🔴 |
| 第19.5.1条 租金保密 | 泄密恢复原价(主合同120万/年,现价3.1倍) | 🔴 |
## 合同捆绑关系
金飞达777+633+499三份合同通过补充协议形成连锁捆绑:任一份出问题牵连全部。
@@ -0,0 +1,35 @@
# 执行诚实性纪律(龙信 2026-07-03 教训)
## 起因
Maggie 发现龙信三份合同的首次梳理没有按 workflow 逐字逐句执行,直接质问"你是自己逐字逐句审阅的么?",要求重做。
## 教训
1. **"看起来差不多/跟上个校区一样/只有2个月风险有限"不是跳过审查的理由**
- 海门临时合同虽仅2个月且已到期,但仍需完整做八维框架审查
- 每份合同独立做,不能从另一份合同复制结论
2. **L列模版比对适用于所有租赁合同,不分制式**
- 龙信广场(甲方制式)→做了40条差异
- 海门临时(甲方制式)→做了32条差异
- 正因为不是新东方制式,差异才更大,客户才更需要知道
3. **速度不是质量的对手**
- 宁可告知"工作量大需要更多时间",不偷偷跳过假装做完
- Maggie原话:"你不按照规则作出的东西很明显质量不行的,务必按照Workflow和规则执行,不能自己发挥"
## 行排序规则补充
默认按签约时间排序(column-rules-0701.md),但 **Maggie明确指定行顺序时以指定为准**
- 龙信0703:Maggie指定"租赁和物业放前两行,海门临时放第三行"→覆盖默认排序
- 原则:用户明确指令 > 默认规则
## K列"模版"禁词
kl-separation-check.py 对 K列 grep "模版|07模版" 是0容忍——即使上下文是"出租方制式模版"这种描述性用法也会触发。
**解法**:K列一律用"制式合同"或"制式文本"替代"制式模版"。
- ❌ "本合同为甲方制式模版"
- ✅ "本合同为甲方制式合同"
- ✅ "条款与龙信广场租赁合同几乎一致(出自同一出租方制式文本)"
@@ -0,0 +1,29 @@
# 文件盘点与归类规则(校区/项目第一次出现时建立)
> Maggie 2026-06-16 确立。**第一次接触某校区(或某新主体),先把文件家底摸清、按规则排好;此后一切变动都照此归位。** 这是台账可追溯的地基。
## 第一步 · 盘点
看校区文件夹里**有哪些文件、如何排列**,逐一核实确认。不要急着分析——先确认清单完整、文件可读(OCR 是否成功、有无缺失主合同/缺页)。空目录或缺失主合同要标注(如悦拾光缺一期主合同、跃龙路/通州金鹰缺租赁合同只有物业合同)。
## 第二步 · 按固定规则归类排序
```
校区文件夹
├─ 一、租赁合同
│ ① 按【租赁场所 / 位置】分类(如 原租/4幢、扩租/507室、不同铺位)
│ ② 每个场所内 按【签约时间】排序:
│ 主合同 → 补充协议1 → 补充协议2 → 变更协议 → 解除协议
└─ 二、物业合同(归类规则与租赁合同【完全相同】)
① 按【场所 / 位置】分类 ② 每个场所内按【签约时间】排序
```
> ⚠️ **物业合同的归类规则与租赁合同一模一样**(场所→时间),**不是"简化归类"**。
> **物业合同同样逐条审查、不挑不跳**(与 SKILL.md「每份合同逐条审查」铁律一致)——物业合同的特点只是**风险点天然少、且无对应标准模版**(L列标注"物业服务协议,无对应标准模版"),**绝不等于"简化审查"或"挑重点审"**。审查深度不打折,覆盖面不缩水;归类方式更不简化。
> **一般一份租赁合同对应一份物业合同(一对一、成对存在)**——盘点时按场所核对配对关系:每个租赁场所应有对应的物业合同,缺失的要标注(如跃龙路/通州金鹰只有物业、缺租赁合同;反之亦然)。
## ★ 铁律(贯穿全流程)
- 后续 **新签 / 变更 / 解除** 的合同,**一律按此规则归位**——不是堆到末尾,而是插进对应场所、对应时间位置。
- 规则贯穿 **生成 · 邮件 · 网盘** 全流程,前后一致。
- **不跨文件夹重新归类**:客户的文件夹结构(房租/扩租/物业)就是 sheet 的板块划分依据,即使物业合同放在"扩租"文件夹里,也归到"扩租系列"板块,不按合同性质重排(Pitfall #14,Maggie 20260609 已纠正过一次)。
## 与汇总表 sheet 板块的对应
校区 sheet 的板块("一、原租赁系列""二、扩租系列""三、物业"等)直接映射这个归类层级。盘点归类做对了,填表板块自然就对。
@@ -0,0 +1,49 @@
# 通读强制纪律(悦拾光0703教训)
## 事件
悦拾光校区审查中,Step2动作A"法律审查"实际做法是:
- grep关键词定位 → 读周围片段 → 参考旧版xlsx填补
这不是"逐字逐句综合全文理解判断"。证据:
- 第36条争议解决写得清清楚楚"该房屋所在地人民法院",但grep没命中"管辖"二字就写了"未明确约定"
- 30.3条"超7日停水停电"散在多行(OCR断行),只读到"每日3%"就停了
- 14.2条甲方维修影响使用应减租——通读时本应纳入
- 26.2/26.5/27.5/8.3/25.5/25.6等条款——如果真通读了不可能漏
## 根因
grep只能命中精确关键词。合同OCR断行严重,一个完整句子散落多行,grep无法还原语义。
旧表印象≠独立审查。参考旧表填补本质上是复制粘贴不是审查。
## 纪律(三层保障)
### 第一层:通读强制
Step2动作A必须用read_file从第1行读到最后一行(每批500行),不得用grep代替通读。
grep只用于**二次定位验证**(如确认某条款的确切行号),不用于替代阅读。
### 第二层:覆盖计数自检
I列写完后检查条款标签数:
- 租赁合同≥15/21类目(有就写没有不写,但必须逐一确认过)
- 物业合同≥10/15类目
不够就跑 `scripts/i-column-coverage-check.py` 报警核查。
### 第三层:争议解决+管辖必检
每份合同I列写完后,手动确认:
- "管辖"类目是否填写?
- 填写内容是否与合同末尾条款原文一致?
如果I列"管辖"为空或含"未约定"→必须回原文最后5页重新确认。
这是悦拾光教训的直接产物。
## Maggie原话
> "不只是关键条款,逐字逐句都要核准校对"
> "对应的法律审查和风险分析你是逐字逐句综合全文理解判断的么?"
> "准确严谨是一切工作的基础"
> "你还是要设计一个机制,让你不会偷懒"
## 自检信号
- I列条款标签数 < 合同章节数×1.5 = 没通读
- "争议解决"写错/写漏 = "逐字逐句"没做到的铁证
- K列风险点与旧版雷同但条款号缺失 = 复制旧表没独立审查
@@ -0,0 +1,32 @@
# H列付款推算——跨费率期计算陷阱(2026-07-08世茂教训)
## 问题根因
合同有多个费率期(如2025.12-2027.2按费率A,2027.3-2029.2按费率B),但付款周期(结算周期12个月)与费率变更日不对齐。
某一付款期可能横跨两个费率期。必须分段计算。
## 错误做法(世茂第3/4期)
```
第3期(2027.5.1-2028.4.30)= 33,280.59 × 12 = 399,367
```
→ 全部按第1费率计算,少算约12,000元
## 正确做法
1. 先确定每个付款期的起止日
2. 看该期内是否跨越费率变更日
3. 如果跨越,分段:变更日前×旧费率 + 变更日后×新费率
4. 如果不跨越,直接用当期费率×月数
## 第2费率反推方法
当合同只给了第1费率和第2期总金额时:
```
第2期总金额 = 第1费率×N个月 + 第2费率×M个月
第2费率 = (第2期总金额 - 第1费率×N) / M
```
验证:不含税×(1+税率)=含税(必须算术闭合)
## 自检清单
- [ ] 每期付款金额是否与当期适用费率一致?
- [ ] 费率变更日是否在某个付款期中间?如果是,是否分段计算了?
- [ ] 最后一期是否不满整个结算周期?月数是否正确?
- [ ] 含税=不含税×(1+税率) 算术是否闭合?
@@ -0,0 +1,39 @@
# H列付款推算规则 (0703)
## 月租换算写法
- **铁律**:月租换算一律用行内括号写法,写在租金标准行内
- 格式:`年XX元(月租≈XX元)`
- **不允许**单独另起一行写月租换算
- 参照标准:通大校区写法
- 来源:Maggie 2026-07-03 明确指示:"以后的都按通大的写法"
## 付款推算从汇总表直接计算(高效方法)
- **不需要每次回md原文**,H列已有支付方式+租金标准+递增规则+物业费,G列有起止日期+免租期
- 流程:
1. G列取起止日期+免租期
2. H列取租金/期、物业费/月、支付方式(半年一付/季付等)
3. 按支付周期切分区间 → 算出每期金额+付款截止日
- 仅当数据存疑(如附件分期跟正文对不上)才回md核实
- OCR文本有识别噪音,能用汇总表数据就不翻原文
## 付款推算写入格式
```
【付款推算】租金+物业费合并列示,半年一付,提前30天(第X条第X款)
 第1期(起止日期):租金XX + 物业XX = XX元 ┃ 签约后7天内
 第2期(起止日期):租金XX + 物业XX = XX元 ┃ 付款期限YYYY.M.D
 ...
 ❗第N期(起止日期,递增后):租金XX + 物业XX = XX元 ┃ 付款期限YYYY.M.D
```
### 格式要点
- 租金和物业费合并列示,每期分别标出两项金额及合计
- ❗标注尚未到期的付款期次(以今天日期判断)
- 付款期限 = 期次起始日 - 提前天数
- 首期特殊处理(签约后X天内)
- 含免租期的期次标注"含免租"
- 递增后的期次标注"递增后"
## 数据验证
- 写入前必须验算:各期租金合计 ≈ 合同总租金
- 允许四舍五入1元以内差异
- 物业费 = 单价×面积×月数,逐期核对
@@ -0,0 +1,34 @@
# 幻觉防火墙(2026-07-02 星月校区教训)
## 事件
星月校区汇总表交付后被Maggie发现:
- E列(租赁标的/用途)写了"办公/教育培训/教学相关经营"——但合同原文只约定了"办公"
- E列地址只写"南通星月智创园2楼"——合同原文完整地址是"南通市崇川区新胜路6号星月智创园2幢2F"
- Maggie判定:整个校区审查不严谨,要求重做
## 根因
**没有真正逐字读合同就开始填表。** 看到"南通新东方教育"就用行业常识推断用途是"教育培训"——这是幻觉,不是笔误。如果逐字读了第二条用途条款,不可能写出合同里不存在的内容。
## 铁律
**E列和I列的每个事实必须能在OCR全文中ctrl+F搜到对应原文。搜不到=编的=必须改。**
具体:
1. E列"用途":grep合同"用途"/"经营范围"条款原字填写,不从租户名/行业推断
2. E列"地址/标的":从第一条"租赁标的物"条款完整抄录(含区/路/号/幢/楼层)
3. I列"核心内容":每条前面的[条款号]必须真实存在于合同中
4. 不需要加括号备注"合同原文仅约定XX"——客户不需要知道你的工作过程
## 自检
填完E/I列后,对每份合同做:
```
grep -c "你写的关键词" 合同OCR全文.md
```
返回0 = 你编的 = 停下来回原文找正确表述
## 后果
一个字段的幻觉 → Maggie判定整表不可信 → 整个校区重做。成本是原来的2倍+信任损失。
@@ -0,0 +1,42 @@
# 幻觉事件:星月校区用途字段编造(2026-07-02)
## 事件
星月校区两份租赁合同原文:
- 1楼:`所租赁房屋仅作为办公用途使用`
- 2楼:`所租赁房屋仅作为办公用途使用`
汇总表E列和I列填写为:`办公/教育培训/教学相关经营`
Maggie发现后质问:"星月的租赁用途,哪里体现了教学配合和教育相关经营?""貌似你出现了幻觉"
## 根因
不是OCR乱码问题——原文清楚写了"办公"。失败模式是**从租户名称推断合同内容**:
- 看到"南通新东方教育科技有限公司"→大脑自动补全"所以用途是教育培训"
- 没有回到合同第二条原文逐字核对就填了表
## 铁律(补充进地基四铁律·逐字逐句)
**E列(租赁标的/用途)和I列(核心内容)的每一个事实,必须对应到合同原文具体条款的具体文字。**
禁止路径:
- ❌ 从公司名称推断经营用途
- ❌ 从"新东方=教培"的常识推断合同约定
- ❌ 从其他校区合同的约定类推本校区
- ❌ 任何"应该是""大概率是""结合实际"的脑补
正确路径:
- ✅ grep "用途" → 定位条款 → 逐字抄写原文
- ✅ 原文空白/未填写 → 如实标注"合同未明确约定"
- ✅ 原文与实际不符 → K列标为风险点,不改E/I列的事实记录
## 自检口诀
填E/I列每一格时问自己:**"这个字在合同哪一页哪一条?"**——答不上来就不能写。
## 修正内容
1. E列:改为"用途:办公(合同原文仅约定办公)"
2. I列:改为"仅限办公用途(合同原文)"
3. K列:新增🔴风险点——约定"办公"与实际教育培训可能不一致,触发擅自改变用途解除权
@@ -0,0 +1,103 @@
# I列核心内容标准(Maggie 2026-07-03 确认)
## 原则
- 按固定类目逐项提炼,有就写没有就不写
- 格式:`· [类目] 关键事实(条款号)`
- 精简干练——用最少的字传达最准确的信息
- **只提取总结,不评论**——I列的职责是客观提取合同条款内容,不做风险评价、不加建议、不写"需注意"。评价属于K列(Maggie 2026-07-08纠正)
- 同模板多合同:后续行写"条款结构与XX一致。差异:·面积 ·租期 ·保证金"
- **只提取总结,不评论**:I列职责是客观提炼条款内容,不做风险评价、不给建议、不加⚠️评论。评价性内容("需确认是否一致""建议补充书面确认")属于K列法律风险分析的职责。I列看到什么写什么,判断留给K列。
## 租赁合同 21 类目
1. 用途
2. 转租
3. 装修改造
4. 广告标识
5. 非竞争
6. 维修责任
7. 保险要求
8. 物业服务联动
9. 配套设施
10. 出租方变更
11. 解除权机制
12. 违约金机制
13. 不可抗力
14. 征收拆迁
15. 房屋抵押查封
16. 政策变化
17. 到期处理
18. 恢复原状
19. 优先权
20. 管辖
21. 备案
## 物业合同 15 类目
1. 物业服务内容
2. 服务标准
3. 公共能耗费
4. 特约服务
5. 共用设施管理
6. 装修管理
7. 安保措施
8. 消防安全
9. 保险要求
10. 联动终止
11. 违约责任
12. 退出交接
13. 免责条款
14. 不可抗力
15. 管辖
## 精简对比示例
❌ 冗余:
```
· [转租] 未经甲方书面同意,乙方对于承租房屋不得以任何形式转租、转让、转借、抵押或其他有损甲方利益的行为(4.5条、第二条)
```
✅ 精简:
```
· [转租] 未经书面同意不得转租/转让/转借/抵押(4.5条)
```
## ❌ 评论混入示例(龙信0708教训)
```
【用途】新东方学习机;增加用途或部分转租须甲方同意(第二条)
⚠️ 合同限定"新东方学习机",如实际用途为教育培训/托管,需与甲方确认是否一致...
```
→ 第二行是风险评价,属于K列。I列只写第一行。
## 关键输出纪律(Maggie 2026-07-08 确认)
1. **I列只提取总结不评论** — 核心条款提炼是事实摘录,风险评论属于K列
2. **K列提前退租分析** — 简明扼要只标条款号,不引用合同原文全文
3. **合同字段"/"=不适用** — 该事项不再提及(如营业额提成为/,H/I/K/L均不写)
4. **L列差异数必须全列** — 禁止写"X项差异"只列其中一部分;要么全列要么不写总数
5. **H列每份合同完整** — 禁止"同上""详见附件",必须写明具体金额+完整付款推算
6. **整体评价含总费用** — 第三部分校区整体评价必须包含合同期内租金+物业费总费用汇总
## 覆盖检查
建表后跑 `scripts/i-column-coverage-check.py`
- 租赁合同达标阈值:15/21
- 物业合同达标阈值:10/15
- 报警≠阻断——有些合同确实没这么多类目,核查确认"是真没有还是漏了"即可
## 通读强制机制(悦拾光 0703 教训)
### 根因
之前做法=grep关键词→读片段→参考旧表填补。本质是关键词抽查+旧表印象,不是独立审查。
后果=争议解决"房屋所在地法院"写成"未明确约定"、逾期停水电门槛7日写成30日——错得很基础。
### 强制规则
Step2动作A法律审查必须用read_file从第1行读到最后一行(分批500行/次),**不得用grep抽查代替通读**。
### 自检信号
- I列类目覆盖数≥阈值
- 争议解决条款有条款号→证明读到了最后几条
- 每个违约金数字都有条款号→证明逐条读了违约章节
### 避免偷懒的三重保障
| 层 | 位置 | 何时触发 |
|---|---|---|
| 规则文档 | column-rules-0701.md | 加载skill时 |
| 模板代码 | builder.py注释里列21+15清单 | 建表时 |
| 检查脚本 | i-column-coverage-check.py | 建完表跑一次 |
@@ -0,0 +1,36 @@
# 增量维护 SOP(新增 / 到期 / 提前解除)
> 汇总表是**持续维护的法律台账**,不是一次性交付。每次变动都走三角色分工(承办—校对—终审),保证隔多久、哪次对话都输出同一套标准。
## 通用前置(所有变动,缺一不可)
1. **绝不基于旧本地副本改**——先从 Nextcloud 拉最新版 xlsx(见 SKILL.md「跨Session续做铁律」)
2. 打开表确认实际现状:改哪个校区、哪一行、当前值是什么
3. 新版本按日期递增命名:`南通新东方-租赁合同汇总表-YYYYMMDD.xlsx`
4. 登记 todo,变动完成前不脱手
## 🟢 新增合同(新签 / 扩租 / 补充协议)
1. **承办层**
- 提取分析员:OCR → 要素提取 → 模版比对(动作B) → 退租敞口 → 提取依据清单
- 法律审查员(独立 subagent):八维全面法律审查(动作A) → 法律风险清单
2. **填表**:定位校区 sheet → 按**文件夹板块**插行(房租/扩租/物业,不跨板块重新归类)→ 同步更新总览对应行(承租主体、面积、租金、风险点等)
3. **校对层**:法律校对(维度1-4)‖ 格式校对(维度5-6:字段齐备、总览-分表一致、面积/金额加总)
4. **终审**:小Maggie 汇总闭环 → 上传 → 清缓存 → 交付 Maggie
## 🟡 合同到期
1. 该合同行"当前状态" → "已到期(YYYY-MM-DD)";总览同步
2. **整校区退出**:历史行**保留不删除**(台账须可追溯),校区状态标注"已退出"
3. **部分到期**(如某铺位到期、其余续租):只改到期那行,面积/租金加总相应调整
4. 一般无新法律内容 → **可跳过法律审查/法律校对**,走格式校对(状态与加总一致性)+ 终审
5. ⚠️ 若到期伴随续租新合同 → 续租合同按🟢新增流程全程审查
## 🔴 提前解除
1. "当前状态" → "已解除(YYYY-MM-DD)"
2. **退租敞口 → 实际结算结果**:把当初测算的预估敞口,替换为实际发生的结算(实付违约金、押金处理、已退未用租金、恢复原状费用等)
3. **法律校对**:核结算结果与解除协议/和解文件一致;若有争议或诉讼,标注状态
4. 格式校对 + 终审
## 变更留痕(所有变动统一收口)
- 表内保留**变更记录**(日期 + 事由 + 改动内容 + 经办),便于审计回溯
- 改后三项校验:①总览与分表数据一致 ②面积/金额加总对得上 ③风险点已同步
- 上传 Nextcloud + `files:scan` + 清 OnlyOffice 缓存并重启(见 SKILL.md Step 5)
- 终审通过后交付 Maggie,附变更说明(改了什么、为什么、影响哪些数)
@@ -0,0 +1,103 @@
# 独立法律审查框架(动作A)
> **定位**:把每一份合同当作一份**新合同**,站在乙方(承租方)立场,做全面法律风险审查。这是判断"合同本身有没有法律风险"的**唯一依据**。
> **与模版比对的区别**:模版比对(动作B)只回答"与内部合规要求差多少",是中性合规差距,**不能**作为法律风险判断。详见 SKILL.md「铁律:模版差异 ≠ 法律风险」。
> **执行者**:独立 subagent(与提取分析员分离),不看承办思路,回到合同原文独立判断。
## ⚠️ 第0步·审查第一性原则:完整通读全文,准确把握每一条款的上下文语境与语义(2026-06-18 世茂14.x教训确立,最上位主规则)
**这不是某类高危条款的特殊要求,是法律审查的地基。** 适用对象是**整个合同文件和每一个条款**,不是只针对违约条款——任何一条都可能因没读全、没读懂语境而误判。
**两层要求,缺一不可:**
1. **完整**——`read_file` 把整篇合同 OCR 文本(.md)一次性通读完,建立全文整体理解,再逐条审。不先通读,不开审。
2. **准确**——通读不等于把字扫一遍。要**完整准确地把握每个条款的上下文语境与语义**:这条款在合同结构中的位置、与其他条款的勾连(被哪条限定/为哪条做前提/被附件或补充协议修改)、它在合同语境下的**真实语义**(不是字面,是这句话实际指什么)。
> 顺序铁律:**通读全文建立整体理解 → 在整体语境下逐条精读 → 再下结论**。跳过整体理解直接抽条款,必然丢语境。世茂14.2 我是"字看到了、语义没读懂"——"除…外"那层意思没整合进来,就是只做了"扫字"没做到"把握语义"。
### 为什么是铁律(拆穿"读不完"的借口)
- 世茂14.2教训的根因诊断:我把"逾期付款违约金千分之2"漏了基数、把14.2"除租赁保证金不予退还冲抵违约金外,还应…,不足补足"的**三层叠加只摘了中间一截**。当时我归因为"PDF 11MB 太大不能通读,只能 grep 抽取"——**这是错的,是给"命中即停"找的借口**。
- **真相(已实测)**:那 11MB 只是**PDF 扫描图像**的体积;法律审查读的是 **OCR 后的 .md 文本,只有 ~58KB、580行、22960字符**,远在单次 read_file 能力内(上限约10万字符)。一份合同 .md 通常 20–60KB,**几次 read_file 就整篇读完**。输入从来不大,是我没读。
- 那句完整的14.2原文**就在 grep 命中的同一行的句首**——"保证金不予退还"那层不需要跨条整合,它就在命中行开头,我却只看了命中点中段。这证明失败点不是"跨条没整合",而是"连命中那一句都没从头读到尾"。
### 协议(介质无关,Word/PDF 通用)
1. **审查第一步:完整通读全文**`read_file` 读整篇 .md(580行的合同分2次读、每次约300行即可),形成全文结构理解,再逐条提取。**不先通读,不开审。**
2. **逐条精读时把握语境语义**:每读一条,问三件事——①这条在合同结构里管什么?②有没有被别处(其他条款/附件/补充协议)限定、修改、设前提?③它的真实语义是什么(整句从头读到尾,不摘单句、不停在命中点)?
3. **grep/检索降级为辅助坐标工具**:只在"已通读、回头定位某条具体位置"时用。**严禁拿 grep 命中的单行当审查依据**——命中行只是"这儿有东西,回去从该条编号读起"的路标。
4. **检索定位后必须回读整条款**:grep 命中 `14.2` → 光标退回 `14.2` 条编号 → 从头读到下一个编号 `14.3` 出现为止 → 整条读完、读懂语义再提取。把"命中即停"改成"命中即回读整条"。
5. **删除错误认知**:"PDF大所以不能通读"是伪命题——审查读的是 KB 级 OCR 文本,不是 MB 级 PDF。文件体积永远不是跳过通读的理由。
### 高发区加重提示(PDF/OCR)
- OCR 把有版式的合同压成**线性长文本**,条款的视觉层次(编号/缩进/分段)塌掉,长句如"除…外,还应…,不足…补足"在纯文本里更易被截断阅读。
- 大 PDF 诱使人用检索抽取代替通读——这正是"命中即停"的温床。**越是大 PDF,越要严格执行第0步通读**(因为 OCR 文本其实不大)。
- docx 相对安全是**结构红利**(python-docx 按段落读,段落边界强制整条读),不是功力——一旦在 docx 里也改用搜索定位+命中即停,照样翻车。规则对 Word/PDF 同等生效。
## 八维审查框架
> ⚠️ **逐条审查铁律(Maggie 2026-06-17)**:八维是审查维度,不是只挑命中的维度写。每份合同(含物业等附属合同)都要**从主体信息到签名落款逐条过一遍,不挑不跳**,禁标"简化审查"。逐条审查是**内部要求**(保证覆盖面),但**交付物不写"✅已审查无异常"展示段**(Maggie 2026-06-18 反转:交付物只列真正风险点,不罗列"审过且无问题"的条款)。详见 SKILL.md「铁律:每份合同逐条审查」节。
### 1. 主体与出租权基础
- 出租方是否为产权人?非产权人出租需核**转租授权链**(产权人→二房东→本合同)是否完整、是否超授权范围
- 签约主体是否有签约权限(法定代表人/授权代理人,授权书是否齐备)
- 合同甲方名称与产权证、与实际收款主体是否一致;主体变更是否有变更协议衔接
- ⚠️ 房屋已抵押/查封:核是否披露、抵押权实现时乙方的继续承租与补偿安排
### 2. 标的合法性
- 租赁用途(教学/培训/办公/商业)与房屋规划用途是否冲突
- 消防验收、安全条件是否具备(教育培训对消防要求高)
- **办学许可前置**:房屋能否办出办学许可证;办不出时乙方有无无责退出通道
- 标的描述(房号、面积、楼层)是否明确、可特定化
### 3. 权利义务对等性
- 甲乙双方违约后果是否失衡(一方重罚、一方轻责)
- 解除权配置是否对等(甲方解除门槛 vs 乙方解除门槛)
- 单方变更权(甲方单方调租、单方修订管理规则)是否过度
### 4. 乙方核心保护(承租方视角重点)
- **退出机制**:有无任意解除权?通知期、违约金是否合理
- **政策/办学许可退出通道**:因政策、房屋原因无法经营/办证时能否无责解除
- **不可抗力/情势变更**:范围是否覆盖疫情、行业治理、政策变更;能否减租
- **优先权**:优先承租权、优先购买权
- **装修投入保护**:装修残值、提前解除时的装修损失赔偿
- **非竞争**:甲方能否租给同类竞争机构
### 5. 违约与救济的合法性与公平
- 逾期付款违约金率:是否过高(年化超法律保护上限,乙方可主张调减,民法典585条)
- 甲方违约救济是否完整(退押金 + 退预付 + 装修损失 + 诉讼/律师费)
- 押金/保证金扣罚条件是否苛刻、退还是否设不合理前提
- 违约金条款的**适用范围**:是否覆盖"无故提前退租",还是仅限列举情形
### 6. 风险分配
- 房屋抵押、查封、拍卖、征收/拆迁的风险由谁承担、有无补偿
- 出租方变更(转让/继承)时合同是否继续有效、有无买卖不破租赁的强化约定
- 房屋瑕疵、维修责任归属(是否倒置给乙方)
- **表述用专业概念统领,别张冠李戴挂法条**(2026-06-17):出租方变更=**所有权变动**(725买卖不破租赁);查封/拍卖=**第三人主张权利**(729,≠725);征收/拆迁=**征收征用**(243)。格式"专业概念(情形举例)",法条默认不写进表格正文。
### 7. 争议解决与条款效力
- 管辖约定(法院/仲裁、地点是否对乙方不利)
- 是否存在无效/可撤销条款(违反强制性规定、显失公平)
- 格式条款的提示说明义务(免除甲方责任、加重乙方责任的条款是否尽到提示,民法典496-497条)
### 8. 完整性与一致性
- 必备条款是否齐备(标的、租金、期限、用途、违约责任)
- 正文与补充协议/附件有无矛盾(如供电功率、面积、租金前后不一致)
- 附件是否完整(产权证明、平面图、授权书、保密承诺书)
- 签署是否有效(签字盖章、日期、骑缝)
## 法条核实铁律
- 引用的每条法律法规**必须核实引用时点的现行有效版本**,判断适用新法或旧法
- 法条**全文引用**,不归纳、不删改
- 案例引用需**案号 + 法院 + 日期**齐全,不写"某法院判决"
- (与「法律文书写作铁律」一致)
## 风险定级纪律(克制,2026-06-17 Maggie 万达打样)
站乙方立场 ≠ 把每处瑕疵都往高风险写。定级看**实际影响**,详见 SKILL.md「风险定级纪律」节:
- **形式瑕疵**(签署日期空白、印章不全、填空未填)→ 关键履行要素已明确约定且合同已实际履行的,评 🟢 低风险,落"建议补正以规范合同管理",不写"效力存疑"。
- **尽调/核验类**(权属/资质/证照核验)→ 用操作性"请确认已核验…并存档"提示,归 🟢 低风险,不写"未核验→风险"。
- **一条风险只讲一件事**,不把真问题与伪问题捆在一条一起拔高。
- **事实问题(签字/印章有无)不靠文本提取断言**——PDF 手写/印章在 OCR 里看不到,必须看原件 PNG 或问 Maggie,再定级。
- 第8维"签署是否有效"按此纪律执行:日期/签字缺失先核原件图,再按形式瑕疵定级,默认不拔高。
## 输出
- 法律风险清单:每条风险标明依据条款、法律后果、对乙方影响、应对建议
- 风险评级:🔴高 / 🟡中 / 🟢低
- 写入汇总表"法律风险"信息(与"模版差异"信息**分列**,互不混淆)
@@ -0,0 +1,51 @@
# 北翼玖玖/金飞达口径新增校准(2026-07-14)
## 一、输出标准锁定规则
当用户明确说:
- “17份合同的审查和汇总规则按照金飞达的来”
- “格式输出也参照金飞达”
则本轮校区任务的输出标准应直接锁定为:
1. **审查规则**按金飞达口径;
2. **汇总规则**按金飞达结构;
3. **格式输出**参照金飞达成品;
4. 不再混用其他校区风格,除非用户再次改口。
## 二、板块组织主键
当用户进一步明确:
- “分类规则还是根据租赁物”
- “同一租赁物相关的合同或文件,按照时间顺序排列”
则必须按以下顺序组织:
1. **先按租赁物分组**
2. **每组内放入该租赁物的全部相关文件**(租赁、补充、退租、物业、说明等);
3. **组内再按时间顺序排列**
4. 不能按“租赁合同一堆、物业合同一堆”做机械分堆。
一句话总纲:
> 内容规则看金飞达,编排逻辑看租赁物,组内顺序看时间。
## 三、进度汇报口径
用户问“开始做了么?”时,如果已经做了以下任一动作:
- 跑开工闸门;
- 实际盘点文件;
- 从容器/网盘拉取材料;
- 实际读取 md / pdf / docx;
- 启动并行提取或子任务;
则必须用“**已实际完成的动作清单 + 当前所处步骤**”回答,避免泛泛说:
- “我开始处理了”
- “我接下来继续做”
- “我会推进”
更好的汇报结构:
1. 已过什么闸门;
2. 已实际盘出多少文件;
3. 已读了哪些关键文件;
4. 当前处于 Step0 / Step1 / Step2 哪一步;
5. 下一步具体做什么。
## 四、适用场景
- 南通新东方校区批量租赁梳理;
- 用户要求“参照某已校准校区成品”执行的批量汇总任务;
- 多文件、多租赁物的校区梳理任务。
@@ -0,0 +1,116 @@
# 北翼玖玖 / 金飞达体例落表补充规则(2026-07-14)
## 触发场景
当用户明确提出:
- “17份合同根据租赁物,同一租赁物相关的合同或文件,按照时间顺序排列”
- “17份合同的审查和汇总规则按照金飞达的来,格式输出也参照金飞达”
- “先把列表梳理出来,再开始审查和汇总”
此时应按本文件执行,而不是直接套一般校区梳理流程。
---
## 一、四个锁定动作
### 1. 规则锁定
审查口径、汇总逻辑、风险表达均按**金飞达**口径执行。
这意味着:
- 不再自创“更像桃坞路/跃龙路”的混搭版
- K列、整体段、文件说明的表达密度与风格优先看金飞达
- 用户明确说“参照金飞达”后,不能再抽象回答“整体参考即可”,必须在实际落表时体现
### 2. 分组锁定
必须先按**租赁物**归组,而不是按“租赁合同一组、物业合同一组、补充协议一组”机械分类。
同一租赁物相关的下列文件应放入同一组:
- 租赁合同
- 物业合同
- 补充协议
- 退租协议
- 主体/权利义务转移协议
- 账户更正说明 / 特殊情况说明
- 其他直接影响该租赁物履行链条的文件
### 3. 排序锁定
同组内按**时间顺序**排列。
若文件未明确写明签署日:
- 先找正文中的原合同签订日
- 再看起租日 / 生效日 / 退租日 / 权利义务转移日
- 仍无法确定时,标注 **“签署日未载明”**,并按业务发生链条排序
禁止把“日期不明”当作不排序的理由。
### 4. 先列表、后审查
如果用户先要求“把列表梳理出来”,必须先单独交付:
> **17份文件按租赁物 + 时间顺序的清单**
等用户确认排序后,再进入:
- H / I / K / L 列审查
- 汇总表落表
禁止一边分组未锁死、一边直接开表。
---
## 二、参考表落表纪律
开始写单校区正式汇总表前,必须先实际取出并读取:
1. **当前校区最近版旧表**
- 用于继承已确认结构、历史行、板块顺序
2. **用户指定的参照校区表**(如金飞达)
- 用于锁定输出格式和表达风格
### 读取参考表时,至少核对这些内容
- 标题行写法
- 项目信息行写法
- 板块标题的组织方式
- K列的整体评价与“〇 需注意”句式
- L列的客观差异写法
- 补充协议、说明文件在表中的落法
### 禁止事项
- 不能只凭记忆说“参照金飞达格式”就直接开写
- 不能只借鉴列头,不核对正文表达方式
- 不能忽略旧表,直接从零发明一个“新版结构”
---
## 三、北翼玖玖类项目的落表顺序建议
### 配套一/配套二这类多租赁物项目
先按租赁物形成独立板块,再在板块内按时间顺序落:
- 主租赁合同 / 主物业合同
- 费用减免补充协议
- 退租协议
- 主体/权利义务转移协议
- 水电费/账户变更类补充协议
- 特殊情况说明
也就是:
> **先主合同,再补充,再退租,再转移,再收尾说明**
这样才能看出完整履行链条。
---
## 四、输出纪律
当用户已经明确让你开始正式审查和汇总时,对外汇报进度必须区分:
- **材料盘点/排序**
- **实质审查(H/I/K/L)**
- **正式落表**
不要把“已经开始整理材料”说成“已经开始正式汇总表写作”;也不要把“已有底稿”说成“已完成审查”。
准确说法应类似:
- Step0已完成
- Step1已完成
- Step2进行中(哪几份已做H/I/K/L)
- Step3是否已开始落xlsx
---
## 五、可复用的一句话原则
> **金飞达定规则,租赁物定分组,时间线定顺序,列表确认后再落表。**
@@ -0,0 +1,54 @@
# K列/L列写法规则(Maggie 2026-07-08 纠正)
## K列提前退租分析
### 规则
- 语言简单明了,不需要引用合同原文全文
- 只标条款号,不贴原文段落
- 分清不同条款的适用场景(如规范退租vs擅自退租)
### 示例
❌ 冗余写法(被纠正):
```
一、规范提前解约(第五条3款):
"租赁期间,乙方如需提前解租的,应当提前3个月书面告知甲方。在此情况下,乙方缴付的全部保证金应被甲方没收,且乙方应按一年总租金的30%向甲方支付违约金。"
→ 提前3个月…合计约158,667元。
```
✅ 正确写法:
```
一、规范提前解约(第五条3款):提前3个月书面告知甲方 + 没收全部保证金56,667元 + 一年总租金30%违约金102,000元,合计约158,667元。
二、擅自退租(第四条15款):甲方可解除 + 没收保证金 + 30%违约金 + 不足弥补损失另行赔偿(无上限)+ 退还已预付未使用租金。
注:5.3条为规范退出,后果封顶;4.15条为擅自退租,后果更重且无上限。
```
### 根因
不同条款法律后果不同(一个封顶一个无上限),混在一起写会误导客户对退出成本的判断。必须分开列。
---
## L列模版比对
### 规则(2026-07-08确立)
- **说多少项就列多少项,不得遗漏**——写"40项差异"就必须全部列出40项
- 不得写"主要差异"然后只列十几项(这是不诚实的)
- 按07模版条款顺序逐条对照,用编号列表
- 格式:`编号. 差异点名:07→XXX;本合同→XXX`
- 差异项分按原合同章节分组(用【第X条·主题】标题)
### 操作方法
1. 从07模版第一条读到最后一条(含附加条款)
2. 每条问:本合同有没有?有的话一样还是不一样?
3. 不一样的记一项,没有的记"无此条款"
4. 数出总数,写在开头
5. 逐项列出,不遗漏
---
## 附件标号"/"的处理
### 规则(2026-07-08确立)
- 合同附件中填写"/"的字段表示**不适用/留空**
- 例如营业额提成比例填"/%"=该合同不适用营业额提成
- 处理方式:**全面清除**——H列不写、I列不提、K列不分析该项风险、L列不比对该项差异
- 不得写"比例未填明,存在后续争议风险"——这是错误理解,"/"不是"忘了填"而是"不适用"
@@ -0,0 +1,137 @@
# nantong-lease-audit Workflow 架构与批量运行指南
## 概述
`nantong-lease-audit` 是南通新东方租赁合同梳理的 uwf 状态机工作流(2026-06-27 建成并通过金飞达12份+人民中路2份测试)。
**架构**
```
OCR(手动/脚本) → uwf thread start(每份合同) → Excel builder(汇总) → delivery-gate → 交付
```
**与 contract-portfolio-analysis skill Step 0→7 的关系**
- workflow 替代 Step 2 的 delegate_task 动作B(模版比对)+ rule-analyzer(法律风险分析)+ data-extractor(结构化数据提取)
- Step 0(盘点)、Step 1(OCR)、Step 3(Excel生成)、Step 6(交付闸门)仍由脚本完成
- Step 4-5(校对+终审)在 workflow 完成后由人工做
## 4 角色定义
| 角色 | 耗时(典型) | 产出 |
|------|-----------|------|
| classifier | 60-90s | 校区、主体、合同类型、模版类型、面积、期限 |
| template-diff | 180-200s | 与07模版逐条比对差异清单(供L列) |
| rule-analyzer | 280-300s | 独立法律风险分析(供K列)+ 提前退租分析 |
| data-extractor | 120-150s | 12列结构化数据 + H列四检 + 数学交叉验证 |
**单份合同总耗时:~10分钟**(4角色串行)。
## YAML Frontmatter 输出大小限制
uwf 的 YAML frontmatter 解析有大小限制。**当角色输出内容超过 ~2000 字符时,frontmatter 解析会失败**,导致 thread suspended。
**data-extractor 的解决方案**
- 将12列数据写入 `/tmp/nantong-lease-audit/row_data.json` 文件
- YAML frontmatter 中只传文件路径:`output_file: /tmp/nantong-lease-audit/row_data.json`
- 下游 Excel builder 从文件读取数据
**规则**:任何 uwf 角色的输出如果包含大量文本(>2000字符),应该:
1. 将详细内容写入文件
2. frontmatter 中只放文件路径和摘要字段
3. 在 procedure 中明确指示 agent 必须写文件
## 批量启动模式
### batch_runner.sh 模板
```bash
#!/bin/bash
UWF=/home/maggie/.hermes/node/bin/uwf
WF=nantong-lease-audit
CAMPUS=校区名
OCR_DIR=/tmp/校区/ocr
LOG=/tmp/校区/threads.log
FILES=("合同1" "合同2" ...) # 文件名(不含.md后缀)
THREAD_IDS=()
for fname in "${FILES[@]}"; do
md_path="${OCR_DIR}/${fname}.md"
[ ! -f "$md_path" ] && continue
OUT=$($UWF thread start $WF -p "校区:${CAMPUS} | 合同文件:${fname} | OCR文本路径:${md_path} | 原始文件名:${fname}.pdf" 2>&1)
TID=$(echo "$OUT" | python3 -c "import re,sys; t=sys.stdin.read(); m=(re.search(r'\"thread\"\s*:\s*\"([^\"]+)\"', t) or re.search(r'Thread\s+(\S+)', t)); print(m.group(1) if m else '')")
[ -n "$TID" ] && { THREAD_IDS+=("$TID"); sleep 8; } # 8s CAS settle
done
# exec all threads
for tid in "${THREAD_IDS[@]}"; do
cd /home/maggie && $UWF thread exec "$tid" -c 20 --background 2>&1
sleep 2
done
# Wait loop with auto-resume of suspended threads
while true; do
ALL_DONE=true
for tid in "${THREAD_IDS[@]}"; do
STATUS=$(cd /home/maggie && $UWF thread list --all 2>/dev/null | grep "$tid" | awk '{print $3}')
if [ "$STATUS" = "suspended" ] || [ "$STATUS" = "idle" ]; then
cd /home/maggie && $UWF thread exec "$tid" -c 10 --background 2>&1
sleep 5
ALL_DONE=false
elif [ "$STATUS" != "end" ] && [ "$STATUS" != "cancelled" ]; then
ALL_DONE=false
fi
done
$ALL_DONE && break
sleep 60
done
```
**关键参数**
- `sleep 8` between thread starts:CAS settle 防止 phantom thread
- `-c 20 --background`:一次跑20步(足够覆盖4角色)
- 自动 resume suspended/idle threads(API 429 限流会导致 suspended)
### API 限流处理
14个并发 thread 会触发 API 429 (rate limiting)。表现:
- rule-analyzer 或 data-extractor 阶段 suspended
- 日志显示 "HTTP 429: Request rate increased too quickly"
**处理**:batch runner 的 wait loop 自动检测 suspended 状态并 resume。不需要手动干预。
### 生成 Excel
```bash
# 单份合同
python3 scripts/nantong-excel-builder.py <thread-id> <校区名> <xlsx路径> [文件名]
# 批量(所有已完成的thread)
python3 scripts/batch-excel-builder.py <xlsx路径>
```
## Thread Read Quota 陷阱
`uwf thread read` 默认 quota 只有 4000 字符,**远远不够读取完整输出**(template-diff 和 rule-analyzer 输出通常 10000-30000 字符)。
**必须用**`uwf thread read <id> --quota 200000 --start`
- `--quota 200000`:200K 字符足够
- `--start`:包含 init step
Excel builder 脚本已内置此参数,不需要手动指定。
## 测试结果(2026-06-27)
| 校区 | 合同数 | 总耗时 | 完成率 |
|------|--------|--------|--------|
| 金飞达 | 12份 | ~60分钟 | 12/12 end |
| 人民中路 | 2份 | ~60分钟 | 2/2 end |
14个并发 thread,API 限流导致部分 thread 需要 auto-resume,但全部完成。
## 已知问题
1. **Excel builder 解析精度**:各 thread 输出格式不完全一致,部分行的 C/F 列(合同类型/面积)可能为空
2. **K/L 列内容**:data-extractor 的 K/L 列有时写占位符而非实际分析内容(已修复 workflow prompt,但需验证)
3. **按租赁物分类排序**:batch builder 按 thread 完成顺序排列,不自动按"租赁物→合同性质→时间"排序——需要后续手动调整或增加排序逻辑
@@ -0,0 +1,76 @@
# Nextcloud File Upload Diagnostics
When files appear visible in the Nextcloud web UI but `find` on the container filesystem returns nothing, use this diagnostic sequence.
## Step 1: Check physical filesystem
```bash
docker exec <container> find '<nc_data_path>/<dir>' -type f -exec ls -la {} \;
```
Look for `.part` and `.ocTransferId*` files — these are incomplete upload fragments.
## Step 2: Rescan
```bash
docker exec -u www-data <container> php occ files:scan admin --path='<path>'
```
## Step 3: Check Nextcloud logs
```bash
docker exec <container> tail -20 /var/www/html/data/nextcloud.log | python3 -c "
import sys, json
for line in sys.stdin:
try:
d = json.loads(line.strip())
if d.get('level', 0) >= 2:
print(f'[{d.get(\"time\",\"?\")}] {d.get(\"message\",\"\")[:300]}')
except: pass
"
```
Upload failures show: "预期文件大小为 X字节,实际...写入...Y字节"
## Step 4: Query MariaDB directly
Find the DB password first:
```bash
docker exec <container> grep dbpassword /var/www/html/config/config.php
```
Then query (use `mariadb` client, not `mysql`):
```bash
docker exec nextcloud-db-1 mariadb -u nextcloud -p<DB_PASSWORD_FROM_CONFIG> nextcloud -e "
SELECT f.fileid, f.path, f.name, f.size, FROM_UNIXTIME(f.mtime) as modified
FROM oc_filecache f
WHERE f.path LIKE '%<search_term>%'
ORDER BY f.path;
"
```
To find children of a directory (by parent fileid):
```bash
... -e "SELECT f.fileid, f.parent, f.path, f.name, f.size
FROM oc_filecache f WHERE f.parent IN (<parent_id1>, <parent_id2>);"
```
## Step 5: Clean up fragments
```bash
docker exec <container> find '<path>' -name '*.part' -delete
docker exec <container> find '<path>' -name '*.ocTransferId*' -delete
```
## Common root cause
Cloudflare Tunnel (free tier) truncates large file uploads. The Nextcloud chunked upload protocol partially writes, then the connection drops. Repeated retries produce the same result.
**Solution**: Receive files via alternate channel (WeChat private message → `~/.hermes/cache/documents/`) and `docker cp` into Nextcloud.
## Quirk: docker cp'd file present + md5 correct, but `files:scan` returns 0 and DB row missing (2026-06-17)
After `docker cp` + `chown www-data` a new xlsx, `php occ files:scan admin --path='小Maggie协作区/.../世茂'` returned all-zeros (`Folders 0 Files 0`) and `oc_filecache` had **no row** for the file — so it was invisible in the web UI despite physically existing with a correct md5.
**Root cause**: `files:scan` keys off directory mtime; a fresh `docker cp` into an existing dir doesn't always bump the parent mtime, so the scanner skips it.
**Fix** (verified):
```bash
# 1. touch the parent dir to force an mtime change
docker exec <container> bash -c "touch '/var/www/html/data/admin/files/<REL_PATH>'"
# 2. rescan using the admin/files/ prefixed --path form (not the bare share path)
docker exec -u www-data <container> php occ files:scan --path="admin/files/<REL_PATH>"
```
This returns `Updated N` and registers the file. **Always verify after upload** by querying `oc_filecache` for the filename (Step 4) — md5 match alone does NOT prove the file is indexed/visible. Don't tell the user "uploaded" until the DB row exists.
@@ -0,0 +1,98 @@
# OCR与Workflow教训(2026-06-29 跃龙路校区)
## 1. 纯扫描件OCR:用ocrmypdf,不用raw tesseract
**问题**:跃龙路两份合同(租赁合同签字.pdf 11MB + 物业合同.pdf 1.4MB)都是纯扫描件(pdftotext提取0字符)。
**错误做法**
- 用tesseract直接OCR → 输出大量乱码("ARR: MEER"应该是"名称:范存益","FAK."应该是"平方米")
- 试图用vision_analyze逐页核实 → 连续7次timeout,完全不可用
- 转而手动delegate_task让subagent做vision → 部分成功部分失败,耗费大量token和时间
**正确做法**
```bash
# 首选:ocrmypdf(有预处理管线,中文识别质量远高于raw tesseract)
ocrmypdf -l chi_sim+eng --output-type pdf 原件.pdf OCR后.pdf
# 然后提取文字层
pdftotext OCR后.pdf output.md -layout
```
**ocrmypdf vs raw tesseract对比**
| 维度 | ocrmypdf | raw tesseract |
|------|----------|---------------|
| 预处理 | 自动去噪、矫正、二值化 | 无 |
| 输出格式 | PDF+A(含文字层) | 纯文本 |
| 中文识别质量 | 较好(仍有少量错误) | 很差(大量乱码) |
| 适用场景 | 扫描件合同、手写签名页 | 印刷体清晰文档 |
**注意**:ocrmypdf输出仍有少量错误(空格分割、个别字误识),但整体可读性远优于raw tesseract。关键数字(金额、日期、面积)仍需人工或vision核实。
## 2. Vision工具timeout处理
**问题**:vision_analyze对大尺寸扫描件图片(>300KB)频繁timeout。压缩到100KB仍timeout。
**解决方案**
1. 先尝试压缩:`PIL.Image.resize((1000, ...))` + JPEG quality=50
2. 如仍timeout,改用`browser_navigate(file://...)` + `browser_vision`
3. 如browser_vision也timeout,用`delegate_task`让subagent做(subagent有独立timeout预算)
4. 最终方案:用ocrmypdf替代vision做OCR,只在关键页面用vision核实
**最佳实践**:先用ocrmypdf做全量OCR,只对关键条款(租金金额、违约金比例、当事人名称)用vision逐页核实。不要试图vision每一页。
## 3. uwf workflow必须使用
**教训**:Maggie明确指出"跃龙路又没有按照workflow进行了"。手动做OCR+分析+建表=跳步+质量失控。
**正确流程**
1. 用ocrmypdf做OCR(或用已有校正OCR文本)
2. 启动uwf `nantong-lease-audit` workflow(4角色:classifier→template-diff→rule-analyzer→data-extractor)
3. 每个合同一个thread,可并行启动
4. 等待所有thread完成(约10-15分钟/合同)
5. 读取JSON输出喂给`single-campus-builder.py`
**uwf启动命令**
```bash
# 启动thread
uwf thread start nantong-lease-audit -p "校区:X | 合同文件:Y.pdf | OCR文本路径:/tmp/xxx/Y_ocr.md | 原始文件名:Y.pdf"
# 后台执行(最多10步)
uwf thread exec <thread-id> --count 10 --background
# 检查进度
uwf thread show <thread-id>
# 读取结果
uwf thread read <thread-id> --quota 8000
```
**JSON输出路径**`/tmp/nantong-lease-audit/<校区>-row-data.json`
## 4. delivery-gate.py文件名模式
**G1检查**:寻找`模版比对-*.md`格式的文件名(不是`*-template-diff.md`)。
- ✅ 正确:`模版比对-跃龙路租赁合同.md`
- ❌ 错误:`跃龙路-template-diff.md`
**修复**:workflow产出的template-diff文件需要复制或重命名:
```bash
cp /tmp/nantong-lease-audit/跃龙路-template-diff.md /tmp/<校区>/模版比对-跃龙路租赁合同.md
```
**G5检查**`数据行数≥3`是默认值。对于只有2份合同的校区(租赁+物业),这是**正常情况**,不算失败。如果G5报错但确认校区确实只有2份合同,可安全忽略此项。
## 5. 物业合同JSON被覆盖
**问题**:两个合同共用同一个uwf workflow名称(`nantong-lease-audit`),data-extractor角色默认写入`/tmp/nantong-lease-audit/row_data.json`。如果两个thread同时跑,后完成的会覆盖先完成的。
**解决方案**
- 物业合同没有对应的07模版,不需要走template-diff角色
- 物业合同的K列和L列内容需要手动填充(从OCR文本+workflow摘要中提取)
- 或者:在builder脚本中直接从OCR文本和物业合同分析摘要手动构造物业数据行
## 6. ocrmypdf产出仍需OCR完整性检查
ocrmypdf产出文字层后,用pdftotext提取的.md文件可能仍有少量乱码。需要:
1. 运行`ocr-integrity-check.py`检查乱码
2. 如果检查失败(检测到公司名乱码等),用vision核实关键页
3. 核实后手动修正.md文件中的乱码
4. 重新运行检查直到通过,生成`step1.verified` checkpoint
**快捷方案**:如果之前已有vision校正过的.md文件(本次session中手动校正的),直接复用,不需要重新跑ocrmypdf+检查流程。
@@ -0,0 +1,53 @@
# OCR Troubleshooting — Binarization Fallback
## Problem
Some scanned PDF pages return empty text when processed with standard `ocrmypdf` + `pdftotext` or `tesseract`. The pages appear to have content visually but the OCR engine produces zero characters.
## Diagnosis
Check if tesseract output is empty:
```bash
tesseract page.jpg stdout -l chi_sim | wc -c
# If 0 chars → page needs binarization
```
Check image statistics to confirm it's not truly blank:
```python
from PIL import Image
import numpy as np
arr = np.array(Image.open('page.jpg').convert('L'))
print(f'mean={arr.mean():.0f} std={arr.std():.0f}')
# If std > 25 → image has content, OCR failure is processing issue
```
## Fix: Binarization (threshold=140)
Convert to pure black-and-white before re-running tesseract:
```python
from PIL import Image
import numpy as np
img = Image.open('page.jpg').convert('L')
arr = np.array(img)
threshold = 140 # Adjust if needed (120-160 range)
arr = (arr < threshold).astype(np.uint8) * 255
Image.fromarray(arr).save('page_bw.png')
```
Then run tesseract on the binarized image:
```bash
tesseract page_bw.png stdout -l chi_sim
```
## Vision Tool Timeout Fix
If `vision_analyze` times out on scanned pages:
1. Check `~/.hermes/config.yaml``vision.timeout` (default 30s is too short for large images)
2. Increase to 90s: `sed -i 's/timeout: 30/timeout: 90/' ~/.hermes/config.yaml`
3. Compress images before sending: resize to 850x1100, quality=60 (~80-120KB per page)
4. Send one page at a time, not multiple simultaneously
## Workflow for Scanned Contracts
1. `ocrmypdf -l chi_sim --skip-text input.pdf output_ocr.pdf`
2. `pdftotext output_ocr.pdf - | wc -c` — check if text layer exists
3. If empty pages: `pdftoppm -jpeg -r 300 input.pdf pages/page` → binarize → tesseract
4. Combine: original tesseract pages + binarized pages into single .md file
5. For remaining garbled fields: use `vision_analyze` on compressed page images
@@ -0,0 +1,47 @@
# OCR乱码检测与核实流程(凤凰文化0701教训固化)
## 背景教训
凤凰文化广场合同第十条10.2,OCR把:
> "提出合同解除的一方,同时应向对方承担初年年租金的【20】%作为违约金。租赁租金及其他费用结算至合同解除日。"
读成了乱码:
> "oe方,同Se【2024】年【10】月【15】日结算至合同解除日"
由于只对8.4-8.9做了vision核实,跳过了10.2的乱码段,导致:
- 整段违约金条款丢失
- 审查结论错误("无违约金"→实际"有20%违约金")
- 返工
## 强制流程
```
Step 1 OCR完成
python3 scripts/ocr-garble-detect.py <file.md>
🔴 高危乱码(无意义英文片段/中英混杂碎片)
→ 定位PDF页码(line number / 每页约50行估算)
→ vision精读对应页面
→ 记录修正内容
→ 修改OCR文本或建修正说明文件
🟡 疑似乱码(中文占比低/异常符号)
→ 区分:纯数字表格=正常;邮箱/账号=正常
→ 真正乱码→同上vision核实
全部消灭 → 进入 Step 2
```
## 关键判断:哪些看似正常实则是乱码
| 表象 | 陷阱 | 正确做法 |
|------|------|----------|
| "Se【2024】年【10】月【15】日" | 看似日期,实则是违约金条款乱码 | 发现中英混杂+不合语境→必须vision |
| "0.4 a/R 元/㎡/月" | 看似单位,"a/R"是"天"的乱码 | 金额/单位附近乱码→vision确认单位 |
| "oe方,同" | 短乱码容易被跳过 | 即使只有几个字符异常也必须核实上下文完整段落 |
## 核心原则
**不是"看哪里乱就修哪里",而是脚本扫全文自动标红,强制逐条过一遍。**
选择性核实 = 选择性遗漏 = 必然返工。
@@ -0,0 +1,53 @@
# OCR全文持久化规则(2026-07-02 Maggie明确要求)
## 核心规则
提取+校对好的合同全文**必须保存为 `<原文件名>_全文.md`**,放在原PDF同目录下(Nextcloud对应校区文件夹)。
## 目的
后续任何任务(起草函件、查条款、更新汇总表、模版比对)直接 read_file 秒读,避免重复15-20分钟的Vision/OCR工作。
## 触发时机
每次对扫描件PDF进行OCR/Vision全文提取后,在完成主任务之前,先保存全文md文件。
## 文件位置示例
```
世茂/青少/世茂新租赁合同.pdf ← 原文件
世茂/青少/世茂新租赁合同_全文.md ← 持久化的提取结果
世茂/青少/世茂物业合同.pdf
世茂/青少/世茂物业合同_全文.md
世茂/高中/3023新东方租赁合同-双签版.pdf
世茂/高中/3023新东方租赁合同-双签版_全文.md
```
## 格式
```markdown
# [文档标题] 全文
(提取方式:Vision校对 / OCR提取,来源:filename.pdf,提取日期:YYYY-MM-DD)
--- 第1页 ---
[page content]
--- 第2页 ---
[page content]
...
```
## 质量优先级
1. **Vision逐页提取**(最佳):delegate_task → vision_analyze每页 → 合并保存
2. **ocrmypdf**(次选):`ocrmypdf --force-ocr -l chi_sim+eng` → pymupdf提取
3. 不接受:仅靠tesseract raw(中文法律文本乱码率太高)
## 操作步骤
1. Vision/OCR提取完成后,写入 `/tmp/` 临时文件
2. `sudo cp /tmp/file.md /home/maggie/nextcloud/data/data/admin/files/...`
3. `sudo chown www-data:www-data <target>`
4. `sudo docker exec -u www-data nextcloud-nextcloud-1 php occ files:scan --path=<path>`
## 与汇总表的关系
- 汇总表(`*-梳理-*.xlsx`)是**结构化审查结果**(12列)
- 全文md是**原始文本存档**(逐页)
- 两者互补:汇总表用于快速查信息,全文md用于精确引用条款原文
- 后续新增校区开工时,先检查是否已有全文md,有则直接读取不必重跑Vision
@@ -0,0 +1,122 @@
# OCR 费率符号核对配方(‰ vs %,扫描件)
**适用**:扫描件合同里逾期违约金/滞纳金/利率等**日费率**字段,OCR 文本出现 `%`/`‰`/`0.5`/`1`/`2` 等,量级可疑(年化超约100%就该警觉)。这是 10× 量级错误的高发点(千分号 ‰ 被误识成百分号 %)。
## 核心原则
1. **绝不信单次 OCR 的小符号**。扫描件 OCR 对 ‰/% 经常判错。
2. **双跑交叉**:整页 OCR vs 裁图放大重 OCR。**两次不一致 = 机器判不准已证明 → 升级人工**,不在两个机器结果里挑一个。
3. **升级人工带放大裁图**`MEDIA:` 发 Maggie 肉眼终判那一个像素符号),不甩空问题。
4. 机器判不准时,交付物先标 `〔OCR数值,单位以PDF原件为准〕`,**不武断定值**。
## 量级常识闸门(先口算,再核符号)
- 日费率写进表前先口算年化:`每日 X × 365`
- **年化超约 100% 就该警觉**:每日 2% = 年化 730%(荒谬);每日千分之2(2‰) = 年化 73%(合理)。
- 逾期违约金/滞纳金日费率正常落在 **万分之几 ~ 千分之几**(年化约 18%~73%)。见到"每日1%、每日2%"先疑 ‰ 被误识成 %。
- 折年化别算错:0.5%/日 = 年化 182.5%(不是18.25%);万分之5/日才是年化 18.25%。
## 命令级配方
### 1. 定位费率条款在第几页(扫描件 PDF 文本层通常为空,必须 OCR 切图)
切图一般已在 OCR 阶段产出(`<合同名>_imgs/p<N>.png`,约 200dpi / 1654×2340)。按章节缩小页范围(如"乙方违约责任/第九条"),对候选页跑 tesseract,命中含费率特征词(`应付未付`/`滞纳金`/`延误一日`/`每延误`)的页:
```python
import subprocess, os
def ocr(img, psm=6):
return subprocess.run(["tesseract", img, "stdout", "-l", "chi_sim+eng", "--psm", str(psm)],
capture_output=True, text=True).stdout or ""
# 对 <imgs>/p{N}.png 跑,命中含"滞纳金"+"延误"+"应付未付"的页即 9.2 所在页
```
### 2. 裁出费率行、放大 3–4 倍、多 psm 重 OCR
整页全文 OCR 找到费率行的行号占比,按比例裁该纵向区段(不依赖 TSV 分词,更稳):
```python
from PIL import Image
lines = [l for l in ocr(img).split("\n") if l.strip()]
idx = next(i for i,l in enumerate(lines) if ("应付未" in l or "付金额" in l or "加付滞纳金" in l))
im = Image.open(img); W,H = im.size
frac = idx/len(lines)
y0, y1 = max(0,int(H*(frac-0.06))), min(H,int(H*(frac+0.10)))
crop = im.crop((0,y0,W,y1)).resize((W*3,(y1-y0)*3), Image.LANCZOS)
crop.save("/tmp/_rate_crop/<条款>.png")
# 对裁图再多 psm 重 OCR:for psm in (7,6,11,13): ocr(crop_path, psm)
```
### 3. 判读
- 两次(整页 vs 裁图)符号**一致** → 采信,去掉待核标记,写进表。
- 两次**不一致**(如 `0.5%` vs `0.5‰`,或 `1%` vs `1‰`)→ 机器判不准已坐实 → 走第4步。
### 4. 升级人工(带证据)
- 把第2步裁好的放大 PNG 用 `MEDIA:/tmp/_rate_crop/<条款>.png` 发 Maggie,问"数字后这个符号是 % 还是 ‰"。
- Maggie 定值后,把交付表里该条从 `〔待核〕` 改成确定值,闭环。
## ✅ vision_analyze 已配好——符号终判的主路径(2026-06-22 人民中路实证)
vision 工具已由技术支持配好可用。**现在符号判不准时,主路径是先自己 `vision_analyze` 看裁好的整页/裁图终判**,能自核就不必裁图发 Maggie;自核仍拿不准的才交人。crop-OCR 多 psm 仍可能裁偏(本次裁两次都偏到隔壁条款),vision 看整页反而一步到位。
**🔑 vision 终判的精髓 = 让它做「同页符号交叉对比」**,不要只问"这是%还是‰"。提问里点名让它拿**同一页别处确定的符号**作参照:
> "请找到第十条4款的违约金率符号,并和同一页第2款/第3款的「10%」对比——4款那个符号右下方是一个圆圈(%)还是两个圆圈(‰)?"
实证:人民中路租赁第十条4款,vision 自动拿同页第2/3款的"10%"百分号比对,确认4款符号"明显比%更宽、右下方两个圆圈"= **0.1‰**(年化3.65%);物业第九条2款同法确认 **0.5%**(年化182.5%)。同页对比比孤立看一个符号可靠得多——人眼/模型判 ‰vs% 都靠"和已知符号比宽窄、数圆圈个数"。
**仍要带量级闸门复核**:vision 给的符号要和年化常识对得上(0.1‰=3.65%对逾期违约金偏低但合理;若 vision 说某逾期费率是"每日5%"=年化1825%就该反问)。vision 偶发 `Connection error`,重试即可(本次租赁那张第一次 Connection error、重试成功)。
## 🔴 发 vision 前必须先压缩图片——防超时(2026-06-23 实测确立)
当前 vision 配置:`gemini-3.5-flash`(经 litellm-sora 代理),**timeout 仅 30 秒**。实测:
- **2.5MB 原图(200dpi PNG,1654×2340)→ Request timed out 失败**
- **压到 ~385KB(宽1100px JPG q85)→ 秒过、读得准**
所以**铁律:任何图发 vision_analyze 前,先 resize 到宽 ≤1100px、转 JPG,体积压到 ~300–400KB**:
```python
from PIL import Image
im = Image.open(src)
w,h = im.size; nw = 1100; nh = int(h*nw/w)
im.resize((nw,nh), Image.LANCZOS).convert("RGB").save(dst, quality=85)
```
- 压缩后清晰度仍足够 vision 数圆圈、读符号、判版面(实测 0.1‰/% 区分无误)。
- 超时是"图太大传不完",不是模型不行——别因一次 timeout 就判 vision 不可用,先压图重试。
- 单次失败重试 1–2 次(偶发 Connection error);连续失败才退兜底(tesseract 双跑+裁图发 Maggie)。
## ✅ vision 能力边界 —— 定位"精核兜底",不是"批量主力"(2026-06-23 实测确立)
vision 在差质量扫描件(文字层=0 的纯扫描 PDF,正是本项目 PDF 识别出问题的根因)上**实测够用**,但用对位置:
- **✅ 适合(单页/单点精读核对)**:① 费率符号 ‰/% 同页交叉终判 ② 标红颜色是否真红(配 PIL 像素检测)③ 版面横向/纵向截断、跨页切断 ④ 关键数字(金额/日期/比例)人工复核兜底。
- **❌ 不适合(批量全文提取)**:8 页合同全文 OCR 不要逐页喂 vision——一页一次调用、还可能超时,慢且不划算。**全文底料仍用传统 OCR(deepseek-ocr / tesseract)跑一遍**。
- **最佳架构(已被实测验证 = 现行 skill 设计)**:传统 OCR 出全文底料 → vision 精核可疑点/符号/版面。两层各司其职,不互相替代。
- **交付前 vision 三查清单**(图都先压缩):① 文字完整(pdftotext 拍平 grep)② 视觉呈现(vision 看渲染图:截断/错位/红色)③ 符号量级(vision 同页对比 + 年化闸门)。三查全过再交。
## 退路(vision 万一又不可用)
-`vision_analyze``No LLM provider configured for task=vision` 或持续连不上:退到 **tesseract 双跑 + 裁图发 Maggie**(上面命令级配方)。
- 这是兜底,不是主路径——vision 已配好,优先自核。
## 像素分析绕过 vision 核「单元格字体颜色」(2026-06-22 世茂实证)
**场景**:交付的标红 xlsx 改完,要确认「某条是不是红字 / 整格有没有误染红」,但 vision 未配、自己看不了图。**不必干等 WeiWei 配 vision**——x2t 渲染成 PDF→PNG 后,用 PIL+numpy 直接读像素统计红/黑占比,机器就能给出客观判断。
```python
from PIL import Image
import numpy as np
img = Image.open("渲染页.png").convert("RGB")
arr = np.array(img)
r,g,b = arr[:,:,0].astype(int), arr[:,:,1].astype(int), arr[:,:,2].astype(int)
red_mask = (r>120)&(g<90)&(b<90)&(r-g>50)&(r-b>50) # 明显红字
black_mask = (r<90)&(g<90)&(b<90) # 黑字
# 逐 20px 水平带判主色,能定位「哪几行是红的」,比整页占比更准
for y in range(0, arr.shape[0], 20):
br, bk = red_mask[y:y+20].sum(), black_mask[y:y+20].sum()
if br+bk < 100: continue # 跳过空白带
print(y, "" if br>bk else "")
```
- **判读**:红字带 ≈ 该红的条数 → 正常;红字带远多于黑字带 → 整格误染红,要查 XML。
- ⚠️ **但像素只是辅助**:渲染会让部分红 rPr 不生效、红黑混杂,像素比例不绝对。**XML 层的精确计数才是真相源**——核颜色最终回 `sharedStrings.xml``rgb="FFFF0000"`(见下条 bug 教训)。像素分析用于「快速判断有没有大面积异常」,精确定位用 XML。
## ⚠️ 颜色计数 bug:精确匹配 `rgb="FFFF0000"`,绝不子串匹配(2026-06-22 教训)
数红色 run 时**必须精确正则** `re.findall(r'rgb="FFFF0000"', xml)`,**绝不能** `'FF0000' in etree.tostring(rpr)`——黑色 `FF000000` 里也含子串 `F0000`,子串匹配会把每个黑字 run 误判成红字,导致长单元格(多 run 的 K列风险格)被误报「整格泛红」。世茂栽点:`red_run_count` 一度报 K8/K11/A16 各 8/15/20 个红 run、全表 47 红,虚惊一场要返工;精确匹配后真红就 6 处全对(4个付款提示 H列 + K8第1条 + A16第6条「需核实」句)。`edit-redmarked-xlsx.py``red_run_count` 已修为精确匹配。**任何「整格泛红」的判断,先用精确 `rgb="FFFF0000"` 复核再下结论。**
## 实证(世茂校区,2026-06-18)
| 条款 | 整页OCR | 裁图重OCR | 判定 |
|---|---|---|---|
| 租赁14.1 逾期付款违约金 | `2%` | (上午已核)`千分之2` | ✅ 确认 **2‰**(年化73%合理;2%=年化730%荒谬) |
| 青少物业9.2 滞纳金 | `0.5%`(读成"0.5吃") | `0.5‰` | ⚠️ 打架 → 裁图发 Maggie 终判 |
| 高中物业9.2 滞纳金 | `1%` / `1‰`(两跑不同) | `1‰` | ⚠️ 打架 → 裁图发 Maggie 终判 |
教训:四份合同同批扫描,OCR 在 ‰/% 上三跑三种组合。任何一条都不能拿单次 OCR 定稿。
@@ -0,0 +1,67 @@
# OCR复用 + OnlyOffice渲染技巧
## OCR复用(跳过Step 1提取)
前session已提取的 `*_全文.md` / `*_全文_OCR.md` 文件若已存放于Nextcloud校区目录,
可直接复用,无需重新OCR。
### 确认步骤:
```bash
# 1. 列出已有MD文件
sudo docker exec nextcloud-nextcloud-1 find "<校区容器路径>" -name "*.md" -type f | sort
# 2. 逐份对照PDF清单——每份PDF都有对应MD即可跳过提取
# 3. docker cp 到本地
sudo docker exec nextcloud-nextcloud-1 cat "<容器内MD路径>" > /tmp/<工作目录>/<文件名>.md
# 4. 仍须跑 garble 检测确认质量
python3 ~/.hermes/skills/legal/contract-portfolio-analysis/scripts/ocr-garble-detect.py <文件>.md
# 5. 高危乱码 vision 核实 → 全灭后进 Step 2
```
### 世茂校区实证(2026-07-02):
4份合同全部已有MD提取文件,直接跑garble检测:
- 青少租赁:15处高危(大部分在目录页OCR噪声+附件表格),关键条款vision验证通过
- 青少物业:6处高危
- 高中租赁:18处高危
- 高中物业:2处高危
关键金融数据(面积/租金/税率)通过vision交叉验证确认准确后,整体Step 1节省约20分钟。
---
## OnlyOffice x2t 渲染 — 写XML的权限问题
### 问题:
`docker exec ... bash -c 'cat > /tmp/convert.xml << EOF ...'` 经常报 "Permission denied",
因为容器内 `/tmp` 可能被前次操作留下的 root 文件占用。
### 解决方案:用 Python 写文件
```bash
sudo docker exec nextcloud-onlyoffice-1 rm -f /tmp/input.xlsx /tmp/output.pdf /tmp/convert.xml
sudo docker cp <本地xlsx> nextcloud-onlyoffice-1:/tmp/input.xlsx
sudo docker exec nextcloud-onlyoffice-1 python3 -c "
with open('/tmp/convert.xml', 'w') as f:
f.write('''<?xml version=\"1.0\" encoding=\"utf-8\"?>
<TaskQueueDataConvert xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\">
<m_sFileFrom>/tmp/input.xlsx</m_sFileFrom>
<m_sFileTo>/tmp/output.pdf</m_sFileTo>
<m_nFormatTo>513</m_nFormatTo>
</TaskQueueDataConvert>''')
"
sudo docker exec nextcloud-onlyoffice-1 /var/www/onlyoffice/documentserver/server/FileConverter/bin/x2t /tmp/convert.xml
sudo docker cp nextcloud-onlyoffice-1:/tmp/output.pdf <本地输出路径>
```
### 关键点:
-`rm -f` 清理旧文件避免权限冲突
-`python3 -c` 写文件比 bash heredoc 可靠(避免容器内 shell 权限/重定向问题)
- `m_nFormatTo=513` = PDF格式
### 已知限制:
- x2t渲染PDF时,白色字体在深色背景上可能不显示(字体嵌入问题)
- 这不影响xlsx本身——用户在OnlyOffice中打开时白色文字正常显示
- 视觉验证时注意:标题行的白色文字不显示≠格式错误,属x2t渲染特性
@@ -0,0 +1,61 @@
# OCR + Vision 双引擎校对流程 (跃龙路经验 0703)
## 问题背景
扫描件PDF用ocrmypdf提取的文字有大量识别错误:
- 数字被乱码覆盖(如金额行变成 `SH CAL bi` 等)
- 合同条款编号错位
- 签章区域干扰正文识别
- "扫描全能王"水印被当正文
## 推荐流程
### Step 1: OCR提取(作为初始底稿)
```bash
ocrmypdf --force-ocr -l chi_sim+eng --sidecar /tmp/XXX-ocr.txt input.pdf output.pdf
```
### Step 2: PDF转图片(逐页)
```python
import fitz
doc = fitz.open('input.pdf')
for i in range(len(doc)):
page = doc[i]
pix = page.get_pixmap(dpi=200)
pix.save(f'/tmp/XXX-p{i+1}.png')
```
### Step 3: Vision逐页核实(关键数据页)
- 优先核实:金额页(租金标准/各年租金/押金/物业费)
- 明确问题:指定需要逐字抄写的数据类型
- 每页一个vision_analyze调用,问题要具体
### Step 4: 数学交叉验证
验算所有可推导的数字关系:
- 年递增:base × (1+rate)^(n-1) = 第n年租金
- 免租分摊:总额 ÷ 年数 = 每年减免
- 减免后 = 合同租金 - 年减免额
- 单价反推:年总额 ÷ 365 ÷ 面积 = 元/㎡/天
- 物业费:单价 × 面积 × 12 = 年总额
### Step 5: 整理md全文
- 以vision核实的数据为准(非OCR原始文本)
- 保留合同结构(条款编号+标题)
- 签章信息如实记录(印章文字、编号)
- 空白/未填写项标注"(未填写)"
- 文件存放:同一文件夹,命名格式 `XXX合同_全文.md`
## OCR常见错误类型(跃龙路实例)
| 原文 | OCR错误 |
|------|---------|
| 崇川区 | 贮川区 |
| 范存益 | 无法识别 |
| 6230520420020988877 | 6930520420020988877 |
| 361017.12 | 3610417.142 |
| 平方米 | FAK |
| 乙方 | EM |
| 续租 | SA |
## 效率提示
- 6页以内的合同:全部逐页vision(最可靠)
- 6页以上:OCR为底稿 + 关键数据页vision + 数学验算
- 所有金额/日期/面积/费率必须经vision确认,不能只信OCR
@@ -0,0 +1,73 @@
# OnlyOffice xlsx 渲染自查 + 行高 409.5 上限真相
> 用途:交付前用 **Maggie 实际看的引擎**(OnlyOffice,非 LibreOffice)把汇总表 xlsx 渲染成 PDF/图片做视觉自查;并厘清"长内容行高调不上去"的根因,避免重复踩坑浪费时间。
> 来源:2026-06-17 万达行高验证 + 世茂重做渲染自查。
## 一、x2t 渲染 xlsx → PDF(用 OnlyOffice 引擎,保真度最高)
OnlyOffice 文档转换器 `x2t` 在容器 `nextcloud-onlyoffice-1` 内,是 Maggie 在线编辑/查看时的同款引擎。比 `libreoffice --headless --convert-to pdf` 更能反映她看到的真实效果。
**format code**:513 = PDF。
**完整配方(已验证可用)**
```bash
# 1. 容器内建可写目录(默认root建后chmod 777,让ds用户能写pdf输出)
docker exec nextcloud-onlyoffice-1 bash -c 'rm -rf /tmp/sx; mkdir -p /tmp/sx; chmod 777 /tmp/sx'
# 2. 主机预先写好转换配置 xml(关键:不要在容器内用 > 重定向,会因权限失败),cp进去
# /tmp/s_convert.xml 内容:
# <?xml version="1.0" encoding="utf-8"?>
# <TaskQueueDataConvert xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
# <m_sFileFrom>/tmp/sx/s.xlsx</m_sFileFrom>
# <m_sFileTo>/tmp/sx/s.pdf</m_sFileTo>
# <m_nFormatTo>513</m_nFormatTo>
# <m_bIsNoBase64>true</m_bIsNoBase64>
# </TaskQueueDataConvert>
docker cp /path/to/target.xlsx nextcloud-onlyoffice-1:/tmp/sx/s.xlsx
docker cp /tmp/s_convert.xml nextcloud-onlyoffice-1:/tmp/sx/s.xml
docker exec nextcloud-onlyoffice-1 bash -c 'chmod 666 /tmp/sx/s.xlsx /tmp/sx/s.xml'
# 3. 以 ds 用户身份跑 x2t(x2t 本身就是 ds 进程,必须 -u ds)
X2T=/var/www/onlyoffice/documentserver/server/FileConverter/bin/x2t
docker exec -u ds nextcloud-onlyoffice-1 bash -c \
"LD_LIBRARY_PATH=/var/www/onlyoffice/documentserver/server/FileConverter/bin $X2T /tmp/sx/s.xml 2>&1; echo x2t=\$?"
# 4. 取回 + 转图片自查
docker cp nextcloud-onlyoffice-1:/tmp/sx/s.pdf /tmp/out.pdf
pdftoppm -png -r 100 /tmp/out.pdf /tmp/out_img/s
```
### ⚠️ 权限坑(踩过3次才通)
- `x2t`**ds 用户**运行,ds 的 HOME(`/var/www/onlyoffice/documentserver`)**不可写**
- ds 能写 `/tmp``/var/lib/onlyoffice/documentserver/App_Data`
- **致命点**:若用 root 建 `/tmp/xxx` 目录,ds 写不进 → `Permission denied` + `x2t=126/2`。修复=建目录后 `chmod 777`
- **第二个坑**:在容器内用 `cat > s.xml <<EOF` 重定向写 xml 也会因 ds 权限失败。**正确做法:主机写好 xml,docker cp 进去**,不在容器内重定向。
- 成功标志:`x2t=0``/tmp/sx/s.pdf``ds ds` 所有、size>0。
### 截断检测用文本提取,不靠肉眼
渲染图片若无 vision 工具看不了内容时,用 `pdftotext` 抽全文,grep 各板块**末尾独特锚点句**是否都在(在=数据完整无截断)。比看图更可靠。
> ⚠️ pdftotext 会按列宽把长句**折行**,直接 grep 完整句会假阴性——先 `tr -d '\n' | tr -d ' '` 把整页拍平再 grep(见主 SKILL Pitfall 1b 末条)。
### ⚠️ LibreOffice headless 渲超高行 xlsx 会卡死/被 SIGKILL → 直接走 x2t,别耗重试(2026-06-22 悦拾光实证)
交付前想出 PDF 自查时,**别先试 `libreoffice --headless --convert-to pdf`**——当 xlsx 含**超高行 + 海量文本单元格**(如 K列法律风险 800字、行高 600–700pt)时,LibreOffice headless 转换阶段会**卡死**,前台 `timeout` 到点被 `-15`、加 `-env:UserInstallation` 独立 profile 重试仍 `-9`(SIGKILL)。诊断特征:`libreoffice --version` 秒回正常、`free -h` 内存充足(11G+ 可用),**唯独 `--convert-to` 这一步挂**——证明不是环境/内存问题,是 LibreOffice 对「超高行+超长单元格」headless 排版的已知缺陷。
- **别浪费在 LibreOffice 上反复试**(本次连耗 4 次 -9/-15 才转向):内存够、版本正常却 `--convert-to` 挂,立即判定为超高行触发的 LO 缺陷,**直接切到本文上半「x2t 渲染」配方**。x2t 是 Maggie 实际看文件的引擎、对超高行无此问题,本就该优先用它,不该先绕 LibreOffice。
- 主 SKILL 已有铁律「LibreOffice 与 OnlyOffice 不同源、最终验收用 x2t」——本条补充其**故障模式**:LO 不只是「页数不准」,遇超高行会**直接转换失败**,更没有当 fallback 的价值。
- 清残留:转换挂掉常留 soffice 僵尸进程,重试前 `pkill -9 -f soffice; pkill -9 -f oosplash`
### ⚠️ 程序化截断自检:用 skill 自带脚本,别每次手写行高估算(2026-06-22 悦拾光教训)
交付前判断「K列长文会不会被行高截断」,**直接跑 `scripts/xlsx-rowheight-analyze.py <文件.xlsx>`**(只读,按列宽折行估每行所需高度、标出「当前行高 < 建议行高」的行),不要在 `execute_code` 里临时手写一版行高估算函数——本次就因手写估算**严重偏低**(K列 800字实际需 43 行≈665pt,手写版只给了 21 行≈321pt),自检时才发现差一倍,白绕一圈。脚本已沉淀正确的 CJK 折行口径(中文按2宽、按 `\n` 切段逐段折行、向上取整),照用即可。设完行高用同口径复检 `可显行数 ≥ 需求行数` 全绿再渲染。
## 二、行高 409.5 pt 上限真相(别再浪费时间调高)
长内容单元格"行高调不上去"的根因已查清:
| 层 | 行为 |
|---|---|
| xlsx 文件格式 | **允许** >409.5pt(openpyxl 写 900 读回 900) |
| OnlyOffice **x2t 批量引擎** | **接受** 900pt,round-trip 不 clamp |
| OnlyOffice **网页版编辑器** | **存盘时 clamp 到 ~409.5pt** ← 这是 Maggie 编辑保存后高行变矮的真因 |
- 实测:给行设 760pt 交付,Maggie 在 OnlyOffice 网页版打开编辑保存后,被压回 409.6pt。**不是 xlsx 上限,是网页编辑器 clamp**。
- **Maggie 的决定(2026-06-17)**:"行距搞不定就算了,就按照目前的最高行距就好。" → **统一用 409.6pt,不再折腾调更高**
- 实务影响:单格约容纳 18–20 个折行;超长内容(如 K 列 700+ 字、第三部分 800+ 字)在网页**在线编辑视图可滚动看全、数据层完整**,只有导 PDF 给客户打印时才需另调版式(拆单元格/缩字号)。平时 409.6 即可,与万达定稿一致。
- 若某份确需更高且**不经网页编辑器存盘**(直接文件层交付),x2t 引擎会认 900pt——但 Maggie 已拍板用 409.6,除非她另有指示,不要自作主张调高。
@@ -0,0 +1,59 @@
# OnlyOffice xlsx 行高、合并单元格截断与渲染核查(2026-06-17 万达表确立)
汇总表的「校区整体风险分析」段是横跨整行的合并单元格(如 A11:L11),内容长达 800~1200+ 字、30~47 个逻辑行。OnlyOffice 渲染这类超长合并单元格时极易**截断显示**,而 Maggie 对「文字必须完整显示不截断」是硬要求。本文件记录踩过的坑、真实成因、以及可复用的核查方法。
## 一、行高 409.5/409.6pt 不是格式天花板,是 OnlyOffice 网页编辑器的 clamp 值
这是反复出现的谜题:**自己设了 760pt 的行,交付后再打开变成了 409.6pt。** 实测厘清三层行为,别再被误导:
| 层 | 行为 | 实测结论 |
|---|---|---|
| **xlsx 文件格式本身** | openpyxl 写 `row_dimensions[N].height = 900` → 存盘 → 读回 = 900 | ✅ 文件层**不限制**行高,900pt 能正常写入并保留 |
| **OnlyOffice x2t 批量引擎** | xlsx(900pt) 经容器内 x2t 做 xlsx→xlsx round-trip → 读回 row 仍 = 900 | ✅ **不 clamp**。从文件层写的高行高,x2t 渲染/转换链路认 |
| **OnlyOffice 网页版编辑器** | 在浏览器里打开编辑、点保存 | ❌ **会把超过 ~409.5pt 的行高压回 409.5/409.6**。这就是「我的 760 变成 409.6」的真凶 |
**结论与操作要点:**
- 「调高行高让全部内容显示」在技术上**可行**——只要**从文件层(openpyxl 脚本)写入并经交付链路上传**,不要让结果再被网页编辑器存盘一次。
- 一旦用户/自己在 OnlyOffice **网页端编辑并保存**过,超限行高就被打回 409.5。若交付后还要网页端再编辑,高行高保不住。
- 设了 `height` 即自动 `customHeight=True`(openpyxl 中 `customHeight` 无 setter,不要试图直接赋值,会 `AttributeError`)。
- **若 Maggie 明确说「就按当前最高行距,不用调」→ 不折腾,保持现状交付。** 别因为自己测出能调高就擅自改——方案≠授权。
## 二、数据完整 ≠ 视图不截断(核查别只看 pdftotext)
OnlyOffice/x2t 导 PDF 时,**被行高裁掉的文字仍会写进 PDF 文本层**。所以:
- `pdftotext` 提取到尾部锚点句 = 数据在文件里(867 字符全在),**但不等于在编辑器视图里可见**。
- 真正的「截断」是**视图层**问题(行高 < 内容所需高度时底部被裁),不是数据丢失。
- 因此「数据完整性」用 pdftotext 验,「视图是否截断」要靠**行高是否 ≥ 内容估算高度**来判断(见下方脚本),或渲染成图片肉眼看。
- ⚠️ 另一个坑:x2t 导 PDF 时**对超长合并单元格本身也会截断渲染**(PDF 里只画出前一部分,如只渲染「12,000」后面没了)——这是导出视图的固有限制,不代表数据丢。判断数据完整以 openpyxl 读单元格 `.value` 字符数为准。
## 三、可复用核查工具
### 行高分析脚本(再跑用)
`scripts/xlsx-rowheight-analyze.py <文件.xlsx>`:只读分析每个 sheet 的长内容单元格,按合并宽度 + 字号估算所需视觉行数和建议行高,与当前行高对比,标出「可能截断」的行。不改文件。中文每字≈2.1 宽度单位、每视觉行≈15.5pt(10pt 字)是经验系数。
### x2t round-trip 测 clamp / 渲染(本地 OnlyOffice 实例)
容器 `nextcloud-onlyoffice-1`,x2t 路径 `/var/www/onlyoffice/documentserver/server/FileConverter/bin/x2t`
```bash
# xlsx→xlsx round-trip(测网页引擎是否 clamp 行高;format 257 = xlsx)
docker cp in.xlsx nextcloud-onlyoffice-1:/tmp/t/in.xlsx
docker exec nextcloud-onlyoffice-1 bash -c 'cat > /tmp/t/c.xml <<EOF
<?xml version="1.0" encoding="utf-8"?>
<TaskQueueDataConvert xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<m_sFileFrom>/tmp/t/in.xlsx</m_sFileFrom><m_sFileTo>/tmp/t/out.xlsx</m_sFileTo>
<m_nFormatTo>257</m_nFormatTo><m_bIsNoBase64>true</m_bIsNoBase64>
</TaskQueueDataConvert>
EOF
LD_LIBRARY_PATH=/var/www/onlyoffice/documentserver/server/FileConverter/bin /var/www/onlyoffice/documentserver/server/FileConverter/bin/x2t /tmp/t/c.xml'
# format 513 = PDF(渲染成 PDF 看版面);之后主机 pdftoppm -png -r 110 out.pdf 转图
```
- x2t 退出码 `88` = 目标 format code 不对(不是文件坏)。xlsx→xlsx 用 257,xlsx→PDF 用 513;不要走 8193 bin 中间格式做 xlsx round-trip。
- 主机有 `soffice`/`libreoffice``pdftoppm`/`pdfinfo`/`pdftotext`、中文字体 wqy-zenhei,可做降级渲染。
### 截断检测(文本锚点法)
取目标长单元格**尾部**几句独特锚点,去掉空格后在 PDF 全文 grep——但记住第二节:命中只证明数据在,视图是否截断仍以行高估算为准。
## 四、一句话 SOP
1. 估算:`scripts/xlsx-rowheight-analyze.py 文件.xlsx` → 看哪些行 `当前行高 < 建议行高`
2. 若需调高且用户同意:openpyxl 设 `row_dimensions[N].height = 建议值`,从文件层写、走交付链路上传,**别再用网页端编辑保存**。
3. 若用户说保持当前最高 → 不动。
4. 数据完整性单独用 openpyxl 读 `.value` 字符数确认,不靠 PDF。
@@ -0,0 +1,71 @@
# openpyxl 富文本局部着色 → Excel "需要修复" 陷阱(2026-06-18 世茂血泪全记录)
## 结论先行(2026-06-18 最终反转:WPS 另存救活了红色)
**openpyxl 的 `CellRichText` 局部标红会让 Microsoft Excel 报"发现部分内容有问题,是否修复"**(点修复后丢富文本→红色全没)。本 session 用尽各种手改 XML 的修法都没在 Excel 里直接救活——**但最后一步成了:openpyxl 写好红 → 用 WPS 打开 → 另存为 xlsx,Excel 不再报错且红色保留**(Maggie 亲验 Excel 正常打开+红色在)。WPS 另存把 openpyxl 的不规范 XML(`inlineStr`+`CellRichText`)整体重写成规范格式(`sharedStrings.xml`),消除所有不合规处,这才是 Excel 认它的真因。
所以"单元格内某条标红、其余黑、Excel 兼容"**有跑通的路**:`openpyxl 写富文本红 → WPS 另存`。两步缺一不可。嫌 WPS 那步麻烦/纯自动化场景,退而用**纯文本前缀**(`【需客户核实】…`,整条黑字零格式,Excel 绝不报错)。
## 需求背景
Maggie 要把汇总表里"需要客户去核实/确认某个事实"的风险点**整条标红**(如"两份合同衔接需核实""建议签约时补明2028租金"),方便客户一眼看到待办。一个单元格里装着一整列风险点(🔴1-3、🟡4-9…),只有其中某一条要红 → 必须"单元格内局部不同颜色" → 只能用富文本。
## 试过的修法,全部在 Excel 里失败
1. **openpyxl 原生 `CellRichText` + `InlineFont(color=...)`** → Excel 报错。
- 数据层验证(解压 xlsx 读 sheet XML)红色精确写入了,WPS 打开也正常,openpyxl readback 也对——**但 Excel 报错**。
2. **修 `<rPr>` 子元素顺序**:openpyxl 输出 `<rFont/><color/><sz/>`,OOXML schema 要求 `<rFont/>→<sz/>→<color/>`(sz 在 color 前)。手改 XML 把 53 个 rPr 全调正 → **Excel 仍报错**
3. **补 `<charset val="134"/>`(中文字符集)+ `<family val="2"/>`**:styles.xml 里正常字体都有 charset/family,富文本 rPr 缺了。补齐 + 正确顺序(rFont→charset→family→sz→color)→ **Excel 仍报错**
4. **去掉所有富文本,恢复纯文本** → Excel **还是报**(轻微,能打开)。说明根子不只是富文本,openpyxl 生成的这张表底层某处本就不合 Excel 严格校验。
## 为什么这么难定位——验证工具全部"宽松",骗过自己
| 工具 | 行为 | 能否当 Excel 合格证据 |
|---|---|---|
| openpyxl readback | 读回红色正确、XML 合法(lxml 解析过) | ❌ 不能 |
| WPS | 打开完全正常、红色在 | ❌ 不能(WPS 容错宽松) |
| OnlyOffice x2t | 转换成功、渲染出红色 | ❌ 不能(太宽松) |
| LibreOffice headless | 本环境**连干净文件都报 `source file could not be loaded`** | ❌ 不能(环境本身坏,不代表 Excel) |
| **Microsoft Excel** | **报"发现部分内容有问题/需要修复"** | ✅ 这才是真相 |
**致命点**:本地没有任何能复现 Excel 严格 OOXML 校验的工具。所有手头工具都比 Excel 宽松,导致我反复"修好了→还是不行",把用户当测试员(连续 4+ 次"还是不行"),用户失去耐心。
## 行为铁律(比技术更重要)
1. **改完无法自验 Excel 行为时,如实说"我这边验不了 Excel,你帮我打开看下",绝不断言"修好了"。** 没有复现工具就别打包票。
2. **"WPS 打开正常"≠交付合格**。Maggie 和她客户用 Microsoft Excel,以 Excel 为准。
3. **别在一个底层格式有问题的文件上反复打补丁**——越补越不可控。识别到"连纯文本版都报错"时就该换根本方案,而不是继续修富文本。
## 正确做法(Excel 绝不报错)
- **首选·纯文本标记**:需核实条前加 `【需客户核实】` / `❗待核实:` 前缀,整条黑字。
- **整格统一格式**:`cell.font=Font(bold=True/color=...)``PatternFill` 背景色——安全,但整格所有条目一起染,仅"整格一条"时可用。
- **真要某条带色**:让用户用 **WPS 打开→另存为 xlsx**,WPS 重写为规范格式后 Excel 不再报错、富文本保留(需用户手动一步)。
## 附
- 若硬要走富文本(不推荐),`scripts/fix-richtext-rpr-order.py` 可把 rPr 顺序修成 Excel 合规——但**本 session 实证:修了顺序 Excel 仍报错**,所以这个脚本不保证解决问题,仅作记录。
- 富文本验证小坑:判断红色 run 别用宽松 `'FF0000' in run`——黑色 `FF000000` 含子串 `FF0000` 会被误判成红。必须精确匹配 `rgb="FFFF0000"`(红)排除 `rgb="FF000000"`(黑)。
## ⚠️ 二次编辑陷阱:openpyxl 重存会把 WPS 救回的红色一键毁掉(2026-06-18 世茂实证)
WPS 另存后的好文件存储 = `sharedStrings.xml` + 富文本红 `<r>` run。**对它再做任何 `openpyxl.load_workbook → 改 → save` 都会把成果作废**:openpyxl 把整表打回 `inlineStr``sharedStrings.xml` 消失、**红色 run 3→0**,Excel 又报"需要修复"。实测:改个 H11 单元格而已,红色全没了——幸亏改前留了 WPS 好基线 `_bak_`,才回得来。
**铁律:改已标红(WPS规范化)的 xlsx,改一个字都不能用 openpyxl 存。** 正解是在 `sharedStrings.xml` 的 XML 层做外科手术。
### 外科手术流程(已验证正确)
1. **改前先备份** WPS 好版本为 `_bak_*.xlsx`(openpyxl 一旦失手,这是唯一退路)。
2. `zipfile` 解压好文件到临时目录。
3. **定位"改哪个格 → 改第几条 si"**`sheet1.xml``<c r="H11" t="s"><v>45</v></c>``<v>45` 就是 sharedString 索引。`--map` 一键列出全部映射。
4. lxml 打开 `xl/sharedStrings.xml`,**只动目标 `<si>``<t>` 文字**:
- 纯文本格(无红 run)→ 清空子节点、重写单个 `<t xml:space="preserve">新文本</t>`
- 含红 run 的格 → **只改黑色 `<r>` 的 `<t>`**;红 `<r>`(带 `<rPr>…<color rgb="FFFF0000"/>`)一个字不碰。
- 删一条红色风险项 + 顺移编号:`si.remove(目标<r>)` 后把后续 `<r>``"10. "→"9. "` 等前缀顺移,保 1–N 连续。删红 run 时红色计数随之 −1。
5. **规范重打包**`[Content_Types].xml` 必须 zip 第一项、`_rels/` 次之,否则 LibreOffice 等严格解析器报 `source file could not be loaded`(Excel/WPS 宽容,但别赌)。`ZIP_DEFLATED`
### 改后五查(本地验不了 Excel 时能做的最强保证,过了再发 Maggie)
`xl/sharedStrings.xml` 仍在(不在 = openpyxl 又把富文本毁了);② 红色 run 数 = 预期(删 1 条红就 3→2,没删则不变,精确数 `rgb="FFFF0000"`);③ `zipfile.testzip()` 通过 + 所有 `.xml/.rels` lxml 可 `fromstring`;④ 目标格文字已更新、编号连续、旧表述("留白/未约定"等)全表 `grep` 0 残留;⑤ 与 WPS 好基线部件清单同构(差异仅空目录条目可接受)。
一键:`python3 scripts/edit-redmarked-xlsx.py --verify 改后.xlsx --baseline WPS好基线.xlsx --expect-red 2`。完整实现(解压→按 si 改 `<t>`→删 run 顺移→规范重打包→五查 + 可 import 的工具函数)见 `scripts/edit-redmarked-xlsx.py`,照抄别重写。
@@ -0,0 +1,66 @@
# Output Consistency for Multi-Campus Batch Processing
**Lesson learned**: 2026-06-30 — 跃龙路 vs 解放中路/星月等7校区
## Root Cause: LLM Context Anchoring
LLM output style is heavily influenced by what's in the context window:
- **Consecutive processing** (same session, same time window): previous campus outputs are still in context → LLM naturally "copies the style" → consistent output
- **Separated processing** (different session, different time): previous outputs are gone → LLM generates with its own default style → drift
This is NOT a workflow bug or a rules problem. The rules were the same. The output format specification was the same. But the LLM produced different styles because the **contextual anchor** was different.
Maggie's concern (verbatim): "金额,风险,模版对比各列内容的审查和修改意见表达都不一样了...第一批里从解放中路到星月的审查逻辑和表达都是一致的,为什么到跃龙路又变掉了?"
## Three-Layer Defense
### Layer 1: Fixed Templates in Workflow Prompt (hardest constraint)
Embed complete output examples + "禁止" (prohibited) rules directly in the data-extractor role's procedure section. Example structure:
```yaml
H列格式规范:
风格: 段落式叙述
结构: 【租金】→【付款推算】→【押金】→【物业费】→【违约金】
禁止:
- bullet符号(•、-、*)
- "【大类·条款号】"合并标题
- 过度拆分为逐行小条目
K列格式规范:
风格: 先【整体评价】段落,再❗【需客户核实】编号列表
禁止:
- "10项风险(3高/5中/2低):"统计式开头
- "1.【高·第十条】"标签格式
- markdown表格列风险
L列格式规范:
风格: 一句话说明+编号列表
禁止:
- "34处差异(16缺失/15修改/3新增)vs 07模版:"统计式开头
- "【核心缺失】"分类小标题
- "vs"分隔模版和合同
```
Updated in nantong-lease-audit.yaml v2 (hash: `C77579MQ9QPKE`).
### Layer 2: Load Previous Campus Output as Reference
Before generating data for a new campus, read the most recent completed campus xlsx and extract H/K/L column content as style reference:
```bash
REF_XLSX=$(ls -t /path/to/campuses/*/*梳理*.xlsx 2>/dev/null | head -1)
```
Use openpyxl to read H/K/L values from the first data row, inject into the data-extractor prompt context.
### Layer 3: Post-Processing Validation Script
Run `scripts/output-style-check.py <row-data.json>` after data extraction:
- Checks 8 rules across H/K/L/J columns
- Exit 0 = pass, exit 1 = fail (lists specific issues)
- If fail → fix the specific issues before proceeding to xlsx generation
## Key Insight
Rules alone are insufficient to prevent style drift. The LLM needs **concrete examples in context** at the moment of generation. Three layers provide redundancy: if Layer 1 is imperfectly followed, Layer 2 gives a fresh anchor, and Layer 3 catches what slips through.
@@ -0,0 +1,143 @@
# 汇总表输出格式规范(跃龙路定稿为唯一基准)
> Maggie 2026-07-03 指令:"输出格式参照跃龙路,别自己创设"
> 龙信首次交付因自创格式被退回重做。
## 铁律:不得自创格式
跃龙路-梳理-MJ-20260703.xlsx 是格式唯一参照物。所有新校区必须与其视觉一致。
不熟悉格式时先 `read_file` 跃龙路汇总表各列内容,照抄格式框架填数据。
---
## D列(合同当事人)格式
```
甲方:XX(自然人)
乙方:南通新东方教育科技有限公司
```
- 简洁,不加多余括号说明(如"出租方""承租方")
- 签约日期不放D列(放G列或不单独列)
## E列(租赁标的/服务范围)格式
```
地址全文
用途:XX
```
- 末行加用途
## G列(合同期限)格式
```
2026年X月X日至20XX年X月X日
(XX个月,含免租装修期XX天/个月)
免租装修期:2026.X.X-2026.X.X
```
## H列(金额/费用)格式
```
【押金】XX元(第X条)
【租金】XX元/年(含税),≈XX元/月(第X条)
【物业费】...
【水电费】...
【支付方式】半年一付XX元/期,先付后用(第X条)
【付款推算】...
 第1期(起-止):金额 ┃ 付款期限
 ❗第2期(起-止):金额 ┃ 付款期限
```
- 付款推算各行用全角空格缩进 + `┃` 分隔金额和付款日期
- ❗标记未付期次(句首)
## I列(核心内容)格式
```
【用途】...(第X条)
【转租】...(第X条)
【装修】...
【维修责任】甲方:...;乙方:...(第X条)
【违约金·逾期付款】每日X‰(第X条)
【违约金·逾期归还】...
【违约金·乙方提前解租】...
【违约金·甲方提前解租】...
【不可抗力/政府拆迁】...
...
【争议解决】XX人民法院(第X条)
```
- 违约金用 `【违约金·XX】` 子标签区分
## K列(法律风险)格式
```
【整体评价·站承租方(乙方)立场】
本合同为...
〇 需注意
1. 条款名(第X条X款):具体风险说明。
2. ...
3. ...
【提前退租法律后果】
提前X月书面告知 + ...
```
### ⚠️ 禁止事项(龙信首次被退回的原因):
- ❌ 不用 🔴🟡🟢 emoji标记风险等级
- ❌ 不用 `【需注意的风险点】` 作小标题
- ❌ 不用 `【整体评价】` 不带"·站承租方(乙方)立场"后缀
- ✅ 用 `〇 需注意` 作风险列表引导词
- ✅ 用纯数字编号(1. 2. 3.)不加emoji前缀
## L列(与07标准模版差异)格式
```
本合同为甲方(XX)制式合同,非新东方标准制式,与07标准模版差异极大(N项差异)。主要差异:
1.【差异标签】07模版:XX→本合同:XX
2.【差异标签】07模版:XX→本合同:XX
...
```
- 每条差异用 `【标签】07模版:...→本合同:...` 格式
- 物业合同写 `物业服务协议,无对应07标准模版。`
## 整体风险分析与建议段格式
```
【整体评价】
XX校区共N份合同...
【其他法律关注点】
1. ...
2. ...
【提前退租法律后果】
- 租赁合同:...
- 物业合同:...
- 5年总租赁成本约:...
【续签建议】
1. ...
2. ...
```
### ⚠️ 注意:
-`【其他法律关注点】` 不是 `【法律关注点】`
- 关注点用纯数字编号不加emoji
- 总成本计算放在提前退租法律后果段末尾
## 物业行K列格式(简洁版)
```
物业合同风险XX。关注点:
1. ...
2. ...
3. ...
```
- 不用 `【整体评价·站承租方(乙方)立场】` 开头
- 直接一句话概括+编号列风险点
@@ -0,0 +1,54 @@
# Output Style Consistency — Three-Layer Defense
## Problem (2026-06-30 Maggie discovery)
When processing multiple campuses in separate sessions, the LLM's data-extractor output drifts in style because earlier campus outputs are no longer in context. The first batch (解放中路→星月, 7 campuses) was consistent due to context anchoring — each campus saw the previous output and copied its style. When 跃龙路 was processed in a new session, the LLM had no reference and generated a different style:
- H列: bullet-list format instead of paragraph-style
- K列: statistical tag format ("10项风险(3高/5中/2低)") instead of narrative ("【整体评价】...")
- L列: categorized headers ("【核心缺失16项】") instead of numbered list
Maggie: "金额,风险,模版对比各列内容的审查和修改意见表达都不一样了"
## Root Cause
LLM context anchoring effect: style consistency comes from seeing previous outputs in context, not from prompt instructions alone. When context window resets between sessions, the anchoring is lost.
## Three-Layer Defense (implemented in nantong-lease-audit v2 workflow)
### Layer 1: Fixed Templates in Workflow Prompt
In the data-extractor role's procedure, embed concrete style examples with explicit "禁止" (prohibited) patterns:
```yaml
# H列 format: paragraph-style with 【】category headers, no bullets
# K列 format: 【整体评价】opening + ❗【需客户核实】numbered list
# L列 format: one-line relationship statement + numbered diff list
# J列: exactly "履行中"/"已到期"/"已解除", no parenthetical explanations
```
### Layer 2: Reference Loading Before Generation
Before generating data for a new campus, load the most recent completed campus's xlsx and extract H/K/L content as style anchor:
```bash
REF_XLSX=$(ls -t /path/to/房租物业合同/*/*梳理*.xlsx 2>/dev/null | head -1)
```
Read with openpyxl, inject into prompt context. LLM sees "previous campus looks like this" and naturally aligns.
### Layer 3: Post-Processing Validation
Run `output-style-check.py` after generation, before upload:
```bash
python3 ~/.hermes/scripts/output-style-check.py <campus>-row-data.json
```
Checks 8 rules:
- H列: no bullet symbols (•/-/*), no "【category·clause】" merged headers
- K列: no statistical openings ("X项风险(Y高/Z中)"), no "序号·等级·条款号" tags, no markdown tables, must have 【整体评价】
- L列: no statistical openings ("X处差异(Y缺失/Z修改)"), no "【核心缺失/修改】" sub-headers, no "vs" separators
- J列: exact match "履行中"/"已到期"/"已解除"
Exit 0 = pass, exit 1 = list violations for fix.
## Workflow Integration
In nantong-lease-audit.yaml v2 (hash C77579MQ9QPKE), the data-extractor procedure sections 5-6 implement layers 1-2. Layer 3 is run manually after workflow completes, before xlsx generation.
## Script Location
`~/.hermes/scripts/output-style-check.py` — standalone, no dependencies beyond stdlib json/re/sys.
@@ -0,0 +1,95 @@
# 输出风格一致性规则(2026-06-30 跃龙路教训)
## 背景
LLM输出风格依赖**上下文锚定效应**——同一session连续处理多校区时,前序输出在上下文中,LLM自然"照着前面写",风格一致。新session/新时间段处理时,前序输出不在上下文中,LLM按自己的理解重新生成风格,导致不一致。
**根因**:这不是规则问题,不是workflow问题,是LLM固有特性。
## 三层防线
| 层 | 机制 | 位置 |
|---|---|---|
| 第一层(治本) | workflow prompt中写死格式模板+示例+禁止项 | `nantong-lease-audit.yaml` data-extractor角色procedure第5节 |
| 第二层(治标) | 跑新校区前自动加载前序校区xlsx的H/K/L列 | `nantong-lease-audit.yaml` data-extractor角色procedure第6节 |
| 第三层(兜底) | 生成后跑校验脚本 | `python3 scripts/output-style-check.py` |
## H/K/L/J列格式规范
### H列(金额/费用)——段落式叙述
**正确风格**:用【】标注大类,条款号用中文括号,不用bullet符号
```
【租金】一期一交,当期缴纳次年租金(首期14个月,后续各期12个月)(第三条)
前三年100,564.80元/年(月租≈8,380.40元),后两年递增5%为105,593.04元/年(月租≈8,799.42元)(含税)
【付款推算】起租日2026/3/18,每年一付,每期租金到期日前一个月内付(第三条):
第1期2026/5/18–2027/5/17:100,564.80元(首期14个月含免租期...
```
**月租换算**(2026-07-03 Maggie确认):一律用行内括号写法,写在租金标准行内。
-`年191,990元(月租≈15,999元)`
- ❌ 单独另起一行写"折合月租约15,999元"
**付款推算逐期格式**(2026-07-03 星月定稿):
```
【付款推算】租金+物业费合并列示,半年一付,提前30天(第四条第3款)
 第1期(2024.8.1-2025.1.31):租金95,995 + 物业12,624 = 108,619元 ┃ 签约后7天内
 第2期(2025.2.1-2025.7.31):租金95,995 + 物业12,624 = 108,619元 ┃ 付款期限2025.1.2
 ❗第7期(2027.8.1-2028.1.31,递增后):租金100,795 + 物业12,624 = 113,419元 ┃ 付款期限2027.7.2
```
- 每期独立一行:区间 + 租金 + 物业 = 合计 ┃ 付款期限
- ❗标注当前尚未到期的付款
- 含免租期/递增的期次加括号说明
- 数据来源:直接从H列(租金标准/物业费/支付方式)+ G列(起止日期/免租期)推算即可,无需每次回OCR原文
**禁止**
- bullet符号(•、-、*)
- "【租金·第四条】"这种"大类·条款号"合并标题
- 过度拆分为逐行小条目
- 月租换算单独占一行
### K列(法律风险)——整体评价+核实项+叙述式
**正确风格**:先【整体评价】段落,再❗核实项编号列表,再具体风险叙述式
```
【整体评价·站承租方(乙方)立场】本合同为甲方(运营管理公司)制式文本,条款整体偏中性...整体风险中等。
❗【需客户核实】
1. 出租方为运营管理公司(南通鸿城运营管理有限公司):建议核验甲方与产权人之间的授权委托关系...
2. 合同用途为"商业"与新东方实际教学/培训用途不符...
```
**禁止**
- "10项风险(3高/1中高/5中/1低):"统计式开头
- "1.【高·第十条第1款】"标签格式
- markdown表格列风险
### L列(模版差异)——叙述式开头+编号列表
**正确风格**:先一句话说明合同与07的关系,再编号列表
```
本合同为甲方(运营管理公司)制式文本,与07模版结构、编号、措辞均不同(非07模版填空版)。
主要文本差异(共25条逐条差异+12条缺失条款):
1. 合同标题:模版为"房屋租赁合同";本合同为"房屋租赁协议"
2. 甲方权属保证:模版有详细权属保证+查验+违约解除条款;本合同仅"甲方承诺..."(第五条)
```
**禁止**
- "34处差异(16缺失/15修改/3新增)vs 07模版:"统计式开头
- "【核心缺失16项】"分类小标题
- "vs"分隔模版和合同
### J列(状态)——只写三个字
`履行中` / `已到期` / `已解除`,不加括号说明。
## 校验
```bash
python3 scripts/output-style-check.py /tmp/nantong-lease-audit/<校区>-row-data.json
# exit 0 = 通过,exit 1 = 列出具体问题
```
@@ -0,0 +1,106 @@
# PDF OCR Troubleshooting for Scanned Chinese Contracts
**Lesson learned**: 2026-06-30 — 通州金鹰 (23MB lease + 1.4MB property contract)
## Problem Pattern: Mixed OCR Results
Scanned PDFs of Chinese contracts often produce:
- Some pages: clean OCR text via `ocrmypdf` + `pdftotext`/`pymupdf`
- Other pages: completely empty output (0 chars)
- Other pages: garbled character soup (`\u0000` null bytes, random symbols)
## Diagnosis
```python
from PIL import Image
import numpy as np
for i in range(1, N+1):
img = Image.open(f'pages/page-{i}.jpg').convert('L')
arr = np.array(img)
mean = arr.mean() # ~160-170 for scanned contracts
std = arr.std() # ~30-37 for text-heavy pages
# If tesseract returns 0 chars but mean/std look normal → needs preprocessing
```
## Fix 1: Binary Threshold Conversion
Standard tesseract OCR produces empty output on some scanned pages. The fix:
```python
from PIL import Image
import numpy as np
for i in empty_pages:
img = Image.open(f'pages/page-{i}.jpg').convert('L')
arr = np.array(img)
threshold = 140 # Key value: too high = lose text, too low = noise
arr = (arr < threshold).astype(np.uint8) * 255
bw = Image.fromarray(arr)
bw.save(f'pages/page-{i}_bw.png')
# Then: tesseract pages/page-{i}_bw.png stdout -l chi_sim
```
**Why this works**: Some scanners produce pages where the background is very slightly off-white (mean ~168 vs pure white 255). Tesseract's adaptive thresholding fails on these. Hard binary threshold at 140 separates text (dark, <140) from background (>140) cleanly.
## Fix 2: Vision Service Timeout
`vision_analyze` times out on large images (>200KB at 300dpi). Always compress:
```python
from PIL import Image
img = Image.open(f'pages/page-{i}.jpg')
img = img.resize((img.width // 3, img.height // 3), Image.LANCZOS)
img.save(f'pages/page-{i}_small.jpg', quality=60)
# Target: 80-95KB per page at 850x1100 pixels
```
## Fix 3: ocrmypdf Text Layer Extraction Failure
Even after `ocrmypdf`, the embedded text layer may be garbled when extracted via `pdftotext` or `pymupdf`. This is a known issue with `ocrmypdf` + tesseract for Chinese text.
**Solution**: Don't rely on `ocrmypdf`'s text layer. Instead:
1. Use `ocrmypdf` to produce the OCR'd PDF (for archiving)
2. Separately extract pages as images: `pdftoppm -jpeg -r 300 input.pdf pages/page`
3. Run tesseract directly on each image
4. For empty pages, apply binary threshold (Fix 1) and retry
## Workflow: Complete OCR Pipeline
```bash
# 1. Extract pages as images
pdftoppm -jpeg -r 300 "contract.pdf" pages/page
# 2. Standard tesseract on all pages
for f in pages/page-*.jpg; do
tesseract "$f" "${f%.jpg}" -l chi_sim 2>/dev/null
done
# 3. Identify empty pages (0 chars)
for f in pages/page-*.txt; do
chars=$(wc -c < "$f")
if [ "$chars" -lt 10 ]; then
echo "EMPTY: $f"
fi
done
# 4. Binary threshold + retry on empty pages (Python)
# 5. Vision for remaining empty pages (compress to <100KB first)
# 6. Concatenate all page texts into final OCR file
```
## Known OCR Garble Patterns in Chinese Contracts
| OCR Output | Likely Value | Context |
|---|---|---|
| "于65" / "也65" / "了芋65" | 765 | Area in ㎡ |
| "巧" / "葬" | 15 / 30 | Working days |
| "101" | 10 | Percentage |
| "0.1‰%o" | 0.1‰ | Daily penalty rate |
| "直通市赴" | 南通市通州区 | City name |
| "驳玉年" | 2025年 | Year |
| "瑞殉年" | 2025年 | Year |
| "嫂万元整" | 肆万元整 | Amount in Chinese |
| "103" | 10 | Working days |
**Rule**: Never guess OCR values. Mark as "⚠️待核实原件" in the Excel and list in 需客户核实 section.
@@ -0,0 +1,136 @@
# 物理依赖链架构(2026-06-29 确立)
## 设计理念
规则写在skill里是"纸面约束"——看了可以跳过。物理依赖链把关键步骤之间的依赖变成"物理约束"——上一步没做完,下一步**跑不起来**。
核心区别:
| | 纸面规则 | 物理依赖链 |
|---|---|---|
| 什么时候卡 | 做完后检查(事后) | 做之前检查(事前) |
| 能不能绕过 | 能(不跑检查直接发) | 不能(脚本直接中止) |
| 靠什么保证 | 人的记性 | checkpoint文件 |
## 三道卡口
### 卡口1:OCR完整性 → 建表
```
Step 1 OCR完成
ocrmypdf → pdftotext → .md文件
python3 scripts/ocr-integrity-check.py <工作目录>
↓ 检查:无乱码/无占位符
↓ 通过 → 生成 step1.verified
↓ 不通过 → 列出问题,不生成checkpoint
Step 3 建表时:
single-campus-builder.py 检查 step1.verified 是否存在
不存在 → sys.exit(1),表中止
```
### 卡口2:模版比对 → 建表
```
Step 2 动作B完成(delegate subagent)
subagent产出 模版比对-*.md
python3 scripts/template-diff-verify.py <工作目录>
↓ 检查:文件存在、>2000字节、≥10个条款号、≥3个差异关键词
↓ 通过 → 生成 step2b.verified
↓ 不通过 → 列出问题,不生成checkpoint
Step 3 建表时:
single-campus-builder.py 检查 step2b.verified 是否存在
不存在 → sys.exit(1),表中止
```
### 卡口3:最终闸门
```
Step 6 交付前
python3 scripts/delivery-gate.py <xlsx> <工作目录> <校区名>
↓ 9项检查(G1-G9)
↓ 全过 → exit 0,可以发
↓ 任一不过 → exit 1,禁止发
```
## 环境变量
建表脚本通过环境变量 `CAMPUS_WORKDIR` 定位工作目录:
```bash
CAMPUS_WORKDIR=/tmp/人民中路 python3 templates/single-campus-builder.py
```
如果不设此变量,建表脚本跳过checkpoint检查(向后兼容),但 delivery-gate 事后仍会拦截 G8/G9。
## 已修复的误报模式(2026-06-29 多校区实证)
### ocr-integrity-check.py 误报
| 模式 | 误报原因 | 修复 |
|------|---------|------|
| 统一社会信用代码(如91320600MA1NAEBX26) | 代码中的英文字母(MA1NAEBX)被识别为"公司名乱码" | 脚本已增加前后字符检查:字母前后有数字→跳过 |
| 合同编号(如XYZL-20241029-2#1F) | 4+连续大写字母匹配乱码正则 | 临时修复:将XYZL替换为Xyzl(小写)避免匹配 |
| 签名页英文残留 | 扫描件签名区域的OCR噪声 | 用正则替换为[签章]占位符 |
**修复后仍可能触发的情况**:如果OCR文件中出现新的非标准英文序列,脚本仍会报"公司名乱码"。此时需判断:
- 前后有数字(如信用代码)→ 手动修正OCR文件中的具体位置
- 签名/盖章区域 → 用正则批量替换为[签章]
- 真正的乱码(关键字段无法识别)→ 用vision或tesseract看原图补全
### template-diff-verify.py 误报
| 模式 | 误报原因 | 修复 |
|------|---------|------|
| "模版表述:"(冒号) | 原正则只匹配"模版表述为" | 已改为`模版表述[为::]`,同时匹配冒号 |
| subagent用"无此条款"描述差异 | 原关键词列表不含此表述 | 已新增"无此条款"和"不存在"为有效关键词 |
## OCR补全的tesseract降级方案
`vision_analyze`超时(常见于高分辨率扫描件)时,tesseract可作为降级方案:
```python
import fitz
doc = fitz.open('合同.pdf')
page = doc[page_index]
mat = fitz.Matrix(200/72, 200/72) # 200 DPI
pix = page.get_pixmap(matrix=mat)
pix.save(f'page_{page_index+1}.png')
```
```bash
tesseract page_X.png stdout -l chi_sim+eng --psm 6 2>/dev/null
```
**适用场景**
- 读取合同首页(甲乙方信息、地址、信用代码)
- 读取特定条款段落(裁剪页面局部区域)
- 补充OCR .md文件中的乱码位置
**不适用场景**
- 手写签名、印章内容(tesseract无法识别)
- 低对比度扫描件(需先二值化处理)
## 企业入驻合同等非标准格式(2026-06-29 星月实证)
部分园区使用"企业入驻合同"而非标准"房屋租赁合同"格式。特征:
- 合同标题为"企业入驻合同"或"入驻协议"
- 包含物业管理费(打包在租金中或单独列出)
- 条款结构与07模版完全不同(无07模版的条款号体系)
- 常见于创业孵化器、科技园区、产业园
**处理方式**:subagent模版比对时仍需与07模版比对(找出缺失的保护性条款),但在L列注明"本合同为企业入驻合同格式,非07标准模版"。
## 脚本清单
| 脚本 | 位置 | 作用 |
|------|------|------|
| ocr-integrity-check.py | scripts/ | 检查OCR .md文件乱码/占位符 → step1.verified |
| template-diff-verify.py | scripts/ | 检查模版比对输出充实度 → step2b.verified |
| single-campus-builder.py | templates/ | 建表前检查两个checkpoint |
| delivery-gate.py | scripts/ | 最终9项闸门(含G8/G9复查checkpoint) |
@@ -0,0 +1,59 @@
# Pitfalls from 2026-07-03 Session (龙信+跃龙路+通州金鹰+星月+小石桥晏园)
## 🔴 Pitfall 30: 所有租赁合同必须做模版比对——不论制式
**栽点(龙信·2026-07-03)**:龙信广场为甲方制式商铺租赁合同(非新东方制式),错误判断"非新东方制式无需比对",L列直接写"非新东方制式合同,无对应07标准模版"。Maggie追问"为什么没有做租赁合同和标准合同的对比?"
**铁律**:所有租赁合同不论制式都必须与07标准模版做delegate_task比对。
- 新东方制式 → 比对(找出微调差异)
- 甲方制式 → 比对(差异更大,更需要让客户看到缺失了哪些保护)
- 自由协商合同 → 比对
- 物业合同、主体变更协议 → 不需要比对(类型不同)
**根因**:误把"是否为同一模版"当作是否比对的判断依据。正确逻辑是:比对的目的是让客户看到"相比新东方标准保护,本合同缺了什么"——越不是新东方制式,差异越大,越需要比对。
---
## Pitfall 31: 多份合同必须分行,不得合并
**栽点(小石桥晏园·2026-07-03)**:将租赁合同和主体变更协议合成一行。Maggie明确要求:"3份合同分别提取,分别审阅,做成三行,不要混在一起写"。
**铁律**:每份独立合同PDF = 汇总表中独立一行。
- 租赁合同 → 独立一行
- 主体变更协议 → 独立一行
- 物业合同 → 独立一行
- 不可将变更协议"合并"到租赁合同行内
---
## Pitfall 32: 不得擅自删除旧版文件
**栽点(通州金鹰·2026-07-03)**:新建汇总表后擅自删除了旧版xlsx(0626/0630版)。Maggie指出"我没让你清理旧表,请恢复"。
**铁律**:只有在Maggie明确说"删掉旧的"/"清理"时才能删除。新版文件上传后旧版保留,不主动清理。
---
## Pitfall 33: K列关联关系的表述须有合同文本依据
**栽点(跃龙路·2026-07-03)**:K8写"物业方(范存益控制的东德物业)与出租方(范存益本人)实为关联方"。Maggie指出:合同只能证明他是联系人,不能证明他是股东/法定代表人/实控人。
**修正后写法**:"出租方(范存益)同时为物业方(东德物业)的联系人,电话地址一致,两者可能存在关联关系,物业服务质量纠纷时需注意利益一致性。"
**铁律**:K列的每一句论断必须能在合同文本中找到直接依据。不能从合同文字推导出未经验证的法律事实(如"控制""实为"等断言)。
---
## 效率提升:H列付款推算可从汇总表数据直接推算
**场景**:Maggie确认"根据汇总表的信息是不是也可以推算?这样是不是效率高点?"
**方法**:H列已有支付方式(半年一付、提前X天)、租金标准、递增规则、物业费单价,G列有起止日期和免租期。这些信息足够直接推算每一期的金额和付款截止日,不需要每次回md原文。
**适用条件**:数据明确无歧义时直接从H/G列推算。只有遇到数据存疑(如附件分期跟正文对不上)才需要回md核实。
---
## H列月租换算写法统一(Maggie 2026-07-03)
月租换算一律用行内括号写法:写在租金标准那行括号里(如"年XX元(月租≈XX元)"),不单独另起一行。
@@ -0,0 +1,141 @@
# 旧表重做模式(2026-07-13 金飞达重做教训)
## 适用场景
当用户明确说:
- “忽略之前做过的汇总表,重新开始”
- “不要沿用旧表,重新做”
- “旧表不作为依据,重新汇总和审查”
则本轮任务进入**旧表重做模式**。
## 核心规则
### 1. 旧表只可作为线索,不可作为数据来源
旧表可以用于:
- 找文件清单
- 参考板块结构
- 回忆历史模版比对方向
- 辅助定位此前做过的合同组合
旧表**不可以**用于:
- 直接复用 D~L 列文字
- 直接复制付款推算
- 直接沿用 K 列风险结论
- 直接沿用 L 列差异表述
**铁律**:新表的 D~L 列内容必须回到现有 `_全文.md` / 合同原文重新生成。
---
### 2. 必须重新读取规则,不得默认“上次做法仍然对”
开工前必须重新核读至少以下规则:
- 输出格式基准(跃龙路/桃坞路)
- K/L 分工规则
- H 列付款推算规则
- I 列类目覆盖规则
- 07 模版逐条比对规则
原因:用户说“重新开始”,不仅是重做表,也是**重置旧判断**。不能因为“这个校区以前做过”就把旧格式、旧逻辑直接续上。
---
### 3. 排序和板块以本轮用户指令为准
如果用户本轮明确指定:
- “按租赁物分类”
- “同一租赁物按签约时间排列”
- “输出格式按照桃坞路校区”
则必须以本轮要求重排,不能沿用旧表的:
- 按合同类型分组
- 按 workflow 完成顺序排列
- 按旧版人工习惯排序
**一句话**:旧表结构不是规范,用户这轮指令才是规范。
---
### 4. 先产出一份新的可验证成品,再继续精修
重做时不要先陷入“必须一次做到极致”而迟迟不出成品。正确节奏:
1. 基于现有 md 和原文先生成一份新的 xlsx
2. 立即跑校验:
- `kl-separation-check.py`
- `i-column-coverage-check.py`
- H 列人工四检
3. 再针对暴露的问题做第二轮精修
这样可以先保证:
- 有真实交付物
- 有工具输出支撑
- 后续优化有具体落点
---
### 5. I 列覆盖报警要区分“合同缺项”与“漏提取”
`i-column-coverage-check.py` 报警后,不能机械追求“全部消警”。
尤其是以下合同:
- 临时仓储合同
- 简短补充协议
- 只有价格/主体变更的协议
这类文本天然不具备完整的 21 类租赁主合同要素,报警可能是**合理报警**。
处理方式:
- 主租赁合同:优先补足到阈值以上
- 简短合同:人工核实“确实没有这些类目”后保留报警结论即可,不为过关硬凑内容
---
### 6. H 列付款时间必须独立重算
用户如果特别点名“付款时间要推算”,则 H 列不能复用旧表付款描述,必须按现有条款重新算:
- 首期付款区间
- 后续期次
- 先付后付 / 后付前置天数
- 免租期是否并入首期
- 物业费是否与租金同步结算
- 补充协议是否改了原付款机制
**铁律**:哪怕旧表看起来“差不多”,也必须重算一遍。
---
### 7. K/L 列必须同步重建,不得“旧K旧L沿用”
重做模式下最容易偷懒的地方就是 K/L 列。
但用户要求“重新开始”时:
- **K 列**必须回到合同原文,重新做八维审查与提前退租分析
- **L 列**必须重新按 07 模版逐条对照,不得只搬旧表结论
尤其当:
- 合同排序变了
- 板块结构变了
- 同一租赁物的合同关系重新被识别了
旧 K/L 直接搬运会导致逻辑错位。
---
## 实务提示
### 推荐工作顺序
1. 盘点本轮实际要纳入的新表文件清单
2. 按用户要求先定板块结构和顺序
3. 逐份回读 `_全文.md`
4. 先写 D~J
5. 再写 K(八维+提前退租)
6. 再写 L(07 模版差异)
7. 生成 xlsx
8. 跑校验并修正
### 不推荐做法
- 直接在旧 xlsx 上修修补补
- 先复制旧表全文再逐格改
- 默认旧 K/L 正确,仅小修
- 用旧付款推算“参考一下就当新结果”
---
## 来源
2026-07-13 金飞达校区:用户明确要求“忽略之前做过的汇总表,重新开始”,并强调付款时间重新推算、八维审查不能忘、07 模版逐条比对、按租赁物分类和同一租赁物按签约时间排列。实践证明:正确做法是把旧表降级为线索源,而不是底稿源。
@@ -0,0 +1,78 @@
# 重做校区 Workflow(Maggie指出质量问题后)
> 2026-07-01 凤凰文化实证:Maggie发现OCR读取质量差→模版比对有错误→汇总表不准确,要求按workflow重做。
## 触发场景
- Maggie看了汇总表后说"有问题"/"重新做"
- 发现OCR误读导致模版比对结论错误
- 发现关键字段(费率、单位、条款内容)与原件不符
## 流程(仍走完整闸门,不跳步)
### 1. 仍然跑闸门脚本
```bash
python3 scripts/campus-workflow-gate.py <校区名>
```
即使是重做,也跑闸门。这不是形式主义——重做时更容易因为"上次做过了"而跳步。
### 2. Step 1 聚焦 vision 修正
- 不需要重跑 ocrmypdf 全篇(原始 OCR 文本已有)
- **聚焦**:对乱码区域逐页 `pdftoppm → vision_analyze`,获取真实内容
- **产出**:一份 OCR 修正说明文档(列出每处修正:原OCR读什么→实际是什么)
- 修正说明存工作目录,后续 delegate_task 要引用
### 3. Step 2 delegate_task 必须携带 vision 修正值
这是**最关键的差异**——重做时 subagent 读到的仍是同一份乱码 OCR 文件。
**错误做法**:只给 OCR 文件路径,期望 subagent 自己发现问题
```python
# ❌ subagent 会重复产出错误结论
delegate_task(context="OCR路径: /tmp/.../租赁-OCR.md", ...)
```
**正确做法**:在 context 中显式列出所有 vision 修正
```python
# ✅ subagent 会用修正值覆盖乱码
delegate_task(
context="""
文件路径:
- 07模版: /tmp/.../07模版-全文.txt
- 租赁合同OCR: /tmp/.../租赁-OCR.md
关键修正信息(vision核实,OCR中这些地方有乱码需用修正版):
- 8.4条: 物业费为0.4元/㎡/天(不是/月),换算=12.167元/㎡/月
- 8.5条: 水费4.2元/吨;电费=国家电价+0.53元/度服务费
- 8.6条: 仅"物业期限=租赁期限"一句,不涉及网络电话
- 第十条10.2: 租赁租金结算至合同解除日(无违约金条款)
""",
toolsets=["file", "terminal"]
)
```
### 4. Step 3-6 正常走
- 用模板脚本(single-campus-builder.py)重新建表
- 新表体现修正后的正确内容
- K/L分工自检、格式卡口、H列四检照做
- 存档时删除旧版、上传新版、files:scan
## 常见陷阱
| 陷阱 | 后果 | 防范 |
|------|------|------|
| 只改 xlsx 不重做比对 | L列仍是旧的错误内容 | 必须重新 delegate_task 做比对 |
| delegate_task 不带修正值 | subagent 重复犯同样错误 | context 写清楚每一处修正 |
| 认为"上次做过了所以快" | 跳步、漏检 | 跑闸门,贴todo,逐项打勾 |
| 修正了物业费单位但忘改K列风险分析 | K列仍引用旧数据 | 全表重建,不手动patch |
| 旧版xlsx没删 | Nextcloud有两个版本,Maggie看到旧的 | 上传新版前rm旧版 |
## 凤凰文化实证(2026-07-01)
发现的OCR问题:
- 8.4: "0.4元/㎡/天" 被误读为 "0.4 a/R 元/㎡/月"(单位吞没)
- 8.5: 严重乱码,水费4.2元/吨和电费+0.53元/度完全丢失
- 8.6: 被错误描述为"网络电话防火门"(实为物业期限=租赁期限)
- 8.7-8.8: 消防条款大段乱码
- 第五条5.3: 水电逾期违约金万分之五/日不可读
修正方法:vision逐页精读第5-8页原始PDF,获取真实文本
结果:31条模版比对差异(旧版25条),所有修正内容正确纳入
@@ -0,0 +1,323 @@
# 纯扫描件合同 OCR 配方(image-only PDF → 可读文本)
> 适用:南通新东方多数校区合同是**纯扫描件**——`pdftotext` 文字层字符数 ≈ 0(个位数),
> 必须 OCR 才能读。人民中路、跃龙路实证此配方干净好用,优先于 deepseek_ocr 路径。
## 配方(tesseract 引擎,中英混排)
```bash
# 1) ocrmypdf 给扫描件加文字层(force-ocr 强制重做,image-dpi 300 提清晰度)
ocrmypdf -l chi_sim+eng --force-ocr --image-dpi 300 租赁.pdf 租赁-ocr.pdf
# 2) pdftotext -layout 抽文本(-layout 保留表格的列对齐,读租金表/费用表更可靠)
pdftotext -layout 租赁-ocr.pdf 租赁-OCR.md
```
- `-l chi_sim+eng`:中文简体+英文,合同里身份证号/账号/英文条款都能认。
- **为什么用 ocrmypdf 而非 deepseek/裸 tesseract**:① 一步产出**带文字层的 PDF**(后续 x2t 渲染、人工查阅都能用);② `pdftotext -layout` 出来的文本**保留空间布局**,财务表格的列不会糊成一行;③ tesseract 对扫描合同的正文条款识别率足够高。
- **大文件后台跑**:10MB+ 的扫描 PDF(如跃龙路租赁 10.9MB)OCR 要几分钟,写脚本 `terminal(background=true, notify_on_complete=true)`,发出后并行做别的(见 SKILL Pitfall 4)。
- **验证**:OCR 后 `wc -c 租赁-OCR.md` 字符数应从 ~0 跳到**数千~数万**(人民中路租赁 8 页→31420 字符、物业 3 页→6111;跃龙路 6+6 页同量级)。仍是个位数=OCR 没生效,回查 ocrmypdf 报错。
## 两个必防的 OCR 失真(扫描件通病)
### ① 财务表格"大写金额"几乎必乱码 → 以阿拉伯数字为准
正文条款 OCR 可读,但租金表里的**中文大写金额**("壹拾壹万玖仟…")常被识别成乱码(`SRSAU``SRSAUT SAAR` 之类)。处置:
- **关键金额一律以阿拉伯数字栏为准**(如 `119,190.75`),大写乱码忽略,**不照抄乱码进表**。
- 数额存疑/阿拉伯数字也不清 → 回原图 `vision_analyze` 核,或走金额数学交叉验证(不含税×(1+税率)=含税,见 SKILL「4d/数学交叉验证」)。
### ② 抬头/公司名乱码 → 裁高清局部图 vision 逐笔辨认 + 多处交叉核
扫描件首页抬头(甲方/乙方公司全称)常因字号小/印章压字而 OCR 乱码,**且这是梳理表的关键基础项,不能蒙**。处置:
```bash
pdftoppm -png -f 1 -l 1 -r 400 租赁.pdf hd_p1 # 400dpi 高清渲染首页
# python: PIL 裁抬头区(高度~4%-16%) → resize 宽1200 → 存 jpg quality≥90 (<400KB 防 vision 超时)
```
- 把抬头局部图喂 `vision_analyze`,要求**逐字辨认、看不清就明说哪个字看不清、禁止猜测填充**。
- **多处交叉核**:公司名在合同里通常出现 3+ 处(抬头、第三条收付款户名、落款、骑缝章),用整页+高清局部两次独立辨认互证;**与旧梳理表/兄弟合同的记法对撞**——不一致就是该名称 OCR 不稳的信号,必须核到底。
- 人民中路实证:旧表(0622)甲方记"南通琳大鞍房屋…",本次 vision 高清逐笔辨认为"南通**森大蒂**房屋建设开发有限公司"("森"=品字三木,确非"琳"),纠正了旧表的误识。**OCR 与旧表都可能错,回高清原图 vision 核才是真值。**
## ③ tesseract 空白输出救援:二值化(binarization)回退(2026-06-26 通州金鹰实证)
### 症状
`ocrmypdf` 整份跑完,`pdftotext` 抽出来的文本**只有前几页、后几页完全空白**(80 字节的 `\f` 换页符)。拆出空白页单独 `tesseract` 直接读 PNG 也**一字不输出**——但 PNG 文件大小正常(2-3MB),`numpy` 统计非白像素 1000 万+,**页面有内容、只是 tesseract 读不出来**。
### 根因
扫描件对比度低/底色不均/噪点多,tesseract 默认的灰度处理在纹理复杂的旧扫描件上找不到文字边界。
### 正解:二值化(threshold=128)→ tesseract
```python
from PIL import Image
img = Image.open('page.png')
gray = img.convert('L') # 灰度
bw = gray.point(lambda x: 0 if x < 128 else 255, '1') # 阈值 128 二值化
bw.save('page_bin.png')
```
```bash
tesseract page_bin.png stdout -l chi_sim+eng
```
二值化后 tesseract 能正常识别,输出与正常 OCR 页面同质量。
### 必做步骤
-`pdftoppm -r 200 -png` 渲染空白页 → 二值化 → tesseract(通州金鹰 p4-p8 五页全救回)
- **不要增加二值化阈值试错**——128 是标准阈值,低对比度扫描件用 128 即可;若 128 仍不输出,优先怀疑页面本身无文字(检查 `numpy` 非白像素数),而非阈值问题
- 二值化后 OCR 出来的文本与正常 `ocrmypdf` 产出同质量,直接合并使用
## ④ 特定乱码字段核实:tesseract → browser_vision 降级链(2026-06-29 解放中路实证)
### 问题场景
OCR/tesseract 能读出大部分合同文本,但**特定字段仍然乱码**(如"壹"被读成"过"、数字被吃0、条款号后的填空值模糊)。需要 vision 核实具体字符,但 `vision_analyze` 反复超时。
### 诊断:fitz 文字层检测
```python
import fitz
doc = fitz.open('合同.pdf')
for i in range(doc.page_count):
text = doc[i].get_text()
if not text.strip():
print(f'Page {i+1}: 纯扫描页(无文字层)')
else:
print(f'Page {i+1}: {len(text)} chars')
```
纯扫描件 `fitz.get_text()` 返回空字符串 → 必须先 OCR。OCR 后的 PDF 有文字层但部分字符乱码 → 进入 vision 核实。
### vision_analyze 超时问题
`vision_analyze` 对合同页面图片(即使压缩到 325KB/400×565px 的 72 DPI JPEG)**连续超时 5 次**(2026-06-29 解放中路实证)。全页 300 DPI PNG(2.5MB)更不用说。**不要反复重试 vision_analyze——它对这个场景不可靠。**
### 正解:tesseract 全页 + browser_vision 局部裁图
**第一层:tesseract 全页 OCR(已做,处理 90% 文本)**
```bash
# 300 DPI 渲染每页 → tesseract 逐页读
python3 -c "
import fitz
doc = fitz.open('合同.pdf')
for i in range(doc.page_count):
pix = doc[i].get_pixmap(matrix=fitz.Matrix(300/72, 300/72))
pix.save(f'page_{i+1}.png')
"
tesseract page_N.png stdout -l chi_sim+eng --psm 6
```
tesseract 对中文合同正文条款识别率高(解放中路第六条完整读出、第十二条大部分可读)。
**第二层:browser_vision 局部裁图(处理 tesseract 仍乱码的具体字符)**
```python
from PIL import Image
# 1. 打开 300 DPI 页面图
img = Image.open('page_3.png') # 2481x3510
w, h = img.size
# 2. 裁剪目标区域(如第12.1条第4项,约在页面25-35%高度)
crop = img.crop((100, int(h*0.25), w-100, int(h*0.45)))
# 3. 缩放到 600-800px 宽,存 JPEG quality=80
crop_resized = crop.convert('RGB').resize(
(600, int(crop.height * 600 / crop.width)), Image.LANCZOS)
crop_resized.save('crop_target.jpg', 'JPEG', quality=80)
```
```
# 4. browser_navigate file:///tmp/xxx/crop_target.jpg
# 5. browser_vision 问具体问题(如"第4项中'拖欠租金累计达几个月的'数字是多少")
```
### 关键要点
- **browser_vision 和 vision_analyze 是不同的后端**——browser_vision 通过浏览器截图+视觉模型,对小图片处理更稳定,本 session 一次成功
- 裁图要**精准定位目标条款区域**,不要裁全页(太大)也不要裁太窄(缺上下文)
- 问题要**具体**:"第4项中拖欠租金累计达几个月的数字是什么"比"请读出全部文字"效果好
- 中文大写数字(壹/贰/叁)在扫描件中常被 tesseract 误识为形近字,**必须 vision 确认**
- 文件大小控制在 50-100KB(JPEG quality=75-80, 600px 宽),避免超时
### 完整降级链总结
```
fitz.get_text() 空 → ocrmypdf 全篇 OCR → pdftotext 取文本
↓ tesseract 能读的 → 直接用
↓ tesseract 乱码的具体字段 → 裁区域 + browser_vision
↓ browser_vision 也超时 → 如实告诉 Maggie,请她核对原件
↓ 绝不能 → 用旧表数据填 + 手动创建 checkpoint(Pitfall 39)
```
## ⑤ 统一社会信用代码 OCR 乱码修复 + 网络交叉验证(2026-06-29 通大附+凤凰文化实证)
### 问题场景
OCR 把统一社会信用代码中的英文字母部分读乱(如 `MA1NAEBX26``MA INAEBX26`,数字间插入空格;`MADQRFT66P` 被完整读出但无法确认是否正确)。`ocr-integrity-check.py` 将 3+ 连续大写字母匹配为"公司名乱码"——但信用代码中的字母部分是合法的。
### ocr-integrity-check.py 修复(已落地 2026-06-29)
脚本增加信用代码排除逻辑:检测匹配到的字母序列前后是否有数字,有则判定为信用代码的一部分并跳过。
### 网络交叉验证技巧
OCR 信用代码不完整时,用企查查/天眼查 web search 验证:
```
web_search: "公司全称" 统一社会信用代码
```
已验证案例:
- 南通青创企业管理咨询有限公司:91320600MA1NAEBX26(企查查)
- 南通新东方教育科技有限公司:91320602MADQRFT66P(校验位验证通过)
- 南通业玖物业管理有限责任公司:企查查未查到(可能是小公司或名称细微差异),但 tesseract 重跑清晰读出
### 信用代码校验位验证
```python
weights = [1,3,9,27,19,26,16,17,20,29,25,13,8,24,10,30,28]
chars = '0123456789ABCDEFGHJKLMNPQRTUWXY'
code_map = {c:i for i,c in enumerate(chars)}
total = sum(code_map[code[i]] * weights[i] for i in range(17))
check = 31 - (total % 31)
expected = chars[check] if check < len(chars) else '?'
# expected == code[17] → 校验通过
```
## ⑥ 签名页/签章区域乱码处理(2026-06-29 多校区实证)
### 问题场景
合同最后 1-2 页(签名盖章页)OCR 产出大量无意义英文字母序列(`AAA``LIV``BREE``RUE``ANON` 等),是印章/手写签名/骑缝章被 OCR 引擎误识别的产物。
### 处理方式
```python
import re
# 替换 3+ 连续大写字母为 [签章] 占位符
lines[idx] = re.sub(r'[A-Z]{3,}', '[签章]', lines[idx])
```
### 判断标准
- 出现在签名页(通常最后 1-2 页)
- 上下文含"甲方签字""乙方盖章""日期"等关键词
- 这些区域不含合同实质条款,替换后不影响完整性检查
- 替换后 `ocr-integrity-check.py` 不再报这些位置的问题
## ⑦ vision 全不可用时的 delegate_task 降级(2026-06-29 跃龙路实证)
### 问题场景
`vision_analyze` 连续超时 7+ 次(即使图片压缩到 119KB JPEG),`browser_vision` 也间歇性超时。所有视觉工具在同一 session 中同时不可用。
### 正解:delegate_task 批量分发 OCR 核实
```python
delegate_task(tasks=[
{"goal": "Read scanned contract page and transcribe ALL text. Focus on: ...",
"toolsets": ["vision", "file"]},
# 最多 3 个并行
])
```
- **subagent 的 vision 后端可能与父 agent 不同**——同一时段父 agent vision 全挂,subagent 有 2/3 成功读取(跃龙路实证:task 1 超时但 task 2、3 成功)
- **混合策略**:给每个 subagent 不同的页面 + 具体的提取目标(不要笼统"读全文"),提高单次成功率
- **subagent 也超时时的兜底**:用已有 tesseract OCR 输出 + 上下文推理重建文本,标注哪些段落是推断而非 vision 确认的
### 关键要点
- delegate_task 的 vision 调用走独立的模型实例,timeout 行为不总与父 agent 一致
- 把最关键的乱码页(租金表、签名页、违约金条款)优先分配给 subagent
- 返回结果里 subagent 会自报"vision failed, used OCR instead"——**检查 status 和 summary**,不能假设全部成功
## ⑧ 单位后缀误读(天→月、年→月等)——数学交叉验证必抓(2026-07-01 凤凰文化实证)
### 问题场景
OCR 将"0.4元/㎡/**天**"误读为"0.4 a/R 元/㎡/月"——单位"天"被吞掉或变成乱码,导致后续模版比对报告错误地认为物业费(0.4元/月)与租金(12.167元/月)是两个不同标准。实际上:0.4元/天 × 365÷12 = **12.167元/月**,两者完全一致。
### 根因
扫描件中"天"字笔画简单(仅3画),在低质量扫描+OCR识别中极易丢失或被误读为符号碎片(如"a/R")。类似地,"月""年"等单位后缀也可能被吃掉。
### 检测方法:数学交叉验证
合同中单价通常后面紧跟一个**月总额表格**。验证公式:
```python
stated_unit_price = 0.4 # OCR 读出的数字
area = 562 # 面积
table_monthly = 6837.854 # 表格中月费
# 如果是 元/㎡/月:
if_monthly = stated_unit_price * area # 0.4 × 562 = 224.8 ≠ 6837.854 → 不匹配!
# 如果是 元/㎡/天:
if_daily = stated_unit_price * area * 365 / 12 # 0.4 × 562 × 30.417 = 6837.85 ✓ 匹配!
```
**匹配失败 = 单位被误读**。必须回原图 vision 确认真实单位。
### 铁律
- 凡 OCR 读出单价+单位(元/㎡/月、元/㎡/天、元/吨等),必须用**表格中的月总额**做交叉验证
- 不匹配时,优先怀疑单位被OCR吃掉/替换,而非合同本身矛盾
- 模版比对报告中若涉及"费率标准不同"的结论,必须先过此验证
### 连锁影响
此类误读会导致模版比对报告产出错误结论(如"物业费0.4元/月与租金12.167元/月**不同**"),进而误导汇总表K列风险判断。修正路径:OCR产出后、写比对报告前,所有涉及金额/费率的字段过一遍数学交叉验证。
## ⑨ OCR严重乱码段落对模版比对的污染(2026-07-01 凤凰文化8.5-8.8实证)
### 问题场景
扫描件第6页8.5-8.8区域OCR产出如下乱码:
```
8.5 甲方按照部 门 标准 及水、电 指 和
定为 :水 国家 电价 计收
8.6 Fak ) 租赁 期限保持 一致
```
基于此写出的模版比对报告将8.5概括为"水电费按部门标准及国家电价计收"(丢失具体费率),将8.6描述为"网络、电话、防火门等配套设施约定"(完全搞错——8.6只是说物业期限=租赁期限,网络电话防火门内容在8.7-8.8)。
### 真实内容(vision核实)
- **8.5**:水费按**4.2元/吨**计收;电费按**国家电价+0.53元/度服务费**(随国家调价联动)
- **8.6**:物业服务期限与租赁期限一致,随解除/终止而终止(仅此一句)
- **8.7-8.8**:消防远程联网、疏散楼梯、二消改造等详细消防义务
### 铁律
- **模版比对报告中,每一条差异的描述必须基于可读的OCR文本或vision核实结果**
- 如果某条款区域OCR输出含3+个连续乱码词(如"Fak""peMet iy ecsapae"),该区域必须先vision确认真实内容,再写入比对报告
- **不得对乱码文本做"合理推测"后写入比对报告**——推测错误比留空更有害
- 比对报告中遇到OCR不可读段落,标注"[OCR不可读,需原件核实]"比写错误描述强
## ⑩ 重做校区时 delegate_task 必须携带 vision 修正值(2026-07-01 凤凰文化实证)
### 问题场景
发现OCR质量问题后重做某校区,重新发 delegate_task 让 subagent 做模版比对。如果 context 只给 OCR 文件路径,subagent 读到的仍是乱码文本,会重复产出错误结论(如"物业费0.4元/月"、"8.6为网络电话条款")。
### 正解:在 delegate_task context 中显式列出所有 vision 修正
```python
delegate_task(
goal="对比租赁合同与07模版,生成逐条差异清单...",
context="""
文件路径:
- 07模版: /tmp/.../07模版-全文.txt
- 租赁合同OCR: /tmp/.../租赁合同-OCR.md
关键修正信息(vision逐页核实,OCR中这些地方有乱码需用修正版):
- 8.4条: 物业费为0.4元/㎡/天(不是/月),换算=12.167元/㎡/月
- 8.5条: 水费4.2元/吨;电费=国家电价+0.53元/度服务费
- 8.6条: 仅"物业期限=租赁期限"一句,不涉及网络电话
- ...
""",
toolsets=["file", "terminal"]
)
```
### 铁律
- **OCR修正后重做比对 ≠ 只重发 delegate_task**——必须把修正值写进 context
- subagent 不会自动跑 vision,它只能读文件;如果文件本身有乱码而 context 没给修正,subagent 就会按乱码理解
- 修正信息的格式:"条款号: 真实内容(不是XXX)"——明确标注哪里被误读、正确值是什么
- 这同样适用于首次做比对时发现OCR乱码区域——先vision核实,再发subagent,context带修正值
## ⑪ OCR产出md文件事后核实流程(2026-07-13 桃坞路实证)
### 场景
md文件已产出并保存,但用户(或后续任务)质疑准确度,需要系统性核实而非重跑OCR。
### 核实方法:vision逐页比对
```bash
# 1. 把PDF关键页渲染成图片(200dpi够用,关键是覆盖含数据的页面)
pdftoppm -png -r 200 -f 1 -l 1 合同.pdf verify/p1
pdftoppm -png -r 200 -f 2 -l 2 合同.pdf verify/p2
# 2. vision_analyze 每张图,问具体数据点(金额、面积、日期、当事人)
# 3. 与md文件对应段落逐项比对
```
### 桃坞路4份合同核实发现的典型OCR失真模式
| 失真类型 | 频率 | 影响 | 处置 |
|---------|------|------|------|
| 封面公章区乱码(前10-16行) | 每份必现 | 不影响条款提取 | 可忽略 |
| 大写金额全乱 | 物业合同必现 | 中等——引用时必须用小写数字 | 以阿拉伯数字为准(已有①) |
| 收款账户段排版错乱 | 偶发 | 低——信息可拼凑 | 核对账号12位数字即可 |
| 个别汉字形近误识(崇→喧、饰→4) | 偶发 | 低——不影响数据 | 已知正确值时直接修正 |
| 手写填空处空白被OCR虚构文字 | 偶发 | 中——会产生虚假数据 | vision确认原件是否真的填写了 |
### 核实策略(效率优先)
1. **不需逐字全文核对**——重点核对:金额、面积、日期、费率、百分比、当事人名称、账号
2. **优先核对第2页**(通常含租金/期限/保证金等核心条款),封面页和签字页优先级最低
3. **数学交叉验证仍是最快手段**:如首期=年租金÷2?物业年费=单价×面积×365?通过=可信
4. **只需vision核对md中有乱码嫌疑的段落**——正常可读的中文条款OCR准确率>98%无需逐字核
5. **结论模板**:核实后向用户报告"核心数据准确+已发现的具体问题列表",让用户知道哪些可信哪些需注意
### 与全流程的关系
- 新做校区:OCR后立即跑 `ocr-integrity-check.py`(自动检测乱码)→ 高危区vision核实 → 写入md
- 事后核实(本场景):直接 pdftoppm + vision 比对关键页,快速出结论
- 两者互补:integrity-check 抓格式异常,vision比对抓语义错误(如空白处虚构文字)
## 与既有规则的关系
- 这是「第一铁律·逐字通读」「OCR 不可信回原件核」在**扫描件取文本**环节的落地配方。
- 费率符号(‰ vs %)的双跑/裁图核对走 `references/ocr-rate-symbol-verification.md`;本文管的是**整篇取文本 + 抬头/金额两类高频失真 + 二值化救援 + 信用代码修复 + 签章乱码处理 + 单位误读 + 乱码段落处理**。
@@ -0,0 +1,48 @@
# 世茂校区合同结构(2026-07-02 核实)
## 文件分布
```
世茂/
├── 青少/
│ ├── 世茂新租赁合同.pdf (26页, 纯扫描无文字层)
│ └── 世茂物业合同.pdf
├── 高中/
│ ├── 3023新东方租赁合同-双签版.pdf (26页, 纯扫描无文字层)
│ └── 世茂物业(高中).pdf
├── 世茂-梳理-MJ-20260626.xlsx
└── 南通新东方-世茂校区租赁合同梳理-MJ-20260618.xlsx
```
## 关键发现:两套合同为同一模版
青少和高中的租赁合同**逐字一致**(世茂制式),包括:
- 页数相同(26页)
- 条款编号对齐
- **第八条(商铺维修)完全相同**
### 第八条核心条款(维修/渗漏相关)
| 条款 | 内容摘要 |
|------|----------|
| 8.1 | 非乙方原因→甲方/管理公司尽快维修;**乙方代修权**:甲方拒不维修→乙方可代修,费用按江苏省修缮定额由甲方承担。例外:玻璃隔断/卷闸门无论归属均乙方责任 |
| 8.2 | 乙方正常使用+爱护内部设施;使用不当→乙方维修/赔偿 |
| 8.3 | 小范围修缮须书面申请甲方批准 |
| **8.4** | **甲方应确保商铺屋顶、墙壁、主要供水管道及动力电缆符合国家安全规范** |
| 8.5 | 甲方保证电梯/消防/空调等公共设施正常 |
| 8.6 | 甲方入户检查须事先书面通知+避开营业 |
| 8.7 | 紧急情况可无通知强入(明显过错除外) |
| 8.8 | 窗户/玻璃破损→乙方绝对责任(无论过错/保险) |
### 第十三条相关(甲方违约)
- **13.4.3**:甲方未承担维修责任或支付维修费用,致使乙方无法继续租用→乙方可单方解约+索赔
## OCR注意事项
- 两份租赁合同均为**纯扫描件**(fitz.get_text() 返回空)
- 必须全量Vision逐页读取
- 物业合同同理需确认是否有文字层
## 法律函件引用建议
因两套合同维修条款完全一致,在起草维修通知/催告函时:
- 可统一引用"《租赁合同》第八条第1款及第4款"(无需区分青少/高中版本)
- 配合民法典第713条(出租人不履行维修义务→承租人可自行维修,费用由出租人负担)
- 物业合同第5.1条亦有类似甲方维修义务约定
@@ -0,0 +1,27 @@
# 本 skill 的自维护:去重、消冲突、防膨胀(2026-06-23 Maggie 授权优化时确立)
## 为什么需要
本 skill 是「一条条积累」起来的(每次 Maggie/Doro 纠正就追加一条),必然产生两类病:
- **重复**:同一规则在多处各自展开,措辞还不完全一致 → 互相「打架」(人民中路 L 列出错的土壤之一)。
- **膨胀**:996 行 / 151K 字节 / 24 个 `##` + 62 个 `###`。开工前光扫一遍就吃掉大量注意力 = **延迟源本身**
## 安全去重方法(已验证,照此做)
1. **先量化诊断,不靠感觉**:用 execute_code 按主题词聚类数重复次数(`text.count(关键词)`),找命中最高的几类(实测:标红 110、H列 102、OCR符号 63、Excel安全 61、模版 58)。
2. **区分「规则本身」vs「技术细节展开」**:规则(标什么/不标什么、判据、退路)一字不留在正文;技术操作细节(XML 外科手术、五查步骤、命令配方、排查叙事)才下沉。⚠️ 关键词**嵌在规则正文里**的(如「4a 跨副本对撞」「违约金基数」)是规则,不是复读,**不能动**。
3. **下沉前提:reference 已完整存着该规则**。先 `read_file` 确认真身在 reference 里,删正文复读才零风险。reference 不全的,先补全 reference 再删正文。
4. **正文留最硬的铁律,细节进 reference**:如标红区保留 4 条硬铁律(唯一路径=WPS另存/红必须最后一步/二次编辑别 openpyxl 重存/别当测试员),把「试过哪些修法、怎么做 XML 手术、五查怎么验」下沉。开工扫一遍仍记得「怎么不踩坑」,动手时再翻 reference 拿完整配方。
5. **改前备份**`cp SKILL.md SKILL.md.bak_$(date +%Y%m%d_%H%M%S)`,改坏随时回滚。
6. **dry-run 定边界再 patch**:先打印将删区块的首尾行 + 锚点唯一性校验(`find_line` 返回恰好 1 个),确认边界外的规则保留、脏 `\n` 字面转义字符一并清,再动手。
7. **改后验收(逐项 grep,不凭印象说「改好了」)**:① 字节数变化(看 `wc -c` 不是行数)② 每条规则判据/退路/指针关键词仍在 ③ 脏字符清零 ④ reference 真身未动(行数不变)。
## 度量陷阱
体量真实指标看**字节数**(`wc -c`),**不是行数**——中文 3 字节,标红那批行数只减 11 但字节减 9.5K(−6%)、标红主题命中 110→71。execute_code 的 `len(text)` 是 UTF-8 字符数(≈68K),与磁盘字节数(151K)差 ~2.2 倍,别混用两个口径报数。
## 🔴 已知设计冲突(2026-06-23 确认,修复需 Maggie 授权)
**闸门脚本 `scripts/campus-workflow-gate.py` 把 Step2 吐成 `Step2-A 法律审查` / `Step2-B 提取分析` 两条串行 todo,与 SKILL.md 正文「动作A 法律审查 ‖ 动作B 提取分析 **并行**」的设计自相矛盾。** 我照串行 todo 逐项打勾 → 把本该并行的两件事做成串行(人民中路实证,是慢的根因之一)。
- **正解**:Step2 开工**第一个动作 = 立刻 `delegate_task` 把动作B(提取+模版比对)甩到后台**,发出的同一秒自己开始读法律 → 两件事真并行。我是单线程,「不外包动作A」≠「串行做两件事」。
- **修法(待授权)**:gate 脚本把 Step2-A/2-B 合成一条,并把「⚡先发 subagent 甩动作B」顶到 Step2 todo 最前。
- **通则**:脚本吐出的 todo 与正文口径必须一致——改了正文流程设计,必须同步改 gate 脚本,否则脚本在物理上诱导我违背正文。
## 执行纪律权重失衡(设计层观察,提权需授权)
核实类铁律在正文命中上百次,而「边说边做/动作与解说分轮」的执行纪律(Pitfall 23)只 11 次、埋在最末。几十条「多核实多写」在每个回合把我拽向「多写、边说边做」,是延迟的**系统性诱因**。实证:同样 77 行合同,前面反复「读不出来」耗 40 分钟,最后一条 `sed` 0.X 秒读完——慢在执行不在任务。提权方向:把执行纪律提到与「第一铁律·逐字通读」并列,并写明它优先于核实类铁律对单个回合的占用(核实照做,放到结果回来那一轮做,不和动作抢同一轮)。
@@ -0,0 +1,23 @@
# Step 0 盘点——配对检查铁律
## 教训来源
世茂校区 2026-07-02:通知函只引了1份物业合同(217L0363b),被Maggie纠正"物业合同也是两份"。实际是2份租赁对应2份物业(青少217L0363a-1→217L0363b,高中217L0457a→217L0457b)。
## 规则
每份租赁合同必须确认是否有对应的物业管理服务合同(同校区/同楼层/同租赁物)。
### 检查清单
1. 列出所有租赁合同(按校区、楼层、面积分)
2. 逐份找对应物业合同(合同编号通常有规律:a→b,a-1→b等)
3. 确认配对关系后在盘点清单中标注:`租赁XXX ↔ 物业YYY`
4. 如果有租赁合同找不到对应物业合同,标注为"待确认"——向客户询问
### 常见配对模式
- 合同编号后缀:`a` / `a-1` = 租赁,`b` = 物业(世茂)
- 同一校区多楼层 = 可能每层各一份租赁+物业
- 扩租补充协议可能有单独的物业补充协议
### 漏配后果
- 汇总表遗漏合同 → 返工
- 法律文书(通知函、催告函)引用不完整 → 被纠正
- 权利主张遗漏物业方义务 → 策略不完整
@@ -0,0 +1,16 @@
# Step 1 快捷路径:复用已有提取文件
## 场景
该校区合同的 `_全文.md``_全文_OCR.md` 已存在于 Nextcloud 对应目录下——通常是因为之前为其他任务(如通知函起草、单条款查询、函件引用等)已做过 OCR/提取。
## 规则
1. **可直接复用**:不需要重新跑 OCR,不需要重新 marker-pdf 提取。已有文件即为 Step 1 产出。
2. **仍需跑 garble-detect 验证**`ocr-garble-detect.py` 必须跑一遍确认质量,高危乱码仍需 vision 消灭后才能进 Step 2。
3. **判断标准**:文件存在 + garble-detect 通过 = Step 1 完成,可直接标记 step1.verified。
4. **不适用情况**:如果文件明显是旧版/不完整(如只提取了部分页面、OCR 质量极差大面积乱码),仍需重做。
## 来源
2026-07-02 世茂校区开工时确认:四份合同(青少租赁/物业 + 高中租赁/物业)的全文 MD 均在之前做渗漏维修通知函时已提取完毕,Maggie 确认可直接复用避免重复工作。
@@ -0,0 +1,34 @@
# Step 1 跳过策略——复用已有OCR全文
## 适用条件
当合同文件夹中已存在 `_全文.md``_全文_OCR.md` 文件时,Step 1 可以跳过OCR提取。
## 前提
- MD文件确实是从对应PDF提取的(检查文件头注释/来源标注)
- 文件修改日期不早于PDF修改日期(排除旧版本残留)
## 仍然必做
即使跳过OCR提取,以下步骤**不能跳过**:
1. **Copy到工作目录**`sudo docker exec cat ... > /tmp/<workdir>/`
2. **跑 ocr-garble-detect.py**:扫全文乱码
3. **高危行 vision 核实**
- TOC页(目录页)的乱码可以标记为"非实质"忽略
- 附件表格(租金/面积/保证金)的乱码必须核实
- 核心条款(维修/违约/解除)的乱码必须核实
4. **关键财务数据交叉验证**
- 单价 × 面积 = 月租总额?
- 不含税 × 税率 = 税金?
- 保底租金 × 月数 ≈ 保证金?
## 实例
世茂校区(2026-07-02):4份合同MD文件都已存在,直接复用。garble检测结果:
- 世茂新租赁:15处高危(主要在TOC+附件表格),vision确认核心条款完整
- 世茂物业青少:6处高危
- 高中租赁:18处高危
- 高中物业:2处高危
关键发现:附件三(租金表)的OCR乱码虽然触发高危,但数字部分清晰可读,经vision确认后可进入Step 2。
## 时间节省
跳过OCR约节省15-30分钟/份(扫描件OCR+后台处理时间)。4份合同合计节省约1-2小时。
@@ -0,0 +1,73 @@
# 汇总表复核/审计方法论
当 Maggie 要求"检查""核查""看看是否按规则做的"时使用。与建表不同:这是对已交付表格的逆向验证。
## 触发词
- "检查下XX的核心条款总结是否…"
- "法律风险分析是否…逐字逐句审查的"
- "付款时间有没有推算"
- "是不是按照要点进行的"
## 审计流程
### 准备阶段
1. 从 Nextcloud 拉取最新 xlsx(docker cp,禁用 /tmp 残留)
2. 拉取对应的合同源文件(.md OCR全文),确保逐字可读
3. Python 读取 xlsx 全部单元格内容
### H列审计
1. **租赁合同行**
- 四检逐项验证(条款号、月租换算、付款推算、❗标注)
- 计算验证:年租金÷2=半年期金额?免租分摊后数字对得上?递增率×base=下年数字?
- 付款日期推算:每期起始日-提前天数=付款截止日?
2. **物业合同行**
- 必须有逐期付款推算(不能只写费率+支付方式)
- 物业费+公共能耗费=每期总额
- 按年/季/半年结算周期列出每期金额+付款截止日
- 电费等按实结算的写明计费标准即可
3. **跨行一致性**:物业费在租赁合同H列提及的数字与物业合同H列一致
### I列审计
1. **对照固定类目清单**
- 租赁合同21类目:用途/转租/装修改造/广告标识/非竞争/维修责任/保险要求/物业服务联动/配套设施/出租方变更/解除权机制/违约金机制/不可抗力/征收拆迁/房屋抵押查封/政策变化/到期处理/恢复原状/优先权/管辖/备案
- 物业合同15类目:物业服务内容/服务标准/公共能耗费/特约服务/共用设施管理/装修管理/安保措施/消防安全/保险要求/联动终止/违约责任/退出交接/免责条款/不可抗力/管辖
2. **逐类目核对**:合同原文有→I列是否覆盖?I列写了→条款号是否正确?
3. **内容准确性**:I列摘述是否忠实于原文含义?有无曲解/遗漏关键限定词?
### K列审计
1. **每项风险回溯原文**:K列每个风险点必须能在合同原文中找到直接文本支撑
2. **计算验证**:如"年化182.5%"→0.5%×365=182.5% ✓
3. **立场检查**:是否一致站乙方(新东方/承租方)立场?
4. **遗漏扫描**:通读合同全文,有无明显对乙方不利的条款未被提及?
- 常见遗漏项:签约主体错配(合同载明方≠实际盖章方)、签约日期空缺、面积约定模糊、付款方向歧义
5. **证据纪律**:有无超出文本做确定性推断?("联系人同名"→只能写"可能关联"不能写"控制")
### L列审计
1. 是否纯客观差异描述(无"风险""建议"等禁用词)
2. 差异条目是否与合同原文一致(非凭记忆写的)
3. 与K列无交叉(风险判断归K,客观差异归L)
## 输出格式
审计结果按以下结构报告:
```
## [校区名]汇总表检查报告
### 一、[检查项名称](❗缺失/✅准确/⚠️有遗漏)
- 现状描述
- 问题点
- 建议动作
### 结论表
| 检查项 | 状态 | 需要动作 |
|--------|------|----------|
| ... | ❌/✅/⚠️ | ... |
```
## 注意事项
- 审计不是重做——只要数字/条款对得上就确认,不重新改写措辞
- 发现问题后报告即可,不自动修改(等Maggie说"去补上"再动手)
- 物业合同容易被忽略(因为"风险较低"),审计时必须同等重视
@@ -0,0 +1,113 @@
# 标准模版对比清单(房屋租赁合同)
> 🔴 **前置关卡(2026-07-01 凤凰文化教训):比对前必须确认OCR文本可靠性。**
> - 逐段扫描OCR输出,凡含3+连续乱码词的段落,必须先 vision 核实真实内容再写入比对报告
> - 所有涉及**金额/费率+单位**的条款,必须做数学交叉验证(单价×面积=月总额?单位是天/月/年?)
> - 典型案例:凤凰文化8.4条OCR读出"0.4元/㎡/月"实为"0.4元/㎡/**天**"(0.4×562×365/12=6837.854=表格月费,验证通过)
> - 乱码段落不得做"合理推测"后写入报告——标注"[OCR不可读,需原件核实]"比写错误描述强
> - 详见 `references/scanned-pdf-ocr-recipe.md` ⑧⑨ 两节
> ⚠️ **铁律(Maggie 2026-06-23,不可违背):所有"与模版的比对",一律以 `07- 房屋租赁合同.docx` 原件为唯一基准,必须打开模版原件逐条核对。**
> - 禁止用"标准商业地产格式""星展商业格式""商业格式常见"等抽象概念当参照系 —— 那是凭印象的二手归纳,不是模版比对。
> - 禁止拿本 checklist 的归纳条款当模版替身 —— 本清单只是导航,真值在模版 docx 里。每次比对都要回原件读真身。
> - 教训来源:悦拾光、人民中路两份梳理表的 L 列最初都用"商业格式"概念写差异,未回 07 原件,被 Maggie 两次纠正。模版原件位置见下。
> - 模版原件取法:`docker cp nextcloud-nextcloud-1:"/var/www/html/data/admin/files/小Maggie协作区/南通新东方/参考文件/07- 房屋租赁合同.docx" /tmp/xxx/07模版原件.docx`(注意 `07-` 后有一个空格)
> 🔴🔴 **K列法律风险 vs L列模版差异——下笔分工铁律(Maggie 2026-06-24 人民中路 K/L 混淆纠正)**:本清单产出的差异**只进 L列**,**绝不**拿来当 K列法律风险的论证。两列指向同一条款也要各写各的:
> | | K列(法律风险·独立审查) | L列(模版差异·纯文本对比) |
> |---|---|---|
> | 参照系 | 法律+司法实践,**与模版无关** | 07 模版原件 |
> | 起笔 | "本合同第X条这样约定→对乙方什么后果→怎么改" | "第X条:模版表述为【原文】;本合同表述为【原文】" |
> | 禁止字样 | 模版/07/被放宽/被删除/被改为 | 风险/不利/建议/应/需关注/详见K列 |
> | 自检 | grep "模版\|07" = 0 | grep "风险\|建议\|不利\|详见" = 0 |
> - **遮模版测试**:把"模版怎么写"整个遮住,K列那条风险论述**仍完整成立**才算独立审查;遮住就垮 = L列逻辑混进了 K列。
> - ✅ K列正例:「本合同约定甲方可将租赁标的抵押或出典(第八条1款)。租赁期间一旦抵押权被实现或标的被司法拍卖,可能影响乙方正常使用…建议约定租赁期间不得抵押/出典。」
> - ✅ L列正例:「第八条1款:模版表述为'甲方不得将租赁标的进行财产抵押或出典';本合同表述为'甲方可将租赁标的进行财产抵押或出典'。」(不加任何评价)
> - ❌ 反例(已废,两件事混了):K列写「抵押限制被放宽:07模版'不得抵押'→本合同'可抵押'」← 用模版差异驱动风险,应改成上面 K列正例的写法。
模版文件:参考文件/07- 房屋租赁合同.docx
## 逐条对比导航(仅供定位条款位置;每条的具体内容、措辞、数值一律以 07 原件为准,不得拿本清单的归纳当模版真值)
### 第一条 租赁标的
- [ ] 甲方保证合法出租权+提供权证
- [ ] 建筑面积/使用面积明确
- [ ] 供电功率保证(不放核心内容栏,仅配套设施)
### 第二条 租赁期限及房屋用途
- [ ] 免租期约定
- [ ] 用途:办公、教学及相关经营
- [ ] 可转租给关联单位(需甲方书面同意)
### 第三条 房屋租金
- [ ] 租金构成说明("租金包括…")
- [ ] **发票条款**:甲方未提供合规发票→乙方可延付且不违约
- [ ] 发票虚假/失效→损失由甲方承担
- [ ] 乙方开票账户信息
### 第四条 租赁押金
- [ ] **押金退还范围**:期满/解除/终止后X工作日内全退
- [ ] 押金收据遗失说明函条款
### 第五条 物业服务费及其他
- [ ] 水电按独立计数表+国家标准
- [ ] 法定税费由甲方承担
- [ ] 除明确约定外不再支付其他费用
### 第六条 房屋维修、装修
- [ ] 甲方维修义务+代为维修+抵消租金
- [ ] 维修拖延违约金(日租金/日,15日以上可解除)
- [ ] **消防条款**:符合国家标准+通过验收+证明文件一致
### 第七条 出租方的变更
- [ ] 转让所有权须提前X天通知
- [ ] 新所有者继续履行本合同+补充协议
### 第八条 房屋的抵押、转租、续租
- [ ] **甲方禁止抵押**出租房屋
- [ ] 续租通知期:**1个月**(注意偏差)
- [ ] 甲方不答复视为同意续租
- [ ] 看房权:须**征得乙方同意**(注意是否降级为"通知")
### 第九条 优先权
- [ ] 优先承租权
- [ ] 优先购买权
### 第十条 合同解除、违约责任 ⭐ 重点
- [ ] **10.2 任意解除权**:提前X天书面通知+年租金X%违约金+**"除法定或本合同约定外"限定**
- [ ] **10.3 甲方终止**:年租金X%违约金+全额退押金+退预付款+装修损失(总造价÷总期间×未用期间)+**诉讼费律师费**
- [ ] **10.4 逾期付款**:0.1‰/日+15日逾期+催缴后X日→可解除。注意OCR可能将‰误识为%
- [ ] 10.5 甲方管理不善→赔损失+**严重影响→可解除且不违约**
- [ ] 10.6 甲方迟延交付→日租金违约金+**免租期/起租日顺延**
### 第十一条 不可抗力 ⭐ 重点
- [ ] 范围含:地震、台风、大火、战争、**疫情**、**政府政策变更**
- [ ] 11.3 **行业治理**:乙方可要求减免租金**或延长租期**
- [ ] 严重影响→乙方可解除且**不构成违约**
- [ ] 是否有"公证机构出具证明"额外举证要求(不利)
### 第十二条 合同的变更、解除或终止
- [ ] 变更须双方协商一致+书面形式
- [ ] 政府征用/拆迁→详细退还义务+装修赔偿归乙方
- [ ] 迁离标准:按**现状**交付(注意是否变为"保持原状"→可能有恢复原状义务)
- [ ] **12.4 办学许可证**:房屋本身原因+政策原因→免责解除+退押金+退预付款
### 第十三条 法律适用与争议
- [ ] 租赁房屋所在地法院管辖
### 第十四条 附则
- [ ] 甲方X日内办理租赁备案+**不配合→乙方可解除+赔偿**
- [ ] **非竞争条款**:大楼+商圈不租给同类机构
- [ ] 反商业贿赂举报条款
- [ ] 补充条款是否填写(有些合同填"无"→缺少办学保障)
### 附加条款
- [ ] 装修改造条款
- [ ] 标识设置及广告位
- [ ] 配套设备(增容等)
## 对比结果分类
- 🔴 重大缺失/偏离(任意解除权缺失、不可抗力范围窄、办学许可证缺失)
- 🟡 中等差异(续租通知期延长、看房权降级、非竞争范围缩小)
- 🟢 基本一致
- ⚪ 形式差异(份数等)
@@ -0,0 +1,50 @@
# 提前退租风险分析框架——法律依据与实务要点
> 源自万达校区提前退租法律意见书(2026.5.6出具),适用于江苏地区租赁合同。
## 核心法律依据
### 1. 违约损害赔偿
**《民法典》第584条**:当事人一方不履行合同义务或者履行合同义务不符合约定,造成对方损失的,损失赔偿额应当相当于因违约所造成的损失,包括合同履行后可以获得的利益;但是,不得超过违约一方订立合同时预见到或者应当预见到的因违反合同可能造成的损失。
### 2. 合同解除后的清算
**《民法典》第566条第1款**:合同解除后,尚未履行的,终止履行;已经履行的,根据履行情况和合同性质,当事人可以请求恢复原状或者采取其他补救措施,并有权请求赔偿损失。
### 3. 非金钱债务不适于强制履行
**《民法典》第580条**:当事人一方不履行非金钱债务或者履行非金钱债务不符合约定的,对方可以请求履行,但是有下列情形之一的除外:(一)法律上或者事实上不能履行;(二)债务的标的不适于强制履行或者履行费用过高;(三)债权人在合理期限内未请求履行。
> 租赁合同中承租人使用房屋的义务属非金钱债务,法院通常认为"不适于强制履行"。
### 4. 空置期损失上限(江苏地区)
**《江苏省高级人民法院关于审理城镇房屋租赁合同纠纷案件若干问题的意见》第26条**:因承租人违约行为导致房屋租赁合同解除的,出租人可以要求承租人赔偿租赁房屋闲置期间的租金损失,但最长不得超过六个月。
> 实际支持金额取决于:房屋实际空置时间、甲方是否积极减损(减损义务)
## 分析模板
### 确定责任
| 项目 | 金额 | 依据 |
|------|------|------|
| 押金没收(如合同约定) | X元 | 合同第X条 |
| 约定违约金(如适用) | X元 | 合同第X条 |
### 不确定责任(需甲方举证)
| 项目 | 金额范围 | 依据 |
|------|----------|------|
| 空置期租金损失 | 0 ~ 月租金×6 | 江苏高院意见第26条 |
| 免租期租金追偿 | 0 ~ 免租期租金 | 司法实践酌情 |
| 恢复原状费用 | 视装修情况 | 合同迁离条款 |
### 可收回金额
| 项目 | 金额 | 依据 |
|------|------|------|
| 已付未使用租金 | X元 | 民法典第566条 |
> 违约金/损失赔偿与已付未使用租金应相互抵扣后计算净额。
## 关键判断点
1. **违约金条款是否覆盖主动退租**:很多合同的违约金仅列举特定违约情形(欠租、擅自转租等),"无故提前退租"不在列举范围内→违约金标准不能直接适用→需依据实际损失主张
2. **免租期追偿的争议**:免租期优惠的前提是完整履行租期,提前退租时甲方可主张追偿,但法院会酌情处理,不一定全额支持
3. **恢复原状vs按现状交付**:注意区分合同约定——"恢复原始结构"意味着需拆除装修,"按现状交付"则无此义务
4. **继续履行风险**:极低。江苏地区法院一致态度——承租人已明确不再租赁甚至已搬离的,即使出租人坚持要求继续履行,法院通常直接判决解除+违约责任
@@ -0,0 +1,65 @@
# UWF Workflow Pitfalls for nantong-lease-audit
**Last updated**: 2026-06-30
## Pitfall 1: File Name Collision — Multiple Contracts per Campus
The `nantong-lease-audit` workflow writes output to:
```
/tmp/nantong-lease-audit/{{ campus }}-row-data.json
```
When a campus has **multiple contracts** (e.g., 租赁 + 物业), running both contracts through the workflow causes the second to **overwrite** the first's JSON file.
**Discovered**: 通州金鹰 — 租赁合同 wrote `通州金鹰-row-data.json`, then 物业合同 overwrote it.
**Fix**: When running multiple contracts for the same campus:
1. Run them sequentially (not in parallel) — OR —
2. After each workflow completes, rename/copy the output before starting the next:
```bash
# After lease contract workflow completes:
cp /tmp/nantong-lease-audit/通州金鹰-row-data.json \
/tmp/nantong-lease-audit/通州金鹰-租赁-row-data.json
# Then run property contract workflow (will overwrite 通州金鹰-row-data.json)
```
3. When building the xlsx, read both JSON files separately
**Current workaround used**: Read the lease contract data from CAS step output (`uwf step show <hash>`) if the JSON was overwritten, then manually reconstruct the property contract data.
## Pitfall 2: Workflow Prompt Variable Not Passed
The classifier role's moderator instruction shows empty values:
```
分类合同。OCR文本路径:,校区:,原始文件名:
```
This happens when the `ocr_path`, `campus`, and `filename` variables are not properly interpolated from the thread start prompt. The classifier still works because the OCR path is in the task context, but the moderator prompt looks incomplete.
**Root cause**: The thread start prompt uses Chinese colons `` which may not match the YAML template variable syntax. Current workaround: the classifier reads the OCR path from the task context anyway.
## Pitfall 3: Workflow v1 → v2 Hash Change
When the workflow YAML is updated and re-registered:
- Old hash: `7KZ5BWT12R5RJ` (v1, no format templates)
- New hash: `C77579MQ9QPKE` (v2, with H/K/L format templates + reference loading)
Threads started before the update continue using the old hash. Always verify which hash is active:
```bash
uwf workflow show nantong-lease-audit # Shows current hash
```
## Pitfall 4: Background Exec Returns "Step 1 running"
When using `uwf thread exec <id> --count 20 --background`, the foreground output may show only:
```
Step 1 classifier → running
```
This does NOT mean only step 1 ran. The background worker continues processing all steps. Check actual progress with:
```bash
uwf step list <thread-id>
uwf thread show <thread-id>
```
Expected progression: classifier → template-diff → rule-analyzer → data-extractor → end.
Total time: ~15-20 minutes for all 4 steps.
@@ -0,0 +1,63 @@
# 金飞达纠偏:workflow执行纪律(2026-07-13)
## 背景
在多校区租赁梳理任务中,出现过一种错误执行方式:先由主线程把 H/I/K/L 全部快速拼一版,再补做 subagent 模版对比和细化。这种做法与既定 workflow 冲突。
## 正确 workflow 分工
### 主线程(小Maggie本人)
负责:
1. 通读合同全文
2.**K列独立法律风险审查**
3.**提前退租法律后果**
4. 最终整合 H/I/K/L 进入总表
### 独立 subagent
负责:
1. **L列07模版对比**
2. 只输出“07模版怎么写 / 本合同怎么写”的客观差异
3. 禁止掺入风险、不利、建议等判断词
### 可选独立 subagent
负责:
1. **H列+I列** 草稿
2. H列付款推算和I列核心条款提炼
3. 但最终仍由主线程统一核表
## 正确顺序
1. 盘点文件,按租赁物分类
2. 读取/确认全文 md 可用
3. **主线程做K列**
4. **subagent并行做L列**
5. H/I同步完成
6. 汇总成表
7. 跑 K/L 分离检查 + I列覆盖检查 + H列四检
## 禁止顺序
- ❌ 主线程先自己把 H/I/K/L 全部搭一版,再说“后面精修”
- ❌ 先出总表骨架,再补起 subagent 的模版对比
- ❌ 用“我知道workflow”代替“我已经按workflow执行”
## 原因
### 1. 防止K列被L列逻辑污染
错误思路:
> 因为和07不一样,所以有风险
正确思路:
> 合同原文这样约定 → 对乙方有什么法律后果 → 风险是什么
### 2. 防止L列掺入法律判断
L列必须保持纯客观。若由主线程在做K列的同时顺手做L列,极易把“风险”“不利”“建议”带入L列。
### 3. 这种任务不接受“阶段性交付思维”
多校区租赁梳理不是“先拼骨架、后慢慢补”的任务。第一次产出就应按完整 workflow 落地,否则很容易在后补过程中混列、漏审、逻辑串位。
## 实务口令
> 我做K列独立法律审查的同时,subagent做L列模版对比;H/I同步处理;最后统一核表。
这句话是该类任务的 workflow 核心。