お知らせ・ブログ一覧へ戻る

LINEに送った文章をClaudeに処理させる|GAS中継の実装コードと詰まりどころ4つ


LINEに送った文章をClaudeに処理させる|GAS中継の実装コードと詰まりどころ4つ

LINEに送った文章をClaudeに処理させる|GAS中継の実装コードと詰まりどころ4つ

「LINEに送った文章を、AIが読み取って処理してくれたら」。思いついたことをLINEに送るだけでタスク表に整理されて登録される、問い合わせ文が要約されて担当者に届く——こうした仕組みは、実は追加のサーバーを立てずに作れます。

結論から書きます。LINEとClaudeをつなぐ最小構成は次の3段です。

  • LINE公式アカウント(Messaging API)が、送られたメッセージをWebhookで通知する
  • GAS(Google Apps Script)の doPost 関数がそれを受け取り、Claude APIに文章を渡す
  • Claudeが返した結果をスプレッドシートに書き込み、必要ならLINEに返信する

中継サーバーの役割をGASのWebアプリが担うので、レンタルサーバーもVPSも不要です。かかる費用はClaude APIの利用料だけで、短い文章の処理なら1通あたり1円前後(使うモデルによってはその数分の一)に収まります。

弊社ではこの構成の土台になる「LINE→GAS→スプレッドシート」のタスク登録システムを2026年から本番運用しており、Claude側からGAS経由で同じシートに書き込む経路も稼働しています。全体像と設計思想は既存記事「Claude×LINE連携をGASで実装|送るだけでタスク自動登録」に書いたので、本記事はその実装編です。コードと、実際に運用して踏んだ「詰まりどころ」を中心にまとめます。

最小実装コード:doPost → Claude API → シート

GAS側のコードは3つの関数に分かれます。まずLINEからのWebhookを受けるdoPostです。

function doPost(e) {
  const data = JSON.parse(e.postData.contents);
  const event = data.events && data.events[0];
  // テキストメッセージ以外(スタンプ・画像・検証リクエスト)は無視して200を返す
  if (!event || event.type !== "message" || event.message.type !== "text") {
    return ContentService.createTextOutput(JSON.stringify({status: "ignored"}));
  }
  const userText = event.message.text;
  const parsed = callClaude(userText);
  saveToSheet(parsed, userText);
  replyToLine(event.replyToken, "登録しました:" + parsed.task);
  return ContentService.createTextOutput(JSON.stringify({status: "ok"}));
}

次にClaude APIの呼び出しです。GASにはSDKがないので、UrlFetchAppでMessages APIを直接叩きます。APIキーはコードに書かず、スクリプトプロパティに保存して読み出します。

function callClaude(userText) {
  const apiKey = PropertiesService.getScriptProperties().getProperty("ANTHROPIC_API_KEY");
  const payload = {
    model: "claude-opus-4-8",
    max_tokens: 1000,
    system: "ユーザーの文章からタスクを抽出し、JSONだけを返す。キーはtask, deadline, priority。読み取れない項目は空文字にする。",
    messages: [{ role: "user", content: userText }]
  };
  const res = UrlFetchApp.fetch("https://api.anthropic.com/v1/messages", {
    method: "post",
    contentType: "application/json",
    headers: {
      "x-api-key": apiKey,
      "anthropic-version": "2023-06-01"
    },
    payload: JSON.stringify(payload),
    muteHttpExceptions: true
  });
  const body = JSON.parse(res.getContentText());
  return JSON.parse(body.content[0].text);
}

最後にシート追記と返信です。

function saveToSheet(parsed, original) {
  const sheet = SpreadsheetApp.openById("シートID").getSheetByName("タスク管理");
  sheet.appendRow([new Date(), "LINE", parsed.task, parsed.deadline, parsed.priority, original]);
}

function replyToLine(replyToken, text) {
  const token = PropertiesService.getScriptProperties().getProperty("LINE_CHANNEL_ACCESS_TOKEN");
  UrlFetchApp.fetch("https://api.line.me/v2/bot/message/reply", {
    method: "post",
    contentType: "application/json",
    headers: { "Authorization": "Bearer " + token },
    payload: JSON.stringify({ replyToken: replyToken, messages: [{ type: "text", text: text }] })
  });
}

これをWebアプリとしてデプロイし、発行された /exec のURLをLINE Developersの Webhook URL に設定すれば骨格は完成です。設計上の注意が1つあります。LINEの応答用トークン(replyToken)は短時間で失効し1回しか使えないため、Claudeの処理に時間がかかる構成では、返信を諦めてシート記録だけにするか、返信をpush送信に切り替える判断が必要になります。

なお、モデルは用途で選びます。上のコードでは高精度なclaude-opus-4-8を指定していますが、短文からのタスク抽出のような軽い処理なら、より安価な軽量モデルに差し替えても実用になります。API料金の詳しい試算は別記事に譲り、本記事は先に「動かなくなるポイント」を潰します。

詰まりどころ1:curlでテストするとdoPostが動かない

実装して最初にぶつかりやすいのがこれです。GASのWebアプリにcurlでPOSTしてテストすると、正しく書いたはずのdoPostが反応しません。

原因はGASのリダイレクト仕様です。GASのWebアプリはPOSTを受けると一度リダイレクトを返し、curlはリダイレクト先へのリクエストをPOSTからGETに変えてしまいます。結果、GAS側ではdoGetが呼ばれ、doPostは一度も実行されません。コードのバグではないので、コードをいくら見直しても直りません。

