Gmailの問い合わせにAIが返信下書きを自動作成【GAS×Claude API】

毎朝、問い合わせメールを開いて、一件ずつ返信を書く。丁寧にやるほど時間が溶けていく——この作業を、AIに「返信の下書き」まで作らせて、送るかどうかは自分が最後に決める形にできたら、負担はぐっと減ります。

私はこれまで、Googleフォームの回答をAIで自動分類する仕組みや、フォーム回答者へ自動返信するGASを作ってきました。今回はその次の一歩として、Gmailに届いた問い合わせに、Claude APIが返信の下書きを自動で用意する仕組みを作ります。

ポイントは「全自動」にしないこと。下書きに保存するだけで、送信はしません。 事故の芽を摘みながら、面倒な下書き作業だけをAIに任せる——実機で踏んだつまずきと、opus/haikuの実測コストまで、一次情報でまとめました。

作るもの(完成イメージ)

作るのは「要返信 ラベルの付いたメールに、AIが返信案を書いて、下書きに保存する」仕組みです。実行すると、下書きフォルダに、元のメールにぶら下がる形で返信案が並びます。

処理の流れはシンプルです。

  1. Gmailで対象メールに 要返信 ラベルを付けておく
  2. GASが label:要返信 -in:draft で対象を集める
  3. Claude APIが件名と本文を読んで返信案を書く
  4. 元のメールへの返信下書きとして保存し、AI下書き済 ラベルを付ける(二重生成を防ぐ)

送信は一切しません。 保存された下書きを人が確認し、必要なら直して、自分の手で送る。この「人が判断する一点だけを残す」設計が、この記事の背骨です。

Gmailの問い合わせにAIが返信下書きを作る処理フロー。人が「要返信」ラベルを付けると、GAS×Claude APIが対象スレッド収集・件名本文の取り出し・返信案生成・返信下書き保存を自動で行い、送信だけは人が最後に確認して判断する。
図:処理フロー。ティールの破線枠=GAS×Claudeが自動で回す範囲(Googleアカウント内で完結)。下書き保存までが自動で、送信するかどうかの判断だけを人が最後に行うのがこの仕組みの背骨。

なぜ 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でも配布しています。

コメント

タイトルとURLをコピーしました