
“发送消息”看起来只是一个普通 Tool。真正接进 Agent 以后它和search_docs完全不是一个风险等级因为它会在外部世界留下不可逆结果收件人会看到消息群聊可能触发后续讨论错误发送甚至可能泄露客户信息。OpenAI 8 月 20 日上线 Apple Messages 插件时默认行为就很值得研究ChatGPT Work 和 Codex 可以读取、搜索 Messages 对话也可以准备或发送 iMessage、SMS 和 RCS默认发送前需要用户批准消息内容和收件人。Business 管理员还可以通过 Computer Use 控制禁用这项能力。这里最关键的不是“多了一个 Messages 插件”而是一个非常具体的安全边界用户批准的不是 send_message 而应该是 把这段确定内容 发送给这组确定收件人如果审批只绑定 Tool NameAgent 在审批后修改收件人或正文整个批准机制就失去意义。一个很危险的错误实现很多 Agent Framework 的审批对象只有publicrecordApproval(StringtoolName,booleanapproved){}运行流程Agent我要调用 send_message ↓ 用户批准 ↓ Agent 继续生成参数 ↓ send_message( recipients ?, body ? )问题就在这里。用户点批准时根本不知道最终参数。如果模型下一步把recipient 张三变成recipient 全公司群系统仍然认为approval true从审计上看“有审批”业务上却完全不是同一件事。Approval必须绑定Action Snapshot我更建议先让 Agent 生成完整 Proposed ActionpublicrecordMessageAction(ListStringrecipients,Stringbody,ListStringattachmentIds,MessageChannelchannel){}然后做标准化publicStringcanonicalize(MessageActionaction){returnJsonCanonicalizer.write(Map.of(recipients,action.recipients().stream().sorted().toList(),body,action.body(),attachments,action.attachmentIds().stream().sorted().toList(),channel,action.channel()));}计算 HashStringactionHashsha256(canonicalize(action));审批记录publicrecordApprovalRecord(StringapprovalId,StringrunId,StringstepId,StringactionHash,StringapprovedBy,InstantapprovedAt,InstantexpiresAt){}真正执行前再次计算current_action_hash必须完全等于批准时的approved_action_hash否则重新审批这一步能挡住大量“审批后参数漂移”。收件人为什么要单独高亮消息内容很长用户很容易只扫一眼正文。真正最危险的是发送给谁所以审批 UI 我不会简单放一个 JSON{recipients:[...],body:...}而是强制拆成发送渠道 Apple Messages 收件人 张三 86xxxxxxxxxxx 消息 …… 附件 customer-report.pdf 风险 外部发送如果收件人数 13 位收件人继续展开全部名单。如果超过阈值例如 10直接升级风险等级BULK_SEND需要更强审批。收件人必须经过Identity Resolution模型说发给老王不能直接让 Tool 自己猜。应该先解析“老王” ↓ Google Contacts / 企业通讯录 ↓ 候选联系人如果有多个王强 王伟 王磊必须要求澄清。不能让 LLM 自己挑一个“最像的”。可以定义publicrecordRecipientResolution(Stringquery,ListResolvedRecipientcandidates,ResolutionStatusstatus){}只有EXACT或者用户明确选择以后才能进入审批。手机号和联系人ID要冻结审批时如果显示张三但执行时 Tool 又重新查询通讯录联系人信息可能已经变化。更稳的是审批对象保存contact_id resolved_address例如{contact_id:contact-912,display_name:张三,address:86138xxxx1234}真正执行使用批准时冻结的地址。群聊比单聊更危险对于已有群聊Agent 最好引用conversation_id而不是重新根据一批号码创建新会话。否则这两件事向“项目A群”回复和重新创建一个包含项目A成员的新群用户体验和副作用都不同。Message Action 应该区分publicsealedinterfaceMessageDestinationpermitsDirectRecipient,ExistingConversation,NewGroup{}NewGroup默认进入更高风险等级。Persistent Approval为什么危险OpenAI 的发布说明特别提醒 Apple Messages 插件存在 persistent-approval 风险并提供撤销说明。这个问题在任何 Tool 都一样。如果用户第一次点“以后都允许发送”平台很容易把一次具体授权升级成永久 send_message 权限但消息发送高度依赖上下文。我更建议持久授权只覆盖低风险准备动作例如read_messages search_messages draft_message真正的send_message仍然按 Action Snapshot 做 Just-in-time Approval。如果一定要做免审批发送必须把边界写死例如自动通知 Agent每天17:30 向固定内部群 发送固定模板日报可以创建 Standing Delegationagent:daily-report-agentcapability:messages.sendconversation_id:team-report-groupschedule:17:30template:daily-report-v4max_messages_per_day:1expires_at:2026-09-30只允许固定群 固定用途 固定频率 固定模板族而不是以后这个 Agent 发消息都不用问我一个必须区分的状态PREPARED和SENTAgent 很容易出现模型生成了正文然后 UI 显示消息完成实际上还没有发送。我会把状态明确拆成publicenumMessageExecutionStatus{DRAFTED,WAITING_APPROVAL,APPROVED,SENDING,SENT,FAILED,UNKNOWN}尤其是UNKNOWN非常重要。为什么发送消息也会出现UNKNOWN请求Agent → Apple Messages Tool → 系统实际发送成功但返回途中网络断了。Agent 只看到timeout如果直接 Retry用户收到两条完全一样的消息这就是典型的“副作用已成功调用方不知道”。所以发送 Tool 应该带idempotency_key如果底层不支持幂等就在 Gateway 层维护发送 Ledger。publicrecordMessageSendLedger(StringsendId,StringactionHash,StringidempotencyKey,MessageExecutionStatusstatus,StringproviderMessageId,InstantcreatedAt){}Retry 前先查这次 Action 是否已经有 SENT 记录Approval和Idempotency要绑定同一个Action Hash这样用户批准的是 A 发送记录也是 A审计链可以完整闭环。Proposed Action ↓ Action Hash ↓ Approval ↓ Execution ↓ Provider Message ID ↓ Receipt发送前还要做一次DLP检查如果正文包含身份证 客户手机号 内部Token 财务数字即使用户点了批准也未必允许发送到外部号码。所以 Policy Engine 应该在 Approval 和 Execution 两个阶段都检查Data Classification Destination Trust例如if(payload.dataClass()DataClass.RESTRICTEDdestination.isExternal()){returnDecision.DENY;}不能把所有安全责任推给审批用户。附件必须独立校验正文批准不代表附件批准。如果 Agent 在审批后新增customer-list.xlsxAction Hash 应变化审批自动失效。附件最好记录artifact_id content_hash classification而不是文件路径。一个完整消息执行链User Intent ↓ Recipient Resolution ↓ Draft ↓ DLP / Policy ↓ Action Snapshot ↓ Action Hash ↓ Approval ↓ Execution Grant ↓ Send ↓ Provider Receipt ↓ Ledger这里真正不可省的不是 LLM。而是Action Snapshot Approval Binding Idempotency Audit最少应该测试这8种情况1. 审批后正文变化 2. 审批后收件人变化 3. 同名联系人歧义 4. 群聊和新建群混淆 5. 发送成功但响应超时 6. 附件审批后被替换 7. Restricted数据发送到外部 8. Persistent Approval被撤销这8个 Case 如果没测send_message还不适合交给自主 Agent。消息发送这类 Tool 很容易让人误以为只是“多一个插件”。实际上它代表 Agent 从生成内容跨到了代表用户影响别人所以审批不能只绑定 Tool Name。必须绑定收件人 正文 附件 渠道 动作版本OpenAI 这次 Apple Messages 默认在发送前确认消息和收件人我认为这个边界是对的。生产 Agent 再往前走一步就应该把这个默认交互固化成真正可审计的Action-bound Approval否则“用户批准过”很容易成为一个看似安全、实际上无法证明用户批准了什么的假保障。