用生成式 AI 在 AWS 上自动化合同智能:Doczy.ai 的实践与可复用架构

2026-06-02 32 预计阅读时间:1 分钟
来源:aws.amazon.com AI 摘要 原文链接

免责声明:本文为 AI 摘要整理,建议结合原文阅读。摘要可能省略上下文、版本差异或边界条件,不作为官方说明。

预计阅读时间:10 分钟

合同是企业数据里最"沉默"的一类——格式各异、条款嵌套、关键信息散落在几十页 PDF 里。传统做法靠人工审阅,速度慢、遗漏率高,规模化时更是噩梦。Doczy.ai™ 把生成式 AI 搬上 AWS,把非结构化合同批量转成结构化洞察,让审批、合规、风控流程可以真正自动化。这篇文章拆解它的思路,并给出一个你可以直接改造的 AWS 合同解析最小原型。

合同智能的核心难题

合同智能不是简单的"OCR + 关键词搜索"。真正的难点有三层:

  • 语义提取:违约条款可能写在"第 8.3 条(b)款"里,措辞千变万化,关键词匹配覆盖不了。
  • 关系推理:一份合同里甲方、担保方、受益方之间的关系需要跨段落理解。
  • 批量一致性:1000 份合同提取出的"终止日期"必须落在同一个 schema 下,否则下游自动化无法消费。

Doczy.ai 的解法是用大模型做语义理解,用 AWS 基础设施做规模化编排,再用结构化 schema 做输出约束——三层问题各击一层。

AWS 上的架构拆解

根据 Doczy.ai 在 AWS 上的部署方式,核心组件可以归纳为:

组件 AWS 服务 作用
文件摄入 S3 + EventBridge 合同上传即触发处理流水线
文档预处理 Lambda / Textract PDF → 文本,处理表格与签名区
语义提取 SageMaker / Bedrock 大模型按 schema 提取条款
结构化输出 DynamoDB / OpenSearch 存储为可查询的结构化记录
编排与监控 Step Functions + CloudWatch 串联全流程,失败自动重试

关键设计决策:用 Step Functions 做编排而非自写脚本。合同处理涉及多步调用(OCR → 分段 → LLM 提取 → 校验),Step Functions 提供可视化流程、内置重试和错误捕获,规模化时比自编排脚本可靠得多。

实践:搭建一个最小合同解析流水线

下面给出一个可以直接改造的 AWS 合同智能最小原型。它用 S3 上传触发 Lambda,Lambda 调用 Bedrock Claude 模型按预设 schema 提取合同关键信息,结果写入 DynamoDB。

1. DynamoDB 表 — 存储结构化输出

aws dynamodb create-table \
  --table-name ContractIntelligence \
  --attribute-definitions \
    AttributeName=contractId,AttributeType=S \
    AttributeName=uploadDate,AttributeType=S \
  --key-schema \
    AttributeName=contractId,KeyType=HASH \
    AttributeName=uploadDate,KeyType=RANGE \
  --billing-mode PAY_PER_REQUEST \
  --region us-east-1

运行前确保你的 AWS CLI 已配置好凭证和区域。表名可按项目替换。

2. S3 Bucket + EventBridge 触发

# 创建 bucket
aws s3api create-bucket \
  --bucket my-contract-intelligence-bucket \
  --region us-east-1

# 添加 EventBridge 通知配置
aws s3api put-bucket-notification-configuration \
  --bucket my-contract-intelligence-bucket \
  --notification-configuration '{
    "EventBridgeConfiguration": {}
  }'

然后在 EventBridge 中创建规则,匹配 ObjectCreated 事件,目标指向下面创建的 Lambda。

3. Lambda 函数 — 调用 Bedrock 提取合同信息

import json
import boto3
import uuid
from datetime import datetime

s3_client = boto3.client("s3")
bedrock_client = boto3.client("bedrock-runtime")
dynamodb = boto3.resource("dynamodb")
table = dynamodb.Table("ContractIntelligence")

EXTRACTION_SCHEMA = {
    "contract_title": "合同标题",
    "effective_date": "生效日期",
    "expiration_date": "到期日期",
    "counterparty": "对方当事人名称",
    "contract_value": "合同金额及币种",
    "termination_clause_summary": "终止条款摘要",
    "key_obligations": "主要义务列表",
    "risk_flags": "风险标记(如违约金、竞业限制等)",
}

def build_prompt(raw_text: str) -> str:
    schema_desc = json.dumps(EXTRACTION_SCHEMA, ensure_ascii=False, indent=2)
    return f"""你是一名合同分析专家。请从以下合同文本中提取关键信息,严格按照 JSON schema 输出。
只输出 JSON,不要添加任何解释文字。如果某字段在文本中找不到,填 null。

Schema:
{schema_desc}

合同文本:
{raw_text}"""

