132089c8eb
用户发送 /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 个单元测试。
14 KiB
14 KiB
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