毎朝、問い合わせメールを開いて、一件ずつ返信を書く。丁寧にやるほど時間が溶けていく——この作業を、AIに「返信の下書き」まで作らせて、送るかどうかは自分が最後に決める形にできたら、負担はぐっと減ります。
私はこれまで、Googleフォームの回答をAIで自動分類する仕組みや、フォーム回答者へ自動返信するGASを作ってきました。今回はその次の一歩として、Gmailに届いた問い合わせに、Claude APIが返信の下書きを自動で用意する仕組みを作ります。
ポイントは「全自動」にしないこと。下書きに保存するだけで、送信はしません。 事故の芽を摘みながら、面倒な下書き作業だけをAIに任せる——実機で踏んだつまずきと、opus/haikuの実測コストまで、一次情報でまとめました。
作るもの(完成イメージ)
作るのは「要返信 ラベルの付いたメールに、AIが返信案を書いて、下書きに保存する」仕組みです。実行すると、下書きフォルダに、元のメールにぶら下がる形で返信案が並びます。
処理の流れはシンプルです。
- Gmailで対象メールに
要返信ラベルを付けておく - GASが
label:要返信 -in:draftで対象を集める - Claude APIが件名と本文を読んで返信案を書く
- 元のメールへの返信下書きとして保存し、
AI下書き済ラベルを付ける(二重生成を防ぐ)
送信は一切しません。 保存された下書きを人が確認し、必要なら直して、自分の手で送る。この「人が判断する一点だけを残す」設計が、この記事の背骨です。

