📖

ChatGPTにマッチングアプリの自己紹介文を作らせてみた

2024/06/22に公開

モチベーション

今や市民権を得てきたマッチングアプリの最初のハードルである「自己紹介文作成」を簡単にやりたい。
自己紹介文は能動的に考えて作っていくため、なかなかめんどくさい。
⇒ 自分の属性を入力したら自動生成してくれる形式で作れないか?

やりたいこと

以下のようなプロンプトをそのままChatGPTに投げてもいいが、必要な情報を入力形式にしてアプリっぽくしてみたい。

プロンプト(深津式を参考)

# 命令書
あなたは、マッチングアプリの利用者です。
以下のconditionから異性から好印象の親しみやすい自己紹介文を作成してください。
例文はexampleに記載します。

<condition>
・冒頭分は「はじめまして!##住所在住の##仕事の##名前です。##登録した理由登録しました!」と入れる
・最後は「よろしくお願いします」と入れる
・小見出しで「👉仕事」を入れる。内容は##仕事に関係することを記述する。
・小見出しで「👉趣味」を入れる。内容は##趣味に関係することを記述する。
・小見出しで「👉性格」を入れる。内容は##性格に関係することを記述する。
・小見出しで「👉好きなタイプ」を入れる。内容は##好きなタイプに関係することを記述する。
・絵文字を入れる
・700文字以内で作る

## 名前
やまだたろう

## 住所
東京

## 仕事
会社員

## 登録した理由
そろそろ彼女が欲しい

## 趣味
アニメ鑑賞、サウナ、カラオケ、サッカー

## 性格
穏やか、聞き上手

## 好きなタイプ
相手を思いやれる人、笑いのツボが合う
</condition>

<example>
はじめまして!横浜在住のエンジニアのやまだじろうです。結婚を考え始めて登録しました!

👉仕事
エンジニアをしています!
大変な時期もありますが、自分が作ったものが社会の役に立っていると思って楽しく仕事をしています💻

👉趣味
旅行✈️と料理🍳が大好きで、休日は新しいレシピに挑戦しています。
アウトドアも好きで、ハイキングやキャンプ⛺️に行くことが多いです。共通の趣味を楽しめる方と出会いたいです!

👉性格
友達からは誠実で優しいと言われます。ポジティブな性格で、どんなことにも前向きに挑戦します。
一緒に楽しい時間を過ごせる方だったら相性がいいかもです✨。

👉好きなタイプ
一緒に笑い合える明るくい方が好きです。お互いに尊重し合い、楽しい時間を共有できる関係を築きたいです✨。
趣味や価値観が合うと嬉しいですし、何事にも前向きな方と出会いたいと思っています!

プロフィール読んでいただきありがとうございました!
よろしくお願いします。

</example>

#出力

上記のプロンプトをChatGPTに投げるとこんな回答が返ってきた。普通に使えそう。

はじめまして!東京在住の会社員のやまだたろうです。そろそろ彼女が欲しいと思い、登録しました!

👉仕事
会社員として日々働いています。忙しいこともありますが、毎日新しいことを学び、成長できる環境で仕事を楽しんでいます。仲間と協力しながらプロジェクトを進めるのが好きです👨‍💼

👉趣味
アニメ鑑賞🎥やサウナ🧖‍♂️が大好きです。休日はアニメを見たり、サウナでリラックスしたりして過ごしています。カラオケ🎤で歌うのも大好きで、友達と一緒に楽しんでいます。さらに、サッカー⚽️も趣味で、週末には友達と一緒にプレイしています!

👉性格
穏やかで、聞き上手と言われます。友達からもよく相談を受けることが多いです。相手の話をしっかり聞いて、理解することが得意です😊

👉好きなタイプ
相手を思いやれる人が好きです。また、笑いのツボが合うと、一緒に楽しい時間を過ごせると思います。お互いに尊重し合い、リラックスできる関係を築きたいです🌟

プロフィールを読んでいただきありがとうございました!
よろしくお願いします。

