AI ยท serverless
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:
- Loss of Relationships: When documents are split into chunks for vectorization, relationships between different document parts are lost.
- Difficulty with Multi-hop Reasoning: Vector-based RAG struggles to answer questions requiring connecting information in different parts of the document.
- Limited Explainability: Vector embeddings make it challenging to understand why specific chunks were selected as relevant.
Advantages of GraphRAG for Legal Documents
GraphRAG addresses these limitations by structuring data as a knowledge graph instead of vector embeddings:
- Relationship Preservation: Entities (such as contracting parties, clauses, obligations) and their relationships are explicitly modeled.
- Multi-hop Reasoning: GraphRAG can navigate through the graph to answer complex questions that require connecting different entities.
- Greater Explainability: The path through the graph provides a visual and understandable explanation of how the answer was reached.
- 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:
- Contract Parties:
- Lender: [bank 1], [bank 2], [bank 3] (Mandated Lead Arrangers and Original Lenders)
- Borrower: [company]
- Agent: [agent] (also Security Agent)
- Financial Elements:
- Total Amount: EURO 100,000,000
- Facilities: Refinancing Facility, Capex Facility, Incremental Facility
- Interest Rates: Margin 4.25% per annum (with possible increase)
- Contractual Clauses:
- Definitions and Interpretations
- Conditions of Use
- Repayment and Cancellation
- Interest and Fees
- Guarantees and Indemnities
- Financial Covenants
- Events of Default
- Regulatory References:
- Italian Civil Code
- Business Crisis and Insolvency Code (CIC)
- Legislative Decree 231/2001 (Administrative Liability)
- Legislative Decree 231/2007 (Anti-Money Laundering)
Key Relationships in the Contract
The relationships between these entities are equally important:
- Relationships between Parties:
- Lender → Borrower: Grants the financing
- Agent → Lender: Represents the Lenders
- Agent → Borrower: Monitors compliance with obligations
- Financial Relationships:
- Borrower → Facilities: Beneficiary of credit lines
- Leverage → Margin: Influences the applicable interest rate
- Obligation Relationships:
- Borrower → Financial Covenants: Obligated to respect the covenants
- Borrower → Information Undertakings: Obligated to provide information
- Risk Relationships:
- Event of Default → Facilities: Can cause acceleration of repayment
- 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:

Main Components
- API Gateway: Exposes RESTful endpoints for interaction with the system
- Lambda Functions: Serverless functions to process requests and manage business logic
- MongoDB Atlas: Database to store the knowledge graph and documents
- Amazon S3: Storage for the original legal documents
- OpenAI GPT-4.1 Mini: LLM for entity and relationship extraction
Processing Flow
- Document Upload: The legal contract is uploaded to S3
- Text Extraction: Text is extracted from the document
- Semantic Analysis: GPT-4.1 Mini analyzes the text to identify entities and relationships
- Graph Construction: Entities and relationships are stored in MongoDB as a graph
- 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.
