Update README.md and add runtime architecture and message flow diagrams

- Translated and updated the README.md to provide a clearer overview of PicoBot's functionality and architecture in Chinese.
- Added a new SVG diagram for message flow to illustrate the process from user input through various components back to the channel.
- Created a new SVG diagram for runtime architecture to depict the high-level structure of PicoBot, including channels, gateway, message bus, session manager, agent loop, tools, providers, storage, scheduler, skills, and MCP.
This commit is contained in:
xiaoski 2026-06-15 23:57:08 +08:00
parent 8f4ee79d8d
commit 48c8a51d9a
3 changed files with 398 additions and 188 deletions

397
README.md
View File

@ -1,114 +1,45 @@
# PicoBot # PicoBot
PicoBot is a Rust-based personal AI assistant runtime. It runs a local gateway, connects chat channels such as the terminal TUI and Feishu/Lark, persists sessions in SQLite, and gives the agent a tool system for files, shell commands, web access, memory, scheduling, skills, MCP tools, and delegated sub-agents. PicoBot 是一个用 Rust 编写的个人 AI 助手运行时。它在本地启动 Gateway接入 CLI TUI、飞书/Lark 等聊天渠道,把会话、消息、记忆和定时任务持久化到 SQLite并为 Agent 提供文件、Shell、HTTP、Web、MCP、Skill、记忆、浏览器和子 Agent 等工具能力。
## What It Does 它更像一个可扩展的“个人助手操作系统”渠道负责收发消息SessionManager 负责会话和上下文AgentLoop 负责模型与工具循环Storage 负责可靠落盘。
- Runs as a gateway server on `127.0.0.1:19876` by default. ![PicoBot runtime architecture](docs/assets/runtime-architecture.svg)
- Provides a Ratatui terminal client over WebSocket.
- Supports Feishu/Lark messages, reactions, file upload/download, and media references.
- Calls OpenAI-compatible providers and Anthropic Messages API providers.
- Persists conversations, messages, memories, scheduled jobs, LLM call metadata, and background sub-agent tasks in SQLite.
- Loads skills from workspace, user, and shared skill directories, with built-in skills installed on first use.
- Compresses long contexts and stores timeline summaries for later recall.
- Can register tools discovered from configured MCP servers.
## Architecture ## 适合做什么
```text - 在终端里和本地 AI 助手持续对话。
Channel -> MessageBus -> SessionManager -> AgentLoop -> LLM Provider - 将同一套 Agent 能力接入飞书/Lark。
| | - 让 Agent 使用本地文件、Shell、搜索、HTTP、浏览器、MCP 工具完成任务。
| v - 把长期偏好、事实和历史摘要存成可检索记忆。
| Tools - 用 Cron 定时执行任务,并把结果发回目标渠道。
v - 通过 Skills 为 Agent 注入项目知识和专用操作指南。
SQLite
Control messages -> SessionManager -> MessageBus -> OutboundDispatcher -> Channel ## 快速开始
```
The main runtime boundary is: ### 1. 准备环境
- `channels` only receive and send external messages. 需要:
- `bus` is an async queue, not a router.
- `session` owns dialog lifecycle, persistence, memory recall, prompt assembly, compression, and task cancellation.
- `agent` runs the stateless LLM/tool loop.
- `providers` are HTTP clients for model APIs.
- `tools` execute agent actions and return string results.
- `storage` owns SQLite schema and CRUD.
- `scheduler` polls due jobs and feeds prompts back into sessions.
## Features - Rust toolchain项目使用 edition 2024。
- 一个可用的 LLM Provider API Key。
### Channels ### 2. 构建项目
- `cli_chat`: terminal TUI client connected through `/ws`.
- `feishu`: Feishu/Lark channel with configurable allow list, media directory, and reaction emoji.
### LLM Providers
- OpenAI-compatible chat completions, including DashScope, Volcengine, and similar APIs.
- Anthropic Messages API.
- Model-specific `input_type` metadata for text/image capability checks.
- JSON Schema cleanup for cross-provider tool compatibility.
### Sessions And Memory
- Session IDs use `<channel>:<chat_id>:<dialog_id>`.
- Each channel/chat can have multiple dialogs.
- Dialog operations include create, list, switch, rename, delete, compact, dump, info, and stop.
- Session history is persisted to SQLite and can be incrementally restored after compression.
- Knowledge memories are recalled into the system prompt each turn.
- Timeline memories are produced by context compression and can be searched later.
### Tools
Base tools registered for the agent:
| Tool | Purpose |
|------|---------|
| `calculator` | Math expressions and statistics |
| `file_read` / `file_write` / `file_edit` | Workspace file operations |
| `file_search` / `content_search` | File and content search |
| `bash` | Run shell commands in the workspace |
| `http_request` | HTTP API requests |
| `web_fetch` | Fetch and extract web page text |
| `get_skill` | List or load local skills |
| `memory_store` / `memory_recall` / `timeline_recall` / `memory_forget` | Long-term memory operations |
| `delegate` | Run inline, background, or parallel sub-agents |
| `send_message` | Send outbound messages to configured channels |
| `chat_manager` | Inspect sessions, channels, and stored messages |
| `cron_add/list/remove/enable/disable/update` | Manage scheduled jobs when scheduler is enabled |
| `browser` | Optional WebDriver browser automation when enabled |
| MCP tools | Dynamically registered from configured MCP servers |
### Skills
Skills are directories containing `SKILL.md`. Load priority is:
1. `{workspace}/skills`
2. `~/.picobot/skills`
3. `~/.agents/skills`
Same-name skills in higher-priority locations override lower-priority ones. Built-in skills from `resources/skills` are embedded into the binary and installed into `~/.picobot/skills` if missing.
## Quick Start
### Prerequisites
- Rust toolchain with edition 2024 support.
- A configured LLM provider API key.
### Build
```bash ```bash
cargo build cargo build
``` ```
### Configure ### 3. 准备配置
PicoBot loads `~/.picobot/config.json` first, then falls back to `./config.json`. On gateway startup, a template is released to `~/.picobot/config.example.json` if it does not exist. The source template is [resources/templates/config.example.json](/home/xiaoxixi/code/PicoBot/resources/templates/config.example.json). PicoBot 按以下顺序加载配置:
Minimal example: 1. `~/.picobot/config.json`
2. 当前目录 `./config.json`
Gateway 首次启动时会把模板释放到 `~/.picobot/config.example.json`。模板源文件在 [resources/templates/config.example.json](resources/templates/config.example.json)。
最小配置示例:
```json ```json
{ {
@ -140,152 +71,242 @@ Minimal example:
} }
``` ```
The `.env` file in the current directory is parsed by PicoBot itself. Values like `<OPENAI_API_KEY>` in JSON are replaced from the process environment after `.env` is loaded. `.env` 会由 PicoBot 自己解析。配置里的 `<OPENAI_API_KEY>` 这类占位符会在 `.env` 和系统环境变量加载后替换。
### Run ### 4. 启动 Gateway
```bash ```bash
cargo run -- gateway cargo run -- gateway
``` ```
The gateway switches the process working directory to `workspace_dir` and stores `picobot.db` there by default. 默认监听 `127.0.0.1:19876`。Gateway 启动后会把进程工作目录切到 `workspace_dir`,默认 SQLite 数据库也会写到该 workspace 下的 `picobot.db`
In another terminal: ### 5. 启动 CLI 客户端
另开一个终端:
```bash ```bash
cargo run -- chat cargo run -- chat
``` ```
The client connects to `ws://127.0.0.1:19876/ws` by default. Override with `--gateway-url`. CLI 默认连接 `ws://127.0.0.1:19876/ws`。如需指定地址,可使用 `--gateway-url`
## Configuration ## 运行时数据流
Top-level config fields: 用户消息进入 PicoBot 后,会被转换为统一的 inbound message经由 MessageBus 交给 SessionManager。SessionManager 选择当前 dialog、组装上下文、调用 AgentLoopAgentLoop 调用模型和工具,最终响应通过 outbound bus 回到原渠道。
| Field | Purpose | ![Message flow](docs/assets/message-flow.svg)
|-------|---------|
| `providers` | Named LLM provider configs |
| `models` | Named model configs |
| `agents` | Agent-to-provider/model binding |
| `gateway` | Bind address, session DB path, cleanup, scheduler, background task limits |
| `client` | Default WebSocket URL for the TUI client |
| `channels` | Channel configs, currently Feishu/Lark |
| `memory` | Recall and consolidation settings |
| `mcp` | MCP server configs |
| `browser` | Optional WebDriver browser tool config |
| `workspace_dir` | Workspace used for file tools, shell commands, DB default, and workspace skills |
Important defaults: 核心边界:
| Key | Default | | 模块 | 职责 |
|-----|---------| |------|------|
| `channels` | 接入外部渠道,只做收发,不直接处理会话或 LLM |
| `bus` | 异步消息队列,承载 inbound、outbound、control 三类消息 |
| `session` | 管理会话生命周期、dialog 操作、上下文、记忆召回、压缩和持久化 |
| `agent` | 执行无状态 LLM/tool 循环,处理模型响应和工具调用 |
| `providers` | OpenAI 兼容接口和 Anthropic Messages API 客户端 |
| `tools` | Agent 可调用工具集合 |
| `storage` | SQLite schema、CRUD、消息和任务持久化 |
| `scheduler` | 轮询 Cron 任务并把任务 prompt 送入目标会话 |
| `skills` | 加载 Skill并把 Skill 指南注入系统提示 |
| `mcp` | 连接 MCP Server将远端工具包装成普通 Tool |
## 核心能力
### 渠道
| 渠道 | 说明 |
|------|------|
| `cli_chat` | Ratatui 终端客户端,通过 WebSocket 连接 Gateway |
| `feishu` | 飞书/Lark 消息、反应、文件上传下载和媒体引用 |
### 会话
Session ID 使用三段式:
```text
<channel>:<chat_id>:<dialog_id>
```
同一个 `channel:chat_id` 下可以有多个 dialog。当前支持的 dialog 操作包括创建、列表、切换、重命名、归档、删除、清空历史、压缩、导出、查看信息和停止任务。
常用 slash commands
| 命令 | 说明 |
|------|------|
| `/new` | 创建新 dialog |
| `/sessions` | 列出最近 dialog |
| `/switch <dialog_id>` | 切换 dialog |
| `/rename <title>` | 重命名当前 dialog |
| `/delete` | 删除当前 dialog 并创建新 dialog |
| `/compact` | 手动压缩上下文 |
| `/info` | 查看当前 dialog 信息 |
| `/dump` | 导出当前 dialog 为 Markdown |
| `/mcp` | 查看 MCP 服务器和工具状态 |
| `/stop` | 停止当前任务并清空队列 |
| `/?`, `/help` | 查看帮助 |
### 记忆
PicoBot 有两类记忆:
| 类型 | 用途 | 生命周期 |
|------|------|----------|
| Knowledge | 偏好、事实、项目规则、长期可复用信息 | 长期保留,手动删除 |
| Timeline | 长对话压缩后的历史摘要 | 默认保留 90 天 |
每轮处理用户消息时MemoryManager 会按用户输入召回最多 `memory.recall_limit` 条 Knowledge并注入系统提示。上下文压缩产生的摘要会保存为 Timeline后续可通过 `timeline_recall` 工具检索。
### 工具
基础工具集:
| 工具 | 说明 |
|------|------|
| `calculator` | 数学表达式和统计计算 |
| `file_read` / `file_write` / `file_edit` | 文件读写和编辑 |
| `file_search` / `content_search` | 文件名和内容搜索 |
| `bash` | 在 workspace 中执行 Shell 命令 |
| `http_request` / `web_fetch` | HTTP 请求和网页文本抽取 |
| `get_skill` | 列出或读取本地 Skill |
| `memory_store` / `memory_recall` / `timeline_recall` / `memory_forget` | 长期记忆操作 |
| `delegate` | 启动 inline、background 或 parallel 子 Agent |
| `send_message` | 向指定渠道发送消息 |
| `chat_manager` | 查看渠道、会话和历史消息 |
| `cron_add/list/remove/enable/disable/update` | 管理定时任务 |
| `browser` | 可选 WebDriver 浏览器自动化 |
| MCP tools | 从配置的 MCP Server 动态发现并注册 |
### Skills
Skill 是包含 `SKILL.md` 的目录。加载优先级从高到低:
1. `{workspace}/skills`
2. `~/.picobot/skills`
3. `~/.agents/skills`
同名 Skill 会按高优先级覆盖低优先级。内置 Skill 位于 [resources/skills](resources/skills),首次运行时会安装到 `~/.picobot/skills`
## 配置速查
顶层配置字段:
| 字段 | 说明 |
|------|------|
| `providers` | LLM Provider 配置 |
| `models` | 模型参数与输入能力 |
| `agents` | Agent 使用哪个 provider/model |
| `gateway` | HTTP/WebSocket、数据库、调度器、后台任务限制 |
| `client` | CLI 客户端默认 Gateway URL |
| `channels` | 渠道配置,目前主要是飞书/Lark |
| `memory` | 记忆召回、归并和 Timeline 保留策略 |
| `mcp` | MCP Server 配置 |
| `browser` | 可选浏览器自动化配置 |
| `workspace_dir` | 文件工具、Shell、数据库和 workspace skills 的工作目录 |
重要默认值:
| 配置 | 默认值 |
|------|--------|
| `gateway.host` | `127.0.0.1` | | `gateway.host` | `127.0.0.1` |
| `gateway.port` | `19876` | | `gateway.port` | `19876` |
| `gateway.max_concurrent_background_tasks` | `10` | | `gateway.max_concurrent_background_tasks` | `10` |
| `gateway.scheduler.enabled` | `true` if `scheduler` is omitted and defaulted | | `gateway.scheduler.enabled` | `true` |
| `client.gateway_url` | `ws://127.0.0.1:19876/ws` | | `client.gateway_url` | `ws://127.0.0.1:19876/ws` |
| `memory.recall_limit` | `5` | | `memory.recall_limit` | `5` |
| `memory.timeline_retention_days` | `90` | | `memory.timeline_retention_days` | `90` |
| `mcp.tool_timeout_secs` | `180` | | `mcp.tool_timeout_secs` | `180` |
| `browser.enabled` | `false` | | `browser.enabled` | `false` |
MCP servers support `stdio`, `sse`, and `streamable-http` transports. Browser automation requires a compatible Chrome/Chromium and chromedriver/WebDriver endpoint. 更完整的配置字段说明见 [resources/skills/about-picobot/references/config.md](resources/skills/about-picobot/references/config.md)。
## Slash Commands
Available from CLI chat and channel text messages:
| Command | Description |
|---------|-------------|
| `/new` | Create a new dialog |
| `/sessions` | List recent dialogs |
| `/switch <dialog_id>` | Switch dialog |
| `/rename <title>` | Rename current dialog |
| `/delete` | Delete current dialog |
| `/compact` | Manually trigger context compression |
| `/info` | Show current dialog information |
| `/dump` | Save current dialog as Markdown |
| `/?`, `/help` | Show help |
| `/mcp` | Show MCP server and tool status |
| `/stop` | Stop active tasks and clear queued messages |
## WebSocket API ## WebSocket API
The gateway exposes: Gateway 暴露:
| Method | Path | Description | | Method | Path | 说明 |
|--------|------|-------------| |--------|------|------|
| `GET` | `/health` | Returns service health and version | | `GET` | `/health` | 健康检查和版本信息 |
| `GET` | `/ws` | WebSocket upgrade for chat clients | | `GET` | `/ws` | WebSocket 聊天协议 |
Inbound WebSocket message types: Inbound 消息类型:
| Type | Main fields | | Type | 主要字段 |
|------|-------------| |------|----------|
| `user_input` | `content`, optional `channel`, `chat_id`, `sender_id` | | `user_input` | `content`,可选 `channel``chat_id``sender_id` |
| `clear_history` | optional `chat_id`, `session_id` | | `clear_history` | 可选 `chat_id``session_id` |
| `create_session` | optional `title` | | `create_session` | 可选 `title` |
| `list_sessions` | `include_archived` | | `list_sessions` | `include_archived` |
| `load_session` | `session_id` | | `load_session` | `session_id` |
| `rename_session` | optional `session_id`, `title` | | `rename_session` | 可选 `session_id``title` |
| `archive_session` | optional `session_id` | | `archive_session` | 可选 `session_id` |
| `delete_session` | optional `session_id` | | `delete_session` | 可选 `session_id` |
| `get_slash_commands` | none | | `get_slash_commands` | |
| `ping` | none | | `ping` | |
Outbound WebSocket message types include `assistant_response`, `error`, `session_established`, `session_created`, `session_list`, `session_loaded`, `session_renamed`, `session_archived`, `session_deleted`, `history_cleared`, `slash_commands_list`, `pong`, `command_executed`, and `system_notification`. Outbound 消息类型包括 `assistant_response``error``session_established``session_created``session_list``session_loaded``session_renamed``session_archived``session_deleted``history_cleared``slash_commands_list``pong``command_executed``system_notification`
## Testing ## 测试
```bash ```bash
# Unit tests # 单元测试
cargo test --lib cargo test --lib
# Integration tests require real API keys in tests/test.env # 集成测试需要 tests/test.env 中有真实 API key
cp tests/test.env.example tests/test.env cp tests/test.env.example tests/test.env
cargo test --test test_integration -- --ignored cargo test --test test_integration -- --ignored
cargo test --test test_tool_calling -- --ignored cargo test --test test_tool_calling -- --ignored
cargo test --test test_request_format -- --ignored cargo test --test test_request_format -- --ignored
``` ```
Integration tests are ignored by default because they make real provider calls. 集成测试默认 `#[ignore]`,因为它们会真实调用模型 API。
## Project Layout ## 项目结构
```text ```text
src/ src/
agent/ LLM loop, context compression, system prompts, media handling, sub-agents agent/ LLM loop、上下文压缩、系统提示、媒体处理、子 Agent
bus/ Inbound, outbound, and control message queues bus/ inbound、outbound、control 消息队列
channels/ CLI chat and Feishu/Lark integrations channels/ CLI chat 和飞书/Lark 集成
client/ Ratatui terminal UI client/ Ratatui 终端 UI
config/ Config loading, env substitution, path expansion config/ 配置加载、环境变量替换、路径展开
gateway/ Axum HTTP/WebSocket server and GatewayState wiring gateway/ Axum HTTP/WebSocket server 和 GatewayState 装配
mcp/ MCP client connections and tool wrappers mcp/ MCP 客户端连接和工具包装
memory/ Memory manager and memory types memory/ 记忆管理和记忆类型
observability/ Agent/tool telemetry observer interfaces observability/ Agent/tool telemetry observer
providers/ OpenAI-compatible and Anthropic clients providers/ OpenAI 兼容和 Anthropic provider
scheduler/ Scheduled job runtime scheduler/ 定时任务运行时
session/ Session lifecycle, dialog commands, persistence integration session/ 会话生命周期、dialog 命令、持久化集成
skills/ Skill loading and embedded built-in skill installation skills/ Skill 加载和内置 Skill 安装
storage/ SQLite schema and CRUD storage/ SQLite schema CRUD
tools/ Agent tool implementations tools/ Agent 工具实现
resources/ resources/
skills/ Built-in skills embedded at build time skills/ 构建时嵌入的内置 Skills
templates/ Config, AGENTS.md, and USER.md templates released on first run templates/ 首次运行释放的配置和用户模板
tests/ Unit and ignored integration tests tests/ 单元测试和 ignored 集成测试
reference/ Third-party reference code; do not modify as project source docs/ 分析报告、文档插图和补充资料
``` ```
## Key Dependencies ## 关键依赖
| Crate | Purpose | | Crate | 用途 |
|-------|---------| |-------|------|
| `axum`, `tokio`, `tokio-tungstenite` | Gateway and WebSocket runtime | | `axum`, `tokio`, `tokio-tungstenite` | Gateway 和 WebSocket runtime |
| `sqlx` | SQLite persistence | | `sqlx` | SQLite 持久化 |
| `reqwest` | LLM and HTTP clients | | `reqwest` | LLM 和 HTTP 客户端 |
| `ratatui`, `crossterm`, `termimad` | Terminal UI | | `ratatui`, `crossterm`, `termimad` | 终端 UI |
| `rmcp` | MCP client support | | `rmcp` | MCP 客户端 |
| `fantoccini` | Optional browser automation | | `fantoccini` | 可选浏览器自动化 |
| `cron`, `chrono-tz` | Scheduling | | `cron`, `chrono-tz` | 定时任务 |
| `jieba-rs` | Chinese tokenization for memory search | | `jieba-rs` | 中文记忆检索分词 |
| `zstd`, `tar` | Embedded built-in skill packaging | | `zstd`, `tar` | 内置 Skill 打包和释放 |
## 进一步阅读
- [架构机制](resources/skills/about-picobot/references/architecture.md)
- [配置说明](resources/skills/about-picobot/references/config.md)
- [命令说明](resources/skills/about-picobot/references/commands.md)
- [工具说明](resources/skills/about-picobot/references/tools.md)
- [数据库结构](resources/skills/about-picobot/references/db-schema.md)
- [代码质量分析](docs/CODE_QUALITY_ANALYSIS.md)

