Radixia

AI ยท serverless

Building a GraphRAG for Legal Contracts with MongoDB Atlas

Building a GraphRAG for Legal Contracts with MongoDB Atlas

The analysis and interpretation of complex legal documents, such as financing contracts, represent a significant challenge even for the most experienced professionals. These documents contain numerous entities, clauses, regulatory references, and intricate relationships that require a deep understanding to correctly interpret.

The recent introduction of GraphRAG (Graph-based Retrieval Augmented Generation) by MongoDB Atlas offers an innovative approach to address this challenge. Combining the power of knowledge graphs with large language models (LLMs), GraphRAG enables the extraction, representation, and querying of complex relationships present in legal documents more effectively than traditional vector-based approaches.

In this article, we will explore how to implement a serverless system on AWS using CDK and TypeScript to extract and build a legal semantic map from a financing contract. We will see how GraphRAG can significantly improve the understanding and querying of these complex documents, providing more accurate and contextualized answers.

Understanding GraphRAG: Beyond Traditional RAG

Before implementing GraphRAG, it's essential to understand what distinguishes It from traditional RAG (Retrieval Augmented Generation) and why it's particularly well-suited for legal document analysis.

Limitations of Traditional RAG

Traditional RAG, based on vector embeddings, presents several significant limitations when it comes to analyzing legal documents:

  1. Loss of Relationships: When documents are split into chunks for vectorization, relationships between different document parts are lost.
  2. Difficulty with Multi-hop Reasoning: Vector-based RAG struggles to answer questions requiring connecting information in different parts of the document.
  3. Limited Explainability: Vector embeddings make it challenging to understand why specific chunks were selected as relevant.

GraphRAG addresses these limitations by structuring data as a knowledge graph instead of vector embeddings:

  1. Relationship Preservation: Entities (such as contracting parties, clauses, obligations) and their relationships are explicitly modeled.
  2. Multi-hop Reasoning: GraphRAG can navigate through the graph to answer complex questions that require connecting different entities.
  3. Greater Explainability: The path through the graph provides a visual and understandable explanation of how the answer was reached.
  4. Structural Understanding: GraphRAG excels in understanding the document's structure, hierarchy, and connections.

These characteristics make GraphRAG particularly suitable for analyzing financing contracts, where understanding the relationships between clauses, obligations, parties, and regulatory references is fundamental.

Analysis of the Financing Contract

The financing contract we will analyze is a € 100 million term loan credit facility agreement. This type of contract is particularly complex, containing numerous entities and relationships that lend themselves well to being represented as a knowledge graph.

You can download a facsimile of the contract here

Main Entities in the Contract

From the analysis of the contract, we can identify several categories of entities:

  1. Contract Parties:
    1. Lender: [bank 1], [bank 2], [bank 3] (Mandated Lead Arrangers and Original Lenders)
    2. Borrower: [company]
    3. Agent: [agent] (also Security Agent)
  2. Financial Elements:
    1. Total Amount: EURO 100,000,000
    2. Facilities: Refinancing Facility, Capex Facility, Incremental Facility
    3. Interest Rates: Margin 4.25% per annum (with possible increase)
  3. Contractual Clauses:
    1. Definitions and Interpretations
    2. Conditions of Use
    3. Repayment and Cancellation
    4. Interest and Fees
    5. Guarantees and Indemnities
    6. Financial Covenants
    7. Events of Default
  4. Regulatory References:
    1. Italian Civil Code
    2. Business Crisis and Insolvency Code (CIC)
    3. Legislative Decree 231/2001 (Administrative Liability)
    4. Legislative Decree 231/2007 (Anti-Money Laundering)

Key Relationships in the Contract

The relationships between these entities are equally important:

  1. Relationships between Parties:
    1. Lender → Borrower: Grants the financing
    2. Agent → Lender: Represents the Lenders
    3. Agent → Borrower: Monitors compliance with obligations
  2. Financial Relationships:
    1. Borrower → Facilities: Beneficiary of credit lines
    2. Leverage → Margin: Influences the applicable interest rate
  3. Obligation Relationships:
    1. Borrower → Financial Covenants: Obligated to respect the covenants
    2. Borrower → Information Undertakings: Obligated to provide information
  4. Risk Relationships:
    1. Event of Default → Facilities: Can cause acceleration of repayment
    2. Change of Control → Event of Default: Can constitute an event of default

These entities and relationships form the basis of our legal semantic map.

System Architecture

To implement our solution, we will utilize a serverless architecture on AWS, with MongoDB Atlas serving as the database for the knowledge graph. Here is an overview of the architecture:

Building a GraphRAG for Legal Contracts with MongoDB Atlas

Main Components

  1. API Gateway: Exposes RESTful endpoints for interaction with the system
  2. Lambda Functions: Serverless functions to process requests and manage business logic
  3. MongoDB Atlas: Database to store the knowledge graph and documents
  4. Amazon S3: Storage for the original legal documents
  5. OpenAI GPT-4.1 Mini: LLM for entity and relationship extraction

