見出し画像

【Difyハンズオン】LINEとOpenAIで作る次世代の名刺管理システム

※こちらの記事は以下のLINE DCハンズオンのためとなっています。


初めに


名刺交換はビジネスの基本ですが、その後のデータ入力は意外と面倒ですよね?

本記事では、そんな面倒な名刺管理を劇的に楽にする、LINEとDifyを組み合わせた自動化システムをご紹介します!このシステムがあれば、名刺のデータ化にかかる時間と労力を大幅に削減し、本来の業務に集中できます。

このシステムのすごいところ!

  • 名刺の画像解析:LINEで名刺の画像を送信するだけで、Difyが名前とメールアドレスを自動抽出

  • 情報の共有・管理:抽出した情報をGoogleスプレットシートで簡単に管理

  • 手間の削減:名刺の手動入力が不要になるため、大幅な業務効率化が期待できる

LINE DCで詳細を解説します!

この画期的なシステムについて、LINE DCでも詳しく解説します。ぜひご参加いただき、その便利さを体感してください!

使い方の流れ
名刺管理の自動化は、以下の簡単な5ステップで完了します:

  1. LINEで名刺を撮影・送信

  2. AIが自動で情報を抽出

  3. Googleフォームのリンクが届く

  4. リンクを開いて必要に応じて情報を編集・追加

  5. 送信すれば登録完了!

案件のご相談はこちらから

これらの技術やサービスに興味がある方、または具体的なプロジェクトのご相談がある方は、ぜひお気軽にご連絡ください。

Difyとは?


Difyは、プログラミングほぼ不要で簡単にAIアプリを開発できるプラットフォームです。今回のシステムでは、Difyが名刺画像から情報を読み取るAIの部分を担当しています。直感的な操作で、誰でも簡単にAIを活用したアプリケーションを作成できるのが魅力です。

Difyを試せます↓

なぜDifyを選んだのか?

  • 最近、Difyを活用した案件が増えてきており、私自身もその可能性に注目していたため。

  • LLMモデルの変更が容易で、最新技術を柔軟に試せる。

  • 他のワークフローを追加するなど、後からのカスタマイズも簡単。

システム全体図


  1. ユーザーがLINEで名刺画像を送信します。

  2. LINE Messaging APIが、WebhookでGoogle Apps Scriptに画像データを送信します。

  3. Google Apps Scriptが、画像データをDifyに送信します。

  4. Difyが名刺画像を解析し、名前とメールアドレスをGoogle Apps Scriptに返します。

  5. Google Apps Scriptが、解析結果を基に、情報を事前入力したGoogleフォームのURLを生成し、LINEユーザーに返信します。

  6. ユーザーは、Googleフォームで情報の確認や修正を行い、送信します。

  7. Googleフォームに入力されたデータが、Googleスプレッドシートに登録されます。

ステップ 1: 事前準備


Googleフォームの作成

このシステムでは、名刺から読み取った情報を登録するためのGoogleフォームを使用します。以下の手順でフォームを作成します。

  • Google Apps Scriptを開きます。

  • スクリプトの入力: 以下のスクリプトをコピーして、スクリプトエディタに貼り付けます。このスクリプトは、「名前」、「メールアドレス」、「メモ」の3つの項目を持つGoogleフォームを自動生成します。

function createGoogleForm() {
  // 新規フォームの作成
  var form = FormApp.create('ユーザー情報フォーム');
  
  // フォームの説明 (任意)
  form.setDescription('以下の質問にお答えください。');
  
  // 「名前」質問を追加
  form.addTextItem()
      .setTitle('名前')
      .setRequired(true);
  
  // 「メールアドレス」質問を追加(バリデーションとしてEmail型)
  form.addTextItem()
      .setTitle('メールアドレス')
      .setRequired(true)
      .setValidation(FormApp.createTextValidation()
        .setHelpText('有効なメールアドレスを入力してください。')
        .requireTextIsEmail()
        .build());
  
  // 「メモ」質問を追加(長文回答用の段落テキスト)
  form.addParagraphTextItem()
      .setTitle('メモ')
      .setRequired(false);

  // 作成したフォームのURLをログに出力
  Logger.log('フォームが作成されました: ' + form.getEditUrl());
  Logger.log('回答閲覧用URL: ' + form.getPublishedUrl()); 
  Logger.log('フォームID: ' + form.getId()); // この行を追加
}
  • コードを保存して、実行します。 初回は、Googleアカウントへのアクセス許可が必要です。環境によっては安全性の確認が未実施である旨の警告が出ます。皆様がご自身で利用される分には問題ないと思いますので、最下部のリンクから許可します。

  • 実行すると、ログにフォームのURLとIDが表示されます。このIDは後で使うので、メモしておきましょう!

  • フォームをスプレッドシートと連携します。 フォームの「回答」タブから、緑色のスプレッドシートアイコンをクリックして、「新しいスプレッドシートを作成」を選んで、作成します。

  • フォームの「設定」タブで、「表示設定」 > 「確認メッセージ」を編集します。 例えば、以下のように、作成したスプレッドシートのURLを記載しておくと便利です。

