# PicoBot PicoBot 是一个用 Rust 编写的个人 AI 助手运行时。它在本地启动 Gateway,接入 CLI TUI、飞书/Lark 等聊天渠道,把会话、消息、记忆和定时任务持久化到 SQLite,并为 Agent 提供文件、Shell、HTTP、Web、MCP、Skill、记忆、浏览器和子 Agent 等工具能力。 它更像一个可扩展的“个人助手操作系统”:渠道负责收发消息,SessionManager 负责会话和上下文,AgentLoop 负责模型与工具循环,Storage 负责可靠落盘。 完整的组件边界、并发不变量、启动/关停顺序和扩展指南见 [架构文档](docs/ARCHITECTURE.md)。 ## 适合做什么 - 在终端里和本地 AI 助手持续对话。 - 从脚本或命令行发送一条任务,等待完整的模型/工具循环后只输出最终结果。 - 在 TUI 或浏览器中实时查看正文、思考过程和工具执行状态,并在完成后收敛到持久化历史。 - 在浏览器中查看日志、任务和记忆,修改运行配置与助手档案。 - 复杂任务可创建 session 级 Todo 计划,把不同子项并行委托给多个子 Agent;聊天页侧栏实时显示进度。 - 将同一套 Agent 能力接入飞书/Lark,并可选用单张卡片实时更新回复。 - 让 Agent 使用本地文件、Shell、搜索、HTTP、浏览器、MCP 工具完成任务。 - 把长期偏好、事实和历史摘要存成可检索记忆。 - 用 Cron 定时执行任务,并把结果发回目标渠道。 - 通过 Skills 为 Agent 注入项目知识和专用操作指南。 ## 快速开始 ### 1. 准备环境 需要: - Rust toolchain,项目使用 edition 2024。 - 一个可用的 LLM Provider API Key。 ### 2. 构建项目 ```bash cargo build ``` ### 3. 准备配置 PicoBot 按以下顺序加载配置: 1. `~/.picobot/config.json` 2. 当前目录 `./config.json` Gateway 首次启动时会把模板释放到 `~/.picobot/config.example.json`。模板源文件在 [resources/templates/config.example.json](resources/templates/config.example.json)。 最小配置示例: ```json { "providers": { "openai": { "type": "openai", "base_url": "https://api.openai.com/v1", "api_key": "", "extra_headers": {} } }, "models": { "gpt-4o": { "model_id": "gpt-4o", "temperature": 0.7, "max_tokens": 4096, "input_type": ["text", "image"] } }, "agents": { "default": { "provider": "openai", "model": "gpt-4o", "max_tool_iterations": 99, "token_limit": 128000 } }, "workspace_dir": "~/.picobot/workspace" } ``` `.env` 会在启动时由 PicoBot 自己解析,不依赖 dotenv。环境变量按以下顺序分层,越靠后优先级越高: 1. `config.json` 所在目录的 `.env`,作为所有 workspace 共用的基础配置。 2. `workspace_dir/.env`,用于当前 workspace 的覆盖值。 3. 启动 PicoBot 时进程中已有的环境变量,例如 Docker Compose 的 `environment`,优先级最高且不会被文件覆盖。 合并后的值既用于替换配置里的 `` 等占位符,也会写入 PicoBot 进程环境,供 MCP Server 和工具子进程继承。`workspace_dir` 的位置由配置目录层和进程环境决定;workspace 自己的 `.env` 不能反过来修改 `workspace_dir`。 ### 4. 启动 Gateway ```bash cargo run -- gateway ``` 默认监听 `127.0.0.1:19876`。Gateway 启动后会把进程工作目录切到 `workspace_dir`,默认 SQLite 数据库也会写到该 workspace 下的 `picobot.db`。 监听地址可通过配置文件或命令行覆盖。命令行参数优先于 `config.json`: ```json { "gateway": { "host": "0.0.0.0", "port": 19876 } } ``` ```bash picobot gateway --host 0.0.0.0 --port 19876 ``` Docker Compose 默认让容器内 Gateway 监听所有 IPv4 接口。监听地址、宿主机发布地址和端口均可通过环境变量调整: ```bash PICOBOT_GATEWAY_HOST=0.0.0.0 \ PICOBOT_PUBLISH_HOST=192.168.1.10 \ PICOBOT_GATEWAY_PORT=19876 \ docker compose up -d ``` `PICOBOT_GATEWAY_HOST` 是容器内进程的监听地址;`PICOBOT_PUBLISH_HOST` 是 Docker 在宿主机上发布端口的地址。对局域网开放时应保持 `gateway.require_pairing=true`,并由防火墙限制可信网段。 ### 5. 启动 CLI 客户端 另开一个终端: ```bash cargo run -- chat ``` CLI 默认连接 `ws://127.0.0.1:19876/ws`。TUI 首次使用先运行 `picobot pair`,再执行 `picobot chat --pair-code `;客户端令牌会以 `0600` 权限保存到 `~/.picobot/tui_auth_token`。如需指定地址,可使用 `--gateway-url`。 ### 5.1 一次性执行 `run` 通过 Gateway 发送一条消息,复用正常的 SessionManager、AgentLoop 和工具调用流程,收到 Turn 终态后打印最终回复并退出: ```bash picobot run "检查这个项目并总结测试结果" printf '使用浏览器打开 example.com 并返回页面标题\n' | picobot run ``` 默认情况下 stdout 只包含最终回复,便于管道和脚本消费。`--verbose` 把阶段和工具状态写到 stderr;`--json` 输出包含 session、turn、状态、正文、usage 和错误的一行 JSON;`--timeout` 设置最大等待秒数。超时或按下 Ctrl-C 时,客户端会先向当前会话发送 `/stop`。 连接本机回环地址时不需要人工配对:`run` 自动读取 `~/.picobot/web_admin_token`,Gateway 只有在真实 TCP 对端也是回环地址时才允许该凭据访问 `/ws`。每次调用使用独立的临时 chat scope,不会替换正在运行的 TUI 连接。连接远程 Gateway 时仍使用 `~/.picobot/tui_auth_token` 中已有的配对令牌。 ### 5.2 使用 WebUI Gateway 启动后直接打开: ```text http://127.0.0.1:19876/ ``` 新浏览器默认不能直接进入。请在运行 Gateway 的同一台设备上生成一次性配对码: ```bash picobot pair ``` 在浏览器配对页输入输出的 8 位代码即可。配对码 5 分钟内有效且只能使用一次;浏览器凭据由 HttpOnly Cookie 保存。需要撤销全部浏览器和 CLI 客户端时运行 `picobot pair --revoke-all`,再用新代码重新配对。 Docker 部署必须在 Gateway 容器内签发配对码,使请求来自容器自身回环地址并能读取映射目录中的管理密钥: ```bash docker compose exec picobot picobot pair --gateway-url http://127.0.0.1:19876 ``` 不要从宿主机经发布端口直接调用签发接口;容器会把该连接识别为非回环来源并拒绝。`picobot` 已加入正式镜像的 `PATH`,可在容器 shell 中直接调用。 WebUI 随二进制嵌入,不需要 Node.js、npm 或单独部署静态文件,提供: - 在线聊天、会话创建/切换、历史回放、流式 Markdown、独立思考区、实时工具状态、可折叠历史工具调用卡片,以及基于 Gateway 实时命令清单的 `/` 斜杠命令补全。 - 文件选择、拖放和剪贴板图片上传;消息中的附件可预览或下载。附件按服务端路径引用,原文件移动或删除后历史附件可能不可用。 - 可持久化的浅色/深色主题,首次访问时跟随系统偏好。 - Cron 定时任务、最近运行记录和后台子任务状态。 - 当前聊天 session 的可展开 Todo 侧栏;计划变化时自动展开,其他 session 的变化显示未读提示。 - Knowledge/Timeline 记忆的分类与全文检索。 - 本地滚动日志的尾部查看、过滤和自动刷新。 - `config.json`、`~/.picobot/USER.md`、`~/.picobot/AGENTS.md` 编辑。 配置接口会掩码 API Key、secret、password 和 token;保留 `********` 再保存不会覆盖原密钥。运行配置采用原子写入,保存后可执行 `picobot reload` 或发送 `/reload` 热重载;`USER.md` 和 `AGENTS.md` 的修改用于后续构建的 Agent 上下文。 WebUI 默认启用设备配对鉴权,管理 API 与 `/ws` 都拒绝未配对客户端;静态配对页、公开健康检查和配对提交接口除外。唯一的 WebSocket 例外是本机 `picobot run`:请求必须同时来自真实回环对端并持有权限为 `0600` 的 `~/.picobot/web_admin_token`,该管理令牌不能绕过任何管理 API 的设备鉴权。配对令牌只以 SHA-256 哈希写入 `~/.picobot/web_auth.json`。鉴权不提供传输加密;如果通过 `--host 0.0.0.0`、反向代理或端口转发暴露 Gateway,仍必须使用 TLS。可通过 `gateway.require_pairing=false` 显式关闭配对,但不建议在非隔离环境使用。 #### WebUI 开发 WebUI 源码位于 `webui/`,使用 Svelte 5、Vite 和无样式的 Bits UI 可访问组件原语。运行发布版 PicoBot 不需要 Node.js;从源码编译或修改前端时需要 Node.js 20+: ```bash cd webui npm ci npm run check npm run build ``` 直接运行 `npm run build` 会在被忽略的 `webui/dist/` 生成独立检查产物。正常执行 `cargo build` 时,`build.rs` 会监听前端源码和构建配置,只有它们发生变化时才调用 Vite,将生产资源生成到 Cargo `OUT_DIR` 并嵌入二进制;`node_modules` 缺失或 `package-lock.json` 变化时会先自动运行 `npm ci`。前端产物不提交到仓库。 ### 6. 作为 systemd 用户服务运行(Linux) 安装会把当前 PicoBot 可执行文件注册为 `picobot.service` 并设置为登录后自动启动;安装本身不会立即启动 Gateway: ```bash picobot service install picobot service start ``` 服务管理命令: ```bash picobot service status picobot service restart picobot service stop picobot service uninstall ``` 修改配置后无需重启 systemd service: ```bash picobot reload ``` 该命令连接正在运行的 Gateway,先解析并校验新配置,再停止接收新工作,等待当前交互 Turn、Scheduler job 和后台子 Agent 到达安全边界后切换运行代。也可在聊天中发送 `/reload`,或让根交互 Agent 在用户明确要求时调用 `reload_config` 工具;子 Agent 与定时任务不能触发重载。监听地址、workspace 和数据库路径涉及进程级资源,不能热重载;修改这些字段时命令会保留旧配置并提示使用 `picobot service restart`。重载会主动断开 WebSocket,TUI/WebUI 随后可重新连接并从持久化历史恢复。受认证客户端可通过 `GET /api/config/reload/status` 查询 generation、切换阶段和最近错误。 unit 位于 `~/.config/systemd/user/picobot.service`,以执行 `service install` 时的当前目录作为初始工作目录。服务异常退出时由 systemd 自动重启;`stop` 和 `restart` 会通过 SIGTERM 触发 Gateway 的有界优雅关停。 TUI 会把一个随机客户端标识保存到 `~/.picobot/tui_client_id`,因此关闭并重新打开客户端后会恢复同一组 dialog 和最近使用的会话。界面支持流式正文、独立思考与工具状态、历史回放、会话列表与归档筛选、命令补全、Unicode/中文编辑、括号粘贴、多行输入和文件传输。 常用快捷键: | 快捷键 | 操作 | |--------|------| | `F1` / `Ctrl+H` | 打开帮助 | | `Tab` / `Ctrl+S` | 切换焦点 / 聚焦会话列表 | | `Ctrl+N` | 新建会话 | | `Ctrl+R` / `Ctrl+A` / `Ctrl+D` | 重命名 / 归档 / 删除所选会话 | | `Ctrl+L` / `Ctrl+O` | 清空历史 / 显示归档会话 | | `Enter` / `Shift+Enter` | 发送 / 换行 | | `Ctrl+F` / `F2` | 添加附件 / 下载历史中最近的附件 | WebUI/TUI 上传文件默认保存到 `~/.picobot/media/cli_chat`,单文件上限 25 MiB;可通过 `gateway.file_transfer` 调整目录、上限和待发送上传的有效期。消息只保存文件路径,不保证文件被移动或删除后仍能下载。 | `PageUp` / `PageDown` | 滚动对话历史 | | 连按两次 `Ctrl+C` | 退出客户端 | ## 运行时数据流 用户消息进入 PicoBot 后,会被转换为统一的 inbound message,经由 MessageBus 交给 SessionManager。SessionManager 选择当前 dialog、组装上下文并创建活动 Turn;AgentLoop 消费 Provider 原生流、执行工具并发出结构化事件,TurnController 将它们归约成可丢中间帧的完整快照。DeliveryCoordinator 把快照投影给 TUI、WebUI 或 Channel,最终消息在 SQLite 原子提交成功后才进入 `Completed`。 详细时序和失败语义见 [架构文档:消息与控制数据流](docs/ARCHITECTURE.md#4-消息与控制数据流)。 同一 session 的普通消息由专属有界队列串行处理,不同 session 可以并发;活动 Turn 使用 latest-wins 快照,慢展示端只跳过中间状态,不反压模型。普通出站消息按 `(channel, chat_id)` 分 lane 保序,两条投递路径共享目标写锁。Gateway 的长生命周期任务统一由 `TaskSupervisor` 取消和限时回收。 核心边界: | 模块 | 职责 | |------|------| | `channels` | 接入外部渠道,只做收发,不直接处理会话或 LLM | | `bus` | 异步消息队列,承载 inbound、outbound、control 三类消息 | | `session` | 管理会话生命周期、dialog 操作、上下文、记忆召回、压缩和持久化 | | `agent` | 执行无状态 LLM/tool 循环,处理模型响应和工具调用 | | `providers` | OpenAI 兼容接口和 Anthropic Messages API 的原生流解析与回放 | | `delivery` | 活动 Turn 的展示过滤、latest-wins 节流、终态投递与 TurnSink 生命周期 | | `tools` | Agent 可调用工具集合 | | `storage` | SQLite schema、CRUD、消息和任务持久化 | | `scheduler` | 领取定时任务,执行普通/巡检 Agent,并按投递策略记录或发送结果 | | `work` | 管理 session 级单 active plan、并行子项状态和 WebSocket 变更事件 | | `skills` | 加载 Skill,并把 Skill 指南注入系统提示 | | `mcp` | 连接 MCP Server,将远端工具包装成普通 Tool | | `task_supervisor` | 统一管理 Gateway 后台任务的取消和有界关停 | ## 核心能力 ### 渠道 | 渠道 | 说明 | |------|------| | `cli_chat` | Ratatui 终端客户端,通过 WebSocket 连接 Gateway | | `feishu` | 飞书/Lark 消息、反应、文件上传下载和媒体引用 | 飞书默认只接受 `allow_from` 中的用户,且群聊消息必须明确 @ 机器人(可通过 `channels.feishu.require_mention=false` 关闭)。回复会使用飞书原生引用/话题语义保持在原消息位置。默认只发送终态结果;设置 `channels.feishu.live_updates=true` 后会创建一张卡片并持续编辑,`live_update_interval_ms` 默认 500ms,运行时限制在 250–5000ms。外部渠道始终不会收到模型 reasoning。 ### 会话 Session ID 使用三段式: ```text :: ``` 同一个 `channel:chat_id` 下可以有多个 dialog。当前支持的 dialog 操作包括创建、列表、切换、重命名、归档、删除、清空历史、压缩、导出、查看信息和停止任务。 常用 slash commands: | 命令 | 说明 | |------|------| | `/new` | 创建新 dialog | | `/sessions` | 列出最近 dialog | | `/switch ` | 切换 dialog | | `/rename ` | 重命名当前 dialog | | `/delete` | 删除当前 dialog 并创建新 dialog | | `/compact` | 手动压缩上下文 | | `/info` | 查看当前 dialog 信息 | | `/dump` | 导出当前 dialog 为 Markdown | | `/mcp` | 查看 MCP 服务器和工具状态 | | `/stop` | 停止当前任务并清空队列 | | `/todo [done\|cancel]` | 查看、完成或取消当前 session 的任务计划 | | `/reload` | 校验并重新加载 Gateway 配置 | | `/?`, `/help` | 查看帮助 | ### 记忆 PicoBot 有两类记忆: | 类型 | 用途 | 生命周期 | |------|------|----------| | Knowledge | 偏好、事实、项目规则、长期可复用信息 | 长期保留,手动删除 | | Timeline | 长对话压缩后的历史摘要 | 默认保留 90 天 | 每轮处理用户消息时,MemoryManager 会按用户输入召回 Knowledge,并作为运行时上下文附加到本轮用户消息。当前召回上限固定为 5;`memory.recall_limit` 已支持解析但尚未接入 worker。上下文压缩产生的摘要会保存为 Timeline,后续可通过 `timeline_recall` 工具检索。Scheduler 默认创建一个每日维护巡检,按 `memory.timeline_retention_days` 清理过期 Timeline;Knowledge 不会被自动删除。 ### 工具 基础工具集: | 工具 | 说明 | |------|------| | `calculator` | 数学表达式和统计计算 | | `file_read` / `file_write` / `file_edit` | 文件读写和编辑;`file_read` 读取受支持图片时可将图片直接提供给多模态模型 | | `file_search` / `content_search` | 文件名和内容搜索 | | `bash` | 在 workspace 中执行 Shell 命令 | | `http_request` / `web_fetch` | HTTP 请求和网页文本抽取 | | `get_skill` | 列出或读取本地 Skill | | `memory_store` / `memory_recall` / `timeline_recall` / `memory_forget` | 长期记忆操作 | | `reload_config` | 在用户明确要求时校验并重新加载 Gateway 配置 | | `delegate` | 启动 inline、background 或 parallel 子 Agent | | `todo` | 为复杂、多轮任务创建并更新当前 session 的持久化计划 | | `send_message` | 向指定渠道或当前会话发送消息,可附带文件/截图;WebUI/TUI 当前 Turn 的附件并入最终回复 | | `chat_manager` | 查看渠道、会话和历史消息 | | `cron_add/list/remove/enable/disable/update` | 管理定时任务 | | `routine_maintenance` | 安全清理超过保留期的 Timeline,不删除 Knowledge | | `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.port` | `19876` | | `gateway.require_pairing` | `true` | | `gateway.max_concurrent_background_tasks` | `10` | | `gateway.scheduler.enabled` | `true` | | `client.gateway_url` | `ws://127.0.0.1:19876/ws` | | `memory.recall_limit` | `5`(当前运行时固定为 5) | | `memory.timeline_retention_days` | `90` | | `mcp.tool_timeout_secs` | `180` | | `browser.enabled` | `false` | | `channels.feishu.live_updates` | `false` | | `channels.feishu.live_update_interval_ms` | `500` | | `channels.feishu.require_mention` | `true` | | `channels.feishu.max_image_bytes` | `10485760` | | `channels.feishu.max_file_bytes` | `26214400` | | `channels.feishu.media_dir_max_bytes` | `536870912` | | `channels.feishu.request_timeout_secs` | `30` | 更完整的配置字段说明见 [resources/skills/about-picobot/references/config.md](resources/skills/about-picobot/references/config.md)。 ## WebSocket API Gateway 暴露: | Method | Path | 说明 | |--------|------|------| | `GET` | `/health` | 健康检查和版本信息 | | `GET` | `/api/auth/status` | 当前设备配对状态 | | `POST` | `/api/auth/pair` | 用一次性代码配对设备 | | `POST` | `/api/auth/code` | 本机 CLI 签发配对码;要求回环来源和管理密钥 | | `GET` | `/ws` | WebSocket 聊天协议;要求配对 Cookie 或 Bearer token | Inbound 消息类型: | Type | 主要字段 | |------|----------| | `user_input` | `content`,可选 `channel`、`chat_id`、`sender_id` | | `clear_history` | 可选 `chat_id`、`session_id` | | `create_session` | 可选 `title` | | `list_sessions` | `include_archived` | | `load_session` | `session_id` | | `get_session_history` | `session_id`,可选 `limit`(服务端限制为 1–2000) | | `rename_session` | 可选 `session_id`,`title` | | `archive_session` | 可选 `session_id` | | `delete_session` | 可选 `session_id` | | `get_slash_commands` | 无 | | `ping` | 无 | Outbound 消息类型包括活动 Turn 使用的 `turn_updated`,以及 `assistant_response`、`error`、`session_established`、`session_created`、`session_list`、`session_loaded`、`session_history`、`session_renamed`、`session_archived`、`session_deleted`、`history_cleared`、`slash_commands_list`、`pong`、`command_executed` 和 `system_notification`。`turn_updated` 每次携带完整快照和单调 revision,客户端只替换当前 session 的活动 Turn;`assistant_response` 保留给独立完整消息。历史消息可包含 reasoning、turn/iteration、completion status 和结构化工具元数据,但不会暴露 Provider 私有回放状态。 ## 测试 ```bash # 单元测试 cargo test --lib # 离线集成/协议测试 cargo test --test test_scheduler cargo test --test test_request_format # 模型 API 集成测试需要 tests/test.env 中有真实 API key cp tests/test.env.example tests/test.env cargo test --test test_integration -- --ignored cargo test --test test_tool_calling -- --ignored ``` 会真实调用模型 API 的测试标记为 `#[ignore]`;离线集成测试默认执行。 ## 项目结构 ```text src/ agent/ LLM loop、上下文压缩、系统提示、媒体处理、子 Agent bus/ inbound、outbound、control 消息队列 channels/ CLI chat 和飞书/Lark 集成 client/ Ratatui 终端 UI config/ 配置加载、环境变量替换、路径展开 delivery/ 活动 Turn 快照投影、节流与 TurnSink 生命周期 gateway/ Axum HTTP/WebSocket server 和 GatewayState 装配 mcp/ MCP 客户端连接和工具包装 memory/ 记忆管理和记忆类型 observability/ Agent/tool telemetry observer providers/ OpenAI 兼容和 Anthropic provider scheduler/ 定时任务运行时 work/ Session 级任务计划与并行子项状态机 session/ 会话生命周期、dialog 命令、持久化集成 skills/ Skill 加载和内置 Skill 安装 storage/ SQLite schema 和 CRUD tools/ Agent 工具实现 task_supervisor.rs Gateway 后台任务的生命周期管理 resources/ skills/ 构建时嵌入的内置 Skills templates/ 首次运行释放的配置和用户模板 tests/ 单元测试和 ignored 集成测试 docs/ 面向维护者和 Agent 的架构与开发文档 ``` ## 关键依赖 | Crate | 用途 | |-------|------| | `axum`, `tokio`, `tokio-tungstenite` | Gateway 和 WebSocket runtime | | `sqlx` | SQLite 持久化 | | `reqwest` | LLM 和 HTTP 客户端 | | `ratatui`, `crossterm`, `termimad` | 终端 UI | | `rmcp` | MCP 客户端 | | `fantoccini` | 可选浏览器自动化 | | `cron`, `chrono-tz` | 定时任务 | | `jieba-rs` | 中文记忆检索分词 | | `zstd`, `tar` | 内置 Skill 打包和释放 | ## 进一步阅读 - [维护者架构文档](docs/ARCHITECTURE.md) - [配置热重载设计与实现](docs/CONFIG_HOT_RELOAD_DESIGN.md) - [WebUI 与 TUI 文件收发设计](docs/FILE_TRANSFER_DESIGN.md) - [内置 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)