モブプログラミングを1年以上継続するコツ

こんにちは、メディアプロダクト開発部のマーケティングサービス開発グループ(通称msdev)の id:asonas です。msdevウィーク最後の記事です。チームメンバーの記事も是非読んでみてください。

マーケティングサービス開発グループでは毎週月曜日13時から17時の決まった時間にモブプログラミングを実践しています。 このモブプログラミングの枠は1年以上継続していて、毎週様々な課題の解決や機能の開発をしています。この記事ではモブプログラミングを長く継続するためのコツをお伝えします。

モブプログラミングとは

まず、モブプログラミングとは、チームメンバーが同時にコーディングを行う手法です。重要な点はこの「チームメンバー」にはソフトウェアエンジニアだけではなく、プロダクトオーナーやデザイナーのような方々も含まれていることです。スクラムガイド(2020)の開発者と同じように考えてもらうと自然かもしれません。

モブプログラミングにはいくつかのメリットがあります。第一にチーム全員が共通の目標に向かって協力するためコミュニケーションの質が向上します。また、プロダクトに深く関わることで、知識の共有が促進され、チーム全体でより高い品質のコードを書くことができます。

さらに、モブプログラミングは開発プロセスを迅速化できます。複数の開発者が同時に機能を実装することで、エラーやミスがすばやく発見され、修正できます。これにより、開発プロセスがスムーズに進み、品質の高いコードをより迅速に提供できます。また、機能の実装途中に意見の分かれるポイントが出てきたときには、プロダクトオーナーやディレクター、デザイナーの方々の意見も取り入れることで"戻し"の作業を省くことができます。

モブプログラミングはチーム全体の知識や技術レベルを向上させることができます。開発者が一緒に開発をすることで、新しいアイデアやテクニックを学び、開発者自身のスキルを向上させることができます。これによりチーム全体の技術力が向上しより高度なプロジェクトに取り組むことで、いわゆる暗黙知から形式知、形式知から暗黙知へのループがモブプログラミングを通して回せます。

このようなモブプログラミングを2021年12月から16ヶ月以上実践しています。また、私たちのモブプログラミングは社員だけではなく、株式会社えにしテックさんの darashi さんと cafedomancer さんを招聘して毎週実践しています。

1年以上開催するうえでどのようなコツが必要でしょうか?

darashiさんとcafedomancerさんは遠方からZoomを使ってモブプログラミングに参加してくださっています。よくある定義として「ペアプログラミング・モブプログラミングはひとつのコンピューターでやる」とありますが、昨今の開発体験の進化により、Zoomによる画面共有や VSCode の Live Share などのツールの発展により遠隔地にいてもモブプログラミングは充分に満足できる形で開催できます。一昔前だと開発者全員でひとつの開発サーバーにsshで入ってscreenやtmuxのようなターミナルマルチプレクサのセッションを共有してコードを書いていましたね(なつかしい。10年以上前の話ですが)

定期的にモブプログラミングを実践するいくつかの良い方法を紹介します。 ひとつは毎週決まった曜日と時間に開催することです。

私たちのやっているモブプログラミングは毎週月曜の13時から17時に固定して実施しています(年末年始の休暇、祝日などが無い限りは基本的に開催です)。

これは私たちがスクラムを実践している点も関係しており、スクラムガイド(2020)には、

スクラムイベント スプリントは他のすべてのイベントの⼊れ物である。(略) スクラムにおけるイベントは、規則性を⽣み、スクラムで定義されて いない会議の必要性を最⼩限に抑えるために⽤いられる

とあります。私の中ではモブプログラミングもスクラムのイベントのひとつ*1 として取り組んだほうがお得だと思っていることです。

また、モブプログラミングは13時から17時の間で実施されるのでその朝会で取り組むことを決めています。モブプログラミングで取り組みたいことは日々のスクラムイベントから適宜Issueに起票されてラベルで管理されています。

ふたつ目は毎週のログを取ることです。

開催時に必ず全員で見るGoogleドキュメントがあります。僕たちのチームでは「モブプロメモ」と呼ばれています。

モブプロメモの様子

