智能合同审查搭建教程:低质量PDF怎么处理?先解析清洗,再分路审阅(附GitHub项目地址)

​项目介绍:​这是一个开箱即用的合同风险检测工具。支持上传 PDF/Word 格式的购销、租赁、服务等合同文件,自动识别主体信息缺失、标的物不明、违约责任不完整等法律风险,并输出结构化审查意见与修改建议,结果可溯源至原文页码。适用于企业法务合规审查、业务合同自查及交易对手风险筛查。

​GitHub 项目地址:​ https://github.com/intsig-textin/xparse-sample-projects

接下来我们将讨论实现方法。

当我们的目标是把扫描件、图片件、带水印合同做成可交付的审查工具,重点不是"让模型看合同",而是先把输入清洗干净,再把不同审查目标拆成不同输出结构。

一、先把目标定义清楚

这类工具真正要解决的是下面几个问题:

  • 输入质量不稳定,很多合同是扫描件、拍照件、带水印 PDF
  • 同一份合同里既有法律风险问题,也有格式规范问题
  • 输出不能只是摘要,还要能生成审查报告

因此更合理的目标不是"总结合同",而是把流程拆成下面这条链路:

  • 上传合同文件
  • 调用 TextIn 做解析和预处理
  • 得到统一中间层 markdown + pages
  • 并行执行条款风险审阅和规范审阅
  • 汇总审阅结果并导出 Word 报告

二、架构应该怎么拆

建议把这类合同审查工具拆成四层:

这四层分别解决:

  • 解析层:把低质量文档先转成统一文本结构
  • 审阅层:把法律语义问题和文本规范问题分开处理
  • 汇总层:把两类 JSON 合并成页面可消费结构
  • 交付层:导出审查报告,而不是停留在聊天结果

三、先把解析层输入输出定义对

真正调用的仍然是 TextIn 的二进制解析接口,而不是 form-data

HTTP 复制代码
POST https://api.textin.com/ai/service/v1/pdf_to_markdown

对应代码里的请求方式如下:

Python 复制代码
headers = {
    "x-ti-app-id": TEXTIN_APP_ID,
    "x-ti-secret-code": TEXTIN_SECRET_CODE,
    "Content-Type": "application/octet-stream",
}

params = {
    "parse_mode": "auto",
    "page_count": 200,
    "dpi": 144,
    "table_flavor": "html",
    "apply_document_tree": 1,
    "markdown_details": 1,
    "page_details": 1,
    "apply_merge": 1,
    "crop_dewarp": 1,
    "remove_watermark": 1,
    "get_image": "both",
}

resp = await client.post(
    "https://api.textin.com/ai/service/v1/pdf_to_markdown",
    headers=headers,
    params=params,
    content=file_bytes,
)

这里值得单独解释三个参数:

  • crop_dewarp=1:用于切边和矫正,适合扫描件、拍照件
  • remove_watermark=1:对带水印合同尤其重要
  • get_image=both:保留页面图像信息,后续做证据页展示更方便

这一层最重要的输出仍然是:

JSON 复制代码
{
  "code": 200,
  "result": {
    "markdown": "合同正文 markdown",
    "pages": []
  }
}

如果前端要上传文件,通常会在本地后端包一层 /api/parse;但需要记住,真正的解析接口协议仍然是"二进制文件流 + 解析参数"。

四、为什么这里一定要先做统一中间层

合同审查里最常见的误区,是把 OCR 之后的原始文本直接丢给模型,然后希望一次生成所有意见。这样做表面上简单,工程上却不稳。

先统一成 markdown + pages 有三个直接好处:

  • markdown 负责承载正文、标题、条款结构
  • pages 负责承载分页信息,后续可以挂页面预览或证据定位
  • 整个系统后续都面向统一中间层,不再面向原始文件类型

也就是说,PDF、DOC、DOCX、扫描件在进入审阅层之前,都会收敛成同一种输入对象。

五、为什么要拆成两条审阅链路

合同审查至少有两类完全不同的目标:

  • 法律语义风险
  • 文本规范与一致性问题

如果把这两类问题塞进同一个 Prompt,模型输出很容易混杂,后续展示和报告导出也很难稳定。更合理的做法是:同一份 markdown,两份不同 Prompt,两棵不同 JSON 树。

前端编排层的核心就是并行调用:

JavaScript 复制代码
callLLM(CLAUSE_PROMPT, markdown)
callLLM(NORM_PROMPT, markdown)

这一步的重点不是"并发"本身,而是把两个不同目标明确拆开。

六、Prompt 应该怎么写,为什么这么写

1. 条款风险审阅 Prompt

条款风险审阅的 Prompt 先把审查范围写死:

