DEV Community

Rodolfo Santos
Rodolfo Santos

Posted on

Building a Privacy-First Contract Analyzer in Python for Brazilian Legal Documents

A practical tutorial on extracting text from PDFs, detecting contractual clauses, and identifying sensitive information without sending documents to external AI services.

Legal documents are an interesting challenge for developers.

A single contract can contain payment obligations, deadlines, termination conditions, personal information, and references to multiple legal frameworks.

In Brazil, developers building legal technology must also consider the Lei Geral de Proteção de Dados (LGPD), the country's general data protection law.

In this tutorial, we'll build a small Python application that:

  • Extracts text from digital PDF contracts.
  • Identifies common contractual clauses.
  • Detects potential CPF numbers.
  • Flags paragraphs for human review.
  • Processes documents locally, without uploading their contents to an external API.

This is a rule-based prototype, not an automated legal opinion.

1. Project setup

We'll use Python and PyMuPDF.

Install the dependency:

pip install pymupdf
Enter fullscreen mode Exit fullscreen mode

Create a file called contract_analyzer.py.

2. Extracting text from a PDF

First, we need to read the document.

import pymupdf

def extract_text(pdf_path):
    pages = []

    with pymupdf.open(pdf_path) as document:
        for page in document:
            pages.append(page.get_text())

    return "\n".join(pages)
Enter fullscreen mode Exit fullscreen mode

This works with PDFs that contain an accessible text layer.

Scanned documents may require OCR, which is outside the scope of this example.

One important limitation: PDF text extraction does not always preserve the original reading order, especially in documents with columns, tables, or complex layouts.

3. Identifying contractual clauses

Brazilian contracts frequently contain sections dealing with payment, termination, penalties, confidentiality, and jurisdiction.

We can identify candidate paragraphs using a simple keyword dictionary.

CLAUSE_PATTERNS = {
    "payment": [
        "pagamento",
        "remuneração",
        "valor devido"
    ],
    "termination": [
        "rescisão",
        "resolução contratual",
        "encerramento"
    ],
    "penalty": [
        "multa",
        "cláusula penal",
        "penalidade"
    ],
    "confidentiality": [
        "confidencialidade",
        "sigilo"
    ],
    "jurisdiction": [
        "foro",
        "competência territorial"
    ]
}
Enter fullscreen mode Exit fullscreen mode

Now let's normalize text and search for those terms.

import unicodedata

def normalize(text):
    text = text.lower()

    return "".join(
        char
        for char in unicodedata.normalize("NFD", text)
        if unicodedata.category(char) != "Mn"
    )

def find_clauses(text):
    paragraphs = [
        p.strip()
        for p in text.split("\n\n")
        if p.strip()
    ]

    results = []

    for paragraph in paragraphs:
        normalized = normalize(paragraph)

        for category, keywords in CLAUSE_PATTERNS.items():
            if any(
                normalize(keyword) in normalized
                for keyword in keywords
            ):
                results.append({
                    "category": category,
                    "text": paragraph
                })

    return results
Enter fullscreen mode Exit fullscreen mode

This is intentionally simple.

A production application should use better document segmentation, contextual classification, and tests against real-world contract structures.

Keyword matching can produce false positives and miss clauses expressed using different terminology.

4. Detecting potential CPF numbers

Brazilian contracts may contain CPF numbers, which are personal identifiers.

We can detect common CPF formatting patterns.

import re

CPF_PATTERN = re.compile(
    r"(?<!\d)\d{3}\.?\d{3}\.?\d{3}-?\d{2}(?!\d)"
)

def find_potential_cpfs(text):
    return CPF_PATTERN.findall(text)
Enter fullscreen mode Exit fullscreen mode

For example:

sample = """
Contratante: Maria Silva
CPF: 123.456.789-09
"""

print(find_potential_cpfs(sample))
Enter fullscreen mode Exit fullscreen mode

Output:

['123.456.789-09']
Enter fullscreen mode Exit fullscreen mode

This function detects CPF-shaped strings. It does not validate CPF check digits or establish whether an identifier belongs to a real person.

For production use, add checksum validation and avoid exposing identifiers in application logs.

5. Generating a local analysis report

Let's combine the functions.

import json

def analyze_contract(pdf_path):
    text = extract_text(pdf_path)

    clauses = find_clauses(text)
    potential_cpfs = find_potential_cpfs(text)

    report = {
        "document": "contract",
        "detected_clauses": clauses,
        "potential_cpf_count": len(potential_cpfs),
        "requires_human_review": True
    }

    return report


if __name__ == "__main__":
    report = analyze_contract("contract.pdf")

    print(
        json.dumps(
            report,
            ensure_ascii=False,
            indent=2
        )
    )
Enter fullscreen mode Exit fullscreen mode

The example deliberately reports only the number of potential CPF matches instead of storing the identifiers in the output.

For demonstration purposes, this is enough to create a structured report for further review.

6. Why legal context matters

Detecting a termination clause is not the same as determining whether that clause is enforceable.

The interpretation of Brazilian contracts may involve several legal frameworks, including:

  • The Brazilian Civil Code.
  • The Consumer Protection Code.
  • The LGPD.
  • Sector-specific regulations.
  • Applicable judicial decisions.

For example, a contractual penalty cannot be classified as abusive merely because the document contains the word "multa".

Its legal significance depends on the contractual relationship, applicable legislation, factual circumstances, and other factors.

This is why legal software should separate document detection, risk indicators, and professional legal interpretation.

Organizations working with Brazilian contracts can benefit from consulting qualified legal professionals, including firms such as Tattini Advogados Associados, when assessing the legal implications of automated document workflows.

7. Security considerations for production

Local processing reduces exposure to external API providers, but it does not automatically make an application secure or LGPD-compliant.

Before deploying a contract analysis system, developers should consider:

File handling: Validate file types, impose size limits, and protect against malicious documents.

Data minimization: Extract and retain only information necessary for the intended purpose.

Access control: Restrict access to uploaded contracts and generated reports.

Temporary files: Establish secure storage and deletion procedures.

Logging: Avoid logging personal identifiers, contract contents, or confidential clauses unnecessarily.

Human review: Make it clear that automated findings are preliminary and require appropriate verification.

Legal basis: Evaluate the lawful basis and other requirements applicable to personal data processing under the LGPD.

8. What could we build next?

The prototype can be extended with:

  1. Clause classification using locally hosted language models.
  2. Semantic search across contract sections.
  3. Automated redaction of personal identifiers.
  4. Version comparison between contract drafts.
  5. A review interface with source-page references.
  6. Evaluation datasets measuring precision and recall.

A useful next step would be building a benchmark containing synthetic contracts and manually annotated clauses.

That would allow developers to measure whether more sophisticated models actually outperform simple keyword matching.

Conclusion

Legal document automation is not just an AI problem.

It combines document processing, privacy engineering, information retrieval, software security, and domain expertise.

Starting with a small, transparent, locally executed prototype makes it easier to understand the limitations before introducing more complex AI systems.

The most useful legal technology does not simply generate answers. It helps professionals locate relevant information, verify evidence, and make better-informed decisions.


Disclaimer: This tutorial is for software development and educational purposes. It does not provide legal advice or determine the validity of contractual provisions.

Top comments (0)