← Back to skills
extension
Category: Development & EngineeringAPI key requirement unconfirmed

本地合同审阅套件

本地合同审阅套件。8 个可互相编排的 Skill 完成「读进来 → 抽条款 → 扫风险 → 比模板 → 引法条 → 给替换条款 → 出报告」全流程,运行在 Intel Core Ultra 上, 敏感合同文本不出机;仅在需要通用法律知识时由 legal-rag 发起去标识化云端补充。 当用户提到审合同、合同风险、违约条款、保密期限、模板对比、合同脱敏、redline、 条款替换、NDA/劳动合同/采购合同/租赁/数据处理协议审查时使用。

personAuthor: NeolnfrahubModelScope

LocalLegal Suite · 本地合同审阅套件

面向企业法务 / 采购 / HR 的本地合同审阅 Agent Skills。8 个可互相编排的 Skill 全流程运行在 Intel Core Ultra AI PC 上,敏感文本不出机;只在需要通用法律知识时 通过 legal.rag 发起去标识化云端补充。

status skills rules platform runtime license

项目简介

企业每天都在签合同。真正的痛点不是模板套用,而是审阅:法务人手不够,业务 急着签,一份 30 页的采购合同压下来,三天见不到反馈。而云端 AI 服务面前横着一堵 墙——合同本身就是商业机密,甲方名字都不允许上云。

LocalLegal Suite 把八个专家动作拆成八个 Skill,装进 Core Ultra 的本地推理里,让 Agent(QwenWork / WorkBuddy / TraeWork / 豆包办公)一句话完成 读进来 → 抽条款 → 扫风险 → 比模板 → 引法条 → 给替换条款 → 出报告。

不止于"标红"。redline.suggest 为每一条风险产出可以直接粘贴进反稿的替换条款 并附法条依据——法务要的不是"违约金无上限有风险",是那句能写进合同的话。

没有模型也能跑。 确定性规则层(33 条风险规则 + 20 条修订模板 + 正则结构切分) 独立工作,风险矩阵、模板偏离、修订建议一条都不少。这也是 125 个测试能在没有 AI PC 的机器上全绿的原因。

核心功能

| Skill | 职责 | 运行位置 | LLM | | --- | --- | --- | --- | | doc.ingest | PDF / DOCX / TXT / 扫描件 → 规范化文本 | 本地 CPU(OCR 走 GPU/NPU) | – | | clause.extract | 抽当事方 / 金额 / 期限 / 条款分类 | 本地 iGPU | Qwen INT4 + 正则兜底 | | risk.scan | 规则库 + LLM 双通道,出 0-100 风险分 | 本地 iGPU | 规则为主,LLM 补充 | | template.diff | 与企业标准模板逐条比对,判更严 / 更松 | 本地 iGPU | Qwen INT4 | | pii.redact | 身份证 / 手机 / 银行卡 / 机构名 / 姓名脱敏 | 本地 CPU | 纯正则 | | legal.rag | 法条检索,本地 corpus ↔ 可选云端 | 本地 CPU / 云 | – | | redline.suggest | 每条风险产出可粘贴的替换条款 | 本地 iGPU | 模板优先,LLM 兜底 | | report.gen | 编排以上全部,出 Markdown / HTML / DOCX | 本地 CPU | – |

调用关系

八个 Skill 通过共享 Registry 相互调用,report.gen 是唯一入口:

 用户 / Agent ──"帮我审这份采购合同,跟标准模板对比一下"
      │
      ▼
 ┌─────────────┐
 │ report.gen  │  编排层:一句指令 → 七次 skill 调用
 └──┬──────────┘
    │
    ├──▶ doc.ingest ─────────── PDF/DOCX/OCR → 文本
    │        ▲
    ├──▶ clause.extract ────────┘(缺文本时自己回调 doc.ingest)
    │        ▲
    ├──▶ risk.scan ─────────────┤(缺条款时回调 clause.extract)
    ├──▶ template.diff ─────────┘
    │
    ├──▶ redline.suggest ──▶ legal.rag ──▶ pii.redact
    │      给出替换条款        法条依据      出站前强制脱敏
    │                            │              (最后一道闸口)
    ├──▶ legal.rag ──────────────┘
    │
    └──▶ pii.redact ──── 报告内的 PII 清单(只报计数,不回填原值)