def lambda_handler(event, context):
    for record in event.get("Records", []):
        bucket = record["s3"]["bucket"]["name"]
        key = record["s3"]["object"]["key"]

        # 1. 从 S3 读取合同文本(假设已预处理为纯文本)
        response = s3_client.get_object(Bucket=bucket, Key=key)
        raw_text = response["Body"].read().decode("utf-8")

        # 2. 调用 Bedrock Claude 3 Sonnet 提取
        prompt = build_prompt(raw_text)
        bedrock_response = bedrock_client.invoke_model(
            modelId="anthropic.claude-3-sonnet-20240229-v1:0",
            body=json.dumps({
                "anthropic_version": "bedrock-2023-05-31",
                "max_tokens": 4096,
                "messages": [{"role": "user", "content": prompt}],
            }),
            contentType="application/json",
            accept="application/json",
        )
        result_body = json.loads(bedrock_response["body"].read())
        extracted_json = json.loads(result_body["content"][0]["text"])

        # 3. 写入 DynamoDB
        contract_id = str(uuid.uuid4())
        upload_date = datetime.utcnow().strftime("%Y-%m-%d")
        item = {
            "contractId": contract_id,
            "uploadDate": upload_date,
            "sourceFile": key,
            **extracted_json,
        }
        table.put_item(Item=item)

        print(f"Contract {key} extracted → contractId={contract_id}")

    return {"statusCode": 200, "body": json.dumps("Processed")}

部署前需要改动的地方:

  • IAM 角色:Lambda 需要权限 s3:GetObjectbedrock:InvokeModeldynamodb:PutItem
  • 如果源文件是 PDF/图片,在调用 LLM 前需要加一步 Textract 预处理,把文件转成纯文本。
  • EXTRACTION_SCHEMA 按你的业务需求定制——加字段如"管辖法院""保密条款"即可,模型会按 schema 输出。

4. 用 Step Functions 编排完整流程(可选增强)

单 Lambda 适合原型。生产环境建议用 Step Functions 把 Textract → 分段 → LLM 提取 → 校验串起来,每步独立重试。下面是一个最小状态机定义的核心片段:

Comment: "Contract intelligence pipeline"
StartAt: ExtractText
States:
  ExtractText:
    Type: Task
    Resource: arn:aws:states:::textract:startDocumentAnalysis
    Parameters:
      S3Bucket.$: "$.bucket"
      S3Key.$: "$.key"
      FeatureTypes:
        - TABLES
        - FORMS
    Next: RunLLMExtraction
    Retry:
      - ErrorEquals:
          - States.ALL
        IntervalSeconds: 10
        MaxAttempts: 3
  RunLLMExtraction:
    Type: Task
    Resource: arn:aws:lambda:us-east-1:123456789012:function:ContractLLMExtract
    Next: ValidateOutput
  ValidateOutput:
    Type: Task
    Resource: arn:aws:lambda:us-east-1:123456789012:function:ValidateContractSchema
    End: true
    Catch:
      - ErrorEquals:
          - States.ALL
        Next: LogValidationFailure
  LogValidationFailure:
    Type: Task
    Resource: arn:aws:lambda:us-east-1:123456789012:function:LogFailure
    End: true

123456789012 替换为你的 AWS 账号 ID,Lambda 函数名按实际部署填写。

落地时的取舍与风险

先做对的,再做快的:

  • Schema 先行:不要先跑模型再想输出格式。先和业务方锁定 schema,再写 prompt。否则 1000 份合同提取出来字段名不一致,下游直接报废。
  • 分段策略:长合同(>50 页)不要整篇丢给模型。按条款分段提取,再合并,准确率更高,token 成本也更可控。
  • 校验层不可省:LLM 输出会有幻觉——比如凭空编造一个到期日期。加一个 Validate 步骤,用规则检查(日期格式、金额范围、必填字段非空),拦截明显错误后再入库。

成本与延迟的平衡:

  • Bedrock Claude 3 Sonnet 在合同提取场景性价比最好;Haiku 更快更便宜但复杂条款容易漏。
  • 批量处理时用 Provisioned Throughput 锁定容量,避免突发请求被限流。
  • 冷合同(历史存档)可以离线批量跑;热合同(新签署需即时审阅)走实时触发,两者分开部署。

安全边界:

  • 合同含高度敏感信息。Bedrock 在 AWS VPC 内运行,数据不出区域,但 S3 bucket 必须启用加密(SSE-KMS)和 bucket policy 限制访问。
  • Lambda 日志默认写入 CloudWatch,确认日志级别不打印原始合同全文——只输出 contractId 和提取结果摘要。

检查清单:上线前逐项确认

状态
提取 Schema 已与业务方锁定并文档化
S3 bucket 启用 KMS 加密 + 访问策略
Lambda IAM 权限最小化(仅 s3/bedrock/dynamodb)
CloudWatch 日志不输出合同原文
Step Functions 每步有 Retry + Catch
长合同分段逻辑已测试(>30 页)
Validate 步骤覆盖必填字段 + 格式校验
Bedrock Provisioned Throughput 按峰值配置
提取结果抽样人工复核流程已建立

最后一项最容易忽略:没有人工抽样复核的 AI 合同智能,不值得信任。 每周抽 20 份让法务快速比对,持续修正 prompt 和 schema,这才是系统越跑越准的闭环。


相关推荐