GPTが人知れず既存の名刺管理アプリを抹殺していた話

抹殺は言い過ぎかもしれませんが簡易な名刺管理アプリであれば自作で十分という時代がきていたようです

これで紙の名刺からはきっとバイバイできるでしょう！

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2100048/fea40871-e3b6-7e14-7327-e939d1f5352d.png" width=75%>

:::note warn
この記事執筆以降`claude3 opus`, `GPT-4o`の発表があり、ますます途中でOCRを入れる意味が薄くなったものと思われます
私もGPT-4oを早速試してみたいと思います！
:::

## 名刺管理アプリ作ってほしいといわれた

それは2/22のお話。
ことの発端は別の部署からかかってきた一本の電話でした。
新規事業の部署でいろいろな取引先様と付き合いがあるものの、紙の名刺が非常に多く管理に困っているとのことのことです。

私は小売業に勤務しているしがない一社員で、現在Eコマースの戦略立案に関する部署に所属しています。

電話先の方は、以前一緒の部署で勤務したことがある方です。現在新規事業のプロジェクト推進をしており、冒頭のような課題感を持っているため既存の名刺管理アプリ導入を考えたのですが、あまりのお値段の高さに卒倒して私に藁をもすがる思いで連絡されたようです。

## これまでのアプリは名刺の識別専門のAI（）を使っていた

話を聞いてみたところ、

1. 名刺の写真を撮る
1. 会社名、部署名、名前、…など項目別にスプレッドシートへ記載される
1. スプレッドシートに次の打ち合わせ日を記載しておくと通知される

といった機能がほしいとのことでした。

１番目は実装したことがないものの、OCRのAPIで実装できるだろうと考えました。
問題は２番目。既存の名刺管理アプリを調べてみると、名刺の識別を専門に学習させたAI（）を利用しているとのことで、専門的なAIを一から実装するのは正直厳しい印象を受けました。

**ですが、世の中にはGPTという便利なAIが生まれたのでこれを使おうと思いました**

まずはプロトタイプとして1, 2の機能を持ったLINE Botを作ってみました

## 構成

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2100048/6fcbd568-3ce8-f43e-d5e0-c48f1f93887e.png)

- 入力インターフェイスとしてLINE
- LINE BotのサーバーとしてFastAPIを選択
  - **他部署に渡すことを考えて社内に書ける人が多い`Python`で実装**
  - 実際相手方の部署にかける方がいるとのこと
  - ~~やっぱりTypeScriptで書きたかった~~
- OCRとして`Google Cloud`の`Cloud Vision API`
- **OCRで読み取った文字列を構造化させるところをGPT4のAPIに任せました**
  - [JSON mode](https://platform.openai.com/docs/guides/text-generation/json-mode)を指定することで、ほぼ確実に意図したスキーマのJSON形式でデータを返してくれます
  - GPT3.5も試しましたが、名前とふりがなをまぜこぜにしてしまう（`渡邊 わたなべ 翼`のような感じ）が見られたので4.0を使うことにしました
- スプレッドシートへの書き込みのところを[Stein](https://steinhq.com/)で実施

https://steinhq.com/

- ホスティングは`Render`
  - FastAPIのDEPLOY方法も丁寧に載っていたので非常に楽でした

https://docs.render.com/deploy-fastapi

## 実行環境

- Python version 3.12.2
- 仮想環境: `venv`

<details><summary>requirements.txt</summary>

```text
aenum==3.1.15
aiohttp==3.9.3
aiosignal==1.3.1
annotated-types==0.6.0
anyio==4.3.0
attrs==23.2.0
cachetools==5.3.2
certifi==2024.2.2
charset-normalizer==3.3.2
click==8.1.7
Deprecated==1.2.14
distro==1.9.0
fastapi==0.110.0
frozenlist==1.4.1
future==1.0.0
google-api-core==2.17.1
google-auth==2.28.1
google-cloud-vision==3.7.1
googleapis-common-protos==1.62.0
grpcio==1.62.0
grpcio-status==1.62.0
h11==0.14.0
httpcore==1.0.4
httptools==0.6.1
httpx==0.27.0
idna==3.6
line-bot-sdk==3.9.0
multidict==6.0.5
openai==1.12.0
proto-plus==1.23.0
protobuf==4.25.3
pyasn1==0.5.1
pyasn1-modules==0.3.0
pydantic==2.6.2
pydantic_core==2.16.3
python-dateutil==2.8.2
python-dotenv==1.0.1
PyYAML==6.0.1
requests==2.31.0
rsa==4.9
six==1.16.0
sniffio==1.3.0
starlette==0.36.3
tqdm==4.66.2
typing_extensions==4.9.0
urllib3==2.2.1
uvicorn==0.27.1
uvloop==0.19.0
watchfiles==0.21.0
websockets==12.0
wrapt==1.16.0
yarl==1.9.4
```
</details>

### GitHub リポジトリ

https://github.com/watanabe-tsubasa/OCR_name_card

### ソースコード

基本的には前述のGitHubリポジトリの`readme.md`を読んでいただければと思いますが、今回の肝となるGPTのAPI部分だけ少し紹介します

```python: create_chat.py
from openai import OpenAI
from dotenv import load_dotenv;load_dotenv()

def create_chat(string: str) -> dict:

  client = OpenAI()
  schema = {
    "会社名": "string",
    "部署名": "string",
    "氏名": "string",
    "会社住所": "string | null",
    "電話番号": "string | null",
    "e-mailアドレス": "string | null",
  }

  response = client.chat.completions.create(
    model="gpt-4-turbo-preview",
    response_format={ "type": "json_object" },
    messages=[
      {"role": "system", "content": f"次の文字列から会社名、部署名、氏名、会社住所、電話番号、e-mailアドレスを抜き出して、JSON形式で出力してください。JSONのスキーマは次の通りです：{schema}"},
      {"role": "user", "content": string}
    ]
  )

  return response
```

`response_format`として`{ "type": "json_object" }`を指定、スキーマを指定することで一定のJSON文字列を返してもらえるようにしています


## 使ってみての結果

> <img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2100048/190cc7e5-1ea7-880a-df77-9338f6419107.jpeg" width=75%>
>
> 一度画像を受け付けたリプライを送った後、成功失敗を通知する形式にしています

### スプレッドシートへの登録

> ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2100048/bfc8c188-34ae-2e46-f532-60b4af64a6ae.png)
>
> 何度画像を送っても同じ結果で登録されています。精度の高さに自分でも驚きました

## 使ってもらってみての感想

 > ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2100048/13b7c6db-4994-7d44-a85a-1dffc0e03609.png)
