AIがデータ抽出から仕分けまで〜人間の代わりに請求書を分析するシステム

株式会社LOWWS CTOのスラヴィ・パンタレーブ(Slavi Pantaleev)にインタビューし、 現在携わっているプロジェクトの中で興味深い技術や最新の知見について聞いていくコーナーです。

今回はAI経費管理システムの設計について聞きました。

This is an interview with Slavi Pantaleev, CTO of LOWWS Inc., where we explore interesting technologies and the latest insights from his current projects.

This time, we discussed the design of an AI expense management system.

The English article follows the Japanese article.


AI経費管理システムの設計

スラヴィ:この経費はシステムに登録されて、ここで追跡されるようになります。そしてあとから分割で支払うこともできます。 一括で支払ってもいいですし、複数回に分けて支払っても大丈夫です。システムはその支払いを追跡し続けます。 最終的には、こうやって新しい支払いの記録を作成して、システムが「この請求書は支払われました」と判断し、どの記録がその請求書を支払ったのかを表示してくれます。

ai-expense-system-file
経費のアップロード画面

これが全体の大まかな仕組みです。そしてもちろん、これらの支払いに関する統計情報もあります。 たとえば、支払うべき義務が発生した日付や、経費が発行された日付、実際に支払った日付などでフィルターをかけることができます。 このような統計があり、CSVなどにエクスポートすることも可能です。 ですが、面白いのは登録の部分です。ご覧いただいたように、かなり簡単になっています。 もともとこのシステムにはAIを入れる予定はなかったのですが、これはAIの活用にぴったりなケースだと思いました。 実際、多くの場合で正確に動作しますので、大きな問題にはなりません。 UI上でお見せしたように、いくつかの処理が必要です。AIによる解析には時間がかかりますので、バックグラウンド処理が重要です。 請求書を保存するとき、たとえば今新しいものをアップロードしますが、保存するとバックグラウンドで処理が行われます。 処理には10秒から60秒、時にはそれ以上かかることもあります。 使用するAIモデルによって処理時間は異なりますし、システムが混雑していると時間が余計にかかります。 そのため、バックグラウンド処理はとても大事です。 他の作業を継続したり、新しい請求書をアップロードしたりできるように、ユーザーの作業をブロックしないようにしています。

そしてもうひとつ重要なのは、適切なAIモデルの選択です。 どのタスクにどのモデルを使うのかを選ぶことが大切です。 また、AIが処理した項目をすべて明示的にマークしておくことも重要です。 たとえば、あるフィールドを使いたくない場合は、拒否することができるようにします。 ユーザーはAIの出力をチェックして、それに応じて行動する必要があります。 それから、AIの設定項目があると便利です。 ユーザーがAIの動作を調整できるようにしています。 こちらがその設定画面です。

このシステムで請求書を送信すると、処理は2段階に分かれています。 最初のステップはOCRです。つまり、データを抽出する段階です。 OCRを使って請求書からデータを取り出し、次に解析を行います。 その際にAIに「何をするべきか」と「どのように振る舞うべきか」のルールを伝える必要があります。 これは請求書そのままを受け取り、サプライヤー情報、金額、明細行などを取り出して、支払うべき合計金額を算出するという作業です。 その中から、「これは銀行振込だ」と判断するわけです。 これが第一のステップで、請求書にある生のデータを取り出します。

そしてこのシステムには第二のステップがあります。 それは「取り出した経費情報を、既存のカテゴリにマッピングする」というものです。 たとえば「これは家賃に関係しているのか?」「材料費か?」「通信費か?」などを判断する工程です。 この第二のステップが分類作業で、このシステムではステップごとに異なるAIモデルを使っています。 OCRのステップは処理が重く、高価なモデルが必要になります。 一方で、分類のステップは比較的簡単で、短いテキストを分析し、それを「通信」や「家賃」などの短いテキストにマッピングするだけです。 そのため、高価なモデルを使わずに済みますし、処理も速くなります。 このシステムのUI上では、両方のステップに対して設定を行うことができます。 私は、デフォルトで生成されるプロンプトもいくつか作成しました。 こちらがデータ抽出用のプロンプトです。OCRのステップ用です。 「添付された請求書ファイルからデータを抽出し、JSON形式で返してください」という内容になっています。 JSONの構造も指定しています。フィールド名と例の値を示してあります。 さらに、日付のフォーマットにも注意点があります。 たとえば、日付が「12/05」のような形式だと、「最初が日、次が月」というルールが必要です。 国によって日付のフォーマットが異なるため、混乱しやすいからです。 また、通貨に関する指示や、通貨が明記されていない場合の扱いについても説明があります。