不是硬编码流水线:risk.scan 只给文本也能跑(自己回调 clause.extract), clause.extract 只给路径也能跑(自己回调 doc.ingest)。Agent 侧可以从任意 一个 Skill 切入。

五类合同实测

python stress.py 在 examples/ 五份样本上的真实输出(stub 模式,纯规则层):

| 样本 | 风险分 | 高危条数 | 结论 | | --- | --- | --- | --- | | sample_procurement | 92 | 5 | reject | | sample_nda | 69 | 2 | negotiate | | sample_labor | 76 | 3 | reject | | sample_lease | 92 | 5 | reject | | sample_dpa | 81 | 4 | reject |

风险分采用软饱和 100×(1-e^(-total/60)),永不达到 100——审阅工具不该宣称自己 什么都发现了。横向比较看分数,纵向比较看高危条数。

安装与启动

1. 安装依赖

python -m pip install -r requirements.txt

# 硬件加速可选(Core Ultra 上强烈推荐)
python -m pip install openvino openvino-genai openvino-tokenizers optimum-intel nncf

也可以一步到位,脚本会装依赖、跑测试并打印设备摘要:

python install.py                      # 依赖 + 测试(不含模型)
python install.py --with-openvino      # 追加 OpenVINO 栈
python install.py --with-model         # 追加模型导出与量化
python install.py --all                # 全部
python install.py --all --model qwen3-1.7b   # 指定档位

2. 导出模型(一次性,可跳过)

先看有哪些档位,按机器算力挑:

python -m models.quantize --list

| 档位 | 量化 | 约占用 | 活跃参数 | 适用 | | --- | --- | --- | --- | --- | | qwen3-30b-a3b | INT4 | ~16GB | 3B | MoE,内存 ≥32GB 时首选:速度接近 3B,质量接近大模型 | | qwen3-8b | INT4 | ~4.8GB | 8B | MoE 装不下时的主力档 | | qwen2.5-7b | INT4 | ~4.2GB | 7B | 无 thinking 模式,输出更省 token | | qwen3-4b | INT4 | ~2.5GB | 4B | 默认档,算力紧张时的推荐选择 | | qwen2.5-3b | INT4 | ~1.9GB | 3B | 低配机器兜底 | | qwen3-1.7b | INT8 | ~1.8GB | 1.7B | 最小可用档(再压会明显掉点) |

python -m models.quantize --profile qwen3-4b

换档位只改一个环境变量,不用改代码:

export LOCALLEGAL_MODEL=qwen3-30b-a3b
python locallegal.py models          # 看当前档位和导出状态

用未收录的模型也不需要改代码:

LOCALLEGAL_MODEL=my-model \
LOCALLEGAL_MODEL_ID=org/my-model \
LOCALLEGAL_LLM_DIR=~/models/my-model-int4-ov \
python locallegal.py review --contract contract.pdf

档位表里的占用是按参数量估算的规划值,导出后 models.quantize 会打印实际大小, 引用数字时以实际值为准。

3. 验证运行状态

python locallegal.py devices
{
  "runtime": {"requested": "AUTO", "resolved": "GPU", "available": ["CPU", "GPU", "NPU"]},
  "llm": {"profile": "qwen3-4b", "bits": 4, "device": "GPU", "stub": false}
}

两半都要看:runtime.resolved 证明算在 iGPU/NPU 上,llm.stub 证明真有模型在答 而不是规则兜底。stub 模式下 CLI 会额外打印一行提醒和导出命令。

使用示例

一键审阅(最常用)

python locallegal.py review \
    --contract examples/sample_procurement.txt \
    --template examples/template_procurement.txt \
    --out out/report.md
风险分: 92/100
报告已写入: out/report.md

输出目录不存在时会自动创建。--format 支持 markdown / html / docx。

其他 CLI 入口

