GASでChatGPT使っていたら$6,000の請求がきた！！！！！！！

# タイトル回収
OpenAIから＄6,000の請求が来た！！！！！！！！！！
8/13～8/24の間でこんなに！？
![スクリーンショット 2024-08-25 12.01.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3328485/36efdc5d-06e4-a064-3215-e88c25e80ec6.png)

66,000リクエスト！？
約十日だから、1日7,000弱のリクエスト数？？
なんじゃこりゃ！
![request.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3328485/c221b0eb-50db-0e30-f3e7-26052cbffb3d.jpeg)

OpenAIからの大量のメール！！！！！
![スクリーンショット 2024-08-25 12.10.59.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3328485/f6da1161-ae60-c033-2c08-dce18da00bb1.jpeg)





---
原因は、`GAS`でした

---

こんにちは。
大阪のITベンチャー [Sky Gird株式会社](https://skygrid.co.jp)で
AIエンジニアとして働く青栁と申します

まさか人生初のQiitaの投稿が
社内での失敗になるとは全く予想していなかった....💦

# 皆さんに伝えたいこと

タイトルに釣られて見に来てくれた人はある程度察しがついてると思いますが
伝えたいことはこれ

- SaaS系のAPIを利用するときはちゃんと挙動確認をせよ！！！！！
- GASの関数を使うときは更新頻度に気をつけろ！！！！！！！！
- 課金が発生するサービスに対して、異変に気づける環境を作ろう！！！！！！！！

です。

この先何が起こっていたかを
コード付きで解説していくので是非ともお付き合いください🙇

しょうもな！って思った方はブラウザバック推奨です


# 何が起こったのか

## 背景

GASを使うことになったきっかけ
- 弊社の営業チームが使用するデータはスプレを使っていた
    - 名刺の情報を溜め込んでおり、HPのリンクもあった
    - はじめはスプレにある `IMPORTXML`関数を使ってサイトのタイトルを取得していた
    - しかし
        - 取得した情報が役に立たない or そもそもエラーになる

というような感じで
なんかいい感じに一覧で会社情報まとめたい！という要望がありました。


<details ><summary>当時の会話</summary>

> **営業さん**
なんかいい感じに一覧で会社情報まとめたいねんけどどしたらいい？

**青栁**
そんなんめちゃ簡単ですやん！！AIにやらせたらいいですやん！
> **営業さん**
そうなん？？ほな任せたわ！

... 1日後 ...

**青栁**
できやした！サイトの要約なんかもしちゃって！名刺にない情報ものってるのでご活用くださいまし！（得意げ）

> **営業さん**:
お！ありがとう！はやいやん！

**青栁**
それほどでも！！
（実際はGPT使って30分ほどしかコーディングしてないけど！AIサイコー！）
とまぁ当時はちょっと浮かれてました。

</details>






## やったこと

### step.1　コーディング
まずは助けてChatGPT。
要件は決まっていたので
その内容をGPTに投げつけて下記のコードをスクリプトに貼り付けました。

```javascript
// javascript
/**
 * @customfunction
 */
function scrapeHTML(url) {
  // URLからHTMLを取得
  var response = UrlFetchApp.fetch(url);
  var html = response.getContentText();
  
  // Step 2: OpenAI APIのエンドポイントとAPIキーの設定
   var apiKey = "<実際はキーを入力>"
   var apiUrl = 'https://api.openai.com/v1/chat/completions';
   var prompt = "次に与える情報を要約してください。営業資料として使用するためわかりやすく簡潔に箇条書きでお願いします。日本語で必ずお願いします！: "
  
  // Step 3: APIリクエストの作成
  var payload = {
    "model": "gpt-4o", // 適切なモデルを選択
    "messages": [
        {"role": "user", "content": prompt + html,}
    ],
    "max_tokens": 300, // 要約の長さを制御
    "temperature": 0.1 // 出力の多様性を制御
  };

  var options = {
    'method': 'post',
    'contentType': 'application/json',
    'headers': {
      'Authorization': 'Bearer ' + apiKey
    },
    'payload': JSON.stringify(payload)
  };

  // Step 4: APIリクエストの送信
  var response = UrlFetchApp.fetch(apiUrl, options);
  var jsonResponse = JSON.parse(response.getContentText());
  
  // Step 5: 要約結果の取得と返却
  var summary = jsonResponse.choices[0].message.content;
  return summary;
}

```

これを`AppScript`に突っ込むだけ

:::note warn
この記事では詳しいスクリプトの設定方法は割愛します
:::

### step.2　スプレッドシートで関数を呼び出し

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3328485/49a0283e-1bca-0542-d125-503152e91486.png)

GASで命名した`scrapeHTML`をセルで呼びだし。
これだけ！

これによって`サイトの要約`にスクレイプしたサイト情報が自動で表現できるようになりました

:::note
これくらいであれば15分くらいで構築できるので、GAS+ OpenAI API自体はおすすめです❤️
:::



## 高額請求になった原因

**スプレの仕様、関数の更新頻度を確認していなかった**ことが原因だと考えています

:::note warn
今回の件は、ログからしっかりデバックできず、半分推測になってます
:::

推測の根拠は下記です

Geminiからの出力を抜粋

>この場合の更新頻度は、手動更新が基本となります。つまり、スプレッドシート上で scrapeHTML 関数を含むセルを再計算させる操作を行わない限り、要約結果は更新されません。
ただし、このコードには `UrlFetchApp.fetch(url)`が含まれています。これは外部の URL からデータを取得する処理であり、この部分が揮発性関数のように振る舞う可能性があります。つまり、スプレッドシートの再計算時に` UrlFetchApp.fetch(url)`が実行され、新しい HTML データを取得して要約を更新する可能性があります。
しかし、`UrlFetchApp.fetch(url)`が毎回新しい HTML データを取得するとは限りません。キャッシュなどの影響で、前回取得した HTML データが再利用される場合もあります。

[Geminiの回答 全文はこちら](https://g.co/gemini/share/8778eaa5eaa5)

Qiitaでも注意を促す記事を発見しました

https://qiita.com/uramotot/items/19b8baa2fb9c4f4109c7

Geminiの回答と、記事を読み合わせて
状況証拠を鑑みると
**関数の更新頻度を制御していなかった**ことが原因になりそうです


## 対策

### 1. 常用するSaaSサービスの料金監視

弊社 SkyGridはSlackを使用しています。
そこで、Mailをフックに料金アラートを任意のSlackチャンネルへ送信されるように設定をしました。
チャンネルには開発者が複数人所属しているためあとは、メールが届くタイミングを適切に設定する必要があります。

OpenAIに関しては下記の画像の箇所から設定できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3328485/4e0ed71d-e0bf-c6f0-733a-f930a83a5526.png)



### 2. コードレビュー、設計レビューをしてもらう
今回は僕と、営業さんとの間だけでスピード感重視で
設計したため誰からもフィードバックをもらうことなくローンチしました。
思い返すとここがだいぶ不味かったなと思います。

GASだと侮っていた自分がいたなと反省しています。

ただ毎度レビューしてもらうってのは工数もかかってしまうので
自分用のレビューボットをローカルにおいても面白そうだなと思いました


## まとめ

今回は失敗談をまとめました。
GASとOpenAIのAPIの組み合わせで高額請求になりましたが
結果的にはエンジニアとして非常に大切な経験をできたと思います。

皆さんはぜひお気をつけを。