MicroCMSで作るAI駆動メディア ～ChatGPT APIで自動要約とタグ付け～


## はじめに
※本記事で紹介する実装例は、Next.js App Routerを前提としています。

こんにちは、フリーランスエンジニアとして業務委託開発を行いながら個人開発を行なっているbani24884です。  
本記事は、microCMS Advent Calendar 2024向けに、現在開発中の個人プロダクト「転職DB」でのユースケースを紹介します。

![スクリーンショット 2024-12-18 15.45.47.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3749758/9b2d8368-41a7-58c7-93c7-d42428aadf22.png)

https://www.jobchangedb.com/

「転職DB」は、noteやはてなブログ、個人ブログなどに点在する転職エントリを集約し、多様なビジネスパーソンのキャリアパスを一元的に参照できるようにすることを目指したプロジェクトです。（開発中のため、掲載情報の正確性は必ずしも保証できませんのでご了承ください）

このサイト運営において課題となっていたのが、「記事内容の要約」や「関連タグ付け」といった編集作業です。手作業で行う場合、想定以上に時間と手間がかかっていました。

そこで、**OpenAIのChatGPT APIを活用して記事本文を自動要約し、その結果をMicroCMSへ構造化データとして登録するフロー**を構築しました。その結果、要約・タグ付けにかかる工数を約80%削減し、より効率的なコンテンツ運用を実現しています。

本記事では、以下のポイントを解説します。

- データモデル（Post・Company・Tag）の役割
- ChatGPT APIを用いた自動要約・タグ抽出、およびMicroCMSへの反映フロー
- JSON形式出力を意識したChatGPTへのプロンプト設計例

これらを理解すれば、小規模なメディア運営でも、高度な編集プロセスを効率的に自動化する一歩となるでしょう。

---

## データモデルの役割

### Post（エントリ）

- **役割**: ユーザー投稿の転職体験記事を1件ずつ管理  
- **主なフィールド例**: `title`, `body`, `summarizedJson`  
- **ポイント**: `summarizedJson`にはChatGPTによる要約結果（JSON）を格納予定

### Company（企業情報）

- **役割**: 記事内で登場する企業をIDで管理  
- **ポイント**: 未登録企業名が出現したら新規Companyエントリを作成し、再利用可能にする

### Tag（タグ）

- **役割**: 転職理由、職種、評価、一般属性など、記事メタ情報をIDで管理  
- **ポイント**: 記事中に出た未登録タグは新規作成し、Postへ紐付ける

これら3モデル（Post・Company・Tag）を組み合わせ、記事内容を「IDで紐付いた構造的データ」としてMicroCMS上で管理します。結果として、読者は「特定のタグ条件での検索」や「特定企業に関連する記事」への簡易アクセスが可能になります。

---

## 全体フロー概要

本システムは大きく2ステップで動作します。

1. **記事投稿後にChatGPTで要約・タグ抽出（summarizeArticle）**  
   - ChatGPT APIへ記事本文を渡し、要約・タグ抽出結果をJSON形式で受け取る  
   - 得られたJSONを`summarizedJson`としてPostに格納

2. **JSON結果を元に、企業・タグをID化してPost更新（updateWithSummarizedJson）**  
   - `summarizedJson`に含まれる企業名やタグ名をMicroCMS上のCompany・Tagモデルと照合・新規作成  
   - 変換したIDをPostへPATCHで反映（`fromCompany`, `reasonTag`など）

この2ステップにより、「元記事 → 要約JSON → IDベースの整理済みメタデータ」の変換が可能になります。

---

## ChatGPT API実装上のポイント

### JSON形式での出力を求めるプロンプト設計

ChatGPTに対してJSON形式の出力を要求することで、結果を`JSON.parse`で扱いやすくなります。  
これにより、`summarizedJson`としてそのままMicroCMSに格納し、後続のID変換処理を容易にすることが可能です。

**プロンプト例:**

```plaintext
以下の記事を分析し、下記JSON形式で出力してください:

{
  "fromCompanyName": "",
  "toCompanyName": "",
  "fromCompanyTag": [],
  "toCompanyTag": [],
  ...
  "postSummary": ""
}

- 記事本文から判断できる情報のみ
- 情報がなければ"記載なし"や空配列を使用
- 必ずJSON形式で回答
```
## 実装ステップ

### 1. summarizeArticle実行（要約JSON生成）