朝会で取り組むことが決まれば事前にこのモブプロメモへ書いておきます。13時になりZoomへ人々が集まり、モブプログラミングで取り組みたいことをオーナーがメンバーに説明をして開始となります。 基本的には、このメモには会話した内容のメモやコードを書きながら発生した議論をまとめたりしてあとから読み直せるようにしています(全文をメモするようなことはしません)。 モブプロは毎週開催しておりその成果物はPull Requestになりますが、休暇などで参加できない方に向けてもこのメモは役に立ちます。

モブプログラミングの座組

誰から取り組むか、という点については毎週の参加者でランダムに順番をきめています。

その日の参加者の名前を書きつつshuffleするスニペット

持ち込んだ課題のオーナーからはじめても良いのかもしれませんが、私たちは特に気にすることなく、ランダムに順番を決めています。明確な理由付けはないですが、施策や課題の知識の偏りがチーム内にあります。この偏りをうまく利用する形で施策の目的や達成したいことはモブプログラミングを通して共有できるようになっています。

そこで私が大切にしていることのひとつとして、自分がドライバーの時は頭の中で思っていることをすべてしゃべるようにしています。「このコードの意図は何だろう」「なるほど、ブランドごとにトピックスの最新の1件を取ってきて、その順序でブランドの一覧を掲出しているのか」「フロントエンドのテストで行数を指定して実行方法はどうやるんだっけ」「ここのコードの修正はvimでやるほうが得意なのでvimに切替えますね」というようなことをしゃべるようにしています。これはドライバーが思っていることをすべて言うことでモブの方々がドライバーの思考を理解しやすくするためです。逐一しゃべりながらやるので少し大変なのですが、ライブコーディングのような感覚でやると楽しめます。エディタやツールのテクニックも実況しながらやると「それなに?」のような会話も発生します。時には取り組む課題とは関係のない話題についても触れて開発体験の共有をするのもよいなと実感しています。

ペアプログラミングでもそうですが、どんどん交代しながら課題を解決していくので、25分+5分休憩を1セットとして回していきます。2時間ほど経過するタイミングで休憩の時間を15分取っています。

5,6人で回していくとなんだかんだで3時間30分ほど経過するので、最後の30分の枠でふりかえりをします。 ふりかえりではよかったこと、取り組んだ課題の難しかったところ、白熱した議論についてなど様々なことが書かれています。

ある日の振り返りの様子。この日は業務分析をしたりReactのテストに苦戦する様子が描かれてる

私たちが実践するモブプログラミングのまとめ

カタはだいたいこのような流れです。ただ、このカタにハマらずブレることもままあります。前週に性能改善系の課題をこなしたときには冒頭で性能改善の様子をメトリクスを眺めたりもしますし、新しい参加者がいれば自己紹介をしたりしますし、Ruby/Rails、ReactなどだけではなくSQLを眺めてみんなでウンウン唸りながら改善をすることもありますしコードをほぼ書かずに業務分析をすることもあります。 おすすめのツールや設定、スニペットがあれば自慢してみたり、最近の技術的な話題で盛り上がることもあります。

モブプログラミングはとてもハードなプラクティスです。一日4時間もやるととてもヘトヘトになります。それでもチームのモチベーションを維持のためにも緩急をつけて、たまには雑談を挟むことで機械的な開催になることを避けるように心がけています。仕事として楽しめるような雰囲気作りもとても重要です。

モブプログラミングを実践するうえで、特にはじめて参加されるチームメンバーの場合はアプリケーションに精通していないこともあります。暗黙知が備わっていない、わからないことは当然あります。ドライバーになる人であれば分からない旨を伝えることも大事ですし、モブの方々も率先して自分たちが持つ知見を展開する心意気も重要です。ここのサイクルを回していくことでチームの生産性が向上しより早くユーザーに価値を届けることができます。

ただ、ここまで書いた内容は、一朝一夕でなしえたものではありません。毎週のふりかえり以外にもチームのモブプログラミングの意義を問うようなふりかえりも別途実施しました。チームメンバー各位がモブプログラミングに対する認識を揃えるなどを経て今のモブプログラミングのカタが完成しています。

モブプログラミングを継続してやっていくうえでメンバーの練度の差も次第に揃ってきます。特に導入時などはバタバタとしてしまうこともありました。それでも毎週のふりかえりの積み重ねでよりよい体験へと持っていくことができます。

モブプログラミングを長く続けることで対外的な登壇もしたりしました。前日の pndcatさんがKaigi on Railsで登壇するきっかけになったプロポーザルも実はモブプログラミングで取り組んだ成果でした。

