# PicoBot 数据库表结构 数据库为 SQLite,默认位于 workspace 下的 `picobot.db`。 连接启用 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` | 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`,默认 visible;hidden 只供模型回放(continuation 内部触发),客户端历史/投影/投递一律过滤 | | `turn_origin` | TEXT | `user` / `agent_continuation` / `scheduled`,默认 user;客户端据此渲染"后台结果处理"标签而不创建用户气泡 | `(session_id, seq)` 有唯一索引,防止并发写入重复序号。删除 session 会通过外键级联删除 messages。索引 `(session_id, client_visibility, seq)` 支撑按可见性分层查询。 ## agent_runs 表(schema v8,Agent 编排) 每次具名委托(foreground 与 background 一致)先落库再执行;`execution_id` 条件更新保证迟到结果丢弃。批量委托只是多个 run 的集合,不再存在组头(schema v7 的 `agent_run_groups` 表已删除)。 | 字段 | 类型 | 说明 | |------|------|------| | `id` | TEXT PK | run ID | | `root_session_id` | TEXT | 根会话 | | `parent_run_id` | TEXT FK | 父 run(RESTRICT),NULL 表示 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 信号契约快照与投递 lane(queue/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 v8,Agent 编排) 每根会话一行,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 v8,Agent 编排) background 完成/信号投递的唯一事实源:`pending → leased → admitted → consumed`,失败按 token 释放回 pending,超限进 dead-letter,崩溃靠 lease 过期恢复。 | 字段 | 说明 | |------|------| | `id` | TEXT PK | | `root_session_id` | TEXT | 根会话 | | `run_id` | TEXT FK NOT NULL(RESTRICT) | | `event_type` | signal / completion | | `event_key` | 去重键(signal 含冷却窗口 id) | | `delivery` | queue / steer | | `requires_continuation` | 是否反向启动 continuation Turn(cancel 产物为 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 | 调度规则 JSON(at/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 标志等摘要;错误响应仍可能包含服务端回显内容。