この記事はAlgomaticで開催している アドベントカレンダー2025の16日目の記事です。
こんにちは。Algomatic でコーポレートエンジニアをしている @raryosu です。
Algomaticは、生成AIを活用したサービス開発を行っているスタートアップです。事業の性質上、多くのプロフェッショナルなパートナー様やフリーランスの方々と協業しており、その数は日々増え続けています。
事業が拡大するのは嬉しいことですが、コーポレート組織の視点では、ある切実な課題が浮き彫りになっていました。 それは、「月末月初の、膨大な請求書処理業務」です。
今回は、この泥臭い業務課題を、自社の強みであるLLM(大規模言語モデル)とSlack Botを組み合わせて解決した話をします。
抱えていた課題:お互いに不幸な「手戻り」と「リマインド」
フリーランスの方との取引が増えるにつれ、以下のような課題が深刻化していました。
提出漏れのリマインドコスト 「あの人の請求書、まだ来てない…」と管理部が一人ひとりにチャットを送る作業は、心理的にも時間的にも負担です。
記載ミスによる手戻り インボイス登録番号の記載漏れ、宛名の間違い、請求月のズレなど。これらは形式的なミスですが、経理処理上はそのまま受け取ることができません。 管理部が目視でチェックし、「ここ直してください」と連絡し、パートナー様が再発行する…。このやり取りは、双方にとって生産性がなく、ストレスフルな時間です。
「パートナー様はサクッと請求書を出して業務に戻りたいし、我々は正しい請求書を期日通りに受け取りたい」 このシンプルな願いを叶えるため、LLMを活用した「請求書回収Bot」を内製開発しました。
解決策:Botが「門番」ではなく「アシスタント」になる
今回開発したBotの挙動は以下の通りです。
対象者の自動抽出 Botが契約台帳(データベース)を参照し、「今月有効な契約があるフリーランスの方」を特定します。
稼働確認DMの送付 SlackのDMで「今月の稼働はありましたか?」とBotが聞きに行きます(Push型)。