kaigionrails.org

今は違うチームに異動してしまったのですが osyoyuさんもモブプログラミングで取り組んだことが採択されました。

kaigionrails.org

今回の記事では私たちのチームでうまく、そして継続的にモブプログラミングを実践する方法を紹介しました。定期的に開催しつつも、義務的にはならずソフトウェアエンジニアとして楽しく取り組めるような雰囲気作りを紹介しました。 もし私たちの取り組みに興味がありましたら、以下のリンク、またはTwitterなどでDMを頂ければカジュアルな面談からでも実施できればと思います。

cookpad.careers

*1:正確にはモブプログラミングはスクラムイベントの枠組みではありませんし、スクラムガイドには定義されていません。どちらかというともっと大きな枠組みの文脈で語られることが多いです。が、そこはスクラムやモブプロもXPのかけらということで解釈してもいいよなと考えています。

ポリモーフィック関連を活用し、森羅万象の「いいね」を実現する手法

こんにちは!メディアプロダクト開発部マーケティングサービス開発グループ (msdev) のなどやま (@pndcat) です。業務では、クックパッドの広告の開発・運用や、新規サービスの開発をしています。本業の推し活動では、今年の夏はたくさんのイベントに参加するため、推し活動もがんばっていく予定です。

今週は msdev week として Techlife を更新しており、この記事は2日目になります。
1日目は三條さんによる「クックパッドの toB 向け事業における ChatGPT API の活用事例紹介」の投稿でした。
本記事も、メーカーズタウンに関するブログなので、ぜひこちらの記事もご覧ください!

はじめに

この記事では、Kaigi on Rails 2022 森羅万象に「いいね」するためのデータ構造 というタイトルで発表をした、Rails を用いたデータ構造のリファクタリングについて紹介します。ポリモーフィック関連を用いたリファクタリングにより、テーブルやコードの重複を排除し、メンテナンス性や拡張性を向上させることができました。データ構造をリファクタリングする背景や、具体的な手法、そしてなぜその変更を行うことができたのかについてを詳しく説明します。

目次

  • データ構造の初期設計
    • 新機能の「いいね」を実装したい
    • 最初のデータ構造
    • リリースをして1年... どうなったか...
  • ポリモーフィック関連を使ったデータ構造に変更
    • 新しいデータ構造: likes と anonymous_likes
    • リファクタリングの手順
      1. 新旧いいねのテーブルに書き込む (Write)
      2. 旧いいねを新いいねにマイグレーションする (バッチ)
      3. 新いいねを使う (Read)
      4. 旧いいねのモデルとテーブルを削除する (Delete)
  • まとめ

データ構造の初期設計

新機能の「いいね」を実装したい

msdev では、中小の食関連メーカーとユーザーをつなぐコミュニケーションプラットフォーム「メーカーズタウン」を開発しています。メーカーズタウンでは、メーカーはトピックスの投稿や商品の登録を行い、ユーザーはコメントやクチコミを投稿したり、いいねをすることができます。

リリースのタイミングで、「いいね」機能を導入することになりました。要件は、以下の3つでした。

  • 何に「いいね」ができるか? → トピックスと商品
  • 1つの対象に何回「いいね」ができるか? → 1回
  • 「誰が」いいねをすることができるか? → 誰でも (ログインユーザーと、未ログインユーザー)

特に、最後の要件をどう実現するのかについて悩みました。メーカーズタウンでは、ログインユーザーと未ログインユーザーのデータの扱いが異なるため、「いいね」の種類を分ける必要がありました。

ここまでをまとめると、「いいね」は以下の4種類に整理することができます。

  • ログインユーザーの商品のいいね
  • ログインユーザーのトピックスのいいね
  • 未ログインのユーザーの商品のいいね
  • 未ログインのユーザーのトピックスのいいね

最初のデータ構造

データ構造の案として、以下の3つの方法を検討しました。

  • 対象別とユーザー区分別で、4つのテーブルを作成する
  • ポリモーフィック関連
  • STI

結果として、対象別とユーザー区分別のテーブルを作成することに決めました。将来的に「いいね」の種類が増える可能性があったため、ポリモーフィック関連やSTIを用いたテーブル構造を選択することもできましたが、サービスが使用され続ける中で仕様が変わる可能性があるため、必要に応じてデータ構造を改善する方針としました。

