Files
iAs/docs/architecture-split-design.md
wunianxiao af6cfdaa83 feat: 拆分 worker 架构并增强 assistant 安全保护
- 新增 daemon/channel/assistant/function/scheduler/logging worker 架构和 JetStream 消息协议

- 修复邮件通知误入 assistant、迁移 checksum、日志 subject 和工具调用无限循环风险

- 新增 assistant 工具轮次熔断、工具结果截断和提示词软收敛规则

- 补充邮件推送关键字、可信发件人配置、升级日志和验证测试
2026-07-10 00:41:36 +08:00

16 KiB
Raw Permalink Blame History

iAs 进程拆分设计方案

1. 设计结论

当前 ias-service::run_service() 同时承载微信长轮询、邮件轮询、AI 队列、HTTP 服务、control socket 和运行态快照,已经形成单进程强耦合。拆分后不再自研进程间队列,统一使用 async-nats + NATS JetStream 承担消息持久化、路由、Ack、重试和 DLQ。

目标边界:

  • Daemon Control Plane:只负责 worker 启停、健康检查、control socket、JetStream 初始化和状态汇总。
  • JetStream 数据面:承载所有业务消息,worker 直接 publish/consume。
  • Worker:承载业务逻辑,包括渠道、AI、工具、日志、定时任务和 HTTP。

Daemon 不调用 AI、不读写上下文、不调用微信、不处理邮件业务、不决定 typing 生命周期。

2. 当前问题

  • core/queue.rs 既是内存队列,又包含上下文记录、AI 调用、工具调用、typing 刷新和微信发送,职责过宽。
  • 微信、邮件、AI、HTTP 共享同一 runtime,任何模块阻塞或崩溃都会影响整体。
  • 邮件通知直接依赖微信 manager,缺少统一的消息路由。
  • 现有 supervisor 只能管理单个后台服务进程,不能管理多个 worker。
  • 内存 mpsc 不具备跨进程持久化、Ack、重试、DLQ 和恢复能力。

3. 目标架构

保留一张核心图,后续实现以这张图为准。

flowchart TB
    User["微信用户"] --> WeChatAPI["WeChat API"]
    MailSrv["IMAP / 邮件服务器"] --> MailWorker["Mail Worker"]
    Browser["浏览器 / 管理页面"] --> HttpSvc["HTTP Service"]

    subgraph Control["控制面"]
        CLI["ias CLI"]
        Daemon["Daemon Control Plane<br/>worker 启停 / health / control socket / JetStream bootstrap"]
        Runtime["runtime files<br/>pid / state / logs"]
        CLI --> Daemon
        HttpSvc -- status --> Daemon
        Daemon --> Runtime
    end

    subgraph MQ["数据面:NATS JetStream"]
        Inbound["IA_INBOUND<br/>inbound.wechat.* / inbound.mail.*"]
        Function["IA_FUNCTION<br/>function.call.* / function.result.*"]
        Outbound["IA_OUTBOUND<br/>outbound.wechat.* / outbound.mail.*"]
        Schedule["IA_SCHEDULE<br/>schedule.trigger.*"]
        Logs["IA_LOGS<br/>logs.*"]
        DLQ["IA_DLQ<br/>dlq.*"]
    end

    subgraph Workers["Worker"]
        WeChatWorker["WeChat Worker"]
        MailWorker["Mail Worker"]
        Assistant["Assistant Worker"]
        FunctionWorker["Function Worker"]
        Scheduler["Scheduler Worker"]
        Logging["Logging Worker"]
    end

    subgraph Storage["存储"]
        Pg["PostgreSQL<br/>账号 / 邮件 / 上下文 / 日志 / 定时任务"]
        JS["JetStream file store"]
    end

    WeChatAPI --> WeChatWorker
    WeChatWorker -- publish inbound --> Inbound
    MailWorker -- publish inbound/notification --> Inbound
    Inbound -- consume --> Assistant
    Assistant -- publish function call --> Function
    Function -- consume --> FunctionWorker
    FunctionWorker -- publish function result --> Function
    Function -- consume result --> Assistant
    Assistant -- publish outbound --> Outbound
    Outbound -- consume --> WeChatWorker
    WeChatWorker --> WeChatAPI
    Scheduler -- publish trigger --> Schedule
    Schedule --> Assistant
    Schedule --> FunctionWorker
    Daemon -. log .-> Logs
    WeChatWorker -. log .-> Logs
    MailWorker -. log .-> Logs
    Assistant -. log .-> Logs
    FunctionWorker -. log .-> Logs
    Scheduler -. log .-> Logs
    Logs --> Logging
    Logging --> Pg
    Inbound -. max deliver .-> DLQ
    Function -. max deliver .-> DLQ
    Outbound -. max deliver .-> DLQ
    Schedule -. max deliver .-> DLQ
    Inbound --- JS
    Function --- JS
    Outbound --- JS
    Schedule --- JS
    Logs --- JS
    DLQ --- JS
    Assistant <--> Pg
    WeChatWorker <--> Pg
    MailWorker <--> Pg
    Scheduler <--> Pg
    HttpSvc <--> Pg

