xiaoxixi 38d92d5883 feat: move default db to config dir, add skill/mcp enable toggles
- Store default SQLite database at <config_dir>/data/picobot.db instead of
  the workspace, keeping session data independent of the workspace; the
  reload equivalence check and docs follow the new default
- Add per-skill enable/disable persisted in <config_dir>/skills_state.json;
  skills default to enabled, disabled skills are excluded from prompts,
  listings, and get_skill at load time
- Add mcp.servers[].enabled (default true); disabled servers are skipped
  at activation and by health checks
- WebUI Tools page: switches for Skills and MCP servers, plus a concrete
  MCP tool list with connection status and errors
- Add PUT /api/skills/{name} API and expose enabled/tool details in the
  skills/status APIs
- Bump version to 1.15.0
2026-08-13 21:58:20 +08:00

203 lines
11 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 数据库表结构
数据库为 SQLite默认位于配置目录`~/.picobot``data/` 下的 `picobot.db`,与 workspace 相互独立。
连接启用 WAL、`synchronous=NORMAL`、foreign keys、5 秒 busy timeout连接池最多 8 个连接。当前 `PRAGMA user_version=8`;启动时会在事务内补齐旧库字段和索引,遇到比程序更新的 schema version 会拒绝启动。
## sessions 表
会话表,一个 session 对应一个 (channel, chat_id, dialog_id) 组合。
| 字段 | 类型 | 说明 |
|------|------|------|
| `id` | TEXT PK | session ID格式 `<channel>:<chat_id>:<dialog_id>` |
| `channel` | TEXT | 渠道名称 |
| `chat_id` | TEXT | 聊天/群组标识 |
| `dialog_id` | TEXT | 对话标识 |
| `title` | TEXT | 会话标题(默认 "新对话" |
| `created_at` | INTEGER | 创建时间Unix 毫秒) |
| `last_active_at` | INTEGER | 最后活跃时间Unix 毫秒) |
| `message_count` | INTEGER | 消息计数 |
| `routing_info` | TEXT | 路由信息 |
| `archived_at` | INTEGER | 归档时间Unix 毫秒NULL 表示未归档 |
| `deleted_at` | INTEGER | 软删除时间戳 |
| `last_consolidated_at` | INTEGER | 上次记忆归并时间 |
| `last_compressed_message_at` | INTEGER | 上次上下文压缩边界时间戳 |
| `delivery_context` | TEXT | 渠道声明的可跨 Turn 复用投递上下文 JSON如飞书 thread/root 身份);一次性 reply/reaction ID 永不写入 |
| `delivery_context_updated_at` | INTEGER | delivery_context 最后更新时间 |
`session_turn_usage``turn_id` 幂等保存已提交 Turn 的 Provider usage包括累计输入、输出、缓存输入、请求数和最后一次请求的 prompt tokens。它与 Turn 消息批次在同一事务中提交,供 WebUI 状态栏和 `/info` 使用;升级前历史无法可靠回填,因此统计起点以首条 usage 记录为准。
`(channel, chat_id, dialog_id)` 唯一。普通列表排除 `deleted_at`;是否包含归档记录由查询参数决定。
## messages 表
| 字段 | 类型 | 说明 |
|------|------|------|
| `id` | TEXT PK | 消息 UUID |
| `session_id` | TEXT FK | 所属会话,外键关联 sessions(id) |
| `seq` | INTEGER | 消息序号 |
| `role` | TEXT | 角色: user / assistant / tool / system |
| `content` | TEXT | 消息内容 |
| `media_refs` | TEXT | 多媒体引用 JSON |
| `tool_call_id` | TEXT | 工具调用 ID |
| `tool_name` | TEXT | 工具名称 |
| `tool_calls` | TEXT | 工具调用参数 JSON |
| `source` | TEXT | 消息来源(跨会话消息时标记来源 session_id |
| `created_at` | INTEGER | 创建时间Unix 毫秒) |
| `reasoning_content` | TEXT | 可展示的模型 reasoning如有 |
| `provider_state` | TEXT | Provider 私有回放状态 JSON只回放给匹配 Provider不下发客户端或 Channel |
| `turn_id` | TEXT | 产生该消息的活动 Turn ID |
| `iteration` | INTEGER | Agent 工具循环中的迭代序号 |
| `completion_status` | TEXT | `completed` / `cancelled` / `interrupted`,旧数据默认 completed |
| `client_visibility` | TEXT | `visible` / `hidden`,默认 visiblehidden 只供模型回放continuation 内部触发),客户端历史/投影/投递一律过滤 |
| `turn_origin` | TEXT | `user` / `agent_continuation` / `scheduled`,默认 user客户端据此渲染"后台结果处理"标签而不创建用户气泡 |
`(session_id, seq)` 有唯一索引,防止并发写入重复序号。删除 session 会通过外键级联删除 messages。索引 `(session_id, client_visibility, seq)` 支撑按可见性分层查询。
## agent_runs 表schema v8Agent 编排)
每次具名委托foreground 与 background 一致)先落库再执行;`execution_id` 条件更新保证迟到结果丢弃。批量委托只是多个 run 的集合不再存在组头schema v7 的 `agent_run_groups` 表已删除)。
| 字段 | 类型 | 说明 |
|------|------|------|
| `id` | TEXT PK | run ID |
| `root_session_id` | TEXT | 根会话 |
| `parent_run_id` | TEXT FK | 父 runRESTRICTNULL 表示 Root 直接委托 |
| `caller_agent_id` / `caller_scope_id` | TEXT | 调用方身份Root 的 caller_scope_id 固定 `"ROOT"` |
| `agent_id` / `definition_hash` / `provider_profile` | TEXT | Definition 快照(绑定运行代,运行中不热切换) |
| `provider_name` / `model_id` | TEXT | Provider 与模型 |
| `mode` | TEXT | foreground / background |
| `depth` | INTEGER | 委托深度(>=1 |
| `plan_item_id` | TEXT | 绑定计划子项(接纳时原子领取) |
| `execution_id` | TEXT | 执行尝试 ID唯一索引 |
| `task` / `context_json` | TEXT | 任务与调用方上下文 |
| `budget_json` | TEXT | 树级剩余预算 |
| `signal_contract_json` / `signal_delivery` | TEXT | Definition 信号契约快照与投递 lanequeue/steer |
| `status` | TEXT | queued / running / waiting_children / completed / failed / timed_out / cancelled / interrupted |
| `result` / `error` | TEXT | 终态完整结果/错误get_result 与 tool 结果同源) |
| `prompt_tokens` / `completion_tokens` / `cost` | INTEGER/REAL | Provider usage |
| `tool_calls_count` / `iterations` | INTEGER | 执行统计 |
| `runtime_generation` / `attempt` | INTEGER | 运行代与重试次数 |
| `completion_slot_reserved` | INTEGER | background 完成槽预留 |
| `deadline_at` / `started_at` / `finished_at` / `created_at` / `updated_at` | INTEGER | 时间线 |
| `revision` | INTEGER | 客户端投影修订号 |
索引:`execution_id` 唯一、`(root_session_id, caller_scope_id, idempotency_key)` 部分唯一、`(root_session_id, created_at DESC)``(parent_run_id, created_at)``(runtime_generation, status, deadline_at)`(恢复扫描)。
## agent_session_state 表schema v8Agent 编排)
每根会话一行inbox 容量与客户端 revision 的权威计数:
| 字段 | 说明 |
|------|------|
| `root_session_id` | TEXT PK |
| `revision` | 单调客户端投影修订号 |
| `pending_event_count` | 未消费事件数(容量条件更新) |
| `reserved_completion_slots` | 已接纳 background run 预留的完成槽 |
| `updated_at` | 最后更新时间 |
容量判断在同一写事务内做条件 `UPDATE``pending + reserved + 新增 <= 上限`),杜绝并发 `COUNT(*)` 漂移。
## agent_inbox_events 表schema v8Agent 编排)
background 完成/信号投递的唯一事实源:`pending → leased → admitted → consumed`,失败按 token 释放回 pending超限进 dead-letter崩溃靠 lease 过期恢复。
| 字段 | 说明 |
|------|------|
| `id` | TEXT PK |
| `root_session_id` | TEXT | 根会话 |
| `run_id` | TEXT FK NOT NULLRESTRICT |
| `event_type` | signal / completion |
| `event_key` | 去重键signal 含冷却窗口 id |
| `delivery` | queue / steer |
| `requires_continuation` | 是否反向启动 continuation Turncancel 产物为 false |
| `severity` / `payload_json` | 信号级别与结构化载荷completion 含 signal_ids |
| `status` | pending / leased / admitted / consumed / superseded / dead_letter |
| `attempt_count` / `lease_token` / `lease_until` / `next_attempt_at` | 投递尝试与租约 |
| `admitted_turn_id` | steer 事件接纳的 Turn/stop 按此条件释放) |
| `last_error` | 最近失败原因 |
| `revision` | 投影修订号 |
| `updated_at` | 最后更新时间 |
| `created_at` / `consumed_at` / `superseded_at` / `dead_lettered_at` / `fallback_notified_at` / `fallback_suppressed_reason` | 状态时间线 |
`(run_id, event_type, event_key)` 唯一signal 冷却窗去重)。消费/死信会同步递减 `agent_session_state.pending_event_count`
## task_plans / task_items 表
`task_plans` 保存 session 级任务计划,通过部分唯一索引保证每个 session 最多一个 `status='active'` 的计划。`version` 在任何子项变化时递增,用于 WebSocket 快照排序和乐观并发检查。
`task_items``(plan_id, id)` 为复合主键,因此每个计划都可使用 `T1``T2` 等稳定显示 ID。子项状态为 `pending``in_progress``completed``blocked``executor_kind``execution_id` 将并行子 Agent 执行绑定到具体子项。只有 active plan 且 execution ID 匹配时,异步结果才能提交。
## memories 表
长期记忆存储。
| 字段 | 类型 | 说明 |
|------|------|------|
| `id` | TEXT PK | 记忆 UUID |
| `key` | TEXT UNIQUE | 记忆唯一键 |
| `content` | TEXT | 记忆内容 |
| `category` | TEXT | 类别: knowledge / timeline |
| `importance` | REAL | 重要性权重 (0-1) |
| `session_id` | TEXT | 关联会话 |
| `created_at` | TEXT | 创建时间 |
| `updated_at` | TEXT | 更新时间 |
配套 FTS5 全文索引虚拟表 `memory_fts(key, content)`,用于关键词搜索,通过触发器自动同步。
## scheduled_jobs 表
| 字段 | 类型 | 说明 |
|------|------|------|
| `id` | TEXT PK | 任务 UUID |
| `name` | TEXT | 任务名称 |
| `schedule` | TEXT | 调度规则 JSONat/every/cron |
| `prompt` | TEXT | 任务提示词 |
| `channel` | TEXT | 执行渠道 |
| `chat_id` | TEXT | 目标对话 |
| `model` | TEXT | 可选模型标记;当前会存储/展示,但 Scheduler 执行仍使用默认 Agent 模型 |
| `enabled` | INTEGER | 是否启用 (1/0) |
| `delete_after_run` | INTEGER | 执行后自动删除 (1/0) |
| `next_run_at` | INTEGER | 下次执行时间 |
| `last_run_at` | INTEGER | 上次执行时间 |
| `last_status` | TEXT | 上次执行状态 |
| `last_error` | TEXT | 上次错误信息 |
| `locked_at` | INTEGER | 本次领取时间 |
| `lock_owner` | TEXT | 领取任务的 Scheduler owner UUID |
| `lease_until` | INTEGER | 租约到期时间;进程崩溃后允许其他实例重新领取 |
| `created_at` | INTEGER | 创建时间Unix 毫秒) |
| `updated_at` | INTEGER | 更新时间Unix 毫秒) |
Scheduler 使用原子 `UPDATE ... RETURNING` 领取到期任务。任务结果、下次运行时间和租约释放在同一事务中提交,并校验 owner防止过期 worker 覆盖已恢复的任务。
## job_runs 表
| 字段 | 类型 | 说明 |
|------|------|------|
| `id` | INTEGER PK | 自增 ID |
| `job_id` | TEXT FK | 关联任务,外键关联 scheduled_jobs(id) |
| `started_at` | INTEGER | 开始时间 |
| `finished_at` | INTEGER | 结束时间 |
| `status` | TEXT | 执行状态 |
| `output` | TEXT | 执行输出 |
| `error` | TEXT | 错误信息 |
| `duration_ms` | INTEGER | 耗时(毫秒) |
## llm_calls 表
记录所有 LLM API 调用的请求/响应详情,自动保留最近 1000 条。
| 字段 | 类型 | 说明 |
|------|------|------|
| `id` | INTEGER PK | 自增 ID |
| `created_at` | INTEGER | 调用时间 |
| `provider` | TEXT | 提供商类型 |
| `model` | TEXT | 模型名称 |
| `request_body` | TEXT | 请求摘要 JSON旧记录可能是完整请求体 |
| `response_body` | TEXT | 响应体 JSON |
| `error` | TEXT | 错误信息 |
| `duration_ms` | INTEGER | 耗时(毫秒) |
旧数据中的 `request_body`/`response_body` 可能包含用户内容,排障和导出数据库时应按敏感数据处理。新 Provider 请求的 `request_body` 只保存模型、消息数、工具数和 stream 标志等摘要;错误响应仍可能包含服务端回显内容。