実装

今回はユーザに入力してほしい項目が決まっているため、ブラウザから入力してもらいそれをプロンプトに含めてOpenAIのAPIを投げる方法で実装していく。
公開するアプリでもないためローカルで雑に実装してみる。

  • フロント:React
  • バックエンド:Django
  • 環境:windowsのローカル環境

Django

プロジェクトの初期環境は構築済みの前提とする。

まず、「setting.py」でOpen AI APIキーの読み込みとCORSの許可設定を行う。
Open AI APIキーは、.envファイルで管理する。

setting.py
# 環境変数の読み込み
env = environ.Env()
environ.Env.read_env(os.path.join(BASE_DIR, '.env'))

# OpenAI APIキーの設定
OPENAI_API_KEY = env('OPENAI_API_KEY')

# CORSの許可
CORS_ALLOWED_ORIGINS = [
    'http://localhost:5173',
]
.env
OPENAI_API_KEY=*********************************

プロジェクトの「urls.py」にurlを追加する。

urls.py
urlpatterns = [
    path('admin/', admin.site.urls),
    # 「api/~」でアクセスできるように設定する。
    # 「aiapp」の部分は自分のアプリケーション名をつけてください
    path('api/', include('aiapp.urls')),
]

アプリケーションの「urls.py」にurlを追加する。(この時点ではViewを作っていないためエラーが派生するが気にしない)

urls.py
urlpatterns = [
    path('matching/', MatchingView.as_view(), name='matching'),
]

「serializers.py」でシリアライザを作る。
雑に作っているため、max_lengthとかは気にしないでおく。また、性別と年齢があったほうがプロンプトの精度も上がる気がしたため性別と年齢も取得する。

serializers.py
class MatchingRequestSerializer(serializers.Serializer):
    # 名前
    name = serializers.CharField()
    # 性別
    gender = serializers.CharField()
    # 年齢
    age = serializers.IntegerField()
    # 住所
    address = serializers.CharField()
    # 仕事
    job = serializers.CharField()
    # 登録した理由
    entry_reason = serializers.CharField()
    # 趣味
    hoby = serializers.CharField()
    # 性格
    personality = serializers.CharField()
    # 好きなタイプ
    ideal_partner = serializers.CharField()

「views.py」でOpenAI APIへの問い合わせを行う。

views.py
class MatchingView(APIView):
    def post(self, request):
            # APIキー読み込み
            openai.api_key = settings.OPENAI_API_KEY
            # プロンプトを定義
            content = f'''# 命令書
            あなたは、{request.data.get('age')}{request.data.get('gender')}のマッチングアプリの利用者です。
            以下のconditionから異性から好印象の親しみやすい自己紹介文を作成してください。
            例文はexampleに記載します。

            <condition>
            ・冒頭分は「はじめまして!##住所在住の##仕事の##名前です。##登録した理由登録しました!」と入れる
            ・最後は「よろしくお願いします」と入れる
            ・小見出しで「👉仕事」を入れる。内容は##仕事に関係することを記述する。
            ・小見出しで「👉趣味」を入れる。内容は##趣味に関係することを記述する。
            ・小見出しで「👉性格」を入れる。内容は##性格に関係することを記述する。
            ・小見出しで「👉好きなタイプ」を入れる。内容は##好きなタイプに関係することを記述する。
            ・絵文字を入れる
            ・700文字以内で作る

            ## 名前
            {request.data.get('name')}

            ## 住所
            {request.data.get('address')}

            ## 仕事
            {request.data.get('job')}

            ## 登録した理由
            {request.data.get('entry_reason')}

            ## 趣味
            {request.data.get('hoby')}

            ## 性格
            {request.data.get('personality')}

            ## 好きなタイプ
            {request.data.get('ideal_partner')}
            </condition>

            <example>
            はじめまして!横浜在住のエンジニアのやまだじろうです。結婚を考え始めて登録しました!

            👉仕事
            エンジニアをしています!
            大変な時期もありますが、自分が作ったものが社会の役に立っていると思って楽しく仕事をしています💻

            👉趣味
            旅行✈️と料理🍳が大好きで、休日は新しいレシピに挑戦しています。
            アウトドアも好きで、ハイキングやキャンプ⛺️に行くことが多いです。共通の趣味を楽しめる方と出会いたいです!

            👉性格
            友達からは誠実で優しいと言われます。ポジティブな性格で、どんなことにも前向きに挑戦します。
            一緒に楽しい時間を過ごせる方だったら相性がいいかもです✨。

            👉好きなタイプ
            一緒に笑い合える明るくい方が好きです。お互いに尊重し合い、楽しい時間を共有できる関係を築きたいです✨。
            趣味や価値観が合うと嬉しいですし、何事にも前向きな方と出会いたいと思っています!

            プロフィール読んでいただきありがとうございました!
            よろしくお願いします。

            </example>

            #出力
            '''

            messages = [
                {"role": "user", "content": content}
            ]

            # APIを投げてレスポンスを受け取る
            response = openai.chat.completions.create(
                model="gpt-4o",
                messages=messages,
                temperature=0.8,
            )
            # レスポンスの中からGPTの応答文を取得
            chat_response = response.choices[0].message.content
            # 応答文を返却
            return Response({'response': chat_response}, status=status.HTTP_200_OK)