4. 核心组件

4.1 Daemon Control Plane

职责:

  • 启动、停止、重启 worker 和 HTTP 服务。
  • 初始化 JetStream stream、consumer、DLQ。
  • 提供 control socket 给 CLI 和 HTTP 状态页。
  • 汇总 worker 心跳、JetStream consumer lag、DLQ 数量和最近错误。
  • 输出本地运行日志,并发布关键控制事件到 IA_LOGS

禁止:

  • 不调用 ias-ai
  • 不调用 ias-context
  • 不调用 ias-wechat
  • 不解析业务消息 payload。
  • 不直接消费或生产业务消息。

4.2 JetStream 数据面

固定使用 async-nats + NATS JetStream

Streams

  • IA_INBOUND:渠道输入消息,例如 inbound.wechat.*inbound.mail.*
  • IA_FUNCTION:工具调用和工具结果,例如 function.call.*function.result.*
  • IA_OUTBOUND:渠道发送命令,例如 outbound.wechat.*
  • IA_SCHEDULE:定时任务触发事件,例如 schedule.trigger.*
  • IA_LOGS:结构化日志事件,例如 logs.daemon.*logs.worker.*logs.audit.*
  • IA_DLQ:死信消息,例如 dlq.*

默认策略:

  • 使用 file storage。
  • 使用 durable pull consumer。
  • 使用 explicit ack。
  • 普通消息 AckWait 默认 60 秒,AI 和工具类消息可单独加长。
  • MaxDeliver 默认 5 次,超过后进入 IA_DLQ
  • 所有业务处理按 at-least-once 设计,下游必须幂等。

4.3 Worker

  • WeChat Worker:微信长轮询、updates_buf 持久化、发布 inbound、消费 outbound、执行发送和 typing。
  • Mail Worker:邮件轮询、邮件通知事件、邮件 inbound 事件。
  • Assistant Worker:上下文、AI 调用、/new、active turn、typing 决策、工具编排、生成 outbound。
  • Function Worker:执行工具、HTTP API、内部功能,返回 function result。
  • Scheduler Worker:扫描定时任务,到期后发布 scheduled trigger。
  • Logging Worker:消费 IA_LOGS,落库、写文件、聚合错误和审计记录。
  • HTTP ServiceREST API、SPA、日志/更新查询、daemon 状态代理。

5. 消息标准

所有 JetStream payload 统一包一层业务消息信封,字段保持稳定:

  • message_id:消息幂等键。
  • topic:业务 topic,与 JetStream subject 对齐。
  • trace_id:跨 worker 链路追踪。
  • created_at_ms:生成时间。
  • attempt_hint:业务层可读的尝试次数提示,真实投递次数以 JetStream 为准。
  • payload:业务内容。

常用幂等键:

  • 入站消息:message_id = channel/account/external_message_id
  • outbound 命令:command_id
  • function 调用:call_id
  • 定时触发:fire_id = schedule_id + fire_time
  • 日志事件:event_id

消息内容可以使用 serde_json;不要在 daemon 中解析业务 payload。

6. 日志设计

日志分两层:

  • 运行日志:所有进程继续用 tracing 输出到 stdout/file,方便 systemd 和本地排障。
  • 结构化事件日志:关键事件发布到 IA_LOGS,由 Logging Worker 落库和归档。

日志事件来源:

  • daemon:进程启动/停止、worker 重启、JetStream 初始化、DLQ 操作。
  • channel worker:长轮询错误、发送成功/失败、账号状态变化。
  • assistant worker:AI 请求摘要、工具调用、上下文错误、/new
  • function worker:工具执行耗时、结果摘要、错误。
  • scheduler worker:任务触发、跳过、重复触发拦截。
  • HTTP service:管理操作、配置变更、API 错误。

日志要求:

  • 所有跨进程消息必须带 trace_id
  • message_idcommand_idcall_idfire_id 必须进入日志字段。
  • 默认不记录完整用户正文,只记录摘要、长度和哈希。
  • Logging Worker 失败不能阻塞业务流。
  • IA_LOGS 必须设置保留周期和最大容量。

7. 定时任务设计

Scheduler Worker 独立运行,不放在 daemon 内。

定时任务来源:

  • 数据库表,例如 ias_schedules
  • 少量系统级任务可来自配置文件或环境变量。

任务字段建议:

  • schedule_id
  • name
  • schedule_typecroninterval
  • expression
  • target_topic
  • payload
  • enabled
  • last_fired_at
  • next_fire_at

