| 走 HTTP 的三个问题 | |
|---|---|
| 白绕一圈 | QA run 是 worker 进程里的 asyncio.Task —— 和 DB 在同一个进程。为了调同进程的一个函数,去序列化 + 走网络栈 + 再反序列化 |
| 把安全口子重新打开 | HTTP endpoint 必须从某处拿 user_id。前端那条是网关注入 header;MCP 走 HTTP 就只能自己伪造 header(丑且脆)或者加一个 user_id 参数 —— 后者正是我们一路在躲的东西 |
| 错误要翻两遍 | HTTP 状态码 → 异常 → MCP 结果。每翻一次就丢一层原因,最后又变成「查询失败」 |
两条路各自解决身份,然后调同一个函数。这才是「不重复造轮子」的正确形态 —— 复用查询逻辑,不是复用传输层。
而且 HTTP 那半边现在不用写 —— 前端还没有邮件列表页面。等真要做的时候再加 router,调的是同一个函数。
| 函数 | 参数 | 返回 |
|---|---|---|
list_mine |
user_id(closure)kind 可选 client/internal/unknownsince_days 可选limit 默认 20 |
索引,不含正文 id · 标题 · 发件人 · 时间 · 类别 · 客户名 · 是否已判定 |
read_mine |
user_id(closure)mail_id |
上面那些 + 正文(截断到 ~8KB) |
search_team |
q 关键词 / customer 客户名limit没有 user_id —— 本来就是全组织 |
同 list_mine + 是谁的邮箱 |
返回里刻意不给的两样:
| 不给 | 为什么 |
|---|---|
file_path | 给了就等于把文件系统还给 agent。它拿 id,我们内部查路径 |
| 列表里的正文 | 20 封正文直接撑爆上下文。大多数问题看标题+发件人就能答,要正文再单独取 |
search_team 现在写不了它的闸门条件是「只查已判定为工作的」:
WHERE triage_status = 'done' AND <triage 的判定结果> IN ('client','internal')
那个「triage 的判定结果」字段还不存在。表里现在只有 rule_kind(规则层判的)和 triage_status(pending/done),没有地方放 triage 的结论。
| 选项 | |
|---|---|
triage 覆写 rule_kind | 省一列,但丢掉「规则层说 unknown、triage 说 client」这个信息 —— 而那恰恰是将来衡量规则层准不准的唯一依据 |
加一列 triage_kind | 两个判断分开存。倾向这个,但这属于 triage 的设计,不该在这一步拍 |
这反过来印证了顺序:search_team 不是「先做也行」,是物理上依赖 triage 先定字段。
| 做什么 | 依赖 | |
|---|---|---|
| 1 | 定时取数 —— 半天的活,做完邮件就开始攒 | 无。现在就能做 |
| 2 | triage —— 判定 + 分类 + 定 triage_kind 字段 | 要有邮件样本 |
| 3 | mail_query.py + MCP(list_mine / read_mine) | 只依赖已有字段,其实随时能写 |
| 4 | search_team | 必须等 ② |
③ 技术上不卡,但现在写完 Mio 只会一直说「你没有邮件」 —— 你那 12 封全是通知,规则层全丢了,本地是空的。