請求書のアップロード & LLM解析 「稼働あり」と答えた方に対し、Botが請求書のPDFアップロードを求めます。 ここが肝です。 アップロードされたPDFをLLMが解析し、その場でバリデーション(形式チェック)を行います。
社内確認へ 形式チェックをパスしたものだけが、各部門管理者や、管理部門へ通知され、最終的な承認・支払処理へと回ります。
こだわりポイント:勝手に「リジェクト」しない
開発にあたり最も意識したのは、「Botが勝手に受領拒否(リジェクト)をしない」という点です。
法的な観点(下請法等)や商習慣から見ても、システムが機械的に「この請求書は受け取りません」と突き返すのはリスクがありますし、何よりパートナー様に対して失礼になりかねません。
そこで、このBotはあくまで「セルフチェックのアシスタント」という立ち位置にしました。
× 機械的な拒否: 「金額が間違っているため、受領できません。」
○ パートナーへの気づき: 「提出ありがとうございます!読み取ったところ、契約金額と請求金額に差分があるようです(交通費などが含まれている場合はそのままで構いません)。このまま提出しますか?それとも修正しますか?」
このように、最終的な提出の意思決定は人間に委ねつつ、ケアレスミスにはその場で気づいてもらう設計にすることで、コンプライアンスとUX(ユーザー体験)の両立を図りました。
技術的な実装の裏側
システム構成としては、Slack API (Bolt) をインターフェースとし、バックエンドで契約DBと Gemini を連携させています。
LLMへのプロンプト戦略:ただのOCRにしないための3つの工夫
請求書の読み取りにおいて最も重要なのは、「文字を認識すること」ではなく「契約内容と合っているか照合すること」です。 そのため、単に画像を渡すだけでなく、プロンプトエンジニアリングにおいて以下の3つの工夫を凝らしました。
1. 契約データの動的注入(Context Injection)
LLMに請求書画像だけを渡しても、「金額が正しいか」は判断できません。 そこで、プロンプトを生成する直前にデータベースから「そのユーザーの今月有効な契約情報(単価、契約ID、所属組織など)」を引き出し、プロンプト内に動的に埋め込んでいます。
実際のプロンプト生成コード(TypeScript)のイメージです。
// プロンプト内で、DBから取得した契約情報を展開してLLMに渡す const prompt = ` ... 請求書提出者の雇用契約情報: ${validatedContracts.map((c, index) => ` [契約 ${index + 1}] - 契約ID: ${c.contractId} - 単価: ${c.unitRate} (${c.unitType}) - 発注部署: ${c.departmentName} - 会計セグメント: ${c.accountingSegmentName} `).join('\n\n')} 上記の契約情報を正とし、請求書の明細がこれと一致しているか厳密に判定してください。 ... `;
これにより、LLMは「画像に書かれた数値」と「DB上の正解データ」をその場で突合し、「単価が契約と異なります(契約: 5,000円, 請求: 5,500円)」といった高度なバリデーションを可能にしています。
2. 表記揺れの「名寄せ」正規化
フリーランスの方によって、宛名や部署名の書き方は千差万別です(例:「Algomatic AI Transformation カンパニー」「AX」「Algomatic AX カンパニー」など)。 これらを従来のプログラム(正規表現など)ですべて網羅するのは困難ですが、LLMの文脈理解能力を活用することで解決しました。
プロンプト内で以下のような「マッピングルール」を定義し、ゆらぎのある表記をシステム上の正式名称(会計セグメント)に正規化させています。
以下の組織名マッピングルールを参考に適用してください: - 「Neo Sales」「ネオセールス」「neosales」→ 「ネオセールス」に正規化 - 「Algomatic Works」「アルゴマティックワークス」「AMW」→ 「Algomatic Works」に正規化 - 「AI Transformation」「AX」→ 「AX」に正規化
3. 「NG」と「指摘」の明確な分離
AIが「インボイス番号がないからNG」と機械的に突き返すと、受領拒否等のトラブルになりかねません。 そこでプロンプトでは、「数値の不整合などの致命的なエラー」と「宛名ミスなどの軽微な不備」を明確に区別して指示しています。
その他、気になる点があれば詳細にコメントを残してください。 例えば下記のような問題点が考えられますが、該当する場合でもコメントのみに留め、NGとはしないでください - 請求書の宛先の会社名(大文字小文字等)が間違っている。 - 請求金額の振込先口座名義が漢字で記載されている(カタカナ推奨)。
このように指示することで、Botは「受け取り拒否」ではなく、「宛名が少し違いますが、このまま提出しますか?」といった人間に判断を委ねるアシストが可能になります。
導入後の効果
実際に社内で運用を開始した結果、以下のような変化がありました。
- 管理部の確認工数が大幅減 形式的な不備がBot段階で解消されるため、管理部に届くのは「中身の確認だけでOK」な請求書ばかりになりました。
- 「うっかりミス」の撲滅 インボイス番号の記載漏れなどが激減しました。
パートナー様からの評判 「Slackで完結するので楽」「その場でOKが出るので、ちゃんと届いたか不安にならなくて済む」といった声をいただいています。
また、リマインドも人間からではなく Bot から行うので、リマインドをする負荷も、受け取った側の負荷も低くなったのではないかと思います。

おわりに:このシステム、実は…
当初は自社の業務効率化のために作った「ドッグフーディング(自社利用)」プロダクトでしたが、運用してみると想像以上に効果が高く、多くの企業様が抱える「請求書回収の泥臭い課題」を解決できるポテンシャルを感じています。
そこで現在、この「LLM請求書回収アシスタント」を、同様の課題を持つ企業様へ提供(外販)することを検討しています。
- フリーランスとの取引が多く、毎月の請求書回収に疲弊している
- インボイス制度対応などで、形式チェックの手間が増えている
- Slack等のチャットツールで、スマートに業務を完結させたい
もし、このような課題をお持ちの情シス・経理担当者様がいらっしゃいましたら、ぜひ情報交換させてください。まだ製品化前の段階ですが、デモをお見せしたり、運用ノウハウをお話しできると思います。
興味がある方は、X(Twitter)@raryosu または、こちらのフォーム https://algomatic.jp/contact からお気軽にご連絡ください!
明日の記事は、荒井さんの記事です! お楽しみに〜