
Zendesk アクションフロー × Amazon Bedrock (Claude) × Contentful による AI カスタマーサポートの構築
はじめに
近年、カスタマーサポートの効率化と品質向上を目的とした AI 活用が注目されています。本記事では、Zendesk アクションフローと Amazon Bedrock (Claude) を組み合わせ、「Contentful ナレッジベースを参照して AI が自動回答するシステム」を構築してみた例を紹介します。顧客からの問い合わせが Zendesk に届くと自動的に記事を検索し、Claude が適切な回答を生成してサポート担当者に提示します。
技術スタック
- Zendesk アクションフロー: ワークフロー自動化
- Amazon Bedrock (Claude): LLM
- Contentful: ヘッドレス CMS (ナレッジベース)
- AWS Lambda: 統合処理
対象読者
- AI を活用したサポート業務の効率化を検討しているカスタマーサポート部門の責任者
- Zendesk や AWS サービスの連携に興味があるエンジニア
アーキテクチャ概要
本システムは以下のような構成です。
[Zendesk Trigger] (チケット作成)
↓
[Zendesk アクション]
↓
[AWS Lambda]
├─→ [Contentful API] (ナレッジ検索)
└─→ [Amazon Bedrock (Claude)] (回答生成)
↓
[Zendesk] (回答提案)
構築手順
1. Contentful でナレッジベース準備
本件は、すでに Contentful にナレッジが蓄積されているユースケースを想定しています。今回は検証のため、まず AI が参照するナレッジベースを Contentful で作成します。
Content Type の作成
Contentful の管理画面で新しい Content Type Article
を作成します。
- Title (Short text): 記事タイトル
- Category (Short text): カテゴリ分類
- Tags (Short text, list): 検索用タグ
- Summary (Long text): 記事要約
- Content (Rich text): 記事本文
サンプル記事の作成
LLM の検索対象となるように、以下のようなサンプル記事をいくつか作成します。
記事例: API 利用方法
Title: API 利用方法
Category: 開発者向け
Summary: 当社 API の基本的な使い方と認証方法について説明します
Content:
## 認証設定
1. API トークンの取得
2. リクエストヘッダーの設定
...
2. Bedrock の準備
今回使用するモデルは「Claude Sonnet 4」とします。Bedrock コンソール の Model catalog から Claude Sonnet 4 のページを開き、EULA に同意して使用承認を取得します。
3. AWS Lambda 関数作成
次に、Contentful と Bedrock を連携する Lambda 関数を作成します。
関数の作成
AWS Lambda コンソールで新しい関数を作成します。
- 関数名:
zendesk-contentful-integration
- ランタイム:
Node.js 22.x
- 実行ロール: 新しいロールを作成
環境変数の設定
Lambda 関数の設定で以下の環境変数を追加します:
CONTENTFUL_SPACE_ID = your_space_id
CONTENTFUL_ACCESS_TOKEN = your_access_token
IAM 権限の設定
Lambda 実行ロールに対し、 Bedrock へのアクセスを許可する IAM ポリシーを追加します。Claude Sonnet 4 の ID は anthropic.claude-sonnet-4-20250514-v1:0
です。(2025.07 現在)
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"bedrock:InvokeModel",
"bedrock:InvokeModelWithResponseStream"
],
"Resource": [
"arn:aws:bedrock:*:*:inference-profile/apac.anthropic.claude-sonnet-4-20250514-v1:0",
"arn:aws:bedrock:*::foundation-model/anthropic.*"
]
},
{
"Effect": "Allow",
"Action": [
"bedrock:ListFoundationModels",
"bedrock:ListInferenceProfiles",
"bedrock:GetInferenceProfile"
],
"Resource": "*"
}
]
}
Lambda 関数の作成
ローカルに環境を作成します。
mkdir zendesk-contentful-lambda
cd zendesk-contentful-lambda
npm init -y
npm install @aws-sdk/client-bedrock-runtime axios
下記の index.js を作成します。
const { BedrockRuntimeClient, InvokeModelCommand } = require('@aws-sdk/client-bedrock-runtime');
const axios = require('axios');
exports.handler = async (event) => {
try {
// Zendesk からのリクエストボディを解析
let body;
if (event.body) {
body = typeof event.body === 'string' ? JSON.parse(event.body) : event.body;
} else {
body = event;
}
const question = body.question || body.message || '';
if (!question) {
return {
statusCode: 400,
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ error: 'Question is required' })
};
}
// 1. Contentful からナレッジを検索
const knowledge = await searchContentful(question);
// 2. Claude で回答生成
const answer = await generateAnswer(knowledge, question);
// 3. Zendesk に結果を返却
return {
statusCode: 200,
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
question: question,
answer: answer,
knowledgeUsed: knowledge.length,
knowledgeArticles: knowledge.map(k => ({
title: k.title,
category: k.category
})),
timestamp: new Date().toISOString()
})
};
} catch (error) {
return {
statusCode: 500,
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
error: 'Internal server error',
message: error.message
})
};
}
};
// Contentful からナレッジ記事を検索
async function searchContentful(query) {
try {
const spaceId = process.env.CONTENTFUL_SPACE_ID;
const accessToken = process.env.CONTENTFUL_ACCESS_TOKEN;
if (!spaceId || !accessToken) {
return [];
}
const response = await axios.get(`https://cdn.contentful.com/spaces/${spaceId}/entries`, {
params: {
access_token: accessToken,
content_type: 'article',
limit: 10
}
});
const articles = response.data.items.map(item => ({
title: item.fields.title || 'No title',
summary: item.fields.summary || 'No summary',
content: item.fields.content || 'No content',
category: item.fields.category || 'No category'
}));
// キーワードマッチングで関連記事を検索
const relevantArticles = articles.filter(article => {
const searchText = `${article.title} ${article.summary} ${article.content}`.toLowerCase();
const keywords = query.toLowerCase().split(' ');
return keywords.some(keyword => searchText.includes(keyword));
});
return relevantArticles.length > 0 ? relevantArticles.slice(0, 3) : articles.slice(0, 2);
} catch (error) {
console.error('Contentful search error:', error.message);
return [];
}
}
// Claude で回答を生成
async function generateAnswer(knowledge, question) {
const client = new BedrockRuntimeClient({
region: 'ap-northeast-1'
});
// ナレッジベースの有無でプロンプトを切り替え
let prompt;
if (knowledge.length === 0) {
prompt = `あなたは企業のカスタマーサポート担当者です。以下の質問に対して、親切で丁寧な回答をしてください。
ユーザーの質問: ${question}
回答:`;
} else {
const knowledgeText = knowledge.map(article =>
`## ${article.title}\nカテゴリ: ${article.category}\n要約: ${article.summary}\n内容: ${article.content}`
).join('\n\n');
prompt = `あなたは企業のカスタマーサポート担当者です。以下のナレッジベースを参考に、ユーザーの質問に正確で親切な回答をしてください。
ナレッジベース:
${knowledgeText}
ユーザーの質問: ${question}
回答の条件:
- ナレッジベースの情報を基に、具体的で実用的な回答をしてください
- 手順がある場合は、番号付きリストで分かりやすく説明してください
- 敬語を使用してください
回答:`;
}
const command = new InvokeModelCommand({
modelId: 'apac.anthropic.claude-sonnet-4-20250514-v1:0',
body: JSON.stringify({
messages: [{ role: "user", content: prompt }],
max_tokens: 500,
anthropic_version: "bedrock-2023-05-31"
})
});
const response = await client.send(command);
const result = JSON.parse(new TextDecoder().decode(response.body));
return result.content[0].text;
}
zip ファイルに圧縮してアップロードします。
zip -r function.zip .
タイムアウト設定
LLM アクセスにかかる時間を考慮し、タイムアウトを 30 秒に設定します。
API Gateway
Lambda にアクセスするためのエンドポイントを API Gateway の REST API で用意します。 CORS 設定を行い、ブラウザからの API アクセスを許可します。認証は今回は検証目的なので「オープン」としますが、本番環境では適切な認証設定を行ってください。
4. Zendesk アクション設定
まず、Lambda 関数を呼び出すためのアクションを作成します。Zendesk 管理センターの アプリおよびインテグレーション > アクション でアクションを作成します。
- 入力:
- 名前: comment
- 説明: チケットコメント
- タイプ: String
- API設定:
- リクエスト方法: POST
- エンドポイントURL: API Gateway のエンドポイント URL
- 認証: 認証なし(本番環境では適切に設定してください)
- 本文:
{ "question": "{{comment}}" }
- 出力:
- answer: 回答
- question: 質問
- timestamp: タイムスタンプ
- knowledgeuserd: 参照数
- knowledgearticles: 参照記事
5. Zendesk アクションフロー設定
次に、Zendesk 管理センターの アプリおよびインテグレーション > アクションフロー でアクションフローを作成します。
- トリガー: Zendesk > Tickets > Lifecycle > Ticket created
- チケット内容取得: Zendesk steps > Look up ticket
- Ticket ID: Ticket created > id
- Ticket ID: Ticket created > id
- 回答生成: 先に作成したアクション
- comment: Look up ticket > description
- comment: Look up ticket > description
- 回答提案:
- Ticket ID: Ticket created > id
- Comment: 先に作成したアクション > answer
- Comment visibility: Internal note
フローの有効化
作成したフローのメニューから Activate を選択して有効化します。
動作確認
テスト実行
Zendesk でテストチケットを作成し、「API の使い方について教えて」とコメントを追加します。
結果確認
アクションフローが実行され、Claude が生成した回答を確認できます。
まとめ
本記事では、Zendesk アクションフロー、Amazon Bedrock (Claude)、Contentful を組み合わせた AI カスタマーサポートシステムを構築しました。 顧客の質問に関連する記事を既存のナレッジ(今回は Contentful を想定)から自動で検索し、自然な文章で回答を提案します。今後の展開としては、複数のシステムに渡るナレッジの横断的な検索や、学習機能なども検討可能です。