名刺情報はこちら: [先ほどコピーしたスプレッドシートのURL]

ステップ 2: Difyの設定


このステップでは、Difyで名刺画像を解析するためのワークフローを作成し、APIキーを取得します。

  • Difyにログイン: Difyのウェブサイトにアクセスし、ログインします。

  • 以下のファイルをインポートします。

  • テストしてみましょう。 右上の「実行」をクリックし、「BusinessCard」という項目に画像をアップロードして、「実行を開始」をクリックします。「結果」に名前とメールアドレスがJSON形式で表示されれば成功です!

  • APIキーを取得します。 右上の「公開」から「APIリファレンスにアクセス」を選択し、開いたページ右上「APIキー」>「新しいシークレットキーを作成」で新しいキーを発行します。このキーは後で使うので、大切に保管してください。

ステップ 3: LINEの設定


LINEで名刺の写真を送るための設定をします。

  1. LINE Developersにアクセスして、新規プロバイダとチャネルを作成します。

  2. チャネル設定から「Messaging API」を選び、「アクセストークン」を取得します。 このトークンは後で使います。

  3. チャネルの「Webhook URL」に、後で設定するGoogle Apps ScriptのURLを設定します。 (今はまだ設定できません)

ステップ 4: Google Apps Scriptの設定


Google Apps Scriptのプロジェクトを開き、新規ファイルを作成、以下のコードをコピペします。

const LINE_ACCESS_TOKEN = PropertiesService.getScriptProperties().getProperty('LINE_ACCESS_TOKEN');
const DIFY_API_KEY = PropertiesService.getScriptProperties().getProperty('DIFY_API_KEY');
const FORM_ID = 'YOUR_FORM_ID';

// LINEからのPOSTリクエストを処理
function doPost(e) {
  Logger.log('--- doPost start ---');
  try {
    const json = JSON.parse(e.postData.contents);
    Logger.log('Received JSON: %s', JSON.stringify(json));
    const event = json.events[0];
    if (!event) {
      Logger.log('イベントが存在しないため、処理を終了します。');
      return ContentService.createTextOutput(JSON.stringify({ content: 'no event' })).setMimeType(ContentService.MimeType.JSON);
    }
    const replyToken = event.replyToken;

    if (event.message && event.message.type === 'image') {
      const messageId = event.message.id;
      Logger.log('画像メッセージ受信: messageId=%s', messageId);
      const imageBlob = getImageFromLINE(messageId);
      Logger.log('画像取得成功。サイズ: %d bytes', imageBlob.getBytes().length);

      const base64Image = Utilities.base64Encode(imageBlob.getBytes());
      Logger.log('画像をbase64エンコード完了: length=%d', base64Image.length);

      const { name, email } = analyzeBusinessCard(base64Image);
      Logger.log('名刺解析結果: name=%s, email=%s', name, email);

      const formUrl = generateFormUrl(name, email);
      Logger.log('フォームURL生成: %s', formUrl);

      replyMessage(replyToken, `名刺の情報:\n名前: ${name}\nメール: ${email}\nこちらのフォームをご記入ください:\n${formUrl}`);
      Logger.log('返信完了');
    } else {
      Logger.log('画像メッセージでないため、メッセージを返信します。');
      replyMessage(replyToken, '名刺画像を送信してください。');
    }

    Logger.log('--- doPost end ---');
    return ContentService.createTextOutput(JSON.stringify({ content: 'ok' })).setMimeType(ContentService.MimeType.JSON);
  } catch (error) {
    Logger.log(`エラーが発生しました: ${error.stack || error}`);
    return ContentService.createTextOutput(JSON.stringify({ content: 'error' })).setMimeType(ContentService.MimeType.JSON);
  }
}

