Files
iAs/ARCHITECTURE.md
wunianxiao 132089c8eb feat(context): /clear 指令截断上下文并按会话过期归档
用户发送 /clear 时主动截断上下文:加载历史 → LLM 生成摘要归档
→ 丢弃在途会话 → 回复确认(带摘要预览)。

- context/types: SummaryReason 新增 Clear 变体
- context/builder: 抽出 archive_session 共享逻辑,
  新增 trigger_clear_summary(返回摘要文本)
- db/models: 新增 latest_session_boundary;list_recent_chat_records
  _for_context 按会话边界过滤(clear/timeout),并排除 source=clear
- daemon: SessionMap 记录 user_id;拦截 /clear 走 handle_clear_command;
  新增 build_clear_reply 纯函数
- migration: session_summaries (user_id, created_at DESC) 索引

会话边界机制统一 clear 与 12h 空闲超时;摘要不注入下一对话,
只存档供 read_summaries 查询。新增 4 个单元测试。
2026-06-23 11:57:07 +08:00

14 KiB
Raw Permalink Blame History

iAs 代码调用图谱

微信 AI 智能助手 —— 完整架构与调用关系


1. 全局架构总览

flowchart TB
    subgraph CLI["CLI 入口 (main.rs)"]
        A1["clap::Parser Cli 解析"]
        A2["cmd_login"]
        A3["cmd_listen"]
        A4["cmd_send"]
        A5["cmd_whoami"]
        A6["cmd_usage"]
        A7["cmd_tool"]
        A8["cmd_daemon"]
    end
    subgraph DAEMON["守护进程 (daemon.rs)"]
        B0["run()"]
        B1["spawn_consumers()"]
        B2["llm_consumer_loop"]
        B3["tool_consumer_loop"]
        B4["send_consumer_loop"]
        B5["run_llm_round / resume_llm_round"]
        B6["handle_chat_result"]
        B7["execute_tool"]
    end
    subgraph QUEUE["消息队列 (queue/)"]
        C1["MessageQueue 公平轮转"]
        C2["QueueRunner 路由调度"]
        C3["EnqueueHandle"]
        C4["ConsumerChannels"]
    end
    subgraph LLM["LLM 对话系统 (llm/)"]
        D1["Conversation 对话管理器"]
        D2["chat_with_tools() / run_tool_loop()"]
        D3["DeepSeekProvider"]
        D4["LlmProvider trait"]
        D5["StreamReceiver"]
    end
    subgraph CONTEXT["上下文管理 (context/)"]
        E1["ChatSession 消息历史+摘要"]
        E2["builder Token估算+上下文构建"]
        E3["MemoryStore 长期记忆"]
        E4["read_summaries"]
    end
    subgraph TOOLS["工具系统 (tools/)"]
        F1["BuiltinRegistry 工具注册表"]
        F2["ApprovalManager 审批管理"]
        F3["build_capability_guide"]
        F4["unpack_call_params"]
        subgraph BUILTINS["内置工具 (builtins/)"]
            G1["datetime"]
            G2["weather"]
            G3["web_search"]
            G4["fetch_page"]
            G5["memos"]
            G6["amap"]
        end
    end
    subgraph WECHAT["微信通道 (wechat/)"]
        H1["WeChatClient"]
        H2["receive_messages()"]
        H3["send_text()"]
        H4["login()"]
        H5["notify_start/stop"]
    end
    subgraph DB["数据库 (db/)"]
        I1["Database::connect()"]
        I2["models CRUD 操作"]
    end
    subgraph OTHER["其他模块"]
        J1["state.rs 文件状态回退"]
        J2["scheduler.rs 定时任务"]
        J3["logger.rs 日志初始化"]
        J4["channel/mod.rs ChannelId"]
        J5["ipc.rs UDS 协议(已废弃)"]
        J6["worker.rs 旧架构(已废弃)"]
    end

    A1 --> A2 & A3 & A4 & A5 & A6 & A7 & A8
    A3 --> B0
    A8 --> B0
    B0 --> B1
    B1 --> B2 & B3 & B4
    B2 --> B5 & B6
    B3 --> B7
    B0 --> C1 & C2 & C3
    C2 --> C4
    C4 --> B2 & B3 & B4
    B2 --> D1
    D1 --> D2
    D2 --> D3
    D3 --> D4
    D4 --> D5
    D1 --> E1
    D2 --> E2
    B2 --> E3 & E4
    D2 --> F1
    B3 --> F1 & F2
    B7 --> F1 & F3 & F4
    F1 --> G1 & G2 & G3 & G4 & G5 & G6
    B4 --> H1
    H1 --> H2 & H3
    B0 --> I1
    B2 --> I2
    B4 --> I2
    A2 --> I2 & J1
    A3 --> J1
    B0 --> J1 & J2
    A1 --> J3