なぜ Gmail×GAS×Claude API(直叩き)なのか
メール返信の自動化は、専用SaaSやノーコードツールでも作れます。それでもGAS×Claude APIの直叩きにするのは、Googleアカウントの中だけで完結し(メールが外の基盤を経由しない)、月額課金も増えず、中身を自分で完全に制御できるからです。
前提として、GAS用のClaude公式SDKはありません。GASの UrlFetchApp から Anthropic の Messages API(POST https://api.anthropic.com/v1/messages)を直接叩きます。「直叩き」と言うと身構えますが、やることは「決まった住所に、決まった形の手紙を送って、返事を受け取る」だけです。
実装
全体像は上の一本道——対象を集める/本文を取り出す/AIに返信案を書かせる/下書きに保存する——をそのままコードにしたものです。書き換えるのはごく一部です。
1. 設定と鍵の取り出し
先頭に設定値をまとめます。ここを直せば対象ラベルや使うモデルを変えられます。
const ANTHROPIC_API_URL = 'https://api.anthropic.com/v1/messages';
const ANTHROPIC_VERSION = '2023-06-01'; // Claude APIに必須のバージョン指定
const MODEL = 'claude-opus-5'; // 返信品質を優先(安いモデルとの比較は後述)
const MAX_TOKENS = 1024; // 返信文の長さの上限(後述の落とし穴に関わる)
const SEARCH_QUERY = 'label:要返信 -in:draft'; // 対象=「要返信」ラベル付き・下書き未作成
const PROCESSED_LABEL = 'AI下書き済'; // 二重生成を防ぐための目印
APIキーはコードに直書きせず、スクリプトプロパティ(ANTHROPIC_API_KEY)から取り出します。
function getApiKey_() {
const key = PropertiesService.getScriptProperties().getProperty('ANTHROPIC_API_KEY');
if (!key) throw new Error('スクリプトプロパティ ANTHROPIC_API_KEY が未設定です');
return key;
}
鍵が無ければその場でエラーにして止めます。原因不明の不具合に悩むより、「鍵がない」と明示されるほうが親切です。
2. 返信案を作る(プロンプト設計=いちばんの肝)
ここが最重要です。Claudeに「どう振る舞ってほしいか」をシステムプロンプトで伝えます。安全な下書きになるかどうかは、この指示文の設計で決まります。
function draftReply_(subject, body) {
const system = [
'あなたは日本語のカスタマーサポート担当です。',
'受け取った問い合わせに対する「返信の下書き」を書いてください。',
'- 丁寧で簡潔に。相手の要点に答える。',
'- 署名は入れない(後で人が付ける)。',
'- 【禁止】金額・納期・在庫など不確かな事実を勝手に約束/断定しない。',
'- 分からない点は「確認のうえご連絡します」と留保する。',
'- 【重要】返信本文だけを出力する。「下書き」等の見出し・注記・メタコメント・囲み記号を一切付けない。',
].join('\n');
const payload = {
model: MODEL,
max_tokens: MAX_TOKENS,
system: system,
messages: [{ role: 'user', content: `件名: ${subject}\n\n本文:\n${body}` }],
};
const res = UrlFetchApp.fetch(ANTHROPIC_API_URL, {
method: 'post',
contentType: 'application/json',
headers: { 'x-api-key': getApiKey_(), 'anthropic-version': ANTHROPIC_VERSION },
payload: JSON.stringify(payload),
muteHttpExceptions: true,
});
const code = res.getResponseCode();
const raw = res.getContentText();
if (code !== 200) throw new Error(`Claude API ${code}: ${raw}`);
const json = JSON.parse(raw);
const textBlock = (json.content || []).find(function (b) { return b.type === 'text'; });
if (!textBlock) throw new Error('返信テキストのブロックが見つかりません: ' + raw);
return textBlock.text.trim();
}
指示文の一行ずつに意味があります。特に 「不確かな事実を勝手に約束/断定しない」「分からない点は確認のうえご連絡しますと留保する」 の二つが安全設計の心臓部です。人間の最終確認だけに頼らず、AI自身にも「知らないことは言い切るな」と守らせています。実際に「土日は営業していますか」というテストを流したとき、AIは営業の有無を断定せず「確認のうえご連絡します」と返してきました。
最後の 「返信本文だけを出力する」 も実務で効きます。これがないと、モデルによっては返信の頭に「【返信の下書き】」といった見出しや運用者向けの注記を付けてくることがあります(後述の実測を参照)。
3. 対象を集めて、下書きに保存する
全体をつなぐ司令塔です。
function createAiReplyDrafts() {
const threads = GmailApp.search(SEARCH_QUERY);
const doneLabel = GmailApp.getUserLabelByName(PROCESSED_LABEL) || GmailApp.createLabel(PROCESSED_LABEL);
threads.forEach(function (thread) {
const msg = thread.getMessages().pop(); // スレッドの最新メッセージに返信
const subject = msg.getSubject();
const body = (msg.getPlainBody() || '').trim(); // 本文を取り出して前後の空白を除く
const reply = draftReply_(subject, body);
msg.createDraftReply(reply); // 元のメールへの返信として下書きに保存
thread.addLabel(doneLabel); // 「AI下書き済」を付けて二重生成を防ぐ
});
}
createDraftReply がこの記事の一線を体現する一行です。「新規メール」ではなく「元のメールへの返信」として下書きを作り、保存されるだけで、送信はされません。 最後に AI下書き済 ラベルを付け、次回の実行で同じメールを拾わないようにしています(このラベルはスクリプトが自動で作ります)。
なお、AIに送るのは件名と本文だけにとどめています。問い合わせの中身が外部AIに渡るからこそ、「何を送らないか」を先に決めておくのが安全設計の一部です。
createAiReplyDrafts を実行し、初回のGmail/AIアクセス承認を許可すれば、要返信 ラベルのメールに返信案が作られ、下書きに保存されます。
実機でつまずいた点(先に手を打っておく)
想像や一般論ではなく、実際に自分の環境で動かして見た挙動だけを載せます。
返信テキストの取り出しには「ひと工夫」が要る
claude-opus-5 は答える前に内部で思考する性質があり、返ってくるデータの先頭が空の「思考」の枠になっていることがあります。そこを返信文だと思って取り出すと中身が空で転びます。だから「最初の枠」ではなく「テキストの枠」を名指しで拾います。
const textBlock = (json.content || []).find(function (b) { return b.type === 'text'; });
この一行があるかないかで、動くか転ぶかが変わります。
出力が途中で切れる「静かな落とし穴」
Claude APIには出力長の上限(max_tokens)があります。返信文がこれを超えると、エラーにならず、正常な応答のまま途中で切れて返ってきます。分類のように出力が短い用途では起きにくいのですが、返信文は長くなりがちです。実測では、いちばん長い返信でも上限のおよそ四割程度でした。この記事は 1024 にしており十分余裕がありますが、長いスレッドで切れる症状が出たら値を上げてください。
動かないときに最初に疑う二か所
APIキーを間違えると認証エラー(401)、anthropic-version を書き忘れるとリクエスト不正(400)が返ります。200番以外はそのままエラーの中身を表示して止めるようにしておくと、原因をその場で切り分けられます。
if (code !== 200) throw new Error(`Claude API ${code}: ${raw}`);
このほか「本文先頭の余計な空白(.trim()で対処)」「下書きが元メールにぶら下がり送信されないことの確認」「ラベルによる二重生成防止」も実機で検証しています。
opus / haiku を「自分のデータ」で選ぶ
モデルは評判や値段だけで決めず、自分の題材で測るのが確実です。質問系・要望系・クレーム系の3題材で、opusとhaikuを実測しました(1ドル150円想定・記事執筆時点の料金)。
| 題材 | モデル | 自然さ | トーン追従 | 一通あたり概算コスト |
|---|---|---|---|---|
| 質問系 | claude-opus-5 | 5 | 5 | 約1.08円 |
| 質問系 | claude-haiku-4-5 | 2 | 2 | 約0.18円 |
| 要望系 | claude-opus-5 | 5 | 5 | 約1.65円 |
| 要望系 | claude-haiku-4-5 | 2 | 3 | 約0.19円 |
| クレーム系 | claude-opus-5 | 5 | 5 | 約1.63円 |
| クレーム系 | claude-haiku-4-5 | 2 | 4 | 約0.18円 |
opusは3題材すべて文句なしでした。見出しや注記を付けず本文から始まり、要望系ではヒアリング項目を的確に並べ、クレーム系では謝罪から入り必要情報を求めてくれます。そのまま下書きに使える品質です。
haikuは安いが、そのままでは出せませんでした。 コストはopusの6〜9分の1と魅力的ですが、返信の頭に「【返信の下書き】」といった見出しや運用者向け注記が混ざり込みました。
選び方の型はこうです。まず賢いモデル(opus)で品質を確かめる。個人や小さなチームの問い合わせ量(1日数通〜数十通)なら、opusでも一通1円台=コスト差は実質誤差です。処理量が増えてコスト差が効く段階に来たら、「安くした分どこが崩れるか」を自分の題材で測り直したうえでhaikuへ差し替える。賢いモデルで品質を担保し、安いモデルに落とすなら自分のデータで崩れどころを確かめる——これが、モデルが増え続ける時代に迷わない選び方です。
まとめ
- Gmailの
要返信ラベルを起点に、Claude APIが返信の下書きを自動作成。送信は人間が最後に決める設計。 - プロンプトで「不確かなことは約束せず留保」「本文だけ出力」を徹底するのが安全と実用の肝。
- opus系のテキスト取り出し(
type==='text'を名指し)と、max_tokensの余裕は先に手を打つ。 - モデルは自分のデータで選ぶ。少量ならopusが素直で合理的。
この記事のコードはコピペで動きます。さらに詳しい設計判断や実機ログの全記録、章立てで腰を据えて読みたい方は書籍版(『Gmailの問い合わせにAIが返信下書きを自動作成【GAS×Claude API】』)にまとめています。もう一歩踏み込んだ自動化テンプレートはnoteでも配布しています。

コメント