レシートを読み込んで家計管理をする記録 ～ChatGPTに情報を抽出してもらおう～

OCR部分が固まったが、その結果をどう処理するか。。
まずは以下を抽出してみます。
・店名
・購入日時
・購入商品
・合計額

というわけで、手っ取り早くChatGPT-APIを利用することにしました。
確か無料枠があったはずだから一定量のリクエストは金かからないはず。

と思ったら、無料枠の期限切れてました。。。
「コストは0円で抑えたい（抑えるとは言ってない）」状態になってしまいますが、ChatGPT-APIおよびそれを効率的に利用できるLangChainは利用価値が高いので、必要なコストと割り切っていきましょう。

さて、以下がOCRで読み取ったレシートデータ。

    領収証
    株式会社 富士薬品
    ドラッグセイムス 新河岸店
    TEL 049-241-2715
    2024年02月23日 (金)20時07分 #3969
    内* キリン生茶2L
    @149
    1
    SEIMS
    内*
    *
    149
    ワンポットエコティーバッグホウジ茶50袋
    @386
    1
    386
    -¥58
    104
    4909411069100
    4901085621929
    自動割引(クーポン) 15%
    内 薬用ハンﾄﾞソープ 詰替
    @104
    1
    4513574023000
    3点
    小計
    合計
    (含む消費税等
    (10%対象
    消費税
    ( 8%対象
    消費稅
    ¥581
    ¥581
    ¥44)
    #104
    ¥9)
    ¥477
    ¥35)
    *は軽減税率8%適用商品
    ¥581
    現金
    ブロンズ会員(今月) XXXXXXXXX7390
    今回現金ポイント
    5P
    累計現金ポイント
    1,110P
    5P
    今回ギフトポイント
    1,859P
    累計ギフトポイント
    今月の御買上金額 (税抜) 8,364円
    上記正に領収いたしました
    店: 009554 レジ : 0002
    登録番号
    累計現金ポイントが400P貯まる毎に
    200円の値引特典がご利用頂けます
    ランクアップシステムは24年2月29
    日をもちまして終了させていただき
    ます。 24年3月から新サービス開始
    (24年春頃予定) までの間、 セイム
    スポイント2倍キャンペーンを実施
    いたします。 ※一部対象外商品もご
    ざいます。
    001800617
    T4030001007483
    0900955400022402243969

ここから以下を抽出します。
・店名：ドラッグセイムス　新河岸店
・購入日時：2024年02月23日 (金)20時07分
・購入商品リスト［商品名、個数、定価、割引額、購入額（=定価-割引額）］：以下3つ
　［キリン生茶2L、1、149円、0円、149円］
　［ワンポットエコティーバッグホウジ茶50袋、386円、58円、328円］
　［薬用ハンドソープ 詰替、1、104円、0円、104円］
・合計額：581円

　

# フェーズ1：とりあえず目的のデータを抽出
まず、形式にこだわらずにデータを取り出せるか確認します。
```python
# LangChainの必要なクラスをインポート
from langchain import PromptTemplate
from langchain.chat_models import ChatOpenAI
from langchain import LLMChain

# OpenAIのモデルのインスタンスを作成
# model_nameでモデルを指定
# temperatureで応答の自由度を指定　0は正確さと再現性に優れている
chat = ChatOpenAI(model_name="gpt-3.5-turbo", temperature=0)

# プロンプトのテンプレート
# dataにレシートのデータが入る
template = """
この領収証データから、以下を抽出してリストアップしてください。
・店名
・購入日時
・購入商品リスト［商品名、個数、定価、割引額、購入額（=定価-割引額）］
・合計額
{data}
"""
prompt = PromptTemplate(
    input_variables=["data"],
    template=template,
)

# チェーンを作成
chain = LLMChain(llm=chat, prompt=prompt,verbose=True)

# チェーンを実行
res = chain(data)

# 結果を表示
print(res['text'])
```

実行結果

    > Finished chain.
    ・店名: ドラッグセイムス 新河岸店
    ・購入日時: 2024年02月23日 (金)20時07分
    ・購入商品リスト:
      1. 商品名: キリン生茶2L、個数: 1、定価: 149円、割引額: 0円、購入額: 149円
      2. 商品名: ワンポットエコティーバッグホウジ茶50袋、個数: 1、定価: 386円、割引額: 58円、購入額: 328円
      3. 商品名: 薬用ハンドソープ詰替、個数: 1、定価: 104円、割引額: 0円、購入額: 104円
    ・合計額: 581円

