Elasticsearchで改善するカスタマーサポート | 株式会社ロジレス
こんにちは、LOGILESSで業務委託として開発のお手伝いをしている山下です。週3でWebアプリケーションの機能追加・改善をしています。 本題の前にLOGILESSについて少し紹介させてください。 ...
https://www.wantedly.com/companies/company_5639490/post_articles/256774
こんにちは、株式会社ロジレスで業務委託として開発のお手伝いをしている山下です。週3でWebアプリケーションの機能追加・改善をしています。
本題の前にLOGILESSについて少し紹介させてください。
ECの市場が拡大していく中、ネットショップの運営は受注処理や在庫の管理、出荷作業など多くの業務を伴います。私たちはそれらの業務の効率化・自動化を行えるシステムを提供するスタートアップです。2020年8月末で物流会社約50社と提携し、240社以上の企業にご利用いただいており、LOGILESSを利用した1ヶ月間の出荷は90万件を超えます。
ところで、ヘッダーの画像のキャラクタは弊社のマスコットキャラ「ロジごま」で、LOGILESSで様々な業務を自動化した結果、自由な時間を過ごせるようになった二足歩行のゴマフアザラシです。表情や所作が愛くるしくて、ロジごまにはよく癒されています。ECの需要は伸びる一方、それを支える人手は不足し続けています。「私たちのプロダクトを通じて、ネットショップ運営の負担を少しでも減らしていきたい」そんな思いがロジごまには込められています。
LOGILESSではカスタマーサポートのツールとして「Intercom」を利用しています。
Intercomはライブチャットで問い合わせができる機能を提供するツールで、数行のJavaScriptの埋め込み または プラグイン(WordPress)などによって簡単にサイトに導入できます。
参考: Intercomからの問い合わせの一覧
LOGILESSのIntercomには1ヶ月あたり1,000件近い問い合わせがあり、1つの問い合わせが解決するまでに平均約120分かかっています。この問い合わせには主にCSスタッフ、CTO、営業(オンボーディング担当)メンバーの3名を中心に対応しています。
月の営業日を22日とした場合、単純計算で45件/日の問い合わせがあり、1人あたり10件/日以上の回答を担当することになります。LOGILESSは(おかげさまで)毎月導入企業が増えており、それに伴い問い合わせ件数も増加傾向です。
お客様の業務に対してミッションクリティカルな機能を提供するLOGILESSでは、課題や疑問が解決するまでの時間が短いことがとても重要です。また問い合わせに対応するためのコストの増加も無視できなくなってきたため、問い合わせ対応の自動化を検討しました。
問い合わせ対応の自動化に向けた具体的な取組みを決定するために、Intercomの問い合わせ内容をラベリングして分析しました。その結果、「すぐに回答できる」「調査して回答」という問い合わせが全体の88%を占めているという結果になりました。
これらの問い合わせは「LOGILESSの使い方や仕様に対する質問」に関することが多く、ヘルプセンターの記事(※)で解決できるケースが多く見られていました。そこで、私たちはチャットボットに問い合わせ内容を分析させ、ヘルプセンターに関連する記事があれば自動で返信するという1次対応を委任することで問い合わせが解決するまでの時間の短縮と、対応コストの削減につなげたいと考えました。
※ LOGLIESSではヘルプセンターと呼ばれるWebページにサービスの仕様や使い方の記事をまとめています。記事総数は400を超えます。
Intercomには問い合わせ内容に対して、自動で記事をサジェストするという機能がありました。しかし、以下の2つの制約があるためLOGILESSではこの機能を使うことができません。
そこで私たちは独自でチャットボットを開発することにしました。このチャットボットの肝となる処理が「問い合わせ内容の分析」と「参考になりそうな記事の取得」です。
もう少し詳しく説明すると、
「問い合わせ内容の分析」とは、問い合わせ内容を形態素解析し、キーワード抽出することに当たります。「参考になりそうな記事の取得」とは、抽出したキーワードからヘルプセンター内の記事に対して全文検索を行い関連性の高い記事を取得することに当たります。
これらを実装する上で1つ課題があります。弊社には自然言語処理に精通したエンジニアはいないため、形態素解析の実装は敷居が高いということです。しかし、そんな私たちにとってピッタリなサービスがAWSにありました。「Amazon Comprehend」です。
Amazon Comprehendはテキスト内からキーワードを抽出したり感情分析・構文解析をしてくれる自然言語処理サービスです。 日本語にも対応しています。Comprehendの自然言語処理には機械学習が活用されており、日々精度が向上しているようです。
Comprehendの素晴らしい点は機械学習や自然言語処理について知識がまったくなくても、テキストをComprehendに送るだけキーワード抽出ができてしまう点です。
一例ですが、画像のようにテキストに対してキーワードを抽出し、その信頼度が得られます。
図のようなシステム構成・処理フローにてアプリケーションを構築します。
Lambda上にチャットボットのアプリケーションを構築し、Intercom、ComprehendやElasticsearchと連携することで問い合わせ自動対応の機能を実現します。Lambdaを採用している理由としては、1日あたりのリクエスト数(問い合わせ数)は少なく時間帯も限られるため、常時コンピューティングリソースを必要としないためです。また、サーバーレスとなるため運用コストを抑えられることも魅力的でした。
Intercom Webhook, Rest APIは「Invercom Developer Hub」にアプリケーションを作成し、Webhookのトリガとエンドポイントを設定し、認証トークンを取得することで利用できます。Intercomのドキュメントに従えば簡単に利用できるので詳しい内容は割愛します。
Comprehendはフルマネージドなサービスなので、インスタンスを立ち上げるなどの作業は必要ありません。AWSのダッシュボードよりComprehendを探し、利用を開始するだけで導入の準備は整います。
Amazon Elasticsearch Serviceはインスタンスを立ち上げ、データを連携する必要があります。こちらに関しては別ストーリーに導入内容を記載しているので、ご興味あればぜひご覧ください。
Lambda上で動かすチャットボットのアプリケーションはNode.jsとExpressを用いて開発しました。全ての実装を記載すると長くなってしまうので、メインとなるコードのみご紹介します。また、コードの見通しをよくするため実際のコードより簡素化しています。
【キーワードの抽出】
Comprehendを利用してキーワード抽出するには、aws-sdk
を利用しクレデンシャル情報などを設定した上でdetectKeyPhrasesを呼び出します。非常に簡単に利用することができます。
// comprehend.js
const AWS = require("aws-sdk");
module.exports.detectKeywords = (text) => {
AWS.config.update({
apiVersion: "AWSのAPIバージョン",
region: "AWSのリージョン",
credentials: new AWS.Credentials(
"AWSアクセスキー",
"AWSシークレット"
),
});
const client = new AWS.Comprehend();
const request = {
LanguageCode: "ja",
Text: text,
};
try {
return client.detectKeyPhrases(request).promise();
} catch (err) {
throw new Error("Comprehend Error");
}
};
【関連のある記事の取得】
Elasticsearchを利用し記事データを取得するには、
@elastic/elasticsearch
を利用し、 requestBodyのquery
に検索対象のフィールドを設定します。また取得した記事に対してスコアの閾値を設定することで、キーワードが一定頻度以上含まれている つまり 関連のありそうな記事のみを取得しています。
// elasticsearch.js
const { Client } = require("@elastic/elasticsearch");
module.exports.fetchArticles = keywords => {
const client = new Client({
node: "Elasticsearchのエンドポイント",
});
const requestBody = {
index: "インデックス名",
body: {
query: {
bool: {
should: [
{ match: { title: keywords } },
{ match: { content: keywords } }
],
},
},
},
size: "取得件数",
};
try {
const { body } = await client.search(requestBody);
return body.hits.hits.filter(
i => i._score > "関連のある記事と判定するスコア閾値"
);
} catch (err) {
throw new Error("Elasticsearch Error");
}
}
【問い合わせへの返信】
Intercom Rest APIを利用し、問い合わせに対して返信します。Comprehend, Elasticsearchより得られた記事情報をもとに返信内容のHTMLを作成し、axiosを用いてIntercom Rest APIにpostしています。
// intercom.js
const axios = require("axios");
modules.exports.replyMessage = async (request, articles)=> {
const userId = request.body.data.item.user.user_id;
const conversationId = request.body.data.item.id;
const endpoint = `https://api.intercom.io/conversations/${conversationId}/reply`;
let message =
"<html><body><p>こんにちは、ロジごまです。</p><p>こちらの記事が参考になるかもしれません。</p>";
for (let i = 0; i < articles.length; i++) {
const title = articles[i]._source.title;
const suggestUrl = `{{ヘルプセンターのURL}}?page_id=${articles[i]._id}`;
message += `<p><a href="${suggestUrl}" target="_blank"><strong>${title}</strong></br></a></p>`;
}
message += "</body></html>";
const headers = {
Authorization: `Bearer {{Intercomのトークン}}`,
Accept: "application/json",
"Content-Type": "application/json"
};
const requestData = {
message_type: "comment",
type: "admin",
admin_id: "{{BOTのID}}",
intercom_user_id: userId,
body: message
};
try {
return axios.post(endpoint, requestData, { headers: headers });
} catch (err) {
throw new Error("Intercom Post Message Error");
}
}
IntercomのWebhookを受け付けるエントリーポイントを作り、これまで書いた一連の処理をつなげます。
//server.js
const serverless = require('serverless-http');
const express = require("express");
const bodyParser = require("body-parser");
const app = express();
const detectKeywords = require("./comprehend");
const fetchArticles = require("./elasticsearch");
const replyMessage = require("./intercom");
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
app.listen(process.env.PORT);
app.post("/suggest", async (request, response) => {
const bot = new BotService(request);
try {
const keywords = await detectKeywords(getText(request));
const articles = fetchArticles(keywords);
replyMessage(articles);
} catch (err){
}
response.status(200).send();
});
module.exports.handler = serverless(app);
Lambdaへのデプロイはserverlessコマンドを使って1発でお手軽にデプロイできます。serverless.ymlにデプロイ情報を記載し、「$ npx serverless deploy」を打てばデプロイ完了です。
※ 事前にAWS CLIやserverless CLIの設定が必要ですが、今回は割愛しています。
//serverless.yml
service: サービス名
provider:
name: aws
runtime: nodejs12.x
region: ap-northeast-1
stage: prod
functions:
app:
handler: server.handler
events:
- http: ANY /
- http: 'ANY {proxy+}'
以下のようなチャットボットが完成しました。ボットとして対応してくれるのはもちろん弊社のマスコットキャラ「ロジごま」です。問い合わせ内容に対して、ロジごまはしっかりヘルプセンターの記事を自動返信できていますね。画像では分からないのですが、ヘルプセンターの記事が見つからない場合や問い合わせ内容からキーワード抽出ができなかった場合は反応しないようになっています。
今回のチャットボットの導入は初の試みであり、うまく自動対応できているか不安がありました。しかしながら、Intercomのすべてのメッセージからチャットボットの対応内容を確認するのは骨の折れる作業です。そこで、自動対応した内容をSlackに投稿しました。Slack上で可視化されることで自分以外のメンバーとも手軽に共有でき、今後の改善に役立てれるような環境を作ることができました。 チャットボットの導入だけで満足せず、回答率や対応内容の質の向上を目指して、改善できることはどんどんやっていきます。
今回チャットボットを開発してみて十分にその効果を感じているのですが、チャットボットで出来ることはまだまだあり、ポテンシャルを秘めていると感じました。自動返信によって解決したかどうかを学習させ自動返信の精度を高めたり、自動返信では解決できない内容を学習させたりとやれることはまだまだありそうです。ロジごまはカスタマーサポート新米ですが、のびしろしかない子です!
おや...噂をすれば...ロジごまは絶賛問い合わせ対応中のようですね!
今回のチャットボット導入にあたって、私は業務委託という立場ながら技術選定から導入までCTOから一任していただき、スピード感をもって開発できました。会社全体として、プロダクトの成長を優先し、立場によらず大きな裁量がひとりひとりに与えられているためです。
また、私たちは新しい技術の採用についても積極的な姿勢をもっており、エンジニアとして技術的刺激を得られる環境があります。ただし、「新しくて流行っている技術だから」という理由で採用するわけではなく、その技術がプロダクトや開発体験にもたらす効果、そしてビジネスへの影響などを総合的に判断した上で採用しています。
株式会社ロジレスではエンジニアを募集しています。
エンジニアチームはCTO 1名、業務委託 1名、インターン 2名と小さなチームですが、全員がエンジニアリングをビジネス課題や社会課題の解決の手段として捉え、広い視野を持って、アプリケーションが顧客やチームに提供する価値にフォーカスしています。
エンジニア以外のメンバーも含め、多様なバックグラウンドをもち、互いをリスペクトしながら対等な立場で議論し、建設的にビジネスを前に進めようとしています。
ぜひお気軽にご応募ください!