記事投稿後、`summarizeArticle`を呼び出し、ChatGPT APIへ記事本文を渡します。  
戻り値は要約・タグ抽出結果JSONを`summarizedJson`に格納します。

```typescript
"use server"

// summarizeArticle関数では、Post記事本文をChatGPT APIへ要約リクエストし、
// その結果をsummarizedJsonとしてMicroCMSに保存します。

export async function summarizeArticle({postId}: {postId: string}) {
　// Postを出力
  const post = await getPost(postId);
  // 記事本文を取得
  const article = `${post.title}\n${post.body}`;
  
  // ChatGPT APIで記事要約（resultは要約結果のJSONオブジェクト）
  const result = await summarizePost(article);

  // 要約結果JSONをsummarizedJsonフィールドとしてMicroCMSのPostに保存
  await fetch(`https://${process.env.MICROCMS_SERVICE_DOMAIN}.microcms.io/api/v1/entries/${post.id}`, {
    method: 'PATCH',
    headers: {
      'X-MICROCMS-API-KEY': process.env.MICROCMS_API_KEY ?? '',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({ summarizedJson: JSON.stringify(result) })
  });

  return { data: { id: postId }, error: null }
}

```

**サンプル出力（summarizedJson）:**

```json
{
  "fromCompanyName": "会社A",
  "fromCompanyTag": ["大手企業","国内企業","プロダクト型","上場企業","BtoC企業"],
  "toCompanyName": "会社B",
  "toCompanyTag": ["国内企業","BtoC企業","上場企業"],
  ...
  "postSummary": "X氏はYYYの理由でA社を退職し...(以下略)"
}
```
### 2. updateWithSummarizedJson実行（ID変換と最終更新）

`summarizedJson`を元にタグ・企業名をIDへ変換し、Postを更新します。

```typescript
"use server"

// updateWithSummarizedJson関数では、summarizedJsonを解析し、
// タグ名・企業名をMicroCMS上のIDに変換してPostを更新します。

export async function updateWithSummarizedJson({postId}:{postId:string}) {
  // Post取得（postは既に取得済みを想定）
  const post = await getPost(postId);
  
  // summarizedJsonフィールドから要約結果JSONをパース
  const result = JSON.parse(post.summarizedJson);

  // 要約結果から抽出した企業名・タグ名をMicroCMS上のIDに変換
  const fromCompanyId = await getCompanyIdByName(result.fromCompanyName);
  const reasonTagIds = await convertTagsToIds(result.reasonTag, reasonTags, '転職理由');
  // ... 他のタグも同様にID取得・・・

  // ID変換後の情報を使ってPostを更新
  await fetch(`https://${process.env.MICROCMS_SERVICE_DOMAIN}.microcms.io/api/v1/entries/${postId}`, {
    method: 'PATCH',
    headers: {
      'X-MICROCMS-API-KEY': process.env.MICROCMS_API_KEY ?? '',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      postSummary: result.postSummary, // 全体要約文
      fromCompany: fromCompanyId,      // 企業ID紐付け
      reasonTag: reasonTagIds,         // 理由タグID紐付け
      // ... その他フィールドも同様にセット ...
    })
  });

  return { data: { id: postId }, error: null }
}
```

これで記事は企業ID・タグIDを持ち、メタデータが整合した状態での公開が可能になります。

---

## 成果と展望

- **作業時間削減**: 要約・タグ付け作業が自動化され、編集者は最低限の確認・修正のみで済み、約80%の時間削減が可能になりました。  
- **検索性向上**: タグや企業情報が整備され、読者は「家族重視」や「スタートアップ転職」など特定条件でエントリを横断的に検索できます。(開発中)
---

## おわりに

以上で、ChatGPT APIとMicroCMSを組み合わせて記事要約やタグ付け工程を大幅に効率化する流れをご紹介しました。

この仕組みを活用すれば、手動で行っていた非効率な編集工程を自動化でき、コンテンツの拡充や品質向上により多くの時間を割けるようになります。小規模メディア運営はもちろん、個人開発でも実装可能なワークフローです。

### Xへのフォローのお願い

もしこの記事が役に立ったと思っていただけたら、ぜひX（旧Twitter）でフォローしていただけると嬉しいです。更新情報や個人開発の進捗、技術的なヒントなどをシェアしています。
https://x.com/bani24884

### 「爆速ホームページ」のご紹介

また、個人開発で取り組んでいるNext.jsとMicroCMSを使ったコードテンプレート「爆速ホームページ」もぜひチェックしてみてください。
https://www.bakusoku-hp.com/