開発中に私が試行錯誤した結果、かなり長いプロンプトを書きました。 これは私がテストした請求書には有効でしたが、将来的にすべてのケースに対応できるかというと、それは難しいかもしれません。 そのため、この設定画面では、システムの運用者が追加の指示を好きな言語で入力できるようにしています。 英語でも日本語でも構いません。 こうした追加指示によって、実際の経験に基づいた調整が可能です。 これは第一ステップ、OCR部分の設定です。 ただし、この部分の指示は基本的に十分なはずです。

追加指示が特に必要になるのは、第二ステップの分類作業かもしれません。 こちらにも、システムが自動で生成する指示があります。 たとえば、「サプライヤーから請求書を受け取り、以下のような明細があります」というプロンプトです。 実際のプロンプトには請求書から抽出されたデータが入りますが、例として「ケーブルと設置作業」や「デバイス名」などが表示されています。 続いて「費目タイプの一覧」があり、これはIDと経費グループ名のペアで構成されています。 たとえば「ガス・燃料」「ディーゼル」「ローン」「給与」など、多数の費目が自動で登録されています。 この指示では、請求書の明細ごとに「インデックス番号(たとえば0)」と、「該当する費目のID(MongoDBのID)」をマッピングするように求められています。 そして、分類する際の補足ルールも含まれています。 たとえば「CAT」や「FTP」があれば、それは銅線ケーブルであることを意味するので、該当カテゴリにマッピングするようにします。 「capacity(容量)」という言葉が出てきたら、それはインターネット容量を指すと考えられます。 こうしたルールは一見すると明白ですが、定期的な経費など、AIがデフォルトの指示だけでは正しく分類できないことがあります。 その場合、運用者が「こういう記述があれば、これは天然ガス関連の経費」といった指示を追加することができます。 そしてそれを保存しておけば、今後の分類に活用されます。

このように、AIではプロンプトが非常に重要です。 ですがプロンプトは完璧とは限りません。 システムを一度セットして終わり、というような決定論的なものではありません。 ですから、AIの設定ボタンを設けて、ユーザー自身が調整・改善できるようにすることが必要だと考えています。 たとえば、今回のケースでは解析に失敗しました。 エラーが返ってきたので、再試行して再提出する必要があります。 また、請求書がスキャンされたPDFの場合、表示が少しズレていたり、ぼやけていたりすることがあります。 こうした場合は、より高性能で高価なモデルを使わなければなりません。 小さなモデルではうまく処理できませんが、高価なモデルであれば可能です。 そのため、ユーザーが自分で選択できるようにしています。 一方で、ヒューリスティック(経験則)を使う方法もあります。 たとえばPDFファイルが一定以上のサイズであれば画像である可能性が高い、といった判断です。 また、PDFの中にテキストが含まれているかどうかをプログラムで解析し、それに応じてモデルを自動で切り替えることも可能です。 こうした処理で、ユーザーの負担を減らすこともできます。 ただ、今回の実装ではあえてそういった複雑な処理を避け、ユーザー自身にモデルの選択を任せるようにしています。


Designing AI Expense System

Slavi: This expense is in the system, it’s being tracked here and you can pay it later in different installments. You can pay it all at once or in multiple payments, and the system will keep track of that. Eventually, you will pay it by creating a new record of the payment, and then the system will know it is paid and will tell you which record paid for this invoice. This is the general idea, and there are of course also statistics about these payments. You can filter by date of obligation, the date the expense was issued, or by the date you paid for the expense. There are statistics like this, and you can export to CSV and whatnot.

But the interesting part is the registration, which, as you saw, was much simpler. Initially, this system was not supposed to have AI, but I thought this would be a perfect case for using AI—a good usage of it—and in most cases, it would do the job correctly. So it’s not going to cause much problems. As you saw on the UI, a bunch of things had to be done. AI analysis takes time, so background processing is important. When you save the invoice—I’ll upload a new one—it’s going to process it in the background, because it could take from 10 to 60 seconds, or sometimes even more. It depends on the AI model that you choose, and sometimes the systems are more loaded, so it takes longer. So background processing is important. We don’t want to block people’s work so they can continue doing other stuff or uploading new invoices.

