AstroResearch/docs/database.md
Asfmq 5db4cc5998 refactor: 全栈架构重构与质量硬化——API 错误统一、工具域重组、安全加固、前端组件化
后端核心变更:
  - API 层: 新增 AppError 枚举统一错误类型,替代散落的 (StatusCode, String)
  - Agent 工具域: 重组为 astro/system/ 和 astro/research/ 两级域,新增 ProcessPaperTool 流水线工具
  - 安全: 新增 SSRF 双层防护 (同步字符串级 + 异步 DNS 解析级),覆盖 IPv4/IPv6 私网段
  - 弱密码检测: 扩展弱密码列表并增加最小长度检查
  - LLM 客户端: 新增 ChatCompleter/Embedder trait,支持依赖注入与批量向量化 embed_batch
  - 批量处理: AssetBatch 从串行改为 Semaphore 并发池 (BATCH_CONCURRENCY=3)
  - 分块器: 重写为三阶段结构化管线 (章节解析→短节合并→带标题路径子块)
  - RAG: embedding 计算移出事务,RetrievalResult 新增 headings/section_index 字段
  - 检索: ADS/arXiv 并行检索 (tokio::join!),去重改用 HashSet,本地库回填批量 IN 查询
  - 天体查询: Sesame API 升级到 v4,新增视差误差/自行/视向速度/多波段测光字段
  - 迁移: 14 个增量文件合并为单一 init.sql,支持 sqlx::migrate! 内存库集成测试
  - 测试: circuit_breaker/hooks/task_board/session/memory/streaming_executor 新增修正 15+ 测试

  前端架构重构:
  - 目录重组: features/ → pages/ + components/ + hooks/ 三层分离
  - App.tsx 从 1181 行压缩至 ~174 行 (逻辑抽入 9 个自定义 Hook)
  - Agent 面板拆分为 AgentSessionSidebar/AgentMessageList/AgentInputArea 子组件
  - 新增 GlobalDialog/PaperDetailModal/UncachedPaperModal 通用对话框组件
  - 工具函数抽取: celestial.ts (天体坐标格式), paper.tsx (文献信息渲染)
2026-06-25 23:45:37 +08:00

327 lines
14 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.

