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,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 完成顺序排列,不自动按"租赁物→合同性质→时间"排序——需要后续手动调整或增加排序逻辑