View File

@ -0,0 +1,101 @@
<svg xmlns="http://www.w3.org/2000/svg" width="1120" height="460" viewBox="0 0 1120 460" role="img" aria-labelledby="title desc">
<title id="title">PicoBot message flow</title>
<desc id="desc">Message flow from channel input through bus, session manager, agent loop, tools, provider, storage, outbound dispatcher, and back to the channel.</desc>
<defs>
<style>
.bg { fill: #fbfbf8; }
.lane { fill: #ffffff; stroke: #d6d3d1; stroke-width: 2; rx: 18; }
.step { fill: #f4f4f5; stroke: #71717a; stroke-width: 1.6; rx: 12; }
.in { fill: #e0f2fe; stroke: #0284c7; }
.state { fill: #ecfdf5; stroke: #059669; }
.agent { fill: #fff7ed; stroke: #ea580c; }
.out { fill: #f5f3ff; stroke: #7c3aed; }
.text { font-family: ui-sans-serif, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; fill: #18181b; }
.title { font-size: 30px; font-weight: 800; }
.label { font-size: 17px; font-weight: 700; }
.small { font-size: 14px; fill: #52525b; }
.arrow { stroke: #3f3f46; stroke-width: 2.4; fill: none; marker-end: url(#arrow); }
.soft { stroke: #71717a; stroke-width: 2; fill: none; stroke-dasharray: 7 6; marker-end: url(#arrow-soft); }
.num { fill: #18181b; font-size: 13px; font-weight: 800; }
.badge { fill: #ffffff; stroke: #a1a1aa; }
</style>
<marker id="arrow" markerWidth="12" markerHeight="12" refX="10" refY="6" orient="auto">
<path d="M2,2 L10,6 L2,10 Z" fill="#3f3f46" />
</marker>
<marker id="arrow-soft" markerWidth="12" markerHeight="12" refX="10" refY="6" orient="auto">
<path d="M2,2 L10,6 L2,10 Z" fill="#71717a" />
</marker>
</defs>
<rect class="bg" width="1120" height="460" />
<text class="text title" x="48" y="56">Message Flow</text>
<text class="text small" x="48" y="82">A user message becomes a session-scoped agent run, then returns to the original channel.</text>
<rect class="lane" x="38" y="122" width="1044" height="236" />
<g transform="translate(70 182)">
<rect class="step in" width="130" height="78" />
<circle class="badge" cx="18" cy="18" r="13" />
<text class="text num" x="14" y="23">1</text>
<text class="text label" x="32" y="37">Channel</text>
<text class="text small" x="22" y="60">CLI / Feishu</text>
</g>
<g transform="translate(230 182)">
<rect class="step" width="130" height="78" />
<circle class="badge" cx="18" cy="18" r="13" />
<text class="text num" x="14" y="23">2</text>
<text class="text label" x="30" y="37">MessageBus</text>
<text class="text small" x="29" y="60">inbound queue</text>
</g>
<g transform="translate(390 182)">
<rect class="step state" width="150" height="78" />
<circle class="badge" cx="18" cy="18" r="13" />
<text class="text num" x="14" y="23">3</text>
<text class="text label" x="34" y="37">SessionManager</text>
<text class="text small" x="31" y="60">dialog + context</text>
</g>
<g transform="translate(580 182)">
<rect class="step agent" width="130" height="78" />
<circle class="badge" cx="18" cy="18" r="13" />
<text class="text num" x="14" y="23">4</text>
<text class="text label" x="34" y="37">AgentLoop</text>
<text class="text small" x="23" y="60">LLM/tool loop</text>
</g>
<g transform="translate(750 182)">
<rect class="step agent" width="130" height="78" />
<circle class="badge" cx="18" cy="18" r="13" />
<text class="text num" x="14" y="23">5</text>
<text class="text label" x="48" y="37">Tools</text>
<text class="text small" x="29" y="60">side effects</text>
</g>
<g transform="translate(920 182)">
<rect class="step out" width="130" height="78" />
<circle class="badge" cx="18" cy="18" r="13" />
<text class="text num" x="14" y="23">6</text>
<text class="text label" x="30" y="37">Response</text>
<text class="text small" x="28" y="60">outbound bus</text>
</g>
<path class="arrow" d="M200 221 H230" />
<path class="arrow" d="M360 221 H390" />
<path class="arrow" d="M540 221 H580" />
<path class="arrow" d="M710 221 H750" />
<path class="arrow" d="M880 221 H920" />
<path class="soft" d="M455 182 C460 120 620 116 652 181" />
<text class="text small" x="500" y="126">recall memory + build prompt</text>
<path class="soft" d="M645 260 C640 322 482 326 458 260" />
<text class="text small" x="496" y="334">persist messages and metadata</text>
<path class="soft" d="M815 182 C820 122 918 122 958 181" />
<text class="text small" x="828" y="126">provider calls</text>
<path class="arrow" d="M985 260 C972 390 139 392 135 260" />
<text class="text small" x="420" y="408">OutboundDispatcher routes the reply back to the same channel/chat scope.</text>
</svg>

After

Width:  |  Height:  |  Size: 4.6 KiB

View File

@ -0,0 +1,88 @@
<svg xmlns="http://www.w3.org/2000/svg" width="1120" height="620" viewBox="0 0 1120 620" role="img" aria-labelledby="title desc">
<title id="title">PicoBot runtime architecture</title>
<desc id="desc">High level architecture diagram showing channels, gateway, message bus, session manager, agent loop, tools, providers, storage, scheduler, skills, and MCP.</desc>
<defs>
<style>
.bg { fill: #f8fafc; }
.panel { fill: #ffffff; stroke: #cbd5e1; stroke-width: 2; rx: 18; }
.box { fill: #f1f5f9; stroke: #64748b; stroke-width: 1.5; rx: 12; }
.accent { fill: #e0f2fe; stroke: #0284c7; }
.warm { fill: #fff7ed; stroke: #ea580c; }
.green { fill: #ecfdf5; stroke: #059669; }
.violet { fill: #f5f3ff; stroke: #7c3aed; }
.text { font-family: ui-sans-serif, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; fill: #0f172a; }
.small { font-size: 16px; }
.label { font-size: 18px; font-weight: 700; }
.title { font-size: 30px; font-weight: 800; }
.note { font-size: 14px; fill: #475569; }
.arrow { stroke: #334155; stroke-width: 2.4; fill: none; marker-end: url(#arrow); }
.soft { stroke: #64748b; stroke-width: 2; fill: none; stroke-dasharray: 7 6; marker-end: url(#arrow-soft); }
</style>
<marker id="arrow" markerWidth="12" markerHeight="12" refX="10" refY="6" orient="auto">
<path d="M2,2 L10,6 L2,10 Z" fill="#334155" />
</marker>
<marker id="arrow-soft" markerWidth="12" markerHeight="12" refX="10" refY="6" orient="auto">
<path d="M2,2 L10,6 L2,10 Z" fill="#64748b" />
</marker>
</defs>
<rect class="bg" width="1120" height="620" />
<text class="text title" x="48" y="56">PicoBot Runtime Architecture</text>
<text class="text note" x="48" y="82">Channels stay thin, SessionManager owns conversation state, AgentLoop remains stateless.</text>
<rect class="panel" x="40" y="118" width="220" height="360" />
<text class="text label" x="70" y="154">Channels</text>
<rect class="box accent" x="70" y="185" width="160" height="58" />
<text class="text small" x="106" y="220">CLI TUI</text>
<rect class="box accent" x="70" y="263" width="160" height="58" />
<text class="text small" x="104" y="298">Feishu/Lark</text>
<rect class="box accent" x="70" y="341" width="160" height="58" />
<text class="text small" x="109" y="376">WebSocket</text>
<text class="text note" x="70" y="437">Only receive and send</text>
<rect class="panel" x="330" y="118" width="450" height="360" />
<text class="text label" x="360" y="154">Gateway Core</text>
<rect class="box" x="370" y="184" width="160" height="64" />
<text class="text small" x="414" y="222">MessageBus</text>
<rect class="box green" x="580" y="184" width="160" height="64" />
<text class="text small" x="606" y="222">SessionManager</text>
<rect class="box warm" x="580" y="288" width="160" height="64" />
<text class="text small" x="621" y="326">AgentLoop</text>
<rect class="box" x="370" y="288" width="160" height="64" />
<text class="text small" x="402" y="326">Outbound</text>
<text class="text small" x="399" y="346">Dispatcher</text>
<rect class="box violet" x="475" y="390" width="200" height="54" />
<text class="text small" x="514" y="423">Control Channel</text>
<rect class="panel" x="850" y="118" width="230" height="360" />
<text class="text label" x="880" y="154">Capabilities</text>
<rect class="box warm" x="880" y="185" width="170" height="50" />
<text class="text small" x="929" y="216">Tools</text>
<rect class="box warm" x="880" y="249" width="170" height="50" />
<text class="text small" x="916" y="280">Providers</text>
<rect class="box green" x="880" y="313" width="170" height="50" />
<text class="text small" x="926" y="344">SQLite</text>
<rect class="box violet" x="880" y="377" width="170" height="50" />
<text class="text small" x="925" y="408">Skills</text>
<text class="text note" x="880" y="457">MCP tools join ToolRegistry</text>
<rect class="box green" x="300" y="520" width="165" height="54" />
<text class="text small" x="340" y="553">Scheduler</text>
<rect class="box violet" x="505" y="520" width="165" height="54" />
<text class="text small" x="552" y="553">Memory</text>
<rect class="box accent" x="710" y="520" width="165" height="54" />
<text class="text small" x="759" y="553">MCP</text>
<path class="arrow" d="M260 216 H370" />
<path class="arrow" d="M530 216 H580" />
<path class="arrow" d="M660 248 V288" />
<path class="arrow" d="M740 320 H880" />
<path class="arrow" d="M880 274 H750" />
<path class="arrow" d="M580 320 H530" />
<path class="arrow" d="M370 320 H260" />
<path class="soft" d="M575 390 V352" />
<path class="soft" d="M660 248 C750 250 800 330 880 338" />
<path class="soft" d="M382 520 C420 470 520 465 610 352" />
<path class="soft" d="M588 520 C600 470 620 420 650 352" />
<path class="soft" d="M792 520 C825 470 860 430 880 402" />
</svg>

After

Width:  |  Height:  |  Size: 4.9 KiB