触发流程:

  1. Scheduler Worker 周期扫描启用任务。
  2. 到期后生成 fire_id = schedule_id + fire_time
  3. 通过数据库唯一约束抢占触发权。
  4. 发布 ScheduledTriggerIA_SCHEDULE
  5. 下游 worker 根据 target_topic 处理。

防重复要求:

  • fire_id 必须唯一。
  • JetStream publish 使用 message_id 去重窗口。
  • 下游 worker 必须按 fire_id 幂等。

8. 模块拆分

目标 crate

  • ias-daemoncontrol daemon、worker registry、process manager、JetStream bootstrap。
  • ias-common:消息类型、JetStream 薄封装、RuntimeSnapshot、control 协议。
  • ias-channel:微信和邮件 worker。
  • ias-assistant:AI、上下文、工具编排、typing 决策。
  • ias-function:工具执行 worker。
  • ias-scheduler:定时任务 worker。
  • ias-logging:日志消费 worker。
  • ias-httpHTTP 服务和 daemon 状态代理。

依赖约束:

  • ias-daemon 可以依赖 ias-commonasync-nats
  • ias-daemon 禁止依赖 ias-aiias-contextias-wechatias-mail
  • ias-channel 禁止依赖 ias-aiias-context
  • ias-assistant 禁止依赖 ias-wechat
  • ias-loggingias-scheduler 不直接调用渠道 SDK 和 AI SDK。

9. CLI 入口

保留现有账号管理命令,不复用 ias channel 作为 worker 启动命令。

新增或调整入口:

  • ias service start|stop|restart|status|logs
  • ias daemon run
  • ias worker assistant
  • ias worker wechat --account-ids ...
  • ias worker mail
  • ias worker function --name ...
  • ias worker scheduler
  • ias worker logging
  • ias http --host 0.0.0.0:9003

10. 部署配置

核心环境变量:

  • NATS_URL=nats://127.0.0.1:4222
  • IAS_NATS_STREAM_PREFIX=IA
  • IAS_NATS_ACK_WAIT_SECS=60
  • IAS_NATS_MAX_DELIVER=5
  • IAS_CONTROL_SOCKET=/run/user/1000/ias/ias.sock
  • IAS_LOG_EVENT_ENABLED=true
  • IAS_LOG_EVENT_RETENTION_DAYS=30
  • IAS_SCHEDULER_ENABLED=true
  • IAS_SCHEDULER_TICK_SECS=10
  • HOST=0.0.0.0:9003

JetStream 部署方式:

  • NATS Server 作为独立基础设施部署,iAs 只通过 NATS_URL 连接。
  • 开发和单机部署优先使用 Docker Compose,便于本地启动、清理和迁移。
  • Docker Compose 中的 NATS 启动参数必须等价于 nats-server --jetstream --store_dir=/data --http_port=8222
  • Docker Compose 必须挂载持久化目录,例如 ./data/nats:/data
  • 本机开发端口只绑定 127.0.0.1:4222127.0.0.1:8222,不要暴露公网。
  • 生产单机也可以用 systemd 管理 nats-server,推荐配置文件放在 /etc/ias/nats-server.confJetStream 数据放在 /var/lib/ias/nats
  • 跨主机或多节点 JetStream 集群不作为第一阶段要求;确实需要时必须补充鉴权、TLS、cluster routes、备份和监控设计。
  • daemon 启动时只负责检查和创建 stream、consumer、DLQ,不负责启动 Docker 容器或 systemd 服务。
  • NATS 不可用时,worker 必须退避重连;daemon 和 HTTP 状态接口显示 MQ 降级状态。

11. 施工计划

Phase 0:边界收敛

  • 确认 daemon 只做 control plane。
  • 确认 JetStream stream、subject、consumer 命名。
  • 确认 worker CLI 命名不与现有账号管理命令冲突。

Phase 1:公共协议

  • ias-common 定义业务消息信封、payload 类型、日志事件和定时触发事件。
  • 封装 JetStreamBus,集中处理 publish、consumer、stream 初始化和错误映射。
  • 增加消息序列化、幂等键生成和 subject 生成测试。

Phase 2:日志和定时基础

  • 创建 IA_LOGSIA_SCHEDULE
  • 实现 Logging Worker 原型。
  • 实现 Scheduler Worker 原型。
  • 将关键操作日志改成发布 LogEvent,保留本地 tracing 输出。

Phase 3:队列解耦

  • core/queue.rs 拆出 JetStream publish/consume 适配。
  • 将 AI、上下文、工具编排、typing 决策迁移到 Assistant Worker。
  • 将微信发送调用改成 OutboundCommand
  • 先保持单进程兼容模式,验证行为不变。