リリースをして1年…どうなったか?

1年後、コメント機能やクチコミ機能が増え、「いいね」の対象が4つに増えました。これに伴い、テーブルが8個になったことで2つの問題が生じました。

  • 「いいね」の対象が増えるたびに、テーブル・モデル・コントローラーを毎回追加するのでつらい
  • 新しい対象について、いいね数の集計バッチを追加することを忘れ、集計漏れが発生した

テーブルを毎回追加することはできますが、新しいテーブルが追加されるたびに、集計バッチの修正を行うことを意識するのは難しいことです。

一方で、「いいね」の対象が4つまで増えたことで、「いいね」の仕様はすべて共通であり、今後も「いいね」対象別の振る舞いはなさそうということがわかりました。

ポリモーフィック関連を使ったデータ構造に変更

ポリモーフィック関連は、複数のオブジェクトを関連付ける場合に適しています。今回の場合は、商品、トピックス、コメント、クチコミに「いいね」をつける必要があるため、ポリモーフィック関連を用いて実現することができます *1

サービスを1年間運用した結果、商品、トピックス、コメント、クチコミの「いいね」に関する仕様や振る舞いが共通していることがわかり、ポリモーフィック関連に移行することを決定しました。移行に際して、チームで以下の2つの制約に合意しました。

  • 今後も「いいね」の仕様を変更しない
  • if 文を書くと破綻するため、if 文を絶対に書かない (=異なる振る舞いはさせない) *2

新しいデータ構造:likes と anonymous_likes

新しいデータ構造は、likes (いいね) と anonymous_likes (未ログインのユーザーのいいね) の2種類にまとめました。 ログインユーザーには UserID がありますが、未ログインユーザーには UserID はありません。そこで、初めのデータ構造と同じように、ログインユーザーと、未ログインユーザーは、2種類のテーブルに分けることにしました。anonymous_likes は、UserID の代わりに like_identifier というカラムを作りました *3

今までの product_likes テーブルは product_id, user_id の2カラムでしたが、ポリモーフィック関連を利用すると下図の likes テーブルの likable_id, likable_type, user_id の3カラムで表現します (他の *_likes テーブルも同様)。

では、モデルはどうなるかというと、今までは、4つのモデル (ProductLike、TopicLike、CommentLike、KuchikomiLike) があり、それぞれに対応するテーブルが存在していました。しかし、ポリモーフィック関連を利用することで、1つの Like モデルで表現できるようになりました。現在は対象が4つしかない「いいね」ですが、10個でも100個でも「いいね」の実装ができる、森羅万象の「いいね」のデータモデルが完成しました🌲🌳

※ before / after を見やすくするために、コードを画像にしています。

リファクタリングの手順

データの移行は、以下の手順で行っています。移行手順は特別な手法ではありませんが、Rails のポリモーフィック関連のマイグレーションの一例として、紹介します。

  1. 新旧の「いいね」テーブルに書き込む (Write)
  2. 旧いいねを新いいねにマイグレーション (バッチ)
  3. 新しい「いいね」を使う (Read)
  4. 旧いいねのモデルとテーブルを削除 (Delete)

1. 新旧のいいねテーブルに書き込む (Write)

これは、コントローラーの create メソッドで実行されます。上部分では、既存の ProductLike モデル (旧いいね) を作成し、下部分で Like モデル (新いいね) も作成します。1つのメソッド内で「いいね」がされた場合、新旧のいいねにそれぞれ書き込まれるようにします。

削除も同様に、旧いいねを削除しつつ、新いいねも削除します。ただし、この時点で必ずしも旧いいねと対応する新いいねが存在するわけではありません。Like が見つからないときのために、Safe Navigation Operator の & を destroy の前に書く必要があります。

2. 旧いいねを新いいねにマイグレーションする (バッチ)

次に、データのマイグレーションについて説明します。まず、product_likes テーブルのデータを likes テーブルに合わせたハッシュをつくります。
Rails 6からは、upsert_all メソッドが導入され、ハッシュをそのまま渡すことでデータを生成することができます。バッチを冪等に実行することができるため、今回のようなマイグレーションを行いたい場合は、upsert_all メソッドをぜひ活用してください。