Another part that’s important is choosing your models correctly—selecting which ones you choose for specific tasks. Something else that’s important is marking everything that was done by AI, so you can refuse to use it. You can reject some field or you should check the AI’s work and act accordingly. Something else that’s nice to have is settings for the AI, so people can adjust how the AI is invoked. I’ll show you the settings here. When you submit an invoice in this system, the work is broken down into two steps. The first step is OCR: extracting the data. It uses OCR and then analysis to extract the data. We have to tell the AI what to do and some rules about how it should behave. This is about taking the invoice as it is—like this—and finding the supplier information out of these fields, then the numbers and the line entries, extracting the sum or the amount that needs to be paid. From here, it figures out that this is a bank transfer payment. That’s the first part of the job: extracting all this raw data. For our system, we have a second part: now that we have all of the original expenses in the invoice, we need to map them to our existing categories. Is this related to rent? Or materials? Or communication costs? The second part is classification. This is broken down into two steps, and we actually use different models for these steps.

The first step—the OCR step—sometimes is very heavy, so we need to use a very expensive model. But the second step is kind of very easy: just analyzing short texts and classifying them, mapping them to other short texts like “communications” or “rent” or something like that. So for the second step, we don’t need to pay for a very big and expensive model—we can get by with just a smaller model, and it’s faster as well. In this part of the user interface of the system, we can configure both of these parts, and I have written some prompts that the system generates by default. Here’s the first one for the data extraction—this is for the OCR step. The prompt is something like: extract the data from the attached invoice file. We’d like to have a JSON result, and the JSON should have this structure, with field names and example values. It includes a lot of information, like if you see a date formatted like this, the first part is the day number and then the month number—because it can get confused about how different nationalities format their dates. We have instructions for this. And we have instructions about currencies and what to do if there’s no currency specified. Based on my experimentation during development, I’ve entered quite a large prompt here. This works well for the invoices I tested with, but it’s probably not ideal for the full lifetime of the system, because someone might find an invoice with something different that doesn’t work perfectly. So, in this configuration screen, I let operators of the system enter additional instructions in whatever language they want. It can be in English, or it can be in Japanese as well. They can enter additional instructions here to adjust things, based on their experience with the system. This is for the first part—data retrieval, the OCR part. These instructions are probably good enough, but what people might want to customize is the second part: classification of the expenses.

We have instructions here as well, automatically generated by the system. You can see what it looks like. The prompt says: I have an invoice from a supplier and it has the following line entries. And this is an example. The real prompt will include the real data from the invoice, but this example says something like: “cable with some installation” and some kind of device. Then the prompt continues: we have the following expense types. This is a big list of IDs and the actual expense groups—like fuel, gas, diesel, loans, salaries, and so on. These expense types are automatically populated by the system. Then it says: you should map each invoice line entry—like index 0—to the expense type. The expense type in this case is a Mongo ID string, because we use the MongoDB database. That best describes it. Then it says: when classifying the items, consider the following additional rules. For example, if you see CAT or FTP, that’s referring to a cable, specifically a copper cable, so it should be mapped to the copper cable category. If it mentions “capacity,” it likely refers to internet capacity. Since this is an internet company, invoices that mention capacity are considered internet capacity expenses.

These rules may seem obvious at first glance, but there are cases, such as recurring expenses, where AI might not classify correctly based solely on default instructions. In such instances, the administrator can add custom rules, such as specifying that “if this description appears, categorize it as a natural gas-related expense.” I think this is also an important part, because everything relies on prompts with AI. These prompts are potentially not perfect. We cannot set the system in stone—it’s not deterministic. It’s not like I can test it once and say: “Okay, this is 100% correct, we’ll ship it and never touch it again.” I think we need to have an AI settings button that lets people control how the system works and improve it themselves based on their experience.

In this case, analysis failed. We get an error, so we can retry and submit it back for analysis. It’s not perfect. Some invoices are scanned PDFs like this one, and you can see it’s a little off-center and blurry—it’s not perfect. This one requires a more expensive and more capable model. We cannot use smaller models here—they won’t do a good job. But the more expensive model would. So we let people decide and choose. We can also use some heuristics—we can detect if the PDF is large enough, which may indicate it’s an image, or we can analyze the PDF with code and figure out if it contains text or images. In that case, the system can automatically choose the model, which makes it simpler for people. But in this case, I decided it’s better to avoid extra logic and just let people choose the model they wish to use.


この記事はインタビューをもとにAIを使用して作成されています。 This article was created using AI based on interviews.