使用 IRIS AI Hub 代理进行收件箱分类:工作原理及运行方法
本文介绍了“互操作性工作流中的AI代理”竞赛中的一份参赛作品。
iris-inbox-triage 项目展示了一个分拣代理,该代理将收到的电子邮件路由至 alert、draft_reply、archive 或 review。
本文将介绍该项目的运作原理、其功能以及如何在 IRIS AI Hub 上运行它。如果您是希望使用 IRIS AI 代理构建互操作性生产系统的开发者,请继续阅读。否则,您不妨观看这段视频演示:
我是一名自由开发者。我撰写技术文章,并从事小型自动化工作。我使用同一个邮箱处理所有与工作相关的邮件。此外,还有关于项目提案、主机服务和邮件列表的邮件。这就是我决定构建自己的邮箱监控工具的原因。
iris-inbox-triage 项目正是以我自己的收件箱监控工具为蓝本开发的。该项目包含:
- 一个用于接收来信的互操作性服务
- 一个 IRIS AI 代理,用于对收到的邮件进行分类
- 一个工作流任务,供人工审核内容不明确的邮件
iris-inbox-triage 项目作为开源软件发布。您可以在 GitHub 的works-on-my-box/iris-inbox-triage 代码库中找到该项目的源代码。该项目还可在Open Exchange上获取,并作为 IPM 软件包 iris-inbox-triage 提供。
它有什么作用?
该系统从 IMAP 邮箱接收电子邮件,并要求代理对每封邮件进行分类。代理会返回类似 alert 或 draft_reply 的分类结果。这些分类结果将转换为以下操作:
- 警报:向负责人触发警报
- 草稿:起草回复,供负责人编辑并发送
- 归档:将邮件标记为已归档
- 审核:利用“工作流收件箱”功能,为指定人员创建审核任务
某人在审核任务中的选择将作为先例。这将用于将来对类似消息进行分类处理。
决策、工具调用和回复草稿均作为行存储在 IRIS 表中。流程的每一步在“可视化追踪”视图中都对应一条消息。
邮件接收
IRIS 提供了一个入站 POP3 适配器,但没有 IMAP 适配器,也没有 %Net.IMAP 类。该项目在嵌入式 Python 中拥有自己的入站 IMAP 适配器。它使用 imaplib 模块、UNSEEN 搜索以及 BODY.PEEK 获取功能。
typ, data = conn.uid("search", None, "UNSEEN")
messages = []
for uid in data[0].split()[:max]:
typ, parts = conn.uid("fetch", uid, "(BODY.PEEK[])")
自定义适配器 Triage.Adapter.IMAPInbound 从收件箱接收电子邮件,并将其转发给其业务服务。
有一个名为 triage_mail.py 的 Python 文件,它使用 email 包将原始电子邮件解析为标头和正文部分。该代码处理了嵌套的多部分结构、quoted-printable 编码、base64 编码以及不同的字符集。 为避免触及 3.6 MB 的字符串长度限制,仅将选定的字段传递给 ObjectScript。
一旦服务处理完某条邮件,适配器会将该邮件标记为“已读”。该邮件不会从收件箱中移除。
代理
该代理在 IRIS AI Hub 上运行,属于名为 Triage.Agent 的业务操作,该操作会接收每封电子邮件对应的一个请求,并将判定结果返回给生产环境。Triage.Agent.Classifier 是该代理操作所使用的 %AI.Agent 子类。其声明式代码如下:
Class Triage.Agent.Classifier Extends %AI.Agent
{
Parameter PROVIDER = "gemini";
Parameter MODEL = "gemini-3.5-flash-lite";
Parameter APIKEY = "@{env:GEMINI_API_KEY}";
Parameter TOOLSETS = "Triage.Agent.Tools";
XData INSTRUCTIONS [ MimeType = text/markdown ]
工具 LookupContact 执行一个只读 SQL 查询。存在一个名为 Triage_Data.Contact 的表,其中包含已知联系人的列表。
工具 SenderHistory 执行一个只读 SQL 查询。存在一个名为 Triage_Data.Decision 的表,其中包含先前电子邮件的决策记录。
接下来是 search_precedents。该工具从使用 IRIS Vector Search 的 %AI.RAG.KnowledgeBase 中读取数据。知识库中的每封电子邮件都对应一个 384 维的向量。该向量是使用 FastEmbed 模型 all-MiniLM-L6-v2 生成的。
此外,还有一个名为 RecordDecision() 的工具,用于记录电子邮件的处理决策。
共有两项策略:OneDecision 允许每封电子邮件仅做出一个决策,而 Audit 则将每次工具调用记录到 Triage_Data.ToolCall 中。
<Policies>
<Authorization Class="Triage.Policy.OneDecision"/>
<Audit Class="Triage.Policy.Audit"/>
</Policies>
<Query Name="LookupContact"
Description="Find the sender in the known contacts by exact address or by domain. Returns kind (client, editor, platform, vendor, personal) and a note."
Arguments="address As %String, domain As %String"
MaxRows="5">
<![CDATA[
SELECT Address, Kind, Note
FROM Triage_Data.Contact
WHERE Address = LOWER(:address) OR MailDomain = LOWER(:domain)
]]>
</Query>
路由
类 Triage.Process.TriageBPL 展示了生产环境如何路由传入的电子邮件。您可以在 IRIS 管理门户中以流程图的形式查看该内容。

该流程首先向代理员征询裁决结果,然后记录该决定。 随后,根据裁决结果是 alert、draft_reply、archive 还是 review,流程会进行分支处理。
由于 Gemini API 的免费配额仅允许我每分钟调用约二十次,因此我在代理操作中添加了 20 秒的 RetryInterval。如果模型调用过程中发生瞬时错误,系统将等待至少 20 秒后再尝试重试。
如果 180 秒后仍失败,BPL 会将该消息标记为 review,并将其发送给人工进行手动分流。
警报消息将发送至 Triage.Alert,该服务会为该电子邮件存储一行记录,并可选地触发 webhook 调用,向所有者发送通知。
回复草稿将发送至另一个业务操作模块 Triage.Draft,该模块会请求代理 Triage.Agent.Writer 生成简短的回复文本。随后,系统将草稿通过邮件发送给所有者,供其编辑并发送。
归档消息仅被标记为已归档。如果分拣代理选择 review,该消息将进入一个工作流任务,该任务会显示在具有“分拣审核员”角色的用户的收件箱中。 该收件箱会显示包含主题、发件人、处理人员的裁决结果及理由,以及正文简短摘录的任务。

用户可以点击审核任务上的“接受”,然后选择“提醒”、“草稿”或“归档”。“ReviewedBy”和“ReviewAction”将存储在决策记录中处理员“操作”项的旁边。这些选择将作为未来电子邮件的先例。
项目运行
您需要:
- 来自InterSystems 早期访问计划的 AI Hub 早期访问版 IRIS
- 您在AI Studio账户中的 Google Gemini API 密钥
docker load -i iris-community-2027.1.0AI.109.0-docker.tar.gz
git clone https://github.com/works-on-my-box/iris-inbox-triage.git
cd iris-inbox-triage
cp .env.example .env # put your GEMINI_API_KEY here
docker compose up -d --build # IRIS with the production, plus a test mail server (GreenMail)
scripts/demo.sh # sends the nine samples over SMTP, waits for the agent, prints the results
有一个用于运行演示的脚本。它会通过 SMTP 发送九封示例电子邮件,然后输出智能代理做出的决策。
Expected for the nine samples: three alerts, three archived, two draft replies (model output can vary between runs),
and one review task: an unknown sender who claims an earlier call and asks for bank details. Complete it in the Workflow Inbox,
http://localhost:52773/csp/user/_DeepSee.UserPortal.Workflow.zen (Accept, then Alert, Draft or Archive), or with scripts/review.sh Archive.
Drafts were also mailed to owner@triage.local (POP3 localhost:3110, password owner).
Traces: http://localhost:52773/csp/sys/UtilHome.csp -> Interoperability -> View -> Messages
您还可以在自己的 AI Hub 实例上安装 IPM 软件包 iris-inbox-triage,并在 IRIS 环境中设置 GEMINI_API_KEY。首先安装 InterSystems Package Manager:
set r=##class(%Net.HttpRequest).%New(),r.Server="pm.community.intersystems.com",r.SSLConfiguration="ISC.FeatureTracker.SSL.Config" do r.Get("/packages/zpm/latest/installer"),$system.OBJ.LoadStream(r.HttpResponse.Data,"c")
zpm "enable -community"
zpm "install iris-inbox-triage"
do ##class(Ens.Director).StartProduction("Triage.Production")
代理的处理结果
应用程序运行后,有两种方法可以查看代理为您执行了哪些操作:
- 在管理门户(_SYSTEM / SYS)中,转至“互操作性”>“查看”>“消息”。
- 使用下文所示的 SQL 查询。
某封电子邮件的“可视化追踪”会显示从入站适配器接收该消息的服务(即 BPL),以及发送到代理的请求。它还会显示判定结果,包括其置信度评分、原因、令牌以及 LLM 处理所耗时间。

您可以使用以下这些 SQL 查询:
SELECT Sender, Category, Urgency, Action, Confidence, Status, ReviewedBy, ReviewAction, PromptVersion, Reason FROM Triage_Data.Decision
SELECT MessageId, ToolName, Arguments, DurationMs, Success FROM Triage_Data.ToolCall
SELECT Recipient, Subject, Status, Body FROM Triage_Data.Draft
PromptVersion 是代理指令的 CRC-32 哈希值。其设计理念在于:如果更改提示词,代理的响应可能会发生变化,因此将它们分组并跟踪其准确性是合理的。您可以在DPI-I-689 提案中了解有关可追溯性的更多信息。
结果与局限性
以下是我在应用程序的一次样本运行中观察到的结果:
- 有三条消息被标记为警报。
- 有三条被归档。
- 有两条消息收到了草稿回复。
- 有一条被提交审核。
早先的一次测试还表明,代理会将其自身对某条消息的先前决定作为发件人历史记录的一部分进行读取。而现在的说明中仅指出,只有 LookupContact 操作能确定发件人身份。
不同运行之间可能会存在一些差异。
总体而言,我认为这款应用对于任何想要构建自定义收件箱监控器或其他电子邮件相关应用程序的人来说,都是一个绝佳的起点。