新いいねと、旧いいねの ID (primary key) の順番がばらばらになることが気になるかもしれませんが、likes テーブルは、product_likes や topic_likes などの4つのテーブルを1つにマージするためのテーブルであり、ID の順序は意味を持ちません。重要なのは、「いつ」「誰が」「何に」に対して「いいね」を行ったのかというデータを正しくマイグレーションすることです。ID の順序については気にする必要はありませんが、「いつ」の情報をコピーするために、created_at を忘れないように注意してください。

また、「1. 新旧のいいねテーブルに書き込む」の段階では、旧いいねと対応する新いいねが必ずあるわけではなかったため、新いいねの削除に Safe Navigation Operator を付けていましたが、データ移行が完了したため、Safe Navigation Operator を外します。

3. 新いいねを使う (Read)

新いいねにデータが入ったので、アソシエーションを以下のように変更します。これからは、product.product_likes ではなく、product.likes を使います *4

「新しい対象について、いいね数の集計バッチを追加することを忘れる」という問題もありましたが、今回の変更により、1クエリで「いいね」を合算することができます。

さらに、「ある時刻以降の Like を求める」「あるユーザーの Like を求める」というケースのクエリも簡単に書くことができるようになりました。

4. 旧いいねのモデルとテーブルを削除 (Delete)

すべてのコードで新いいねを参照したら、旧いいねの書き込みを削除します。最後に、旧いいねのモデルとテーブルを削除することができたら、ポリモーフィック関連への移行が完了です 🎉

まとめ

本ブログでは、似ているけどちょっと違う「いいね」の設計の紹介をしました。最初は、変更に耐えられるようなデータ構造にし、実装が複雑になったタイミングで、データ構造を見直しました。今回は、ポリモーフィック関連へのリファクタリングをしました。

ポリモーフィック関連を適用できるケースは多くないと思いますが、場合によってはとても強力なデータ構造の手法であり、特に Rails ではフレームワークレベルでのポリモーフィック関連付けの支援があるため、テーブルやコード量を減らし、かつ、わかりやすいコードに置き換えることが可能です。
ポリモーフィック関連を利用したことがない人や、ポリモーフィック関連は SQL アンチパターンでよく挙げられているから抵抗があるという人も多いと思いますが、今回のブログを通して、今後のデータ構造でポリモーフィック関連を選択肢の一つに入れてもらえると嬉しいと思います。

クックパッドでは、toB 事業をやりたい!データ構造の話をたくさんしたい!リファクタリングに興味がある!というエンジニアを募集しています。以下のリンクからのご応募をお待ちしています!

cookpad.careers

*1:ポリモーフィック関連の具体的な実装に関しては、Railsガイド を参照してください

*2:Techlife: Kaigi on Rails 2022 にて『森羅万象に「いいね」するためのデータ構造』の発表をしました の「ポリモーフィック関連で if 文を書くと破綻するの例は?」 に詳細を書いています

*3:Techlife: Kaigi on Rails 2022 にて『森羅万象に「いいね」するためのデータ構造』の発表をしました の「匿名いいねの like_identifier ってなに?」を詳細を書いています

*4:grep をしやすいように、product.likes ではなく product.product_likes の書き方をしていました。product_likes は likes に書き換える必要があります

クックパッドの toB 向け事業における ChatGPT API の活用事例紹介

メディアプロダクト開発部マーケティングサービス開発グループ(通称 msdev)の三條です。広告システムやメーカーズタウンというBtoBtoCプラットフォームなどクックパッドにおける toB 向け事業の開発・保守・運用を担当しています。
今週は msdev week と題して、 msdev のメンバーから連続で記事の投稿をしていきます。楽しみにしていてください!

今回は、今流行りの ChatGPT API をメーカーズタウンというプロダクトに活用して機能開発を行い、課題解決を試みた例を紹介したいと思います。
私たちのチームでは、新しい技術を積極的に取り入れつつ、楽しみながらサービスを作っていっていますので、もし興味を持っていただけたら末尾に採用サイトへのリンクがあるのでそちらからご応募いただけると嬉しいです!

メーカーズタウンとは

