Google Chat 设置
将 Hermes Agent 作为机器人连接到 Google Chat。该集成使用 Cloud Pub/Sub 拉取订阅(pull subscriptions)处理入站事件,并使用 Chat REST API 发送出站消息。其易用性与 Slack Socket Mode 或 Telegram 长轮询相当:你的 Hermes 进程不需要公共 URL、隧道或 TLS 证书。它通过连接、认证并监听订阅来工作——这与 Telegram 机器人监听令牌的方式相同。
运行
hermes gateway setup并选择 Google Chat 以获取引导式 walkthrough。
Google Chat 是 Google Workspace 的一部分。你可以使用个人 Workspace(通过 Google 注册的 @yourdomain.com)或你拥有发布应用管理员权限的工作 Workspace 进行此集成。仅限 Gmail 的账户无法托管 Chat 应用。
概览
| 组件 | 值 |
|---|---|
| 库 | google-cloud-pubsub, google-api-python-client, google-auth |
| 入站传输 | Cloud Pub/Sub 拉取订阅(无公共端点) |
| 出站传输 | Chat REST API (chat.googleapis.com) |
| 身份验证 | 服务账号 JSON,在订阅上具有 roles/pubsub.subscriber 角色 |
| 用户标识 | Chat 资源名称 (users/{id}) + 电子邮件 |
步骤 1:创建或选择一个 GCP 项目
你需要一个 Google Cloud 项目来托管 Pub/Sub 主题。如果你还没有项目,请在 console.cloud.google.com 创建一个——个人账户获得的免费层级足以覆盖机器人流量。
记下项目 ID(例如 my-chat-bot-123)。你在后续的每个步骤中都会用到它。
步骤 2:启用两个 API
在控制台中,前往 APIs & Services → Library 并启用:
- Google Chat API
- Cloud Pub/Sub API
对于个人机器人产生的流量,这两者都是免费的。
步骤 3:创建服务账号
IAM & Admin → Service Accounts → Create Service Account。
- 名称:
hermes-chat-bot - 跳过“授予此服务账号访问项目的权限”步骤。你只需要特定订阅上的 IAM 权限——不要授予项目级别的 Pub/Sub 角色。
创建后,打开该服务账号,前往 Keys → Add Key → Create new key → JSON 并下载文件。将其保存在只有 Hermes 可以读取的位置(例如 ~/.hermes/google-chat-sa.json,执行 chmod 600)。
一个常见的错误是搜索特定的 Chat IAM 角色并在项目级别授予它。该角色并不存在。Chat 机器人的权限来自于安装在空间(space)中,而非来自 IAM。你的服务账号只需要在下一步创建的订阅上拥有 Pub/Sub 订阅者权限。
步骤 4:创建 Pub/Sub 主题和订阅
Pub/Sub → Topics → Create topic.
- 主题 ID:
hermes-chat-events - 其他所有选项保留默认值。
创建后,在主题的详情页面有一个 Subscriptions 标签页。创建一个订阅:
- 订阅 ID:
hermes-chat-events-sub - 交付类型:Pull
- 消息保留:7 days(以便积压消息在 hermes 重启后仍然存在)
- 其余保留默认值。
步骤 5:主题上的 IAM 绑定(关键)
在主题(而非订阅)上,添加一个 IAM 主体:
- 主体:
chat-api-push@system.gserviceaccount.com - 角色:
Pub/Sub Publisher
如果没有这一步,Google Chat 无法向你的主题发布事件,你的机器人将永远收不到任何消息。
步骤 6:订阅上的 IAM 绑定
在订阅上,将你自己的服务账号添加为主体:
- 主体:
hermes-chat-bot@<your-project>.iam.gserviceaccount.com - 角色:
Pub/Sub Subscriber
同时在该订阅上授予 Pub/Sub Viewer 权限——Hermes 在启动时会调用 subscription.get() 作为可达性检查。
步骤 7:配置 Chat 应用
前往 APIs & Services → Google Chat API → Configuration。
- App name:你希望用户看到的名称(“Hermes”是合理的选择)。
- Avatar URL:任何公开的 PNG 图片(Google 有一些默认图片)。
- Description:在应用目录中显示的一句简短描述。
- Functionality:启用 Receive 1:1 messages 和 Join spaces and group conversations。
- Connection settings:选择 Cloud Pub/Sub,输入主题名称
projects/<your-project>/topics/hermes-chat-events。 - Visibility:限制为你的工作区(或特定用户)——在测试期间不要发布给所有人。
保存。
步骤 8:在测试空间中安装机器人
在浏览器中打开 Google Chat。通过在 + New Chat 菜单中搜索应用名称,开始与你的应用进行私聊(DM)。第一次向其发送消息时,Google 会发送一个 ADDED_TO_SPACE 事件,Hermes 利用该事件缓存机器人自身的 users/{id} 以用于自我消息过滤。
步骤 9:配置 Hermes
将 Google Chat 部分添加到 ~/.hermes/.env:
# Required
GOOGLE_CHAT_PROJECT_ID=my-chat-bot-123
GOOGLE_CHAT_SUBSCRIPTION_NAME=projects/my-chat-bot-123/subscriptions/hermes-chat-events-sub
GOOGLE_CHAT_SERVICE_ACCOUNT_JSON=/home/you/.hermes/google-chat-sa.json
# Authorization — paste the emails of people allowed to talk to the bot
GOOGLE_CHAT_ALLOWED_USERS=you@yourdomain.com,coworker@yourdomain.com
# Optional
GOOGLE_CHAT_HOME_CHANNEL=spaces/AAAA... # default delivery destination for cron jobs
GOOGLE_CHAT_MAX_MESSAGES=1 # Pub/Sub FlowControl; 1 serializes commands per session
GOOGLE_CHAT_MAX_BYTES=16777216 # 16 MiB — cap on in-flight message bytes
项目 ID 也可以回退到 GOOGLE_CLOUD_PROJECT,服务账号路径可以回退到 GOOGLE_APPLICATION_CREDENTIALS——使用你喜欢的约定即可。
安装 Google Chat 适配器所需的依赖项(目前尚未发布 Hermes extra——直接安装它们):
pip install google-cloud-pubsub google-api-python-client google-auth google-auth-oauthlib
启动网关:
hermes gateway
你应该会看到类似如下的日志行:
[GoogleChat] Connected; project=my-chat-bot-123, subscription=<redacted>,
bot_user_id=users/XXXX, flow_control(msgs=1, bytes=16777216)
在测试私聊(DM)中发送“hola”。机器人会先发布一个“Hermes is thinking…”标记,然后就地编辑该消息,替换为实际响应——不会出现“消息已删除”的占位符。
格式与功能
Google Chat 仅支持有限的 Markdown 子集:
| 支持 | 不支持 |
|---|---|
*bold*、_italic_、~strike~、`code` | 标题、列表 |
| 通过 URL 嵌入图片 | 交互式 Card v2 按钮(本网关的 v1 版本) |
原生文件附件(在执行 /setup-files 后——参见步骤 10) | 原生语音笔记 / 圆形视频笔记 |
代理的系统提示中包含针对 Google Chat 的特定提示,使其了解这些限制并避免使用无法渲染的格式。
消息大小限制:每条消息 4000 个字符。较长的代理响应会自动拆分为多条消息。
线程支持:当用户在线程内回复时,Hermes 会检测 thread.name 并在同一线程中发布回复,因此每个线程拥有独立的 Hermes 会话。
步骤 10:原生附件交付(可选)
默认情况下,机器人可以发布文本、通过 URL 嵌入图片,以及用于音频/视频/文档的下载卡片。要交付原生 Chat 附件(即人类拖放文件时出现的相同文件小部件),每位用户需通过每用户 OAuth 流程对机器人进行一次授权。
为何需要单独的流程
Google Chat 的 media.upload 端点明确拒绝服务账号认证:
此方法不支持使用服务账号进行应用认证。请使用用户账号进行认证。
没有任何 IAM 角色或范围可以解决此问题。该端点仅接受用户凭据。因此,机器人在上传文件时必须以用户身份行事——具体而言,是以请求文件的用户身份。
一次性设置(每个配置文件)
- 在同一 GCP 项目中,前往 APIs & Services → Credentials。
- Create credentials → OAuth client ID → Desktop app。
- 下载 JSON 文件。将其移动到运行 Hermes 的主机上。
- 向 Hermes 注册客户端(在希望限定范围的配置文件下运行):
# Default profile:
python -m plugins.platforms.google_chat.oauth \
--client-secret /path/to/client_secret.json
# A named profile gets its own separate registration:
hermes -p <profile> python -m plugins.platforms.google_chat.oauth \
--client-secret /path/to/client_secret.json
这会将客户端密钥写入活动配置文件的 Hermes 主目录(例如,默认配置文件为 ~/.hermes/google_chat_user_client_secret.json)。客户端密钥是配置文件范围的,不在配置文件间共享——每个配置文件注册自己的密钥。这是有意为之:配置文件是隔离的认证边界,因此两个配置文件可以指向不同的 Google OAuth 应用/账号。仅为需要 Google Chat 附件交付的每个配置文件注册一次。
每用户授权(在聊天中)
每位用户在其与机器人的私聊(DM)中运行一次该流程:
- 他们向机器人发送
/setup-files。机器人回复状态和下一步操作。 - 他们发送
/setup-files start。机器人回复一个 OAuth URL。 - 他们打开该 URL,点击 Allow,并观察浏览器无法加载
http://localhost:1/?...&code=...。这种失败是预期的——认证代码位于 URL 栏中。 - 他们复制失败的 URL(或仅复制
code=...值)并将其粘贴回聊天中,作为/setup-files <PASTED_URL>。机器人将其交换为刷新令牌。
令牌存储在 ~/.hermes/google_chat_user_tokens/<sanitized_email>.json。该用户后续在私聊中的文件请求将使用其令牌,因此机器人以其身份上传,消息出现在其空间中。
如需稍后撤销:/setup-files revoke 仅删除该用户的令牌。其他用户的令牌不受影响。
范围
该流程仅请求一个范围:chat.messages.create。这涵盖了 media.upload 和引用已上传 attachmentDataRef 的 messages.create。不包含 Drive,也不包含更广泛的 Chat 范围——这是有意遵循最小权限原则。
多用户行为
当提问者尚无每用户令牌时,机器人会回退到遗留的单用户令牌 ~/.hermes/google_chat_user_token.json(如果存在来自预多用户安装的令牌)。如果两者均不可用,机器人会发布明确的文本通知,告知提问者运行 /setup-files。
用户撤销仅清除其自己的槽位。来自某用户令牌的 401/403 错误仅驱逐该用户的缓存。用户之间不会相互干扰。
故障排除
发送“hola”后机器人保持沉默。
- 在控制台中检查 Pub/Sub 订阅是否有未投递的消息。如果有,说明 Hermes 未通过认证——验证
GOOGLE_CHAT_SERVICE_ACCOUNT_JSON并确保服务账号在订阅中被列为Pub/Sub Subscriber。 - 如果订阅中没有消息,说明 Google Chat 未发布消息。仔细检查主题上的 IAM 绑定:
chat-api-push@system.gserviceaccount.com必须具有Pub/Sub Publisher角色。 - 检查
hermes gateway日志中是否有[GoogleChat] Connected。如果看到[GoogleChat] Config validation failed,错误消息会告诉你需要修复哪个环境变量。
机器人回复了,但显示的是错误消息而非代理的答案。
检查日志中是否有 [GoogleChat] Pub/Sub stream died——如果反复出现,你的服务账号凭据可能已被轮换或订阅已被删除。尝试 10 次后,适配器会将自身标记为致命错误。
每条出站消息都出现“403 Forbidden”。
机器人已从空间中移除,或者你在 Chat API 控制台中撤消了它。
在空间中重新安装它(下一个 ADDED_TO_SPACE 事件将自动重新启用消息传递)。
“Rate limit hit”警告过多。
Chat API 的默认配额允许每个空间每分钟发送 60 条消息。如果你的代理生成了超过该限制的长流式响应,适配器会使用指数退避进行重试——但你仍然会看到用户可见的延迟。请考虑使用更简洁的响应或在 GCP 控制台中提高配额。
机器人持续发布“/setup-files”通知而不是文件。
提问者没有每用户 OAuth 令牌,且没有旧版回退机制。在他们的私聊(DM)中运行 /setup-files 并按照步骤 10 操作。交换完成后,下一次文件请求将原生上传,无需重启网关。
/setup-files start 显示“No client credentials stored.”
尚未为此配置文件执行一次性设置(客户端密钥是配置文件范围的,因此在一个配置文件下注册的密钥不会在另一个配置文件中可见)。从终端中,在网关使用的配置文件下运行它:
# Default profile:
python -m plugins.platforms.google_chat.oauth \
--client-secret /path/to/client_secret.json
# Named profile:
hermes -p <profile> python -m plugins.platforms.google_chat.oauth \
--client-secret /path/to/client_secret.json
然后再次发送 /setup-files start。
/setup-files <PASTED_URL> 显示“Token exchange failed.”
授权代码是一次性且短效的(通常只有几分钟)。发送 /setup-files start 以获取新的 URL 并重试。
安全说明
- 服务账号范围:适配器请求
chat.bot和pubsub范围。IAM 应是实际的执行机制——授予你的服务账号最小权限(订阅上的roles/pubsub.subscriber+roles/pubsub.viewer),而不是项目级或组织级的 Pub/Sub 角色。 - 附件下载保护:Hermes 仅将服务账号持有者令牌附加到主机与 Google 自有域名的简短允许列表匹配 URL(
googleapis.com、drive.google.com、lh[3-6].googleusercontent.com以及其他几个域名)。任何其他主机都会在 HTTP 请求之前被拒绝,以防止 SSRF 场景,即精心构造的事件可能将持有者令牌重定向到 GCE 元数据服务。 - 脱敏:服务账号电子邮件、订阅路径和主题路径会通过
agent/redact.py从日志输出中剥离。调试信封转储(GOOGLE_CHAT_DEBUG_RAW=1)通过相同的脱敏过滤器路由,并以 DEBUG 级别记录。 - 合规性:如果你计划将此机器人连接到受监管的工作区(任何具有数据驻留或 AI 治理策略的工作区),请在首次安装前获得批准。
- 用户 OAuth 范围:每用户附件流仅请求
chat.messages.create——这是涵盖media.upload以及后续messages.create的最小范围。令牌以纯 JSON 形式持久存储在~/.hermes/google_chat_user_tokens/<sanitized_email>.json(文件系统权限是保护机制——与服务账号密钥文件的模型相同)。每个令牌仅由一个用户拥有;撤消范围仅限于该用户。