// LINEから画像を取得
function getImageFromLINE(messageId) {
  Logger.log('getImageFromLINE start: messageId=%s', messageId);
  const url = `https://api-data.line.me/v2/bot/message/${messageId}/content`;
  const response = UrlFetchApp.fetch(url, {
    headers: { 'Authorization': `Bearer ${LINE_ACCESS_TOKEN}` },
  });
  Logger.log('LINE画像取得レスポンスステータス: %s', response.getResponseCode());
  Logger.log('getImageFromLINE end');
  return response.getBlob();
}

// Difyで名刺を解析
function analyzeBusinessCard(base64Image) {
  Logger.log('analyzeBusinessCard start');
  const fileId = uploadFileToDify(base64Image);
  Logger.log('DifyファイルID取得: %s', fileId);

  const result = runDifyWorkflow(fileId);
  Logger.log('Difyワークフロー実行結果: name=%s, email=%s', result.name, result.email);
  Logger.log('analyzeBusinessCard end');
  return result;
}

// Difyにファイルをアップロード
function uploadFileToDify(base64Image) {
  Logger.log('uploadFileToDify start');
  const url = 'https://api.dify.ai/v1/files/upload';
  const user = 'line-user'; // 任意のユーザーIDを設定
  const imageBytes = Utilities.base64Decode(base64Image);
  Logger.log('画像バイト長: %d', imageBytes.length);

  const blob = Utilities.newBlob(imageBytes, 'image/jpeg', 'business_card.jpg');
  Logger.log('Blob作成完了: type=%s, name=%s', blob.getContentType(), blob.getName());

  const formData = {
    'file': blob,
    'user': user
  };

  const options = {
    method: 'post',
    headers: {
      'Authorization': `Bearer ${DIFY_API_KEY}`
    },
    payload: formData,
    muteHttpExceptions: true
  };

  const response = UrlFetchApp.fetch(url, options);
  const status = response.getResponseCode();
  const responseText = response.getContentText();
  Logger.log('Difyファイルアップロードレスポンスステータス: %d', status);
  Logger.log('Difyファイルアップロードレスポンスボディ: %s', responseText);

  const result = JSON.parse(responseText);

  if (!result.id) {
    Logger.log('ファイルアップロード失敗: %s', JSON.stringify(result));
    throw new Error('ファイルアップロードに失敗しました。');
  }

  Logger.log('uploadFileToDify end');
  return result.id; // file_idを返す
}

// Difyのワークフローを実行し、結果から情報を取得
function runDifyWorkflow(fileId) {
  Logger.log('runDifyWorkflow start: fileId=%s', fileId);
  const url = 'https://api.dify.ai/v1/workflows/run';
  const user = 'line-user'; // 任意のユーザーID
  const payload = {
    "inputs": {
      // Difyワークフローで定義された変数名をBusinessCardとした例
      "BusinessCard": {
        "transfer_method": "local_file",
        "upload_file_id": fileId,
        "type": "image"
      }
    },
    "response_mode": "blocking",
    "user": user
  };
  Logger.log('ワークフロー実行ペイロード: %s', JSON.stringify(payload));

  const options = {
    method: 'post',
    headers: {
      'Authorization': `Bearer ${DIFY_API_KEY}`,
      'Content-Type': 'application/json'
    },
    payload: JSON.stringify(payload),
    muteHttpExceptions: true
  };

  const response = UrlFetchApp.fetch(url, options);
  const status = response.getResponseCode();
  const responseText = response.getContentText();
  Logger.log('Difyワークフローレスポンスステータス: %d', status);
  Logger.log('Difyワークフローレスポンスボディ: %s', responseText);

  const result = JSON.parse(responseText);
  
  if (result && result.data && result.data.outputs) {
    Logger.log('Difyワークフローoutputs全体: %s', JSON.stringify(result.data.outputs));
    const outputs = result.data.outputs;

    // textフィールドにJSON文字列が入っているため、これをパース
    if (outputs.text) {
      try {
        const parsed = JSON.parse(outputs.text);
        const name = parsed.name || '不明';
        const email = parsed.email || '不明';
    Logger.log('取得した出力: name=%s, email=%s', name, email);
    Logger.log('runDifyWorkflow end');
    return { name, email };
      } catch (parseError) {
        Logger.log('textフィールドのJSONパースに失敗しました: %s', parseError);
        throw new Error('名刺解析結果のパースに失敗しました。');
      }
    } else {
      Logger.log('textフィールドが存在しませんでした。');
      throw new Error('名刺解析結果が取得できませんでした。');
    }
  } else {
    Logger.log('ワークフローの結果が期待した形式でありません: %s', JSON.stringify(result));
    throw new Error('名刺解析結果の取得に失敗しました。');
  }
}