メーカーズタウンは、食関連メーカーとクックパッドユーザーが双方向のコミュニケーションを取りながら、さまざまな課題を解消していく、企業マーケティングを支援するユーザー参加型のプラットフォームです。
メーカーズタウンというプラットフォーム上で、企業はユーザーとコミュニケーションを取りながらマーケティングを行うことができます。 例えば、自社商品を活用したレシピをユーザーに募集したり、一部のファンユーザーに対して発売前の商品へのフィードバックをもらったりできます。

メーカーズタウンには他にも様々な機能があるのですが、今回はその中の機能のひとつである「トピックス」機能について焦点を絞って紹介します。

トピックスとは

トピックスは企業がユーザーに対して情報を発信することができる機能です。 ここでは企業に興味があるユーザーに対してレシピや商品の紹介、イベントの告知など企業主体で情報を発信することができます。

ユーザーはそのトピックスに対してコメントをすることができ、さらに企業はそのコメントに対して返信を行うことができます。
これらのやり取りを通して、企業とユーザーがコミュニケーションを行っています。

トピックスの例
トピックスの例

トピックスにおける課題

サービスの成長にともなってユーザーからコメントが投稿されることが多くなってきました。 企業もコメントが増えたことを喜んでいて、とても丁寧に返信作業を行ってくださっています。

その一方で、現在利用している企業の多くはメーカーズタウン専用の担当者がいるわけではなく、その他業務の傍らでメーカーズタウンの運用をしているため、ユーザーからのコメントが増えるにつれて返信作業の負荷が高くなってきています。
企業も慣れないなか、多くの時間を割いて返信作業を行っていますが、返信作業が滞ってしまったり、負担に感じている企業もいるのが現状です。
せっかくユーザーがコメントをしてくれているにも関わらず、返信がこなかったり時間がかかったりすると、ネガティブな印象を与えてしまうため、どうにか返信作業の負荷を下げたいという要望が企業や営業担当の方からありました。

しかし、安易に返信テンプレートを用意したりすると、サービスに同じような返信が投稿されることになるし、本来のコミュニケーションを取る目的からも逸れてしまうのではないか、ということでいい解決策が思いついていない状況でした。

そこで白羽の矢を立てたのが ChatGPT でした。

ChatGPT とは

ChatGPT は OpenAI が提供している AI チャットサービスです。
GPT と呼ばれる大規模言語モデルをチャット用にチューニングしたものを利用しており、自然言語の理解や文章の自動生成などを高度に行うことができます。
ChatGPT は GUI で提供されており、気軽に利用することができますが、開発者向けに API も提供されており、今回は API を利用しました。

ChatGPT では利用するモデルを選択できますが、今回は gpt-3.5-turbo を利用しています。(gpt-4 を使いたい)

ChatGPT API の活用

ChatGPT を使うとコメントに対してとても自然な返信内容を生成することができます。 返信作業の中で負荷が高いのは文の構成を 0 から考える部分であると考え、ChatGPT に基礎となる返信内容の骨組みを考えてもらい、それを元に企業が本来取りたいコミュニケーションにあわせて加筆修正を加えるだけで返信ができるようにすることで、負荷軽減ができるのではないかと考えました。

今回は返信内容の骨組みを生成することが目的で、加筆修正されることが前提にあるので、文言生成に求められる精度が高くないのもまだ不慣れな ChatGPT を活用する上でとても嬉しいポイントでした。

実際に作成した画面は以下になります。

作成したUI
作成したUI

この画面を構築するにあたって、私たちのチームにはデザイナーがいないため、ディレクターやビジネスチームと一緒になって UI や UX について議論しました。

UI/UX においての工夫点

以下、議論の中で考えた工夫点です。

  • 本来の目的であるコミュニケーションを阻害しないように注意する
    自分で 0 から考えたい担当者がいた際に意図せずバイアスを与えてしまうことを避けるため、担当者がアクションを起こした場合のみ、生成された文言を提示するようにしました。
  • あくまで参考程度にして、自身で加筆修正を行ってほしいことを伝える
    ChatGPT は不適当な内容を生成する可能性があり、企業とユーザーのコミュニケーションを行うのが本来の目的であるため、生成内容をそのまま投稿するのではなく、参考にしつつ自身の文言で返信をしてもらいたいと考えています。
  • ワクワク感の演出
    ChatGPT の Web UI の徐々に文言が表示される表現がワクワク感が出て楽しいのでそれを取り入れたいという意見から、メーカーズタウンでも同じようにローディングの演出を行いました。
    実装コストの観点から、1 文字ずつ表示されるものではなく、既存 UI で利用していたローディングを再利用しました。