無事に目的のデータを抽出できた！

# フェーズ2：JSON形式で抽出
お次はプロンプトを変更して、JSON形式で抽出してもらいます。
変更といっても、フェーズ1で「この領収証データから、以下を抽出してリストアップしてください。」と書いてたプロンプトを「この領収証データから、以下を抽出してJsonで構造化してください。」と書き換えるだけです。
```python
# LangChainの必要なクラスをインポート
from langchain import PromptTemplate
from langchain.chat_models import ChatOpenAI
from langchain import LLMChain

# OpenAIのモデルのインスタンスを作成
# model_nameでモデルを指定
# temperatureで応答の自由度を指定　0は正確さと再現性に優れている
chat = ChatOpenAI(model_name="gpt-3.5-turbo", temperature=0)

# プロンプトのテンプレート
# dataにレシートのデータが入る
template = """
この領収証データから、以下を抽出してJsonで構造化してください。
・店名
・購入日時
・購入商品リスト［商品名、個数、定価、割引額、購入額（=定価-割引額）］
・合計額
{data}
"""
prompt = PromptTemplate(
    input_variables=["data"],
    template=template,
)

# チェーンを作成
chain = LLMChain(llm=chat, prompt=prompt,verbose=True)

# チェーンを実行
res = chain(data)

# 結果を表示
print(res['text'])
```
実行結果

    > Finished chain.
    {
      "店名": "ドラッグセイムス 新河岸店",
      "購入日時": "2024年02月23日 (金)20時07分",
      "購入商品リスト": [
        {
          "商品名": "キリン生茶2L",
          "個数": 1,
          "定価": 149,
          "割引額": 0,
          "購入額": 149
        },
        {
          "商品名": "ワンポットエコティーバッグホウジ茶50袋",
          "個数": 1,
          "定価": 386,
          "割引額": 58,
          "購入額": 328
        },
        {
          "商品名": "薬用ハンﾄﾞソープ 詰替",
          "個数": 1,
          "定価": 104,
          "割引額": 0,
          "購入額": 104
        }
      ],
      "合計額": 581
    }

問題なくJson形式で出せた！
出力値はあくまで「JSONの形した文字列」なのでJSON形式にパースすることも忘れずに。

```python
# LangChainの必要なクラスをインポート
from langchain import PromptTemplate
from langchain.chat_models import ChatOpenAI
from langchain import LLMChain

# OpenAIのモデルのインスタンスを作成
# model_nameでモデルを指定
# temperatureで応答の自由度を指定　0は正確さと再現性に優れている
chat = ChatOpenAI(model_name="gpt-3.5-turbo", temperature=0)

# プロンプトのテンプレート
# dataにレシートのデータが入る
template = """
この領収証データから、以下を抽出してJsonで構造化してください。
・店名
・購入日時
・購入商品リスト［商品名、個数、定価、割引額、購入額（=定価-割引額）］
・合計額
{data}
"""
prompt = PromptTemplate(
    input_variables=["data"],
    template=template,
)

# チェーンを作成
chain = LLMChain(llm=chat, prompt=prompt,verbose=True)

# チェーンを実行
res = chain(data)
print(type(res['text']))

# GPTから受け取った回答をJson形式にパース
structured_response = json.loads(res['text'])

print(type(structured_response))
print(structured_response)
```

実行結果

    <class 'str'> ←Jsonパース前のresponse
    <class 'dict'> ←Jsonパース後のresponse
    {'店名': 'ドラッグセイムス 新河岸店', '購入日時': '2024年02月23日 (金)20時07分', '購入商品リスト': [{'商品名': 'キリン生茶2L', '個数': 1, '定価': 149, '割引額': 0, '購入額': 149}, {'商品名': 'ワンポットエコティーバッグホウジ茶50袋', '個数': 1, '定価': 386, '割引額': 58, '購入額': 328}, {'商品名': '薬用ハンﾄﾞソープ 詰替', '個数': 1, '定価': 104, '割引額': 0, '購入額': 104}], '合計額': 581}

これで良し！
（改行とインデントなくなって読みづらくなったけど、まあいいでしょう。）
これでOCRで読み取ったデータの抽出処理の目途が立ったので、あとはGoogle Vision APIによるOCR処理とがっちゃんこすればメインの処理はいったん完成です。

そろそろ（というか次回投稿で）、全体的なアーキテクチャについても書いていきます。