2. 消息处理全链路

sequenceDiagram
    participant WX as 微信用户
    participant WC as WeChatClient
    participant MQ as MessageQueue
    participant QR as QueueRunner
    participant LC as LLM Consumer
    participant TC as Tool Consumer
    participant SC as Send Consumer
    participant LLM as DeepSeek API
    participant BT as BuiltinRegistry

    WX->>WC: 发送消息
    WC->>MQ: enqueue(UserMessage)
    MQ->>QR: dequeue (公平轮转)
    QR->>LC: llm_tx.send(UserMessage)
    LC->>LC: 加载历史/记忆/摘要
    LC->>LLM: chat_stream(上下文)
    LLM-->>LC: 流式回复 + tool_calls

    alt 有工具调用
        LC->>MQ: enqueue(ToolCall)
        MQ->>QR: dequeue
        QR->>TC: tool_tx.send(ToolCall)

        alt 高风险工具
            TC->>TC: ApprovalManager.create()
            TC->>MQ: enqueue(LLMReply: 确认码)
            MQ->>QR: dequeue
            QR->>SC: send_tx.send(LLMReply)
            SC->>WC: send_text(确认码)
            WX-->>WC: 回复确认码
            WC->>MQ: enqueue(UserMessage: 确认码)
            MQ->>QR: dequeue
            QR->>LC: llm_tx.send(确认码)
            LC->>TC: ApprovalManager.handle_reply()
            TC-->>LC: Approved
            LC->>MQ: enqueue(ToolCall + approved=true)
            MQ->>QR: dequeue
            QR->>TC: tool_tx.send(ToolCall)
        end

        TC->>BT: execute(工具名, 参数)
        BT-->>TC: SkillResult
        TC->>MQ: enqueue(ToolResult)
        MQ->>QR: dequeue
        QR->>LC: llm_tx.send(ToolResult)
        LC->>LLM: resume_tool_loop()
        LLM-->>LC: 最终回复
    end

    LC->>MQ: enqueue(LLMReply)
    MQ->>QR: dequeue
    QR->>SC: send_tx.send(LLMReply)
    SC->>WC: send_text(回复)
    WC->>WX: 显示回复

3. 模块依赖关系

flowchart LR
    subgraph CORE["核心层"]
        MAIN["main.rs"]
        DAEMON["daemon.rs"]
        CLI["cli.rs"]
    end
    subgraph PIPELINE["消息管线"]
        QUEUE["queue/"]
        CHANNEL["channel/"]
    end
    subgraph AI["AI 层"]
        LLM["llm/"]
        CTX["context/"]
    end
    subgraph TOOL["工具层"]
        TOOLS["tools/"]
        BUILTINS["tools/builtins/"]
        APPROVAL["tools/approval.rs"]
    end
    subgraph IO["IO 层"]
        WECHAT["wechat/"]
        DB["db/"]
        STATE["state.rs"]
        SCHED["scheduler.rs"]
    end
    subgraph UTIL["工具层"]
        LOG["logger.rs"]
        IPC["ipc.rs"]
        WORKER["worker.rs"]
    end

    MAIN --> CLI & DAEMON & LOG & STATE & DB & CTX & LLM & TOOLS & WECHAT & SCHED
    DAEMON --> QUEUE & CHANNEL & WECHAT & DB & STATE & LLM & CTX & TOOLS & SCHED
    QUEUE --> CHANNEL
    LLM --> CTX & TOOLS
    TOOLS --> BUILTINS & APPROVAL
    CTX --> DB
    TOOLS --> DB
    APPROVAL --> DB
    DB --> STATE
    WORKER --> IPC & LLM & TOOLS

4. 数据流(消息生命周期)

