電子取引保存法の面倒な事務作業を自動化する
背景
電子取引保存法に対応しなければいけないが、正直面倒なので手間を減らしたい
作ったもの
-
input: レシート画像をNotionDBにアップロードする
-
output: レシートの画像に基づいて他のプロパティが自動で追加される
構成
主要なプロセスを時系列順に示したものです。【】内は対応するツールやAPIです。
- 【make】NotinoDBにアイテムが追加されたことをトリガーに、以下の処理を行うFunctionを呼ぶ
- クエリに含まれたレシート画像のURLか画像データを取得する
- 【Google Document AI】OCR(画像から文字を読み取る)する
- 【openAI API】: 抽出した文字列をJSONデータにする
- 【NotionAPI】: NotionDBに書き込む
FunctionはGoogle Cloud Functionsにデプロイしています。Cloud Functionsについては、プラットフォーム特有の知識が要り時間を食ってしまいました。
各工程の解説
続いて工程ごとの詳しい解説をしていきます。
1/ 【make】NotionDBへのアイテム追加をトリガーにする
NotionのAPIはWEBHookに非対応なので、外部連携ツールを使います。Zapierなどが有名ですが、今回はコストメリットのあるmakeを使います。makeは今まで馴染みがありませんでしたがノーコードツールとして打ち出しており、直感的なUIが印象的です
Notionとの連携です。
ちなみにMakeではトリガーの発火は①アイテムの更新、或いは②アイテムの作成、どちらかを選べます。今回は②を選択しています。
呼び出したFunctionがNotinoDBのアイテムを更新することになるので、その更新がトリガーとなりmakeがFunctionを呼び出すという無限ループが起きてしまいます。アイテム作成をトリガーとすることでこれを回避しています。
Notionとmakeの連携における注意点
Read user information icluding email address
にチェックが必要です。連携する際にエラーが出て、原因がよくわからず困りました。
続く、Httpリクエストの設定です。NotionDBから取得した任意のプロパティをクエリに含めることが可能です。UIから選ぶだけなので簡単です。
今回は画像のurlとDBアイテムのIDをクエリパラメーターとして渡します。
2/ 画像データの取得
クエリパラメータとして画像のurlが渡されているので、バイナリデータを取得します。特筆することはありませんが、標準搭載されたfetch()を使うことでおおよそ次のように取得できます。
//Cloud functionではこのようにクエリパラメータを取得します
const imageUrl = req.query["imageUrl"]
const response = await fetch(imageUrl);
const arrayBuffer = await response.arrayBuffer();
const buffer = Buffer.from(arrayBuffer);
コードはこちらの記事を参考にしています。
3/ 【Document AI】によるOCR
画像はDocument AIをコンソール画面から試したものです。レシートがかなりくしゃくしゃな状態ですが、それでも正確に読み取れていることがわかります。
実際に抽出されたテキストはこんな感じになります。
STARBUCKS 札幌北野店 #1431 TEL 011-888-0035 1 S 7x th マグ 301 Starbucks eGift適用 本体合計(1点) (10%対象 総合計 455 (カスタム) 455 455 消費税 45) 500 Starbucks eGift (つり銭なし 500 釣り 0 「軽」印は軽減税率(8%)適用商品 For Here 030162350103 4068 2024/02/20 14:21:27 スターバックスコーヒージャパン株式会社 登録番号 7011001031943 発行日: 02/20 二次元コードを読み込んで ひと足早いお花見体験、 満開の桜と 踊るベアリスタAR #スターバックスさくら 2024 https://sbux.jp/sakuraa2024_receipt 毎月20日は ETHICALLY CONNECTING DAY エシカルなコーヒーの日 公式サイトでブラックエプロン バリスタの コーヒーストーリー ゼロ
OCRに用いるAPI、2種の使い分け
ちなみにGCPで利用できる、OCR向けのAPIには次の2種類があります。
- Document AI
- Vison API
以下の記事によると、それぞれ得意不得意があるようです。適切に使い分けることで、読み取り精度の向上が期待できます。
3/ openAIによるデータの構造化
続いてOCRで読み取ったテキストから必要な情報を抽出して、JSONデータにします。抽出してほしい情報やどのようなJSONデータで返してほしいかについては、APIを呼び出す際にプロンプトとして渡します。
画像はコンソールから試したものですが、テキストからいい感じに推論・抽出してくれます。よく見るとjson以外の余計な応答も混じっていますが、Jsonを返すだけのjsonmodeに設定することで解決します。
ちなみにこちらの記事のいい使い方だなと思ったので、それを真似しています。
2種のOpenAI API、両者の違いとか
- Assistants API: 過去行ったやり取りを踏まえた応答が得られる。実装がちょっと面倒
- Completion API: シンプルで手軽に呼び出しできる
それぞれの特徴について、両方試した上で自分はざっくりこのように理解しています。(間違ってたらごめんなさい🙇)
リリースされて間も無くまだbeta版のAssistansAPIですが、スレッド上にやり取りが累積すればするだけ、応答の生成にお金がかかってしまうという仕様があります。これのおかげで、ちょっと使いにくさがあります。
対するCompletion APIはお手軽。とにかくシンプルに呼び出しでき、コストもかかりにくい点に優位性があります。
今回は渡された文字列をいい感じにjsonに変換してもらうだけなので、completion APIを使っています。
4/ NotionDBに各種プロパティを書き込む
NotionAPIを使用します。こちらも公式のsdkを使うと楽です。
こちらのドキュメントを見て実装しています。データ構造が少々特殊なこと以外、特筆することはないです。該当部分のJSコードです。
const properties = {
Summary: {
title: [
{
text: {
content: obj.summary,
},
},
],
},
Accounts: {
select: {
name: obj.accounts,
},
},
Supplier: {
rich_text: [
{
text: {
content: obj.supplier,
},
},
],
},
Price: {
number: obj.price
},
Date: {
date: {
start: obj.date
}
}
};
所感
今回は個人的に愛用しているのもあってNotionDBを使用していますが、Zapierや今回活用したmakeなどの外部連携ツールさえ対応していれば、同じ要領で自動化が可能です。
割とAIを活用していて、楽しく開発ができました。
余談
元々はこんな感じでLINEを画像アップロードのインターフェイスにしていました。
しかし現在のNotionAPIの仕様ではNotionDBに画像ファイルを追加できないことがわかりました。電子取引保存法ではレシートの画像そのものも保存しておく必要があるため、直接NotionDBにファイルを突っ込んでそれをトリガーにFunctionを呼び出す今の構成に落ち着きました。
Discussion