Envoy
Envoy
Envoy 是面向会话实例(WhatsApp、Telegram、电子邮件、Slack、Discord 或微信)外部发送者的 Agent 角色。它声明在 src/agent/envoy/+<id>.envoy.ts 中,并在每个 envoy 指向它的实例上作答。
// src/agent/envoy/+field_ops_whatsapp.envoy.ts (templates/field-operations, excerpt)
import { envoy } from '@norbital-ai/bolt';
export default envoy({
name: 'Norbital',
audience: 'private',
policies: ['field_ops_whatsapp'],
groupMessages: 'mention_or_reply',
delegation: 'disabled',
triage: {},
task: 'You answer questions about job assignments on WhatsApp and keep them up to date from what people report. …'
});
// src/+workspace.ts (excerpt): the WhatsApp instance it answers on
connections: {
field_ops_whatsapp: { integration: 'whatsapp', envoy: 'field_ops_whatsapp' }
} 声明
name——人们对它的称呼:提到这个名字的消息,无论是否提及,都视为发给它audience——'public'回答任何人;'private'(私有)只回答其标识已登记在成员资料上的发送者(WhatsApp 为手机号码,邮件为邮箱地址,Telegram 为 Telegram 账号),并向其他人发送一条固定的“无法识别”回复policies——必填且不能为空:每个轮次的权限来源,也是其限流的来源groupMessages——'mention_or_reply'(默认)、'disabled'或'all'delegation——'enabled'允许它在相同权限下把工作交给子 Agenttriage——false表示不使用分流;否则为{ scope?: 'dm' | 'group' | 'all' },默认'all'。主机提供 AI 时,分流决定回复、等待或将群消息保留为环境消息;提及与回复绕过分流dm——已关联成员的私信:'union'(默认)合并其自身权限,'envoy'只保留 Envoy 的权限task——在简报之上的长期指令,从不规定谁可以做什么
轮次持有谁的权限
- 来自已关联成员的发送者的私信,在 Envoy 的
policies与该成员自身权限的合并下运行;设为dm: 'envoy'时,只依据 Envoy 的policies运行,其操作数绑定到该成员,且从不以管理员身份运行。来自未关联发送者的私信,在公开 Envoy 上只依据 Envoy 的policies运行,在私有 Envoy 上不被接纳:只收到一条固定的“无法识别”回复,不运行回合。管理员在“设置 → 成员”中设置成员的手机号码与 Telegram 账号;没有注册流程。 - 群组轮次只依据 Envoy 的
policies运行,无论消息由谁发出;发送者会写在消息头部,因此模型知道是谁提问。 - Envoy 本身永远不是管理员,它的限流始终来自它自己的策略。
// src/access_control/policy/+field_ops_whatsapp.policy.ts (excerpt)
import { policy } from '@norbital-ai/bolt';
export default policy({
grants: {
job_assignments: {
read: true,
update: {
fields: ['status', 'completed_at', 'summary', 'photo_evidence', 'communication_logs']
}
},
photo_evidence: {
create: { fields: ['job_assignment_id', 'photo', 'source'] }
},
communication_logs: {
create: { fields: ['job_assignment_id', 'message', 'sent_at', 'sender', 'source_message_id'] }
},
sys_user: { read: true, fields: ['id', 'name'] }
},
limits: {
act: '60/min',
'envoys.receive': [
{ rate: '30/min', per: 'sender' },
{ rate: '300/min', per: 'subject' }
]
}
}); 群组
每个群聊对应一个会话。使用 mention_or_reply 时,提及或回复会直接启动轮次。若分流涵盖群组,其余消息可进入分流,由 Agent 决定回复、等待或忽略。使用 triage: false 或 triage: { scope: 'dm' } 时,未寻址的群消息保留为环境消息——存入历史并可用 read_messages 读取,不启动轮次。groupMessages: 'disabled' 禁用群组轮次及其分流。轮次进行中被接纳的消息在下一步并入当前轮次。邮件始终视为已寻址,且绕过分流。
会话中的写入
会话中没有确认卡片,因此 Envoy 的写入会直接执行,每个结果都作为回执附加在回复后;挂起的写入会报告为等待审批。入站附件是一个文件,Envoy 可以用 read_attachment 读取,并存入某个 file 字段。
在哪里作答
Envoy 由实例部署,而不是由它自己的声明部署:+workspace.ts 中的某个连接指定它(connections: { field_ops_whatsapp: { integration: 'whatsapp', envoy: 'field_ops_whatsapp' } }),或者管理员在集成的配置选项卡中选择 Envoy。一个实例至多部署一个 Envoy;同一角色可在多个实例上作答,每个实例在助理渠道下有独立卡片。暂停某个实例只让 Envoy 停止在该账号上作答。个人实例(某位成员拥有的实例)从不部署 Envoy。
文件名提供角色 id(field_ops_whatsapp),name: 'Norbital' 提供显示名称,连接槽位提供实例 id。它们可以同名,但角色与实例各自独立。将角色、策略和连接一起发布到工作区版本。具名实例由源码创建,在运行时配对。sys_integration.envoy 选择角色,sys_integration.active 控制是否运行。凭据与连接状态属于该实例。种子数据不得包含具名实例、Envoy 部署或凭据。参见集成。
在工作区中连接与配置
- 以工作区管理员身份登录,打开更多 → 系统 → 集成(
/settings/integrations)。在助理渠道下找到声明的显示名称。现场运营显示 Norbital · 私有,以及对应现有field_ops_whatsapp连接的 WhatsApp 卡片。已连接时使用管理;否则先确认导入起点,再按下方说明配对。角色尚无连接时,连接操作会创建工作区实例并为它指定该角色。 - 若提供导入起点,在点击连接前选择:从现在开始保存主机当前时刻;从指定日期开始从指定 IANA 时区中开始日期的午夜起算;全部可用历史记录允许导入提供商能够提供的历史。设置时间界限时,更早的消息及没有来源时间戳的消息会跳过。请确认显示的选项:现有连接保留已存设置,未保存时使用全部可用历史记录。连接后可在管理 → 连接 → 导入起点 → 应用导入起点中修改。修改影响后续导入,已存消息仍保留。
- 按照提供商的设置说明操作。若需选择提供商,选择非官方(WhatsApp 网页版)。使用专用的 WhatsApp 账号;WhatsApp 可能限制其判定为自动化的账号。若表单显示手机号码(可选),留空后点击连接以获取二维码,或填入国家代码在前的纯数字号码后点击连接以获取配对码。在手机上打开 WhatsApp → 设置 → 已关联的设备 → 关联新设备,扫描二维码或输入配对码。二维码已显示时,改用手机号码可打开代码配对表单。等待已连接,确认显示的账号正确。
- 点击助理渠道卡片上的管理,打开详情面板,若提供连接 → 测试连接则运行测试。WhatsApp 网页版会向已连接账号本身发送测试消息,在该手机的“向自己发送消息”中检查。这验证传输投递;下方的入站报告用于验证成员准入、Agent 与记录写入。
- 在管理 → 配置中确认 Envoy 为 Norbital 且启用已打开,修改后点击保存。模板默认指定该角色;运行时修改会保留。现有连接未配对或已暂停时,仍可使用管理。通过该渠道卡片管理助理连接;其他连接列出其余外部账号,工作区未声明助理角色时,该区标题为连接。工作区会话实例没有成员所有者。只有集成定义声明字段时才显示额外配置字段;现场运营使用内置 WhatsApp 定义,无须额外配置值。配对密钥填入提供商设置,不填入配置 JSON。
- 在更多 → 设置 → 人员中,为每位发送者的有效工作区账号设置带国家代码的手机号码。为承包商分配 Contractor 团队以获得应用访问权限。私有 WhatsApp Envoy 通过号码匹配成员账号;WhatsApp 显示名与消息正文不能证明身份。没有单独的 Envoy 注册链接。
主机提供的其他会话渠道使用相同的实例设置。Telegram 机器人要求填写 @BotFather → /newbot 获得的令牌;主机自动注册 Webhook,测试时须先给机器人发送消息并提供聊天 id。邮件使用主机提供的邮箱服务商:完成 Google 登录窗口,或按所选服务商的表单操作。私有角色分别匹配成员的 Telegram 账号或邮箱地址。没有成员标识的渠道(包括内置 Slack、Discord 和微信会话)无法向私有 Envoy 接纳发送者。更换传输方式还需要适合该渠道的任务与策略;现场运营的任务明确描述 WhatsApp。
验证现场运营流程
示例策略可读取所有任务分配,并为任意任务更新状态、完成信息、摘要,以及新增证据和通信记录。它并不限于发送者被分配的任务。Envoy 策略不授予任务创建、删除、重新分配、工作单修改、证据或日志读取,以及可疑活动审查访问权限。已关联成员的私信默认还合并其自身授权;群组轮次只使用 Envoy 策略。如需其他边界,请修改源码策略与 dm 设置。
- 由控制员创建并分配一个现有任务。使用另一个已关联的承包商号码,向连接的 WhatsApp 账号发送私信,明确现场与工作,例如:“[现场]的扶手已安装。工作完成,照片见附件。”附上一张工作照片。若匹配多个任务,Envoy 应询问具体是哪一个。它不会根据报告创建工作单。
- Envoy 搜索任务分配并提交一次
job_assignments.update:进度与摘要、每张照片对应的嵌套photo_evidence.create,以及报告正文对应的嵌套communication_logs.create。它在更新成功后确认。在控制员或承包商应用中打开该任务,核对完成状态、摘要、照片与通信记录。 - 每张照片的来源记录 WhatsApp 会话、消息、附件、发送者与时间,每条通信记录保存来源消息 id。照片检查与可疑活动审查在归档后独立运行。请以控制员身份检查这些结果;Envoy 写入成功的回执不代表证据已通过审查。
- 用只读问题检查准入:已关联号码可询问现有任务;未知号码的私信收到固定的“无法识别”回复,不运行模型。在包含已连接账号的群组中,已关联发送者的提及或回复会直接启动轮次。源码中的
triage: {}也涵盖已关联发送者未寻址的群消息,Agent 可回复、等待或将其保留为环境消息。若每个群组轮次都必须由提及或回复触发,将源码改为triage: false或triage: { scope: 'dm' }。
暂停、重新连接与排查
- 暂停:打开助理渠道 → 管理 → 配置,关闭启用并保存。渠道卡片显示已暂停,Envoy 停止作答。再次打开管理,重新启用并保存即可使用已存配对恢复运行。
- 连接选项卡中的重新连接会再次打开提供商设置。断开连接会解除外部账号配对并删除已存凭据。在助理渠道卡片中,它还会删除在此创建的实例;
+workspace.ts的具名连接则保持未配对状态,可再次连接。只需暂时停止回复时使用暂停。 - 此主机不提供表示主机没有为该渠道提供可用的提供商。源码声明不会配置主机的提供商或 OAuth 应用。若角色未显示,请检查当前工作区版本是否包含 Envoy 与策略文件。配对失败时,按提供商显示的错误操作并重试设置。
- 已连接的 Envoy 不回复时,检查启用状态、所选 Envoy、发送者有效的成员记录与号码、群组寻址及限流。打开渠道卡片的管理 → 消息 → 刷新,查看入站消息、出站投递状态与错误。传输测试通过并不证明入站消息具备更新记录的权限。
限流
Envoy 的策略包含 envoys.receive(按发送者与按 Envoy)与 envoys.unrecognised(默认每个发送者每 15 分钟一次“无法识别”回复),此外还有限制整个服务台的 act、read 与 agent 配额。