AWS上で請求書処理を自動化するためのサーバーレスアプリケーションの構築

Building a serverless application on AWS for automating invoice processing.

このブログ記事では、Amazon Textract、AWS Lambda、およびGoプログラミング言語を使用して、請求書処理のためのサーバーレスソリューションを構築する方法について学習します。Amazon S3にアップロードされた請求書や経費領収書の画像は、Lambda関数をトリガーし、AWS Go SDKを使用して請求書のメタデータ(ID、日付、金額など)を抽出し、Amazon DynamoDBテーブルに保存します。また、AWS CDKのGoバインディングを使用して、ソリューション全体の「インフラストラクチャ・コード」を実装し、AWS Cloud Development Kit(CDK)CLIで展開します。

コードはGitHubで利用可能です。

Amazon Textractの紹介

Amazon Textractは、スキャンされた文書からテキスト、手書き、およびデータを自動的に抽出する機械学習サービスです。簡単な光学的文字認識(OCR)を超えて、フォームや表からデータを識別、理解、および抽出します。ビジネスが文書処理ワークフローを自動化し、手動でのデータ入力を削減し、時間を節約し、エラーを減らし、生産性を向上させるのを支援するアプリケーションにドキュメントテキスト検出と分析を追加するのに役立ちます。

Amazon Textractの一般的な使用例には、次のものがあります。

  • インテリジェントな検索インデックスの構築 – 画像やPDFファイルで検出されたテキストのライブラリを作成します。
  • 自然言語処理(NLP)のためのインテリジェントなテキスト抽出の使用 – NLPアプリケーションの入力としてテキストがグループ化される方法を制御できます。テキストを単語や行として抽出し、ドキュメントテーブル分析が有効になっている場合は、テーブルセルごとにテキストをグループ化することもできます。
  • フォームからのデータキャプチャの自動化 – 構造化データをフォームから抽出し、既存のビジネスワークフローに統合して、フォームを介して提出されたユーザーデータを使用可能な形式に変換できます。
  • 文書分類と抽出の自動化 – Amazon TextractのAnalyze Lendingドキュメント処理APIを使用すると、貸出書類をさまざまな書類クラスに自動分類し、分類されたページを正しい分析操作に自動的にルーティングすることができます。

手を動かしてAmazon Textractを学びましょう!

前提条件

先に進む前に、次のものがインストールされていることを確認してください:

  • Goプログラミング言語(v1.18以上)
  • AWS CDK
  • AWS CLI

プロジェクトをクローンして、正しいディレクトリに変更します:

git clone https://github.com/abhirockzz/ai-ml-golang-textract-text-extraction

cd ai-ml-golang-textract-text-extraction

AWS CDKを使用してソリューションを展開する

AWS CDKは、サポートされているプログラミング言語の1つでクラウドインフラストラクチャをコードとして定義し、AWS CloudFormationを介してプロビジョニングするフレームワークです。

展開を開始するには、cdk deployコマンドを呼び出します。作成されるリソースのリストが表示され、続行するために確認を求められます。

cd cdk

cdk deploy

# 出力

Bundling asset TextractInvoiceProcessingGolangStack/textract-function/Code/Stage...

✨  Synthesis time: 5.26

//.... 省略

これらの変更をデプロイしますか (y/n)? y

アプリケーションに必要なAWSリソースの作成を開始するには、yを入力します。

AWS CloudFormationテンプレートを確認するには、cdk synthを実行し、cdk.outフォルダを確認します。

スタック作成の進行状況をターミナルで追跡するか、AWSコンソールに移動して確認できます:CloudFormation > スタック > TextractInvoiceProcessingGolangStack

スタックの作成が完了すると、次のものがあるはずです:

  • S3バケット(画像をアップロードするソースバケット)
  • Amazon Textractを使用して請求書画像を処理するLambda関数
  • 各イメージの請求書データを保存するDynamoDBテーブル
  • その他のいくつかのコンポーネント(IAMロールなど)

ターミナルで次の出力(リソース名はあなたの場合と異なります)も表示されます。これらはCDKによって作成されたS3バケットの名前です:

✅  TextractInvoiceProcessingGolangStack

✨  デプロイ時間: 113.51秒