テストはPythonのurllib.requestなど、リダイレクト後もPOSTを維持するクライアントで行うのが正解です。弊社のClaude→GAS登録経路も、この理由で送信処理をPythonに統一しています。なお、LINEプラットフォームからの本番Webhookはこの問題の影響を受けないため、「curlでは動かないがLINEからは動く」という状態は正常です。切り分けを間違えないでください。

詰まりどころ2:コードを直しても本番に反映されない

GASのWebアプリは「デプロイした時点のバージョン」が固定で公開されます。エディタでコードを修正して保存しても、本番の /exec URL の挙動は変わりません。これを知らないと「直したはずのバグが直らない」状態で時間を溶かします。

正しい手順は、デプロイの管理画面から同じデプロイの「バージョンを更新」を行うことです。「新しいデプロイ」を作ると /exec のURL自体が変わってしまい、LINE Developers側のWebhook URLを貼り替えない限り、旧バージョンが動き続けます。URL貼り替え漏れは「修正が反映されない」の典型原因なので、運用ではURLが変わらないバージョン更新を標準にしておくと事故が減ります。

ここは弊社でも運用ルール化するまで手戻りが出やすかったポイントで、逆に言えば「同じURLのままバージョン更新」を徹底すれば、LINE側の設定には二度と触らずに済みます。

詰まりどころ3:返信が二重に来る(応答メッセージとの競合)

Webhook連携を有効にしたあと、LINE公式アカウント標準の「応答メッセージ」「あいさつメッセージ」をONのままにしていると、Bot側の返信と標準機能の自動返信が両方送られ、ユーザーには二重に届きます。

正しい設定は、応答メッセージ・あいさつメッセージをOFF、WebhookをONです。弊社が美容室グループ向けに本番運用している口コミ通知Bot(友だち追加時に登録案内を自動返信する構成)でも、この設定で二重返信を防いでいます。あいさつをBot側で返すなら標準あいさつは切る、標準機能を使うならBot側では返さない、と役割をどちらかに寄せるのが原則です。

応答モードまわりの優先関係で「そもそも反応しない」パターンにはまった場合は、切り分け手順を「公式LINEのキーワード応答が反応しない原因」にまとめています。

詰まりどころ4:push送信が400エラーで落ちるときの切り分け順

返信ではなくpush送信(Botから能動的に送る)を使う構成で、「トークンも正しい、ペイロードの形式も正しい、なのに400 Failed to send messages」という状況が起こり得ます。弊社でも物販企業のグループ通知構築で実際に遭遇しました。

このとき疑う順番を間違えると長引きます。実体験からの推奨順は次の通りです。

  • まずチャネルアクセストークンの取り違え(別チャネルのトークンを使っていないか)
  • 次にペイロード形式(messages配列・型)
  • それでも直らなければ送信経路の競合を疑う。同じ公式アカウントのWebhookを別の拡張ツールが使っている構成や、グループへの参加がそのツール経由になっている場合、Messaging APIからの直接pushが通らないことがある
  • 429が返る場合はエラーではなくプランの送信通数上限。実装ではなく契約プランの問題

特に3つ目は見落としやすいポイントです。宛先IDやトークンを何度も疑う前に、「この公式アカウントのWebhookを今、誰が握っているか」を確認する方が早く着地します。

どこまでClaudeに任せるかの判断基準

最後に設計の話です。実は弊社の本番タスク登録は、LINEからの入力を「内容・期限・優先度」の定型フォーマットで受けており、この範囲ならClaude APIを挟まなくても動きます。Claudeを中継させる価値が出るのは次の条件に当てはまるときです。

  • 入力が自然文で、フォーマットを強制したくない(家族やスタッフにも使わせる場合など)
  • 表記ゆれの吸収が必要(「来週金曜まで」「7/31」「月末」を同じ期限フィールドに落とす)
  • 分類・要約・振り分けの判断が入る(問い合わせ内容から担当者を決める等)

逆に、入力者が自分だけで定型入力が苦にならないなら、GAS内の文字列処理で十分です。API利用料もレイテンシもゼロで済みます。

Claudeを挟む場合は、失敗時の逃げ道を必ず作ってください。AIの応答は稀に指定したJSONにならないことがあるため、パースに失敗したら原文をそのままシートの末尾列に記録して終わる設計にしておくと、メッセージを取りこぼしません。上のコードでも原文(original)を常に保存しているのはこのためです。

まとめ

LINEに送った文章をClaudeに処理させる構成は、GASのdoPostを中継にすればサーバーなしで実装できます。ただし実装よりも、curlテストが効かないリダイレクト仕様、デプロイのバージョン運用、応答メッセージとの競合、push送信の経路問題といった「GAS×LINE特有の詰まりどころ」の方が時間を奪います。この4つを先に知っておけば、骨格は半日で動くはずです。

graciautoでは、LINE公式アカウントとAI・スプレッドシートを組み合わせた業務自動化の設計・構築を支援しています。「うちの業務ならどこまで自動化できるか」といったご相談もお気軽にどうぞ。

無料相談はこちら

関連記事

2026.08.05

制作会社を変えるときサイトの何を引き継ぐか|受け取っておくべきものと、確認しないと動かなくなる箇所

2026.08.05

中小企業のAI導入は何から始めるか|先に自動化して効く工程と、AIに任せてはいけない工程

2026.08.04

ホームページのバックアップは何をどこまで取れば足りるか|「戻せる状態」の具体的な条件


お知らせ・ブログ一覧へ戻る