Phase 4Daemon 和 Worker 提取

  • 创建 ias-daemon,只迁移 control、worker registry、process manager、JetStream bootstrap。
  • 创建 ias-assistantias-channelias-schedulerias-logging
  • 实现 worker 注册、心跳、JetStream Ack/Nack、断线重连。

Phase 5HTTP 和部署

  • 添加独立 ias http 入口。
  • 添加 daemon control client。
  • 更新 service start/stop/status/logs
  • 增加 NATS JetStream Docker Compose 和 systemd 部署说明。

Phase 6:端到端验证

  • 微信消息到回复链路。
  • AI 工具调用链路。
  • worker 崩溃后的 redelivery。
  • 定时任务触发和去重。
  • 结构化日志落库和查询。
  • HTTP 独立运行和 daemon 状态降级。

12. 施工标准

12.1 架构标准

  • daemon 不在业务消息数据路径中。
  • 所有业务消息必须经过 JetStream。
  • worker 之间不得直接调用彼此内部 API。
  • 新增业务能力优先通过新增 subject 和 worker 实现,不扩展 daemon 职责。

12.2 依赖标准

  • ias-daemon 只能依赖 ias-commonasync-nats、日志和进程管理相关依赖。
  • ias-common 可以定义协议和薄封装,但不能包含业务逻辑。
  • 渠道 SDK 只允许出现在 channel worker。
  • AI 和上下文依赖只允许出现在 assistant worker。

12.3 消息标准

  • 所有消息必须有 message_idtrace_id
  • 所有 outbound 必须有 command_id
  • 所有 function call 必须有 call_id
  • 所有定时触发必须有 fire_id
  • 所有 worker 按 at-least-once 投递语义实现幂等。

12.4 日志标准

  • 所有关键状态变化必须发 LogEvent
  • 日志字段必须包含相关 IDmessage_idcommand_idcall_idfire_id
  • 默认不得记录完整用户正文。
  • 日志流必须设置保留周期和容量上限。

12.5 定时任务标准

  • 定时任务触发权必须通过数据库唯一约束或等价机制保证。
  • Scheduler Worker 可以多副本运行,但同一 fire_id 只能触发一次。
  • 定时任务失败必须可重试或进入 DLQ。

12.6 测试标准

  • 每个 worker 至少有消息序列化和幂等测试。
  • JetStream consumer Ack/Nack、redelivery、DLQ 必须有集成测试。
  • daemon 边界必须通过依赖检查验证。
  • 关键端到端链路必须有人工或自动化验证记录。

12.7 部署标准

  • NATS 必须作为独立进程或容器部署,不嵌入 iAs daemon。
  • JetStream 必须开启 file storage,并配置持久化目录。
  • 开发和单机默认使用 Docker Compose;生产单机可改用 systemd。
  • NATS client 端口和监控端口默认只监听本机。
  • NATS 跨主机访问必须启用鉴权;公网访问必须启用 TLS。
  • iAs 代码只能负责连接、初始化和检查 JetStream,不能负责管理 Docker 或 systemd 生命周期。

13. 验收标准

架构验收:

  • ias-daemon 编译依赖中不包含 ias-aiias-contextias-wechatias-mail
  • 不存在自研 ias-queue.sock 业务数据面。
  • 所有 MQ 访问通过 async-nats + JetStreamias-common 的 JetStream 薄封装完成。

消息验收:

  • 没有 assistant worker 时,IA_INBOUND 能接收并保留消息。
  • 没有 channel worker 时,IA_OUTBOUND 能保留 outbound 消息。
  • worker 崩溃或断线后,未 Ack 消息能 redeliver。
  • 超过最大投递次数的消息进入 IA_DLQ

业务验收:

  • 微信消息能完成:inbound -> assistant -> outbound -> 微信回复。
  • /new 仍能打断同一会话旧 turn。
  • typing 由 assistant 生成命令,channel worker 执行。
  • 邮件提醒不直接调用微信 manager,而是发布消息后由路由完成发送。

日志验收:

  • Logging Worker 能消费 IA_LOGS
  • 结构化日志能写入数据库或文件。
  • 日志中能通过 trace_id 串起跨 worker 链路。
  • 默认不会记录完整用户正文。

定时任务验收:

  • Scheduler Worker 能按 cron/interval 发布 ScheduledTrigger
  • 同一 fire_id 重复启动不会重复触发。
  • 定时任务失败能重试或进入 DLQ。

运维验收:

  • HTTP 可以在 daemon 不在线时启动,状态接口返回降级结果。
  • daemon 重启不影响 JetStream 中未 Ack 消息。
  • Docker Compose 或 systemd 重启 NATS 后,JetStream file store 中的未处理消息仍保留。
  • NATS 重启后 worker 能退避重连并恢复消费。
  • NATS 端口默认只绑定本机;跨主机部署时必须验证鉴权配置。
  • service status 能看到 daemon、worker、stream、consumer lag 和 DLQ 摘要。