実装においての工夫点

ChatGPT API において生成文言をコントロールするためには、以下が重要になってきます。

  • どのようなプロンプトを与えるか
  • HyperParameter をどう設定するか

今回は上述のように求められる精度がそれほど高くなかったのと実現までのスピードを優先したため、網羅的な実験を行ったわけではないのですが、プロンプトについていくつか工夫した点を紹介します。

返信内容を生成するにあたって、定量的に評価できる指標などを用意することができていなかったので、以下の 2 点を軸にして主観でよいと思う文言が生成されるようなプロンプトにしました。

  • コメント内容に自然に触れていること
  • 企業の返信スタイルに沿っていること

また、 HyperParameter のチューニングはできていないのですが、利用したパラメーターは temperature=0.7 でその他はデフォルト値にしています。
パラメーターの詳細はドキュメントに詳しく記載されています。

コメント内容に自然に触れていること

これは以下のようなプロンプトを ChatGPT に与えることでユーザーのコメントに触れつつ、自然な返信を生成することができました

[
  { 'system': system_role_prompt },
  { 'user': user_role_prompt },
]

system role prompt

メーカーである${企業名}の広報担当者として振る舞ってください。
あなたのSNSの投稿にユーザーからコメントが来たので返信を考えてください。

## メーカー名
${企業名}

## メーカー情報
${企業の説明文}

## 返信の目的
より${企業名}の商品を好きになってもらうこと。

## 制約
- ユーザーのコメント内容に間接的に触れること
- 投稿内容・コメント内容を繰り返さないこと
- 返信内容のみを回答すること
- ${その他システムやサービスの都合で必要な制約を列挙}

user role prompt

## あなたの投稿内容
${トピックスの内容}

## ユーザーの名前
${ユーザー名}

## ユーザーのコメント
${コメント内容}

具体的な生成例

生成文言例を作成するソースは、今回の記事用に用意したテストデータを利用しています。
この後説明する企業のスタイルに合わせて生成文言が変わっていることを示すための例なので、企業の説明文など細かい情報は省略します。

上記プロンプトを用いて生成された返信文言は以下になります。

種別 内容
コメント みそにこんなにたくさんの栄養素が含まれていることは知りませんでした!
生成された返信文言 クックWU2JRU☆さん、コメントありがとうございます!
そうなんです、みそは発酵させることでたくさんの栄養素が生成されるんですよ。食事の栄養補給にもってこいですね!私たちも、美味しさと共に栄養たっぷりの味噌をお届けできるよう取り組んでいます。ぜひ、新郷醸造の味噌を試してみてください!

かなり自然な受け答えになっていると思います。(個人的には「食事の栄養補給にもってこいですね!」あたりはやや違和感がありますが、このへんは加筆修正してもらう想定です。)

企業の返信スタイルに沿っていること

返信内容には企業によって特色が異なります。 例えば、絵文字をよく使う企業や、言い回し、ユーザー名の引用方法などは企業によって異なります。
これらを再現するため、過去の企業とユーザーのやり取りを利用し few-shot prompting を行いました。 具体的には以下のようにしました。

[
  { 'system': system_role_prompt },
  { 'user': ${過去のトピックスとユーザーコメント} },
  { 'assistant': ${過去の企業の返信} },
  ... // 3つ分繰り返す
  { 'user': user_role_prompt},
]

生成例

先程例を紹介したものと同じコメントに対して、 few-shot prompting を行った際の例を紹介します。

過去の企業の返信コメントを1つだけ与えてみました。(下表)
わかりやすく極端な例を用意しました。名前の引用方法や、絵文字を多用している点などが先程の生成された文言とは異なります。

種別 内容
コメント 「畑の肉」という別名かっこいいですね☆
豆知識勉強になります!
企業の返信内容 >クックW66VXK☆さん
コメントありがとうございます✨
私たちも「畑の肉」という別名があることに驚きました❗
今後もたくさんの役に立つ情報を発信していきますのでよろしくお願いします✨

これを例として与えた場合の生成文言は以下になります。

>クックWU2JRU☆さん
コメントありがとうございます!
そうなんです!みそにはさまざまな栄養素が含まれていて、体に良い影響を与えることが期待されています。また、無添加や有機の味噌もあるので、安心して食べることができますよ✨