flowchart TB
    START(["📱 用户发消息"]) --> A["WeChatClient receive_messages()"]
    A --> B["入库: chat_records (inbound)"]
    B --> C["PipelineMessage kind: UserMessage"]
    C --> D["MessageQueue 公平轮转"]
    D --> E["加载历史消息 (最近20条)"]
    E --> F["加载长期记忆 (MemoryStore)"]
    F --> G["加载摘要 (read_summaries)"]
    G --> H["构建上下文 (builder)"]
    H --> I["Conversation chat_with_tools()"]
    I --> J{"LLM 调用了工具?"}
    J -->|"是"| K["Tool Consumer 执行工具"]
    K --> L{"高风险?→ 审批流程"}
    L --> M["结果回喂 LLM"]
    M --> I
    J -->|"否"| N["LLM 生成最终回复"]
    N --> O["Send Consumer send_text()"]
    O --> P["入库: chat_records (outbound)"]
    P --> ENDD(["📱 用户收到回复"])

5. 关键函数调用关系

5.1 工具执行链路

flowchart TB
    subgraph LLM_SIDE["LLM 侧"]
        A["LLM 调用 call_capability()"]
        B["ToolExecutor (闭包)"]
        C["返回 ASYNC_MARKER + 工具名"]
    end
    subgraph QUEUE_SIDE["队列侧"]
        D["PipelineMessage::tool_call()"]
        E["MessageQueue.enqueue()"]
        F["QueueRunner → Tool Consumer"]
    end
    subgraph TOOL_SIDE["工具侧"]
        G["tool_consumer_loop()"]
        H{"高风险?"}
        I["ApprovalManager 审批流程"]
        J["execute_tool()"]
        K{"工具类型?"}
        L["BuiltinRegistry.execute()"]
        M["上下文工具 直接处理"]
        N["unpack_call_params()"]
    end
    subgraph RESULT["结果回喂"]
        O["PipelineMessage::tool_result()"]
        P["LLM Consumer resume_llm_round()"]
        Q["Conversation resume_tool_loop()"]
    end

    A --> B --> C --> D --> E --> F --> G
    G --> H
    H -->|"是"| I --> J
    H -->|"否"| J
    J --> K
    K -->|"内置工具"| L
    K -->|"上下文工具"| M
    K -->|"call_capability"| N --> K
    J --> O --> P --> Q

5.2 审批流程

flowchart TB
    A["LLM 请求高风险工具"] --> B["ApprovalManager.create(user_id, tool)"]
    B --> C["生成 6 位确认码 SHA-256 哈希存储"]
    B --> D["创建 oneshot channel"]
    C --> E["发送消息给用户 ⚠️ 操作确认"]
    D --> F["等待用户回复 超时 5 分钟"]
    F --> G{"用户回复"}
    G -->|"匹配确认码"| H["ApprovalDecision::Approved"]
    G -->|"输入 0/取消"| I["ApprovalDecision::Rejected"]
    G -->|"不匹配(最多3次)"| J["减少尝试次数"]
    G -->|"超时"| K["ApprovalDecision::Expired"]
    J --> F
    H --> L["重新入队 UserMessage + approved_tool"]
    I --> M["回复用户: 已取消"]
    K --> N["回复用户: 已超时"]

5.3 上下文构建流程

flowchart TB
    A["ChatSession (消息历史)"] --> B["estimate_tokens()"]
    B --> C{"Token 超 budget? (默认 28000)"}
    C -->|"是"| D{"checkpoint 之前有未摘要消息?"}
    D -->|"是"| E["trigger_overflow_summary() → 调用 LLM 生成摘要"]
    D -->|"否"| F["裁剪最早的消息 直到符合 budget"]
    C -->|"否"| G{"空闲超时? (12h 无消息)"}
    G -->|"是"| H["trigger_idle_summary() → 清空消息, 保留摘要"]
    G -->|"否"| I["build_context() → 组装最终上下文"]
    E --> I
    F --> I
    H --> I
    I --> J["发送给 LLM"]

5.3.1 /clear 清空上下文流程

用户发送 /clear 指令时,主动截断上下文并把本次会话归档为摘要(按会话过期处理):

flowchart TB
    U(["📱 用户发送 /clear"]) --> K{"is_clear_command() 匹配?"}
    K -->|"否"| N(["走正常 LLM 对话流程"])
    K -->|"是"| L["加载历史(过滤掉 /clear 本身)"]
    L --> M{"历史非空?"}
    M -->|"否"| R2["回复: 上下文已是空的"]
    M -->|"是"| S["一次性 Conversation + summarizer"]
    S --> T["trigger_clear_summary() → LLM 生成摘要"]
    T --> DB[("session_summaries\nreason=clear")]
    DB --> D["丢弃该用户在途会话 (sessions.retain)"]
    D --> R1["回复确认 + 摘要预览"]
    R2 --> END(["📱 用户收到回复"])
    R1 --> END
  • 会话边界:摘要的 created_at 作为边界,之后加载历史时通过 latest_session_boundary() 过滤掉此前的消息(/clear 与 12h 空闲超时 共用同一套边界机制)
  • 注入与否/clear 摘要不会注入下一次对话的 system prompt 只存档供 read_summaries 工具查询(与空闲超时摘要一致)
  • 在途会话/clear 会丢弃该用户尚未完成的工具往返,避免用旧上下文继续

