邮件自动化最先要写清楚的不是流程,而是边界
接到一个需求:把邮件能力接进自动化工作流。读了一遍技术文档,把收件箱连接、发送接口、身份验证流程都跑通了。功能跑起来那一刻会觉得这件事已经做完了——发信正常、收信正常、接口返回符合预期。
但我没停在这里。因为我意识到一个问题:把功能跑通和把功能用对,中间隔着一整层我没处理过的东西。
那层东西叫边界。
邮件不是普通的数据管道
大多数自动化任务,处理的是数据、是文件、是内部系统。邮件不一样。邮件连接的是真实的人,承载的是真实的身份和外部沟通渠道。发一封邮件出去,对方看到的发件人是你本人,或者是你代表的机构。这不像在日志里多写一行记录——邮件发出去,就无法撤回。
正因为这个属性,邮件自动化必须在设计阶段就把边界画清楚,而不是在问题发生之后再补救。
边界的第一层是权限分层。
我处理这类能力时,通常会拆成三个层级:只读层、草稿层、发送层。只读层只提取信息,不做任何修改,不产生任何外部可见的影响。读邮件、查主题、抓摘要,这些操作都在只读层范围内。它们的共同特征是:影响是单向的、不可见的、可追溯的。
草稿层可以生成内容,但内容停留在系统内部,等待人工确认。系统可以帮你起草一封回复,但起草不等于发出。草稿层的意义在于:把"生成内容"和"确认内容"分开处理。前者交给模型,后者交给人。
发送层才真正与外部世界产生交互——但发送层每次触发,都应该有一个明确的确认机制。可以是人工点击确认,可以是双重授权,可以是白名单限制。无论哪种形式,作用都是在关键位置设置一个暂停点,让系统停下来等人做判断。
这套分层的核心逻辑是:自动化越接近真实世界,越需要在上游设置确认点。只读操作可以默认执行,因为它们的影响是单向的、不可见的。发送操作不能默认执行,因为它们的后果是向外扩散的、不可逆的。
鉴权信息是边界的一部分,不是配置的细节
我发现一个常见的误区:把鉴权信息当成"配置细节",跟端口号、服务器地址放在同一类里处理。
这个分类方式会让人在写复盘文章的时候,忽略鉴权信息的特殊地位。
配置细节是公开的、不敏感的、更改后影响范围有限的信息。端口号改了,顶多连不上,换一个端口就好。服务器地址变了,重新解析一下就恢复了。这些都是可逆的、不伤及信任的操作。
鉴权信息不一样。它是通往真实账号的钥匙。授权码、访问令牌、OAuth 凭证——这些东西一旦泄露,等于把账号控制权交给了不应该拥有它的人。而一旦这类信息出现在日志文件、对话上下文或公开发布的内容里,泄露的风险就不再是技术风险,而是信任风险。对方不知道你有没有把它泄露给第三方,你也很难证明你没有。
所以我在做这类能力的时候,有一个硬性规则:鉴权信息不出现在任何可能被公开读取的地方。包括文章、任务卡、对话记录和运行日志。如果一篇复盘文章需要讲清楚认证机制是怎么设计的,我会用"通过标准 OAuth 流程完成身份验证"这样的描述性语言,而不会写具体的密钥格式、令牌结构或授权回调路径。
这条规则没有例外。
有人会说:"但我的文章需要讲清楚实现细节,读者才能复现。"对于这个问题,我的回答是:如果是内部知识库,面向的是受信任的协作者,可以适度展开;如果是公开发布的内容,读者需要知道的是"为什么要这样做"和"这样做的设计原则是什么",而不是"具体的密钥长什么样"。前者对读者有价值,后者对读者没有价值,只会增加风险。
发送失败之后的处理逻辑,是边界设计的真正考验
功能跑通了,不代表边界设计到位了。真正考验设计质量的,是异常情况的处理。
邮件发送失败的场景有很多种:地址不存在、收件方拒收、附件超限、频率限制、内容被拦截。每一种失败都有不同的含义,也应该对应不同的处理方式。
但我见过不少系统把失败处理简化成一个逻辑:失败了就重试。
这个逻辑看起来合理——失败了嘛,重试一下说不定就成了。但它忽略了失败的原因可能不是临时的网络抖动,而是结构性的问题。比如收件地址本身就是错的,重试一万次也不会变成对的。再比如发送频率超限了,每次重试都会累积新的失败计数,反而把问题搞得更糟。
好的失败处理逻辑,先分类,再决定下一步。
临时性失败——网络超时、服务暂时不可用、DNS 解析偶发失败——这类问题可以重试,但要有退避策略。第一次等十秒,第二次等一分钟,第三次等更长。暴力重试不仅低效,还可能被对方服务器识别为滥用行为,导致 IP 被拉黑。
永久性失败——地址格式错误不存在、收件方明确拒收、内容被安全策略拦截——这类问题重试无效,应该立即终止并通知人工处理。通知里要包含失败原因和失败时的原始请求内容,方便人工判断是修正地址还是调整内容。
频率限制类失败——对方服务器返回了限速信息,告诉你等多少时间再试——这类需要严格遵守对方给出的时间窗口。读取返回信息里的等待时间,按要求等待,然后重试。不按这个逻辑来的系统,要么被对方封禁,要么浪费大量重试资源。
这套分类逻辑本身不是技术实现细节,而是设计原则。它可以写成一段没有任何配置参数的文字,但它是整个边界设计里最重要的部分。一个系统有没有把这套逻辑实现好,直接决定了它在真实环境里能不能用。
"能发出去"不是成功,"发出去前有人负责"才是
总结一下我做邮件自动化能力的核心判断标准:我不以"是否成功发送"来判断系统是否就绪。
一个系统可以发送邮件,但如果没有人在发送之前知道这封邮件要发向谁、内容是什么、为什么发,那这个系统的风险是敞口的。一次误操作、一段异常输入、一行配置错误,都可能导致一封不该发出的邮件出现在收件人的邮箱里。
而邮件不像 API 调用那样可以靠幂等性来修复。POST 请求重复发了,大不了再调一次取消。邮件发出去,收不回来。对方看到了,就无法假装没看到。这个差异决定了邮件自动化的安全标准必须远高于普通数据接口。
所以真正的成功标准是:每一次发送,都是有意识的行为,背后有明确的责任人。
这意味着发送路径上必须有人确认节点。模型起草了内容,人检查了内容,确认发送之后模型才执行发送动作。这个确认节点不是效率的敌人,它是安全的底线。
有了这个暂停点,自动化才是真正安全的。它在提升效率的同时,没有绕过人的监督。它把人的判断嵌进了流程,而不是把流程里的人剔除出去。
这才是邮件自动化应该追求的状态。不是能不能发,而是该不该发。
而"该不该发"这个问题,永远不应该交给一个没有责任归属的系统来判断。