Gmail
Gmail Connectorは、メールボックスのメッセージ、スレッド、ラベル、下書き、添付ファイルを読み、メッセージのラベルを変えたりゴミ箱に移したりし、下書きを作ってコンテンツを添付し、送信、返信、転送を行います。プログラムは顧客が書いたものを読み、LLMに分類や返信の下書きをさせて、それを送れます。どのメッセージが送られるかを人が正確に確認したあとで、です。
読み取り元
gmail.messages— メールボックスのメッセージ。id、thread_id、from、to、cc、subject、date、snippet、body_text(プレーンテキストの本文)、labels(配列)、has_attachments、history_id。キーはid。日付、ラベル、送信者、件名で絞り込んでください。Connectorがメールボックス全体を読むことはありません。gmail.threads— スレッド。id、subject、message_count、last_message_date、labels。キーはid。gmail.labels— メールボックスのラベル。id、name、type。キーはid。gmail.drafts— 下書き。id、message_id、to、subject、body_text、updated。キーはid。gmail.attachments— 添付ファイルごとに1レコード。resource、version、message_id、attachment_id、filename、media_type、size。キーは(message_id、attachment_id)。これは一覧で、何もダウンロードしません。gmail.contents— 添付ファイルの内容。resource、version、media_type、そしてcontent_refのcontent。gmail.attachmentsからresourceとversionでJOINしてください。ConnectorはJOINで指定された添付ファイルだけをダウンロードします。
型
| Gmail | ActQL | 備考 |
|---|---|---|
id、threadId、historyId、attachmentId | string | 安定した識別子 |
RFC 2822の Date ヘッダー、internalDate | timestamp | UTCに変換して読む。ヘッダーが不正なら internalDate を使う |
From、To、Cc | string、array<string> | ヘッダーに書かれたとおりのアドレス。to と cc は配列 |
labelIds | array<string> | ラベルの名前ではなくid。名前は gmail.labels とJOINして取る |
| 本文 | string(body_text) | プレーンテキストの部分。HTMLだけのメールはテキストに変換する |
| 添付ファイルのバイト列 | media_type 付きの content_ref | gmail.contents を通して読む |
sizeEstimate、添付ファイルの size | int64 |
書き込みは同じ規則で逆方向に変換します。
Effectと操作
レコードのEffectはキーで書きます。
- update —
EFFECT UPDATE gmail.messages ID id SET (labels = …)は、ラベルを追加または削除します。labelsには変更後の完全な集合を書きます。 - delete —
EFFECT DELETE gmail.messages ID idは、メッセージをゴミ箱に移します。そこではメールボックスのゴミ箱のポリシーに従います。完全に削除するには、下のgmail.purgeを使います。 - insert —
EFFECT INSERT gmail.drafts (to, subject, body) VALUES (…)は、送信せずに下書きを作ります。
id はGmailのメッセージidで、メールボックスの中のちょうど1つのメッセージを識別します。
テーブルとコンテンツ:
- attach —
EFFECT STORE gmail.attachments SET (draft = draft_id, filename = …, content = content)は、レコードのcontent(SERIALIZEまたはcontentsの読み取り元から来たもの)を下書きに添付します。
操作。送信はレコードの書き込みではなく操作です。
- send —
EFFECT COMMAND gmail.send ID key INPUT (to = …, subject = …, body = …)。 - reply —
EFFECT COMMAND gmail.reply ID key INPUT (in_reply_to = message_id, body = …)は、件名と宛先を引き継いでスレッドの中で返信します。 - forward —
EFFECT COMMAND gmail.forward ID key INPUT (message = message_id, to = …[, body = …])。 - purge —
EFFECT COMMAND gmail.purge TARGET message_id ID keyは、メッセージをゴミ箱を経由せずに完全に削除します。mail.google.comの完全なスコープが必要で、Gmailが削除を確認するとappliedになります。
送信、返信、転送は接続のメールボックスに対する操作なので TARGET はありません。完全削除はメッセージを TARGET に指定します。ID はレコードのキーで、プレビューとステータスで各送信を識別します。宛先ではありません。Gmailが受け付けた送信は accepted で終わります。Gmailがメッセージを配送のために受け付けたということで、相手に届いたことや読まれたことを意味しません。Effectが適用されるまで何も送られません。
セットアップ
"gmail": {
"connector": "gmail",
"mailbox": "support@acme.com",
"credentials": {
"service_account": {"env": "GOOGLE_SERVICE_ACCOUNT_JSON"},
"delegate": "support@acme.com"
}
}Google Workspaceのメールボックスでは、サービスアカウントに gmail.readonly(読み取り)、gmail.modify(ラベル、ゴミ箱、下書き)、gmail.send(送信)のスコープでドメイン全体の委任を付与し、delegate にそのメールボックスを指定します。単一のアカウントでは、同じスコープを持つOAuthのリフレッシュトークンを代わりに渡せます。ActQL Cloudでは、ホスト型のOAuthでサービスアカウントなしにメールボックスにサインインできます。要求するスコープは同じです。
Capabilityとプッシュダウン
| 演算 | プッシュダウンされる条件 |
|---|---|
| 列の選択 | 常に。Connectorは、プログラムの列に必要なメッセージの部分だけを要求する(本文の列を使わなければメタデータだけ) |
WHERE | 述語が from、to、subject、labels、date、has_attachments に対する =、IN、<、>、LIKE 'word%' のとき。Gmailの検索構文で正確に表せる範囲。本文に対する述語とそれ以外はHostで実行し、混在する述語は分割して残りをHostが評価する |
ORDER BY、LIMIT | ORDER BY date DESC のみ。Gmail自身の並び順。ほかの並び順はHostで実行する |
AGGREGATE、JOIN | されない。どちらもHostで実行する |
explain は、読み取りをSite connector:gmail、Engine Gmail search で表示します。
プレビューと突き合わせ
プレビュー。 ラベルの変更とゴミ箱への移動は full_diff です。Connectorはメッセージを読み、現在のラベルと history_id を記録し、プレビューには現在のラベルの集合と新しい集合が出ます。リクエストにはその history_id が付きます。その間にラベルが変わったメッセージは、項目が failed(conflict)になります。下書きの作成と添付は targeted です(下書きの宛先、件名、本文と、添付ファイルの名前、メディアタイプ、サイズ)。送信、返信、転送も targeted です。プレビューには送られるメッセージがすべて並びます。宛先、件名、本文です。承認されるまで何も送られません。
突き合わせ。 送信は冪等ではありません。送信前に、Connectorはリクエストのマーカーをメッセージの X-ActQL-Request ヘッダーに入れます。応答が失われた場合は、送信済みラベルの中からそのヘッダーを探して突き合わせます。宛先と本文が同じでもマーカーのない送信済みメッセージは、証跡になりません。ラベルの変更とゴミ箱への移動にはGmailが保持するマーカーがないので、応答が失われた項目は unknown のままです。history_id が進んでいることや新しいラベルの集合だけでは証跡になりません。下書きの作成は、下書きの一覧からヘッダーのマーカーを探して突き合わせます。どれでも結果が確定しなければ、項目は unknown のままで、再送はしません。
上限と費用
- Gmailはアカウントごとに1日の送信数を制限しています(新しいアカウントではさらに少なくなります)。超えそうなプログラムは、何も送らずに失敗します。
- APIにはユーザーごとの1秒あたりのクォータ単位があります。Connectorはその範囲内でペースを調整し、プログラムの件数の上限を超えそうな読み取りは、途中までの結果ではなくエラーで止まります。
- 添付ファイルはプログラムのバイト数の上限に数えられます。残りに収まらない添付ファイルは拒否され、切り詰められることはありません。メッセージそのものは、添付ファイルを含めてGmailが25 MBに制限しています。
- 本文はプレーンテキストとして読みます。HTMLだけのメールは変換され、書式とインライン画像は失われます。
例
直近1日の、まだ返信していないサポートメールです。
FROM gmail.threads
|> WHERE 'INBOX' IN UNNEST(labels) AND message_count = 1
AND last_message_date >= TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 1 DAY)
|> SELECT id, subject, last_message_date
|> ORDER BY last_message_date;それぞれにLLMで返信の下書きを作り、送信します。
FROM gmail.messages
|> WHERE 'INBOX' IN UNNEST(labels) AND 'UNANSWERED' IN UNNEST(labels)
|> INFER `model-task:support-reply.v1` INPUT (subject, body_text) AS d
|> WHERE d.outcome = 'ok' AND d.value.confidence = 'high'
|> EFFECT COMMAND gmail.reply ID id INPUT (in_reply_to = id, body = d.value.text);メールボックスのポリシーは、ふつう送信のすべてを人の判断に回します。そのためRunは、送られる返信をそのまま示してCheckpointで止まります。
顧客がメールで送ってきたPDFの請求書をLLMに渡します。
FROM gmail.attachments AS a
|> WHERE a.media_type = 'application/pdf'
|> JOIN gmail.contents AS c ON a.resource = c.resource AND a.version = c.version
|> INFER `model-task:invoice-fields.v1` INPUT (document = c.content) AS inv
|> WHERE inv.outcome = 'ok'
|> SELECT a.message_id, a.filename, inv.value.supplier, inv.value.total;Source: actql/actql/docs/connectors/gmail.md at d904416674e4 · Edit on GitHub