文章建议在 Agent 工作流与测试邮箱之间加入轻量信号队列,为预期邮件定义匹配契约、有效期和证据。通过明确状态处理旧邮件、等待超时与临时错误,减少重复查询带来的工具调用消耗。
一个创建试用账户、然后等待 email 的 LLM Agent,看起来很简单。但在实际运行中,等待是两个系统之间的边界:Agent 决定做什么,邮件服务商决定信号何时出现。
如果没有设计好这条边界,Agent 往往会反复执行同一个查询,消耗工具调用次数,最后给出一个没什么帮助的诊断:“email 没收到”。我更倾向于把每一封预期收到的邮件,都视为一个带有契约、过期时间和证据的信号。
这里的想法不是再造一个 Agent 框架,而是在 workflow 和测试收件箱之间,加上一个小型信号队列。这个队列让状态清晰可见,也为不确定性带来的成本设定上限。
一个简单直接的流程大致如下:
crear_usuario()
repetir:
revisar_bandeja()
si hay_email: continuar()
这段伪代码很短,却把几个不同的问题混在了一起:
如果没有明确回答这些问题,每一次重试看起来都合情合理,累积起来却会变得昂贵。同时,也很难判断究竟是服务商出了问题、搜索过滤条件出了问题,还是 Agent 自身的指令出了问题。
一个有用的信号,需要有标识符和明确的成功条件。对于验证邮件,我会使用类似下面这样的记录:
{
"signal_id": "sig_01J9MAIL42",
"run_id": "run_20261020_01",
"kind": "verification_email",
"recipient": "test-user-42@example.invalid",
"expected_subject": "Confirma tu cuenta",
"status": "waiting",
"expires_at": "2026-10-20T02:35:00Z"
}
Agent 不需要读取整个收件箱。它只请求一个范围明确的操作:根据预期主题和时间窗口,查找信号 sig_01J9MAIL42。邮件适配器返回四种状态之一:waiting、found、expired 或 provider_error。
这种区分很重要。waiting 表示目前还没有证据;provider_error 表示这次查询的结果不可靠。如果两者都被归为“未找到”,Agent 就会学会在本该改变策略的时候继续重试。
对于使用隔离邮箱的测试,tempmailso 这样的服务可以作为外部适配器的备选,前提是团队在将其接入 pipeline 之前,审查它的服务条款、隐私政策和使用限制。服务商可以替换,信号契约则应保持稳定。
查询邮件的 worker 必须具备幂等性。如果 Agent 因为 timeout 而重复调用,不应该再创建一个信号,也不应该把第一份证据当成另一封邮件重新处理。
signal = queue.get(signal_id)
si signal.status en [found, expired]:
devolver signal.status
resultado = mailbox.search(
recipient=signal.recipient,
subject=signal.expected_subject,
after=signal.created_at
)
si resultado.matches:
queue.mark_found(signal_id, resultado.message_id)
si resultado.provider_error:
queue.record_error(signal_id, resultado.code)
si no:
queue.record_check(signal_id)
message_id 字段可以防止同一封 email 被重复计数。我还会保存每次查询的时间、匹配数量和简短的错误代码,但不会保存完整邮件正文。这些证据已经足够用于调试,也不会让日志变成个人数据仓库。
在搜索和技术讨论中,还会出现 tem email、temp org mail 这样的变体。它们可以用于分析真实查询,但不应该进入状态名称或匹配规则:它们只是文本线索,不是系统契约。
Agent 需要一个明确可见的预算。例如,最多查询收件箱十次,或者最多等待两分钟,以先达到的限制为准。每次查询都应该说明原因:首次检查、webhook 之后的等待检查,或从临时错误中恢复。
我不会采用过于激进的固定查询间隔。使用较短的 backoff,并设置上限,可以减轻服务商的压力。如果流程有 webhook 信号,Agent 就可以休眠,直到事件发生,再把查询作为兜底恢复手段。在这种设计下,简洁的工具使用契约有助于确保每次调用的输入和输出都可以验证。
队列还可以区分可恢复错误和终止状态:
waiting:暂时没有匹配项;按照预算继续等待。provider_error:使用 backoff 重试,并记录错误代码。expired:停止 workflow,生成诊断结果。found:将证据标识符传递给下一步。Agent 不应该仅仅因为之前的一次调用创建了用户,就凭空认定邮件已经到达。账户创建和 email 到达是两个不同的事件。
我会用一张场景表来测试这个设计,而不只是测试正常路径。每一行都应该说明预期状态、最大调用次数,以及最终保留的证据:
评估还应该检查 Agent 是否解释了下一步该做什么。expired 结果应该给出可执行的建议,而不是再说一次“检查收件箱”。对于 Kubernetes 场景,适合将这张测试矩阵与不会制造告警噪声的合成测试结合起来,避免外部延迟自动触发运维告警。
在连接邮件适配器之前,我会检查以下几点:
signal_id、run_id、过期时间和成功条件。waiting、provider_error 和 expired。这个设计的价值在于让契约足够小,而不是增加更多自主性。一个知道自己在等待什么信号、最多可以等多久,以及必须返回什么证据的 Agent,更容易评估,运行成本也低得多。收件箱会变,契约保持不变。
如需采取进一步措施,你可以考虑屏蔽此人和/或举报滥用行为。