Plain 复制代码
重点审查以下5类风险点:
1. 责任条款
2. 违约条款
3. 知识产权
4. 保密条款
5. 争议解决

接着把每条问题的输出字段写死:

Plain 复制代码
- issue_type: 必须是 "风险"、"缺失"、"冲突"、"建议" 之一
- description: 问题的详细描述,不超过100字
- value: 从合同中精确引用的相关原文片段
- suggestion: 具体的修改或补充建议

最终输出结构固定为:

JSON 复制代码
{
  "clause_review": {
    "责任条款": [{"issue_type":"","description":"","value":"","suggestion":""}],
    "违约条款": [],
    "知识产权": [],
    "保密条款": [],
    "争议解决": []
  },
  "parties": {
    "party_a": {"name":"","role":"甲方"},
    "party_b": {"name":"","role":"乙方"}
  }
}

为什么要把 parties 也放在这一条链路里?因为条款风险判断天然依赖主体关系,很多责任边界和义务归属都离不开甲乙方身份。

2. 规范审阅 Prompt

规范审阅的目标完全不同,所以 Prompt 也必须单独定义:

Plain 复制代码
检查以下4类规范性问题:
1. 错漏
2. 一致性
3. 格式
4. 修订

对应输出结构也固定为:

JSON 复制代码
{
  "norm_review": {
    "错漏": [{"description":"","value":"","suggestion":"","severity":""}],
    "一致性": [],
    "格式": [],
    "修订": []
  }
}

这里多出来的 severity 很重要,因为规范问题后续往往需要分优先级展示和导出。

七、输入、Prompt、输出是怎么一一对应的

把这类工具做稳,至少要先对齐下面三件事:

1. 输入层

两条审阅链路共用同一份合同 markdown。这意味着解析层只做一次,后续所有审阅都面向同一个中间层。

2. Prompt 层

  • 条款风险审阅 Prompt 定义 clause_review + parties
  • 规范审阅 Prompt 定义 norm_review

也就是说,Prompt 不是为了"多说几句提示",而是为了把返回 JSON 的形状提前定下来。

3. 输出层

页面展示和 Word 报告导出都依赖这两棵固定 JSON 树:

  • clause_review 适合按风险类别分组展示
  • norm_review 适合按严重程度和问题类型展示
  • parties 则可以直接进入报告首页或摘要部分

只有输入和输出先对齐,导出层才不会再做一轮混乱的二次解析。

八、和传统做法相比,差别在哪里

1. 不是 OCR 后直接全文摘要

全文摘要很容易把法律风险、格式问题、修订建议全部混在一起,后续既不方便统计,也不方便导出。

2. 不是只靠规则审查

规则很适合检查编号、日期、金额一致性,但对责任边界、违约责任、知识产权、争议解决这些语义问题不够。

更合理的边界是:

  • 低质量输入问题交给解析层处理
  • 语义风险交给条款风险 Prompt
  • 规范性问题交给规范审阅 Prompt

以上是一种"先解析清洗、再双链路审阅"的合同审查实践方案。核心思路是把低质量合同(扫描件、拍照件、水印 PDF)先通过 TextIn 统一成干净的 markdown + pages 中间层,再将法律语义风险与文本规范问题拆成两条独立的审阅链路,分别输出固定 JSON,最终合并生成结构化审查报告。方案已发布在 GitHub,欢迎大家在项目中与我们交流。如果你在实际处理合同时遇到更复杂的场景(如手写体干扰、印章遮挡、多版本修订痕迹等),或者有不同的架构思路,欢迎留言或私信探讨。

相关推荐
三无推导1 小时前
深入解析 ComposioHQ/awesome-codex-skills:从 Prompt 复用到 AI 工程技能化的实践路径
人工智能·性能优化·开源·prompt·github
第一程序员2 小时前
2026年GitHub上最火的10个Python项目,Rust开发者必看
python·rust·github
wangruofeng2 小时前
AI 正在压垮 GitHub
github·aigc
darkb1rd4 小时前
clawsweeper:开源仓库维护自动化避坑实战指南
开源·github·好物分享
南风微微吹4 小时前
【管综】考研199管理类综合联考历年真题及答案解析PDF电子版(2009-2026年)
考研·pdf
优化控制仿真模型4 小时前
【英一】考研英语一历年真题及答案解析PDF电子版(1980-2026年)
经验分享·pdf
其实秋天的枫4 小时前
【英一】考研英语一历年真题及答案解析PDF电子版(1980-2026年)
经验分享·pdf
要不枉此行4 小时前
【Python 实战】一键爬取 HTML 文档并合并为完整 PDF
python·pdf·html
optimistic_chen5 小时前
【AI Agent 全栈开发】提示词技巧(prompt)
java·人工智能·ai·prompt·agent