# AstroResearch Database Schema / 数据库设计
AstroResearch 使用轻量级、零配置的 **SQLite** 数据库作为持久化存储。数据库文件默认保存在项目根目录下的 `astro_research.db`(可通过 `.env` 中的 `DATABASE_URL` 配置),由 Rust 中的 `sqlx` 驱动管理并自动执行迁移。
---
## 1. 实体关系图 (Entity-Relationship Diagram)
```mermaid
erDiagram
PAPERS {
text bibcode PK
text title
text authors "JSON Array"
text year
text pub "Journal/Publisher"
text keywords "JSON Array"
text abstract
text doi
text arxiv_id
integer citation_count
integer reference_count
text doctype "文献类型"
text pdf_path "PDF 物理路径 或 error:诊断"
text html_path "HTML 物理路径 或 error:诊断"
text markdown_path "Markdown 物理路径"
text translation_path "翻译文件物理路径"
datetime created_at
}
NOTES {
integer id PK
text bibcode FK
integer paragraph_index
text note_text
text highlight_color
text selected_text
datetime created_at
}
CITATIONS_REFERENCES {
text source_bibcode PK
text target_bibcode PK
}
SYNC_QUERIES {
integer id PK
text query "检索关键词"
text source "数据源"
integer limit_count "拉取上限"
datetime last_run "最近运行时间"
datetime created_at "创建时间"
}
PAPER_CHUNKS_CONTENT {
integer rowid PK
text bibcode FK
integer paragraph_index
text content
text headings "章节路径"
integer section_index "章节编号"
}
PAPER_TARGETS {
integer id PK
text bibcode FK
text target_name "标准化天体名称"
text ra "赤经"
text dec "赤纬"
real parallax "视差 mas"
text spectral_type "光谱分类"
real v_magnitude "视星等"
text aliases "JSON Array"
text otype "天体类型"
text oname "SIMBAD 主名称"
real pm_ra "自行 RA"
real pm_de "自行 Dec"
real radial_velocity "视向速度"
real parallax_err "视差误差"
text photometry "测光数据 JSON"
}
AGENT_SESSIONS {
text session_id PK
text title
text model
integer turn_count
text last_error
text summary
text metadata
datetime deleted_at
integer rewind_count "回退计数"
text parent_session_id FK "分支来源"
text branch_metadata "分支元数据"
text mode "模式标识符"
}
AGENT_MESSAGES {
integer id PK
text session_id FK
integer turn_index
integer step_index
text role "system/user/assistant/tool"
text content
text thought "思考链"
text tool_calls "工具调用 JSON"
text tool_call_id "工具调用 ID"
integer token_count
text metadata
text raw_json
text agent_name "消息归属"
integer active "软删除标记"
}
AGENT_TASKS {
integer id PK
text session_id FK
text task_id
text content
text status "pending/in_progress/completed"
text blocked_by "JSON Array — DAG 依赖"
text owner "分配目标 agent 名称"
}
AGENT_AUDIT_LOG {
integer id PK
text session_id FK
integer step
text tool_name
text status "OK / FAIL / SESSION_STOP"
integer elapsed_ms
text output_preview
text agent_name
}
AGENT_TEAM_MEMBERS {
integer id PK
text session_id FK
text agent_name
text agent_role
text status "spawning / active / idle / shutdown"
}
PAPERS ||--o{ NOTES : "has"
PAPERS ||--o{ CITATIONS_REFERENCES : "cites / cited_by"
PAPERS ||--o{ PAPER_CHUNKS_CONTENT : "chunked into"
PAPERS ||--o{ PAPER_TARGETS : "contains"
AGENT_SESSIONS ||--o{ AGENT_MESSAGES : "contains"
AGENT_SESSIONS ||--o{ AGENT_TASKS : "owns"
AGENT_SESSIONS ||--o{ AGENT_AUDIT_LOG : "records"
AGENT_SESSIONS ||--o{ AGENT_TEAM_MEMBERS : "members"
AGENT_SESSIONS ||--o| AGENT_SESSIONS : "branches from"
```
---
## 2. 数据表结构详述 (Table Schema Details)
### 2.1 papers 表 (文献元数据)
存储文献的核心元数据和本地物理存储路径。
- **特殊字段说明**
- `pdf_path` / `html_path`:正常情况下存储相对路径(如 `library/PDF/2024arXiv.pdf`)。当下载失败时,会以 `error:` 前缀存储诊断信息(如 `error:Cloudflare 拦截`)。特殊值 `error:no_resource` 表示用户手动标记了"无有效全文资源"。
- `doctype`:文献类型标识,如 `article`、`eprint`、`proceedings`、`phdthesis`、`catalog`、`software`、`circular`、`book` 等。
- **索引**
- `idx_papers_doi` -> 基于 `doi`
- `idx_papers_arxiv_id` -> 基于 `arxiv_id`
### 2.2 citations_references 表 (引文与参考文献拓扑)
多对多关联表,存储文献之间的引用网络(即拓扑星系图的基础数据)。
- **复合主键**`(source_bibcode, target_bibcode)`
- **索引**
- `idx_citations_ref_source` -> 优化以 `source_bibcode` 查询参考文献
- `idx_citations_ref_target` -> 优化以 `target_bibcode` 查询被引文献
### 2.3 notes 表 (高亮与阅读笔记)
存储学者在阅读器中对特定段落创建的高亮和笔记。
- **外键**`bibcode` 级联删除 (`ON DELETE CASCADE`)。
- **索引**
- `idx_notes_bibcode` -> 优化单篇文献的笔记列表查询。
### 2.4 sync_queries 表 (同步检索条件)
存储用户保存的批量同步检索条件,支持快速重新同步。
- **唯一约束**`UNIQUE(query, source, limit_count)` 确保相同条件的检索不会重复保存。
### 2.5 paper_chunks_content 表 (RAG 文本切片)
存储文献 Markdown 段落的文本切片,用于向量嵌入和混合检索。
- `rowid`:自增主键,与 `vec_paper_chunks` 虚拟表的 rowid 保持 1:1 对齐。
- `headings`:章节路径,如 `"Introduction > Background"`,用于结构化检索。
- `section_index`:章节内的段落编号。
- **外键**`bibcode` 级联删除 (`ON DELETE CASCADE`)。
- **关联 FTS5**`paper_chunks_fts` 虚拟表通过触发器自动同步 `content` 字段。
### 2.6 paper_targets 表 (天体目标缓存)
存储从文献中提取并通过 CDS Sesame 解析的天体物理参数。
- `target_name`:标准化天体名称(如 `GD 358`)。
- `ra` / `dec`:赤道坐标(赤经/赤纬)。
- `parallax`视差mas毫角秒
- `spectral_type`:光谱分类(如 `DA1`)。
- `otype` / `oname`Sesame v4 返回的天体类型和 SIMBAD 官方主名称。
- `pm_ra` / `pm_de`自行分量mas/yr
- `radial_velocity`:视向速度。
- `photometry`多波段测光数据JSON 文本)。
- **唯一约束**`UNIQUE(bibcode, target_name)`。
- **索引**`idx_paper_targets_name`。
### 2.7 agent_sessions 表 (Agent 会话)
存储 ReAct 智能体对话会话的元数据。
- `deleted_at`软删除时间戳NULL 表示活跃会话)。
- `rewind_count`会话回退undo次数用于追踪重做历史。
- `parent_session_id`:分支来源会话 ID支持会话分叉branch
- `branch_metadata`分支元数据JSON 文本)。
- `mode`:会话模式标识符(`default`、`deep-research`、`literature-reader`),控制 Agent 的行为配置。
- **索引**`idx_agent_sessions_updated`WHERE deleted_at IS NULL仅索引活跃会话
### 2.8 agent_messages 表 (Agent 消息)
存储 Agent 会话的完整对话历史,包括思考链和工具调用。
- `role``system` / `user` / `assistant` / `tool`
- `thought`LLM 思考链内容CoT
- `tool_calls`:工具调用 JSON函数名 + 参数)。
- `tool_call_id`:工具调用唯一标识(用于 SSE 流中 tool_call ↔ tool_result 配对)。
- `agent_name`:消息归属(`lead` / 子代理名 / teammate 名),支持身份隔离。
- `active`:软删除标记(`0` = 已回退隐藏,`1` = 正常可见),用于 session rewind。
- **索引**`idx_agent_messages_session`、`idx_agent_messages_turn`、`idx_agent_messages_agent`、`idx_agent_messages_active`。
- **关联 FTS5**`agent_messages_fts` 虚拟表通过触发器自动同步 `content`、`thought`、`tool_calls` 字段。
### 2.9 agent_tasks 表 (Agent 任务追踪)
持久化智能体的结构化待办任务,支持 DAG 依赖模式。
- `blocked_by`JSON 数组,前置任务 ID 列表。
- `status` 生命周期:`pending` → `in_progress``completed`
- `owner`:分配目标 agent 名称(多 Agent 团队协作路由)。
- **索引**`idx_agent_tasks_session`、`idx_agent_tasks_status`、`idx_agent_tasks_session_task` (UNIQUE)。
### 2.10 agent_audit_log 表 (Agent 工具审计)
记录所有工具调用的审计信息:工具名称、执行状态、耗时 (ms)、输出预览。
- `status``OK` / `FAIL` / `SESSION_STOP`
- `agent_name`:区分 lead/子代理/teammate 的调用来源。
- **用途**`GET /api/chat/metrics` 聚合指标、会话审计回溯。
### 2.11 agent_team_members 表 (多 Agent 团队)
管理多智能体团队中每个成员的生命周期。
- `status``spawning` → `active``idle``shutdown`
- `agent_role`:区分 team lead / teammate 等角色。
- **唯一约束**`UNIQUE(session_id, agent_name)`。
### 2.12 全文搜索 (FTS5)
系统在以下实体表上建立了 FTS5 全文索引,通过触发器自动保持同步:
| FTS5 虚拟表 | 实体表 | 索引字段 | 用途 |
|:---|:---|:---|:---|
| `papers_fts` | `papers` | title, authors, keywords, abstract, pub | 本地文献 BM25 全文检索 |
| `paper_chunks_fts` | `paper_chunks_content` | content | 段落级稀疏检索,配合 `vec_paper_chunks` 向量做混合搜索 |
| `agent_sessions_fts` | `agent_sessions` | session_id, title, summary, metadata | 会话历史搜索 |
| `agent_messages_fts` | `agent_messages` | session_id, role, content, thought, tool_calls | 对话内容搜索 |
每个 FTS5 虚拟表通过 `content='<实体表>'` 声明为外部内容表,并配套 `_insert`、`_update`、`_delete` 三个触发器保持数据同步。
---
## 3. 数据库迁移说明
### 3.1 迁移机制
迁移脚本存放在 `migrations/` 目录下,使用 `sqlx`**change-based 迁移** 模型:
- **编译期嵌入**`sqlx::migrate!("./migrations")` 宏在编译时将 `.sql` 文件嵌入二进制
- **启动时自动执行**`src/main.rs`、`src/bin/cli.rs` 以及所有测试的 in-memory 数据库在启动时调用 `.run(&pool)`,对比 `_sqlx_migrations` 追踪表,按版本号顺序执行未应用的迁移
- **版本号规则**:文件名格式 `YYYYMMDDHHMMSS_description.sql`,时间戳前缀保证全局唯一排序
- **幂等性**:所有 DDL 必须使用 `IF NOT EXISTS` / `IF EXISTS`,确保可重复执行
- **checksum 校验**sqlx 对每个迁移文件计算 SHA-256 校验和并存入 `_sqlx_migrations`;若文件内容在已应用后被修改,启动时将拒绝执行(防止 schema 漂移)
### 3.2 当前迁移
| 迁移文件 | 说明 |
|:---|:---|
| `00000000000000_init.sql` | **完整初始化**包含所有表、索引、FTS5 虚拟表及同步触发器,详见第 2 节各表结构。 |
> **注意**`vec_paper_chunks` 向量虚拟表由 `src/main.rs` 在迁移之后动态创建,维度由 `EMBEDDING_DIM` 环境变量控制(默认 1536不在迁移文件中管理。
### 3.3 新增迁移
开发新功能需要变更数据库 schema 时:
1.`migrations/` 下新建文件,命名 `YYYYMMDDHHMMSS_<简短描述>.sql`(取当前时间)
2. 编写增量 SQL`ALTER TABLE ADD COLUMN`、`CREATE TABLE IF NOT EXISTS` 等)
3. 运行 `cargo build && cargo test` 验证
4. 更新本文档的 3.2 节和对应的表结构说明
**SQLite 约束**SQLite 不支持事务性 DDL 的全部语义,且 `ALTER TABLE` 能力有限(不支持 `DROP COLUMN`、`RENAME COLUMN` 等旧版本的常见操作)。新增字段时务必使用 `DEFAULT` 值以保证向后兼容。
### 3.4 迁移 Squash
开发期产生的增量迁移应在 **首次正式发布前** squash 为单一 `init.sql`避免迁移文件线性膨胀。Squash 流程:
1. **导出完整 schema**`sqlite3 library/astro_research.db .schema`
2. **移除以下内容**
- `_sqlx_migrations`sqlx 自动管理)
- `sqlite_sequence`SQLite 内部表)
- `vec_paper_chunks` 及其关联内部表(由 main.rs 运行时创建)
- 所有 FTS5 内部表(`_data`、`_idx`、`_docsize`、`_config`,由 FTS5 自动管理)
3. **写入 `migrations/00000000000000_init.sql`**:确保所有 DDL 带 `IF NOT EXISTS` / `IF EXISTS`
4. **删除所有旧迁移文件**
5. **重建二进制**`cargo build`(新迁移文件需嵌入)
6. **更新已有数据库**(保留数据):
```sql
DELETE FROM _sqlx_migrations;
-- 重启应用后 sqlx 自动应用新的 init 迁移(所有 IF NOT EXISTS 均为 no-op
```
7. **验证**`cargo test --lib && cargo run`
> 本项目在 2026-06-25 执行了一次 squash将 18 个开发期增量迁移合并为 1 个 `00000000000000_init.sql`,同时保留了已有的 1341 条文献记录和 6477 条引用关系。
---
## 4. 错误诊断存储约定
系统使用 `papers` 表的 `pdf_path``html_path` 字段的双重语义来同时存储正常路径和错误诊断:
| 字段值模式 | 含义 | 前端展示 |
|:---|:---|:---|
| `NULL` | 尚未尝试下载 | 琥珀色"未下载"角标 |
| `library/PDF/xxx.pdf` | 下载成功,正常物理路径 | 蓝色"已下载"角标 |
| `error:具体原因` | 下载失败,原因为前缀后的文本 | 红色"下载失败"角标,悬浮显示原因 |
| `error:no_resource` | 用户手动标记为无有效全文资源 | 灰色"无资源"角标 |
`health_check` 工具在 `--fix` 模式下会清理损坏文件并将路径重置为 `NULL`,但**不会**清除 `error:` 前缀的记录(以保留诊断线索)。