出力:
TextractInvoiceProcessingGolangStack.invoiceinputbucketname = textractinvoiceprocessin-invoiceimagesinputbucket-bro1y13pib0r
TextractInvoiceProcessingGolangStack.invoiceoutputtablename = textractinvoiceprocessin-invoiceimagesinputbucket-bro1y13pib0r_invoice_output
.....

エンドツーエンドのソリューションを試す準備ができました!

請求書から経費メタデータを抽出する

ソリューションを試すには、自分自身の画像を使用するか、GitHubリポジトリで提供されているいくつかのサンプル請求書を使用することができます。

私はS3 CLIを使用してファイルをアップロードしますが、AWSコンソールを使用することもできます。

export SOURCE_BUCKET=<CDKの出力からソースS3バケット名を入力してください>

aws s3 cp ./invoice1.jpeg s3://$SOURCE_BUCKET

# アップロードされたファイルを確認する
aws s3 ls s3://$SOURCE_BUCKET

このLambda関数は、画像から請求書データ(ID、総額など)を抽出し、DynamoDBテーブルに保存します。

他のファイルをアップロードしてください。

export SOURCE_BUCKET=<CDKの出力を確認してソースS3バケット名を入力してください>

aws s3 cp ./invoice2.jpeg s3://$SOURCE_BUCKET
aws s3 cp ./invoice3.jpeg s3://$SOURCE_BUCKET

AWSコンソールでDynamoDBテーブルを確認してください。抽出された請求書情報が表示されます。

DynamoDBテーブルは、ソースファイル名をパーティションキーとして設計されています。これにより、指定されたイメージのすべての請求書データを取得できます。

AWS CLIを使用してテーブルをスキャンすることもできます。

aws dynamodb scan --table-name <CDKの出力を確認してテーブル名を入力してください>

クリーンアップ

完了したら、すべてのサービスを削除するには、単に次のコマンドを使用してください。

cdk destroy

# 出力プロンプト('y'を選択して続行してください)

Are you sure you want to delete: RekognitionLabelDetectionGolangStack (y/n)?

完全なソリューションを設定して試すことができました。締めくくる前に、裏側で何が起こっているかを理解するために、コードの重要な部分をすばやく見てみましょう。

コードの解説

いくつかのコード(エラーハンドリング、ログなど)は簡潔にするために省略されています。

AWS CDK

完全なCDKコードはこちらを参照してください。

    bucket := awss3.NewBucket(stack, jsii.String("invoice-images-input-bucket"), &awss3.BucketProps{
        BlockPublicAccess: awss3.BlockPublicAccess_BLOCK_ALL(),
        RemovalPolicy:     awscdk.RemovalPolicy_DESTROY,
        AutoDeleteObjects: jsii.Bool(true),
    })

最初にソースS3バケットを作成します。

    table := awsdynamodb.NewTable(stack, jsii.String("invoice-output-table"),
        &awsdynamodb.TableProps{
            PartitionKey: &awsdynamodb.Attribute{
                Name: jsii.String("source_file"),
                Type: awsdynamodb.AttributeType_STRING},

            TableName: jsii.String(*bucket.BucketName() + "_invoice_output"),
        })

次に、各画像の請求書データを保存するためのDynamoDBテーブルを作成します。

    function := awscdklambdagoalpha.NewGoFunction(stack, jsii.String("textract-function"),
        &awscdklambdagoalpha.GoFunctionProps{
            Runtime:     awslambda.Runtime_GO_1_X(),
            Environment: &map[string]*string{"TABLE_NAME": table.TableName()},
            Entry:       jsii.String(functionDir),
        })

    table.GrantWriteData(function)
    bucket.GrantRead(function, "*")
    function.Role().AddManagedPolicy(awsiam.ManagedPolicy_FromAwsManagedPolicyName(jsii.String("AmazonTextractFullAccess")))

次に、DynamoDBテーブル名を環境変数として関数に渡してLambda関数を作成します。また、関数にDynamoDBテーブルとS3バケットへのアクセス許可を付与します。さらに、関数にAmazonTextractFullAccessのマネージドポリシーへのアクセス許可を付与します。

