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,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 定稿。