PicoBot/ARCHITECTURE.md
oudecheng e6b2fcfb6d docs+test: 补充架构文档与 anthropic provider 单测
P3 文档:
- 新增 ARCHITECTURE.md,聚焦数据流与 7 个关键设计决策
  (MessageBus 解耦/SessionPool 隔离/AgentLoop 循环/SQLite 池化/重启机制/嵌入静态文件/Safety Guard)
- 不写代码导读,只写'为什么这样设计',代码是唯一真相源

P2 测试:
- src/providers/anthropic.rs 曾零测试(396 行),补 15 个纯函数单测
- 覆盖:data URL 解析、图片过滤逻辑、内部字段过滤、响应反序列化、错误链格式化
- providers 模块测试密度 31% -> 提升,重点补齐 anthropic 空白
2026-08-03 23:45:41 +08:00

117 lines
7.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# PicoBot 架构
> 本文档聚焦"为什么这样设计"和"数据如何流动",不是代码导读。
> 代码是唯一真相源,文档可能滞后;有冲突以代码为准。
## 一句话定位
PicoBot 是一个**多渠道接入的 Agent 网关**:外部消息(微信/飞书/Web/CLI经统一总线进入
由会话管理器路由到对应 Agent 循环Agent 调用 LLM + 工具完成任务,结果原路返回。
## 核心数据流
```
┌─────────┐ ┌──────────┐ ┌──────────────┐ ┌────────────┐ ┌──────────┐
│ Channel │──▶│ MessageBus│──▶│InboundProc. │──▶│ Session │──▶│AgentLoop │
│ (微信/ │ │ (解耦) │ │(并发+限流) │ │ Manager │ │(LLM+工具)│
│ 飞书/Web)│ │ │ │ │ │ │ │ │
└─────────┘ └──────────┘ └──────────────┘ └────────────┘ └────┬─────┘
▲ │
│ ▼
┌────┴────┐ ┌──────────┐ ┌────────────┐ ┌──────────┐
│Outbound │◀──│ MessageBus│◀─── SessionMessageSender ◀─│ storage │◀─│ tools │
│Dispatcher│ │ │ │ (SQLite) │ │(bash/file│
└─────────┘ └──────────┘ └────────────┘ │ /memory) │
└──────────┘
```
**入站**Channel → MessageBus → InboundProcessor并发控制 + 限流)→ SessionManager → AgentLoop
**出站**AgentLoop → SessionMessageSender → MessageBus → OutboundDispatcher → Channel
**持久化**AgentLoop / Tools → SessionStoreSQLite + r2d2 连接池)
## 关键设计决策
### 1. MessageBus 解耦 Channel 与 Session
**问题**:多个 Channel微信/飞书/Web接入每个 Channel 协议不同,但 Session 处理逻辑相同。
**决策**:引入 MessageBus 作为中间件Channel 只负责协议适配和收发Session 不关心消息来自哪个 Channel。
**代价**:多一层间接。换来的是新增 Channel如钉钉只需实现 Channel trait不碰 Session 逻辑。
### 2. SessionPool 会话隔离
**问题**:多用户同时对话,会话状态不能串扰。
**决策**SessionManager 持有 SessionPool按 (channel_name, chat_id) 路由到独立 Session。
每个 Session 有自己的 AgentLoop、消息历史、工具上下文。
**代价**:内存占用随活跃会话数增长。用 session_ttl_hours 过期回收。
### 3. AgentLoop 的工具循环
**问题**LLM 需要多轮工具调用才能完成任务(如"读文件→分析→写文件")。
**决策**AgentLoop 是一个有界循环max_tool_iterations默认 1000每轮
1. 把消息历史 + 工具定义发给 LLM
2. LLM 返回文本或 tool_call
3. 如果是 tool_call执行工具把结果加入历史回到 1
4. 如果是文本,结束循环
**代价**:单次对话可能很长。用 CancelManager 支持中途取消。
### 4. SQLite + r2d2 连接池
**问题**:需要持久化会话历史、话题、记忆、待办,且要支持并发读写的 sub-agent 场景。
**决策**SQLitebundled+ r2d2 连接池max_size=8+ busy_timeout(30s)。
**代价**SQLite 写并发有限。通过 busy_timeout + 事务隔离级别(之前的修复)缓解锁冲突。
**不选 Postgres 的原因**:单机部署、零外部依赖、足够用。
### 5. 重启机制watch channel
**问题**:配置变更后需要重启 gateway但不能要求用户手动杀进程。
**决策**main.rs 用 `while should_restart` 循环gateway 通过 `watch::Sender<bool>` 通知是否需要重启。
配置 API `/api/restart` 触发 graceful shutdownmain 收到 should_restart=true 后重新初始化。
**代价**:重启期间短暂不可用。比热重载简单且可靠。
### 6. 嵌入式静态文件
**问题**Web 前端需要随二进制分发,但不想要求用户额外下载。
**决策**build.rs 在 cargo build 时执行 npm run build产物通过 rust-embed 编译进二进制。
开发时设 `STATIC_DIR` 环境变量走磁盘文件,支持热更新。
**代价**:二进制体积增大。换来的是单文件部署。
### 7. Safety Guard命令安全护栏
**问题**Agent 可以调用 bash 工具执行任意命令,需要防止误操作(如 `format C:``rm -rf`)。
**决策**platform 模块按平台注入危险命令正则,执行前匹配拦截。
**关键教训**:正则要精确(曾因 `\bformat\s+` 误拦 `dart format`),按平台分组避免跨平台误伤。
## 模块职责速查
| 模块 | 职责 | 关键文件 |
|------|------|---------|
| gateway | HTTP/WS 服务、路由、生命周期 | `gateway/mod.rs`, `gateway/runtime.rs` |
| gateway/session | 会话管理、路由、池化 | `gateway/session.rs`, `session_pool.rs` |
| gateway/processor | 入站消息处理、并发控制 | `gateway/processor.rs` |
| agent | Agent 循环、上下文压缩 | `agent/agent_loop.rs` |
| providers | LLM Provider 抽象OpenAI/Anthropic | `providers/openai.rs`, `anthropic.rs` |
| tools | 工具实现与注册 | `tools/` (bash/file/memory/task/...) |
| storage | SQLite 持久化 | `storage/mod.rs`, `migrations.rs` |
| channels | 渠道适配(微信/飞书/CLI | `channels/` |
| bus | 消息总线(解耦 channel 与 session | `bus/message.rs` |
| command | 前端命令处理(话题/会话/记忆 CRUD | `command/handlers/` |
| mcp | Model Context Protocol 客户端 | `mcp/client.rs` |
| scheduler | 定时任务调度 | `scheduler/mod.rs` |
| skills | 技能加载与激活 | `skills/mod.rs` |
| experts | 专家配置运行时 | `experts/mod.rs` |
## 测试策略
- **单元测试**:与代码同文件 `#[cfg(test)] mod tests`,覆盖纯函数和逻辑分支
- **集成测试**`tests/` 目录,覆盖跨模块请求格式
- **测试密度**test 行 / 总行tools 98.8%、gateway 96.4%、storage 91.8% 为高覆盖区;
providers 31%anthropic 曾为 0%已补、bus 18.7%、cli 4.5% 为薄弱区
- **不强制覆盖率工具**:静态审计 + 高风险区定向补测,比全量 tarpaulin 更务实
## 工程化基线
- **格式化**rustfmtRust+ prettier前端CI 强制 `--check`
- **静态检查**clippyRust+ eslint前端CI 强制
- **CI**GitHub Actions双平台ubuntu + windows跑 fmt + clippy + test + eslint + tsc + vitest
- **本地**`make check` 与 CI 完全对齐