python locallegal.py skills     # 列出 8 个 Skill 及其参数 schema
python locallegal.py models     # 档位表 + 哪些已导出
python locallegal.py devices    # 运行设备与当前模型
python bench.py --repeat 3      # 各阶段延迟与 LLM 吞吐实测

在 Python 里单独调用某个 Skill

from skills.registry import load_all, call

load_all()

text = call("doc.ingest", path="contract.pdf")["text"]
clauses = call("clause.extract", text=text)["clauses"]
risks = call("risk.scan", clauses=clauses)
redlines = call("redline.suggest", risks=risks["risks"], cite=False)["redlines"]

for r in redlines:
    print(r["clause"], "→", r["after"])

挂载到 Agent 平台

每个 Skill 一份 manifests/*.yaml,兼容 QwenWork / WorkBuddy / TraeWork / 豆包办公。 描述遵循 OpenAI function-calling schema,Agent 侧可以直接把 function 字段喂给 tool-use 通道。集合 manifest 见 suite.yaml,平台接入细节见 docs/agent_platforms/。

目录结构

locallegal-suite/
├── SKILL.md                 # 本文件:项目说明与使用指南
├── locallegal.py            # CLI 入口(review / skills / models / devices)
├── install.py               # 一键安装 + 冒烟测试
├── bench.py                 # 延迟与吞吐实测
├── gen_cases.py             # 生成压测语料 → cases/
├── stress.py                # 全链路压测 + 假阴性检测
├── suite.yaml               # 套件级 manifest
├── manifests/               # 每个 Skill 的 skill.yaml(8 份)
├── skills/                  # 8 个 Skill 实现
│   ├── registry.py          # 相互调用的关键点
│   ├── doc_ingest.py
│   ├── clause_extract.py
│   ├── risk_scan.py
│   ├── template_diff.py
│   ├── pii_redact.py        # 出站脱敏闸口
│   ├── legal_rag.py
│   ├── redline.py           # 可粘贴的替换条款
│   └── report_gen.py
├── models/
│   ├── ov_runtime.py        # OpenVINO 设备选择 / 降级
│   ├── profiles.py          # 模型档位表(换模型只改这里或环境变量)
│   ├── llm.py               # 本地推理 + JSON 容错 + stub 回退
│   └── quantize.py          # NNCF 权重量化脚本
├── rules/                   # 全部可由法务直接编辑,无需改代码
│   ├── risk_rules.yaml      # 33 条风险规则(商事 / 劳动 / 租赁 / 数据)
│   ├── redline_templates.yaml  # 20 条修订模板
│   └── legal_corpus.json    # 离线 mini-corpus(31 条)
├── examples/                # 采购 / NDA / 劳动 / 租赁 / DPA 五类样本 + 标准模板
├── cases/                   # 13 份压测语料(由 gen_cases.py 生成)
├── tests/                   # 125 个 pytest 用例
├── docs/                    # ModelScope 文章、混合智能说明、平台接入
├── demo/                    # 路演 PPT 大纲、生成脚本、录屏脚本
└── out/                     # 报告输出目录(运行时自动创建)

混合智能与隐私

  • 默认全本地:所有涉及原始合同文本的 Skill(doc.ingest、clause.extract、 risk.scan、template.diff、pii.redact、redline.suggest、report.gen) 在 Core Ultra 上完成,无网络出口。

  • 可选云端:legal.rag 支持通过 LOCALLEGAL_RAG_ENDPOINT 指向企业内网 / 云端 法条服务。出站前强制经过 pii.redact——甲方名、姓名、手机号、身份证、 地址一律脱敏,云端只能看到"违约金 无上限"这样的抽象法律短语。

  • 弱网降级:endpoint 连不上时 legal.rag 回落内置 mini-corpus,报告照常产出 (tests/test_hybrid_degradation.py 用一个必然连接失败的地址实测这条路径)。

  • 可审计:设 LOCALLEGAL_RAG_AUDIT=1 后,每一次检索决策(含未出站的) 都写入本地 JSONL,合规团队可核查究竟什么离开过这台机器:

    LOCALLEGAL_RAG_AUDIT=1 python locallegal.py review --contract contract.pdf
    cat ~/.cache/locallegal/rag_audit.jsonl
    # {"ts":"...","egress":false,"query":"违约 无上限","backend":"local-offline","results":3}
    

    也可以直接传路径:LOCALLEGAL_RAG_AUDIT=/var/log/locallegal/audit.jsonl。

运行设备

| 阶段 | 默认设备 | 备注 | | --- | --- | --- | | PDF / DOCX 文本层抽取 | CPU | 纯 I/O,OpenVINO 不介入 | | 扫描件 OCR | GPU / NPU | PaddleOCR + OpenVINO 后端 | | 条款抽取 · 风险 · 模板 diff · 修订 | GPU(iGPU 优先) | 当前档位的本地模型,OpenVINO GenAI | | 法条 RAG | CPU 或云端 | 敏感度分级后决定 |

测试

python -m pytest tests/ -q
# 125 passed

| 测试文件 | 证明什么 | | --- | --- | | test_registry.py | Skill 注册与互相调用 | | test_doc_ingest.py | 多格式摄入、编码回退 | | test_clause_and_risk.py | 条款分类与规则命中 | | test_multi_domain.py | 五域各自的毒条款都被抓到 | | test_pii_redact.py | PII 脱敏与 keep 名单 | | test_template_diff.py | 模板偏离的确定性兜底(无模型也能出结果) | | test_hybrid_degradation.py | 弱网降级、出站强制脱敏、审计留痕 | | test_e2e_report.py | 端到端报告结构 | | test_json_robustness.py | 小模型 JSON 输出容错 | | test_small_model_efficiency.py | 小模型下的 token 效率 | | test_regressions.py | 十个真实缺陷的回归锁(见下) |

压测:

python gen_cases.py && python stress.py

test_regressions.py 里每条断言都对应一个先用真实输入复现、再修掉的缺陷:

| 缺陷 | 现象 | | --- | --- | | 身份证号紧贴中文标签不脱敏 | 身份证320102199003071234 原样出站(\b 在汉字后失效) | | 末条无尾随换行被静默漏抽 | 报告里少一个条款且不报错 | | 风险分硬封顶 | 五份样本三份 100/100,失去区分度 | | stub 模式标注成"本地LLM" | 没有模型却说机器起草了条款 | | 编号格式强耦合"第X条" | 用 1. 编号的毒合同得 0 分、结论"可签署"(改成"第一条"即 87 分 reject) | | 无法分类的条款被丢弃 | "最终解释权"这类没有 clause_type 的条款整条消失 | | 真实霸王条款大面积漏检 | 概不退款 / 人身损害免责 / 单方变更全都没规则 | | 姓名角色标签不全 | 乙方(员工):李晓明 漏脱敏 | | 中文数字缺"零" | 第一百零一条 整条丢失 | | 单行文本切不开 | PDF 文本层挤成一行时所有条款并成一块 |

测试默认在 stub-LLM 模式下跑(无模型也可 CI);真实模型到位后同一套测试仍成立 (断言的是结构契约,不是措辞)。

已知限制

以下五条是设计取舍或需要较大改动才能解决,使用前请知悉:

  1. 不支持外文合同。规则库、分类器、法条语料全是中文。目前会标"结构未识别, 需人工审阅"兜住,但没有真正支持。
  2. _classify 关键词计数优先于类型优先级。"第三条 租金与押金"因"租金"出现 多次被归为租金,押金专属规则挂不上——风险仍会抓到,只是归类不够准。
  3. 法条检索是字面关键词重叠,会错配("付款"风险关联到《个人信息保护法》 第 51 条)。引用报告里"参考法律依据"前自己核一遍。
  4. 全角数字漏检。100% 这类全角百分比匹配不到预付比例规则。
  5. 重复条款去重后不提示。100 条重复条款与 1 条同分(去重本身是对的,但文档 异常没有告警)。

报告若出现"⚠️ 结构未识别,需人工审阅",表示没有审到,不等于审过了没问题, 请勿依据该结论签署。

许可

Apache-2.0