6. 文件依赖矩阵

文件 依赖的关键模块
main.rs cli, daemon, db, llm, context, tools, wechat, state, scheduler, logger
daemon.rs queue, channel, wechat, db, state, llm, context, tools, scheduler
cli.rs clap (独立)
llm/conversation.rs context/builder, context/types, llm/provider, llm/types
llm/deepseek.rs llm/provider, llm/types
llm/provider.rs llm/types
llm/types.rs serde (独立)
context/types.rs llm/types
context/builder.rs context/types, llm/types
context/tools.rs db/models
queue/message_queue.rs channel/mod
queue/runner.rs queue/message_queue
tools/builtin.rs tools/types, tools/builtins/*
tools/approval.rs db/models
tools/mod.rs tools/types
wechat/client.rs wechat/types
wechat/types.rs serde (独立)
db/mod.rs db/models
db/models.rs sqlx (独立)
state.rs serde (独立)
scheduler.rs db/models
worker.rs ipc, llm, tools (已废弃)
ipc.rs serde (已废弃)

7. 启动流程

flowchart TB
    START(["ias listen --llm"]) --> A["main()"]
    A --> B["clap::Parser::parse()"]
    B --> C{"命令类型"}
    C -->|"Tool"| D["cmd_tool() 直接执行工具"]
    C -->|"Login"| E["cmd_login() 扫码登录"]
    C -->|"Listen/Service"| F["cmd_listen()"]

    F --> G["加载 .env"]
    F --> H["Database::connect()"]
    H --> I{"数据库可用?"}
    I -->|"是"| J["创建 MemoryStore (DB 模式)"]
    I -->|"否"| K["创建 MemoryStore (文件模式)"]

    F --> L["加载认证信息"]
    L --> M{"已登录?"}
    M -->|"否"| N["报错退出"]
    M -->|"是"| O["WeChatClient.set_auth()"]

    O --> P["WeChatClient.notify_start()"]
    P --> Q["创建 ApprovalManager"]
    Q --> R["创建 Conversation + ToolExecutor"]
    R --> S["启动 Scheduler (定时任务)"]
    S --> T["启动 listen_loop()"]

    T --> U["WeChatClient.receive_messages()"]
    U --> V{"收到消息?"}
    V -->|"是"| W["enqueue PipelineMessage"]
    W --> X["LLM/Tool/Send 消费者处理"]
    V -->|"否/超时"| U

8. 消费者架构(三消费者)

flowchart TB
    subgraph QUEUE2["消息队列"]
        Q["MessageQueue 公平轮转"]
        R["QueueRunner 路由"]
    end
    subgraph LLM_C["LLM Consumer"]
        L1["接收: UserMessage / ToolResult / ScheduledTask"]
        L2["加载用户历史 + 记忆 + 摘要"]
        L3["创建/恢复 Conversation"]
        L4["chat_with_tools() / resume_tool_loop()"]
        L5{"有异步工具等待?"}
        L6["发送 LLMReply"]
        L7["保留 session"]
        L8["清理 session"]
    end
    subgraph TOOL_C["Tool Consumer"]
        T1["接收: ToolCall / ApprovalRequest"]
        T2{"高风险 + 未审批?"}
        T3["ApprovalManager.create()"]
        T4["发送确认码"]
        T5["execute_tool()"]
        T6["发送 ToolResult"]
    end
    subgraph SEND_C["Send Consumer"]
        S1["接收: LLMReply"]
        S2["WeChatClient.send_text()"]
        S3["入库: chat_records (outbound)"]
    end

    Q --> R
    R -->|"UserMessage / ToolResult"| L1
    R -->|"ToolCall"| T1
    R -->|"LLMReply"| S1
    L1 --> L2 --> L3 --> L4
    L4 --> L5
    L5 -->|"是"| L7
    L5 -->|"否"| L6