Processing Flow

  1. Document Upload: The legal contract is uploaded to S3
  2. Text Extraction: Text is extracted from the document
  3. Semantic Analysis: GPT-4.1 Mini analyzes the text to identify entities and relationships
  4. Graph Construction: Entities and relationships are stored in MongoDB as a graph
  5. Querying: Queries are processed using GraphRAG to answer questions about the contract

Implementation with AWS CDK and TypeScript

Let's see how to implement this architecture using AWS CDK and TypeScript.

Infrastructure Configuration with AWS CDK

The first step is to define the AWS infrastructure using CDK:

// lib/legal-graph-rag-stack.ts
import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import * as lambda from 'aws-cdk-lib/aws-lambda';
import * as apigateway from 'aws-cdk-lib/aws-apigateway';
import * as s3 from 'aws-cdk-lib/aws-s3';
import * as iam from 'aws-cdk-lib/aws-iam';
import * as secretsmanager from 'aws-cdk-lib/aws-secretsmanager';

export class LegalGraphRagStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    // S3 bucket for legal documents
    const documentsBucket = new s3.Bucket(this, 'LegalDocumentsBucket', {
      removalPolicy: cdk.RemovalPolicy.RETAIN,
      cors: [
        {
          allowedMethods: [
            s3.HttpMethods.GET,
            s3.HttpMethods.POST,
            s3.HttpMethods.PUT,
          ],
          allowedOrigins: ['*'],
          allowedHeaders: ['*'],
        },
      ],
    });

    // Secret for MongoDB Atlas
    const mongoDbSecret = new secretsmanager.Secret(this, 'MongoDBAtlasSecret', {
      secretName: 'mongodb-atlas-credentials',
      description: 'Credentials for MongoDB Atlas',
    });

    // Secret for OpenAI API
    const openAiSecret = new secretsmanager.Secret(this, 'OpenAISecret', {
      secretName: 'openai-api-key',
      description: 'API Key for OpenAI',
    });

    // Lambda for document upload and processing
    const documentProcessorLambda = new lambda.Function(this, 'DocumentProcessorFunction', {
      runtime: lambda.Runtime.NODEJS_18_X,
      handler: 'index.handler',
      code: lambda.Code.fromAsset('lambda/document-processor'),
      timeout: cdk.Duration.minutes(5),
      memorySize: 1024,
      environment: {
        DOCUMENTS_BUCKET: documentsBucket.bucketName,
        MONGODB_SECRET_ARN: mongoDbSecret.secretArn,
        OPENAI_SECRET_ARN: openAiSecret.secretArn,
      },
    });

    // Lambda for graph querying
    const graphQueryLambda = new lambda.Function(this, 'GraphQueryFunction', {
      runtime: lambda.Runtime.NODEJS_18_X,
      handler: 'index.handler',
      code: lambda.Code.fromAsset('lambda/graph-query'),
      timeout: cdk.Duration.minutes(1),
      memorySize: 512,
      environment: {
        MONGODB_SECRET_ARN: mongoDbSecret.secretArn,
        OPENAI_SECRET_ARN: openAiSecret.secretArn,
      },
    });

    // Permissions for Lambdas
    documentsBucket.grantReadWrite(documentProcessorLambda);
    mongoDbSecret.grantRead(documentProcessorLambda);
    mongoDbSecret.grantRead(graphQueryLambda);
    openAiSecret.grantRead(documentProcessorLambda);
    openAiSecret.grantRead(graphQueryLambda);

    // API Gateway
    const api = new apigateway.RestApi(this, 'LegalGraphRagApi', {
      restApiName: 'Legal Graph RAG Service',
      description: 'API for legal contract analysis with GraphRAG',
      defaultCorsPreflightOptions: {
        allowOrigins: apigateway.Cors.ALL_ORIGINS,
        allowMethods: apigateway.Cors.ALL_METHODS,
      },
    });

    // Endpoint for document upload
    const documentsResource = api.root.addResource('documents');
    documentsResource.addMethod('POST', new apigateway.LambdaIntegration(documentProcessorLambda));

    // Endpoint for graph queries
    const queriesResource = api.root.addResource('queries');
    queriesResource.addMethod('POST', new apigateway.LambdaIntegration(graphQueryLambda));

    // Output
    new cdk.CfnOutput(this, 'ApiEndpoint', {
      value: api.url,
      description: 'URL of the API Gateway',
    });

    new cdk.CfnOutput(this, 'DocumentsBucketName', {
      value: documentsBucket.bucketName,
      description: 'Name of the S3 bucket for documents',
    });
  }
}

You're halfway in โ€” the full article lives on our blog.

Continue reading on blog.radixia.ai โ†’

Free to read. Subscribe there to get new posts by email.