React

React + Viteで作る。
プロジェクトの初期環境は構築済みの前提とする。

めんどうくさいためコンポーネント分割はやらずに、「App.tsx」の中ですべて作る

App.tsx
const App = () => {
  // textfieldで入力値を管理するState
  const [name, setName] = useState("");
  const [gender, setGender] = useState("");
  const [age, setAge] = useState<number | undefined>();
  const [address, setAddress] = useState("");
  const [job, setJob] = useState("");
  const [entryReason, setEntryReason] = useState("");
  const [hoby, setHoby] = useState("");
  const [personality, setPersonality] = useState("");
  const [idealPartner, setIdealPartner] = useState("");

  // 自己紹介文を管理するState
  const [profile, setProfile] = useState("");

  // ボタン押下時に発火して、APIを呼ぶ
  const handleClick = () => {
    axios
      .post("http://localhost:8000/api/matching/", {
        name,
        gender,
        age,
        address,
        job,
        entryReason,
        hoby,
        personality,
        idealPartner,
      })
      .then((res) => {
        // レスポンスを自己紹介文にセット
        setProfile(res.data.response);
      });
  };

  return (
    <div css={{ margin: "0 10%" }}>
      <h1>自己紹介文メーカー</h1>
      {/* 入力値を羅列 */}
      <div css={textFieldContainer}>
        <p>名前</p>
        <TextField
          value={name}
          onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
            setName(event.target.value);
          }}
        />
      </div>
      <div css={textFieldContainer}>
        <p>性別</p>
        <TextField
          value={gender}
          onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
            setGender(event.target.value);
          }}
        />
      </div>
      <div css={textFieldContainer}>
        <p>年齢</p>
        <TextField
          value={age}
          onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
            setAge(Number(event.target.value));
          }}
        />
      </div>
      <div css={textFieldContainer}>
        <p>住所</p>
        <TextField
          value={address}
          onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
            setAddress(event.target.value);
          }}
        />
      </div>
      <div css={textFieldContainer}>
        <p>仕事</p>
        <TextField
          value={job}
          onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
            setJob(event.target.value);
          }}
        />
      </div>
      <div css={textFieldContainer}>
        <p>登録した理由</p>
        <TextField
          value={entryReason}
          onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
            setEntryReason(event.target.value);
          }}
        />
      </div>
      <div css={textFieldContainer}>
        <p>趣味</p>
        <TextField
          value={hoby}
          onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
            setHoby(event.target.value);
          }}
        />
      </div>
      <div css={textFieldContainer}>
        <p>性格</p>
        <TextField
          value={personality}
          onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
            setPersonality(event.target.value);
          }}
        />
      </div>
      <div css={textFieldContainer}>
        <p>好きなタイプ</p>
        <TextField
          value={idealPartner}
          onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
            setIdealPartner(event.target.value);
          }}
        />
      </div>
      {/* 自己紹介文を作成するボタン。再作成も可能 */}
      <div css={buttonContainer}>
        <Button variant="contained" onClick={handleClick}>
          自己紹介文を作成する
        </Button>
      </div>
      {profile && (
        <div>
          <h2>自己紹介文</h2>
          <p css={profileContainer}>{profile}</p>
        </div>
      )}
    </div>
  );
};