>
> 私からのメール

**▽▽▽▽▽▽▽▽▽▽▽▽▽▽▽▽▽▽▽▽▽▽▽▽▽▽▽▽▽▽▽▽**

> ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2100048/2a7486aa-e6ea-8d34-af83-fb14ccda5691.png)
>
> ひとまず使ってもらってみてうまくいってそうなのでよかったです

今後は残りの機能の実装と、相手の部署への引き渡しですね

## 実装してみて気づいたこと

　GPTの出現で自然言語の処理が安定するようになったことを頭ではわかっていたものの、実際のプロダクトとして組み込む機会は少なかったので、今回改めてChatとは異なる用途でのtext generationの有用性を実感できました。
　今回は名刺のOCRでしたが、今までは難しかった処理も技術の発展とともに自分の手が届く範囲になるということを改めて肝に銘じて、今後も視野を広げていろいろと実装していきたいです。

 ### こんなタイトルの記事書いて抹殺されるのはお前だぞ？

　そのことも十分理解しているので夜道には気を付けたいと思います

## 参考記事

### Cloud Vision API

公式：基本はこれを読みながらやりました

https://cloud.google.com/vision/docs/ocr?hl=ja

認証周りは下記の記事がわかりやすいかと思います

https://self-development.info/python%E3%81%A7google-cloud-vision-api%E3%82%92%E5%88%A9%E7%94%A8%E3%81%99%E3%82%8B%E6%96%B9%E6%B3%95/

### FastAPIのLINE Bot

https://github.com/line/line-bot-sdk-python

FastAPIのLINE BotはPythonのBot-sdkの公式GitHubにサンプルコードが載っているのでこれをベースにしました

https://github.com/line/line-bot-sdk-python/tree/master/examples/fastapi-echo

## Q & A

たくさんの閲覧やコメントありがとうございます！
いくつかQ & A形式でお返ししたいと思います。

### OCRの過程を省略することはできないか

#### 記事執筆時点(2024/2/26）では難しかったのですがClaude3の登場でできるかもしれません

- `GPT-4-V`では名刺によっては問題なくできる場合もありますが、会社名が崩れてしまう（私の場合、イオンリテールではなくイオンディライトというグループ会社やイオンディジタルという架空の会社が出てきたり）といった現象が見られました
- ~~日進月歩な領域なので将来的には可能なる可能性も高いですが、現状ではOCR通した方が安定しそうです~~
- `Claude3`でOCRに近い精度で画像から起こせるという話が出ています。まだ自分自身で試していないので確証はありませんが、可能であれば更なる技術革新ですね

https://claude.ai/login

### GPTに個人情報を入力して学習されてしまわないか

#### 規約の通りであれば学習されません

- 今回GPTのAPIを利用していますが、APIとEnterpriseに入力された情報は学習には用いないと、OpenAI側の規約に明言されています
- もちろんそれも100%ではないと思いますので、最終的には自己責任になるかと思います
- ~~万一今後APIに入力したデータが学習されていることが判明した場合は、OpenAIを訴えましょう~~

### LINE以外のプラットフォーム使いたい

#### もちろん可能です

- 今回打ち合わせ日程をスプレッドシートに記載したら通知してくれる機能が欲しいとのことを言われていたので、通知まで一貫して実行できるLINEを選択しました
- Webアプリやネイティブアプリ、デスクトップアプリなど、お好きな媒体でご利用ください

### コードかけるとこんなこともできて便利だと思いました

#### コード書けなくても可能です

https://qiita.com/ikeuchi-kota/items/b5673052a3a782f9bb94

- この記事で本投稿と技術要件としてはほぼ同じ操作をノーコードで実施されています
- ノーコードツールの制限もありますが、このような選択肢もありますので便利な世の中になりました

### 使ってみたい

#### GitHubにソース公開していますのでご自由にどうぞ

- 私はしがない小売業の一社員で、その辺のお店で店だしをしているような人なので、特にこれをサービス化してリリースするつもりはありません
- むしろこの記事を読んでいただいた方がインスピレーションを受けて、さらなる発展をさせていただければ本望です
  - Xの投稿でも、地籍境界の抽出に応用できそうという議論をされている方がいらして、記事を発信した良かったなと思いました