// GoogleフォームURL生成
function generateFormUrl(name, email) {
  Logger.log('generateFormUrl start');
  const baseUrl = `https://docs.google.com/forms/d/e/${FORM_ID}/viewform?usp=pp_url`;
  const url = `${baseUrl}&entry.778341093=${encodeURIComponent(name)}&entry.866235067=${encodeURIComponent(email)}`;
  Logger.log('generateFormUrl end: url=%s', url);
  return url;
}

// LINEにメッセージを返信
function replyMessage(replyToken, message) {
  Logger.log('replyMessage start');
  const url = 'https://api.line.me/v2/bot/message/reply';
  const payload = {
    replyToken: replyToken,
    messages: [{ type: 'text', text: message }],
  };

  const options = {
    method: 'post',
    headers: {
      'Authorization': `Bearer ${LINE_ACCESS_TOKEN}`,
      'Content-Type': 'application/json',
    },
    payload: JSON.stringify(payload),
  };

  Logger.log('返信ペイロード: %s', JSON.stringify(payload));

  try {
    const response = UrlFetchApp.fetch(url, options);
    Logger.log('返信成功: %s', response.getContentText());
  } catch (error) {
    Logger.log(`返信失敗: ${error.stack || error}`);
    throw new Error('返信に失敗しました。');
  }
  Logger.log('replyMessage end');
}
  1. スクリプトプロパティを設定します。

    • Google Apps Scriptの画面左側、歯車アイコンの「プロジェクトの設定」をクリックします。

    • 「スクリプトのプロパティ」セクションで「スクリプトのプロパティを追加」をクリックします。

    • 「プロパティ」にLINE_ACCESS_TOKEN、「値」に先ほどLINE Developersで取得したアクセストークンを入力し、「スクリプトのプロパティを保存」をクリックします。

    • 同様に、「プロパティ」にDIFY_API_KEY、「値」にDifyで取得したAPIキーを入力し、「スクリプトのプロパティを保存」をクリックします。

  2. コードの中の YOUR_FORM_ID を、ステップ1で取得したGoogleフォームのIDに書き換えます。

  3. コードを保存し、「デプロイ」から「新しいデプロイ」を選択して、ウェブアプリとして公開します。

    • 「種類の選択」で「ウェブアプリ」を選択します。

    • 「次のユーザーとして実行」は「自分」にします。

    • 「アクセスできるユーザー」は、「全員」にします。

    • 「デプロイ」をクリックします。

    • 初回は、Googleアカウントへのアクセス許可が必要です。環境によっては安全性の確認が未実施である旨の警告が出ます。皆様がご自身で利用される分には問題ないと思いますので、最下部のリンクから許可します。

  4. デプロイした時に表示されるURLをコピーします。

  5. 先ほどのLINE Developersの「Webhook URL」に、このURLを貼り付け、「検証」をクリックして成功することを確認します。

これで、すべての設定が完了です!LINEで名刺の写真を送って、自動で登録されるか試してみてください!

終わりに


LINEとDifyで作る、名刺管理の自動化システム。データ入力の手間をなくし、あなたのビジネスを加速させます。

柔軟なカスタマイズで、御社に最適なシステムへ。

  • 抽出項目の追加・変更

  • 既存システムとの連携

  • 独自の管理フォーマット対応

  • 部署間の情報共有

  • データ分析機能

  • Slack通知連携

など、様々なカスタマイズが可能です。

お仕事の依頼・相談について


提供できるサービス

私は、AIの先端技術を駆使したシステム開発と、価値あるコンテンツ制作を通じて、貴社のビジネス成長を力強くサポートいたします。OpenAI APIやRAGをはじめとする多様なテクノロジーを自在に操り、お客様の課題に真摯に向き合い、最適なソリューションをオーダーメイドでご提供いたします。

  • システム開発:AIを利用したカスタマーサポート、FAQシステム、チャットボットなど、業務効率化を図るためのシステムを構築します。ニーズに応じて最適なツールや技術を組み合わせて対応します。

  • GPTs/Dify開発:OpenAIの技術を活用し、カスタムGPTモデルやDifyを使用したシステム開発をサポートします。

詳しくはこちら:

ご相談はこちらから


いいなと思ったら応援しよう!