const textFieldContainer = css`
  display: grid;
  align-content: center;
  grid-template-columns: 20% 80%;
  p {
    font-size: 1.2rem;
    font-weight: bold;
  }
`;

const buttonContainer = css`
  display: flex;
  justify-content: center;
`;

const profileContainer = css`
  white-space: pre-wrap;
  border: 1px solid #bbb;
  padding: 10px;
`;

export default App;

完成形

完成した画面がこちら。
TextFieldにそれぞれ値を入力してボタンを押すと、自己紹介文を作成してくれます!

実際の動作画面(港区在住の港区男子で入力しています、、、)

出力された「自己紹介文」はこんな出来です。↓↓↓

自己紹介文

はじめまして!東京都港区在住の事業開発責任者の港太郎です。素敵な出会いを求めて登録しました!

👉仕事
事業開発責任者をしています。新しいプロジェクトを立ち上げるのが大好きで、毎日がチャレンジと学びの連続です。自分のアイデアが形になり、多くの人に喜ばれる瞬間が最高にやりがいです!

👉趣味
ゴルフ⛳️が大好きで、週末はよくコースに出かけています。また、サウナ🧖‍♂️でリフレッシュするのも楽しみの一つです。ファッション👔にも興味があり、休日にはショッピングを楽しんでいます。一緒に趣味を共有できる方と出会えたら嬉しいです!

👉性格
活発で元気がいいとよく言われます。ポジティブな性格で、どんなことにも前向きに取り組むタイプです。新しいことに挑戦するのが好きで、好奇心旺盛です。

👉好きなタイプ
一緒に笑い合える明るい方が好きです。お互いに尊重し合い、楽しい時間を共有できる関係を築きたいです。共通の趣味や価値観が合うと嬉しいですし、何事にも前向きな方と出会いたいと思っています!

プロフィール読んでいただきありがとうございました!
よろしくお願いします。


マッチングアプリへの組み込み

今回作成してみて、マッチングアプリに組み込むことは比較的簡単では?と思いました。
というのも、自己紹介文作成に必要な情報はユーザのプロフィールにすでに入力がある情報ばかりだからです。

  • 名前
  • 年齢
  • 居住地
  • 性格
  • 好きなタイプ(相性)
    などなどです。これらを使ってサンプルの自己紹介文を作成することは簡単です。わざわざ追加情報をユーザに入力してもらわなくても、ワンタップで自己紹介文の自動生成サービスは作成可能だと思われます。

■生成AIならではの利点
生成AIのデメリットは出力のばらつきです。出力の結果や形式を固定してアプリケーションに組み込みたい場合に、ばらつきがデメリットになります。
一方で、「自己紹介文作成サービス」のようなユースケースの場合は、このばらつきをメリットと捉えることが可能でしょう。ユーザが気に入らない出力がされた場合に、再作成によって口調やフォーマットが異なった出力を得ることができます。

まとめ

OpenAIのAPIを使ってマッチングアプリの自己紹介文作成サービスを作ってみました!
先ほども少し述べましたが、「口調」「フォーマット」「プロフィール画像から読み取ったその人の印象」「出力文字数」等をプロンプトに埋め込むことで、さらに柔軟に自己紹介文ができそうです。

みなさまの何かしらのインスピレーションに繋がれば幸いです。

Discussion