function.AddEventSource(awslambdaeventsources.NewS3EventSource(sourceBucket, &awslambdaeventsources.S3EventSourceProps{
        Events: &[]awss3.EventType{awss3.EventType_OBJECT_CREATED},
    }))

イベントソースをLambda関数に追加し、ソースS3バケットに請求書画像がアップロードされたときにトリガーされるようにします。

    awscdk.NewCfnOutput(stack, jsii.String("invoice-input-bucket-name"),
        &awscdk.CfnOutputProps{
            ExportName: jsii.String("invoice-input-bucket-name"),
            Value:      bucket.BucketName()})

    awscdk.NewCfnOutput(stack, jsii.String("invoice-output-table-name"),
        &awscdk.CfnOutputProps{
            ExportName: jsii.String("invoice-output-table-name"),
            Value:      table.TableName()})

最後に、AWS CloudFormation出力としてバケットとDynamoDBテーブル名をエクスポートします。

Lambda Function

完全なLambda関数コードについては、こちらを参照してください。

func handler(ctx context.Context, s3Event events.S3Event) {
    for _, record := range s3Event.Records {

        sourceBucketName := record.S3.Bucket.Name
        fileName := record.S3.Object.Key

        err := invoiceProcessing(sourceBucketName, fileName)
    }
}

請求書がソースバケットにアップロードされるとLambda関数がトリガーされます。関数は請求書のリストを反復処理し、各請求書のためにinvoiceProcessing関数を呼び出します。

それでは、以下を進めていきましょう。

func invoiceProcessing(sourceBucketName, fileName string) error {

    resp, err := textractClient.AnalyzeExpense(context.Background(), &textract.AnalyzeExpenseInput{
        Document: &types.Document{
            S3Object: &types.S3Object{
                Bucket: &sourceBucketName,
                Name:   &fileName,
            },
        },
    })

    for _, doc := range resp.ExpenseDocuments {
        item := make(map[string]ddbTypes.AttributeValue)
        item["source_file"] = &ddbTypes.AttributeValueMemberS{Value: fileName}

        for _, summaryField := range doc.SummaryFields {

            if *summaryField.Type.Text == "INVOICE_RECEIPT_ID" {
                item["receipt_id"] = &ddbTypes.AttributeValueMemberS{Value: *summaryField.ValueDetection.Text}
            } else if *summaryField.Type.Text == "TOTAL" {
                item["total"] = &ddbTypes.AttributeValueMemberS{Value: *summaryField.ValueDetection.Text}
            } else if *summaryField.Type.Text == "INVOICE_RECEIPT_DATE" {
                item["receipt_date"] = &ddbTypes.AttributeValueMemberS{Value: *summaryField.ValueDetection.Text}
            } else if *summaryField.Type.Text == "DUE_DATE" {
                item["due_date"] = &ddbTypes.AttributeValueMemberS{Value: *summaryField.ValueDetection.Text}
            }
        }

        _, err := dynamodbClient.PutItem(context.Background(), &dynamodb.PutItemInput{
            TableName: aws.String(table),
            Item:      item,
        })
    }

    return nil
}
  • invoiceProcessing関数は、Amazon Textract AnalyzeExpense APIを呼び出して請求書データを抽出します。
  • 関数は、ExpenseDocumentsのリストを反復処理し、特定のフィールドから情報を抽出します – INVOICE_RECEIPT_IDTOTALINVOICE_RECEIPT_DATEDUE_DATE
  • 次に、抽出されたデータをDynamoDBテーブルに保存します。

結論と次のステップ

この記事では、Amazon Textractを使用して請求書を処理するサーバーレスソリューションを作成する方法を紹介しました。AWS CDKを使用して、インフラストラクチャのライフサイクル全体を自動化しました。これらすべては、AWS LambdaとAWS CDKでよくサポートされているGoプログラミング言語を使用して行われました。

この解決策を改善/拡張するには、ExpenseDocument sレスポンス内の他のフィールドで実験して、より多くの情報を抽出してみてください。

楽しいビルディングを!

We will continue to update VoAGI; if you have any questions or suggestions, please contact us!

Share:

Was this article helpful?

93 out of 132 found this helpful

Related articles

    Discover more