例を与えなかった場合(zero-shot)と比べて、 few-shot では名前の引用や絵文字などが反映されていることがわかります。

種別 内容
コメント みそにこんなにたくさんの栄養素が含まれていることは知りませんでした!
zero-shot クックWU2JRU☆さん、コメントありがとうございます!
そうなんです、みそは発酵させることでたくさんの栄養素が生成されるんですよ。食事の栄養補給にもってこいですね!私たちも、美味しさと共に栄養たっぷりの味噌をお届けできるよう取り組んでいます。ぜひ、新郷醸造の味噌を試してみてください!
few-shot >クックWU2JRU☆さん
コメントありがとうございます!
そうなんです!みそにはさまざまな栄養素が含まれていて、体に良い影響を与えることが期待されています。また、無添加や有機の味噌もあるので、安心して食べることができますよ✨

過去に企業が実際に返信している例を参考情報として ChatGPT に与えることで、絵文字や言い回しなど、企業の返信スタイルを再現してくれるようになりました。

余談ですが、プロンプトのトークン数は、投稿内容によりますが、多いもので 2000~2500 トークンくらいなので、1つの返信内容を生成するのにだいたい $0.05 くらいの金額感でした。

今後の課題と試してみたいこと

今回は、コミュニケーションの負荷軽減を目的として、 ChatGPT API を利用して、返信の参考文言を生成しました。 ある一定のクオリティで文言を生成することができましたが、課題もあります。

  • 文言生成性能の定量評価
    定量的に評価するための指標が定められておらず、主観による定性評価しかできていません。
  • Hallucination の検知
    定量評価ができていないことにも関連しますが、生成された文言を評価することができていないので ChatGPT が生成する文言が不適当だった場合に自動で検知することができていません。
    現在はコメント数的に手動で確認できる量なので直接見て不適当なものがあれば、手動で再生成するようにしています。
  • UI/UX の改善
    UI/UX の専門家がいないので手探り状態です。これらは企業からのフィードバックをもらいながら徐々に改善していく予定です。

試してみたいこと

上記の課題に対して改善案として試してみたいことは以下になります。
企業からのフィードバックを元に優先度等を調整して、挑戦していく予定です。

  • 性能改善について
    few-shot の例として取得する過去事例を、今回対象とするトピックスやコメントの内容に近いものを利用するようにしてみたいなと考えています。
    現在は最新の事例を3つ取得しているので、テキストをベクトルに変換して類似度が高いものを採用するとより文脈に沿った文言を生成してくれるかもしれません。
  • 定量評価について
    定量評価についてはまだいい方法が思いついていないのですが、人手でいくつかの正解データセットを作り、評価するのが一歩目かなと思っています。
  • Hallucination の検知
    出力結果を評価するようにプロンプトで統制した GPT を利用して、生成された返信内容が適当かどうかを評価させるとどうだろうか、考えています。
    その GPT モデルが不適当と判断した場合は再生成したり、アラートを上げるなどして検知させる仕組みを構築できるといいなと考えています。
  • UX 改善
    企業が返信内容の主題を入力し、それを元に文言を生成させるのはどうだろうか、と考えています。
    現在は私たちが用意した目的が固定化されているので、企業の意図した返信内容ではない可能性があります。
    しかし、ここは本来の目的であるコミュニケーションに大きく関わる部分なので慎重に検討していきたいと思っています。

まとめ

今回は、ChatGPT API をメーカーズタウンというプロダクトに活用して機能開発を行い、企業の課題解決を試みた例を紹介しました。
網羅的な実験はできていないものの、うまく活用できた一例としてみなさんの参考になっていれば幸いです。
私たちのチームでは、新しい技術を積極的に取り入れつつ、楽しみながらサービスを作っていっていますので、もし興味を持っていただけたら下記の採用サイトからご応募いただけると嬉しいです!

また、今回、ChatGPT を活用するにあたり、機械学習グループの深澤さんに多大なるサポートをいただきました。
機械学習グループの目標として「ChatGPT などの LLM を会社全体で活用できる状態にする。」というものがあるので、プロダクトに LLM を活用していくことに興味がある方はぜひご連絡ください!
機械学習グループについては下記のインタビューをご覧ください。

cookpad.careers

note.com