PlantAI-rag-new 首次提交:文献知识工作台

- BFF 薄适配层(FastAPI,8090):QA 代理/SSE 中继/Neo4j 直连/抽取代理/会话持久化
- Vue3 前端:五相位检索流水线、文献来源与证据链、知识图谱、提取任务四 tab
- 四档响应式(桌面/笔记本/平板/手机)、深浅主题
- 81 个 FR 全部实现,测试追溯矩阵 81/81,后端 121 测/前端 156 测
- 真实凭据已脱敏,运行时配置在 backend/.env(不入 git)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
jerry
2026-08-25 20:47:52 +08:00
co-authored by Claude Fable 5
commit 1e94de7464
171 changed files with 26291 additions and 0 deletions
+231
View File
@@ -0,0 +1,231 @@
# ARCHITECTURE.md —— PlantAI-rag-new 架构与协作约定
> 版本 1.0 | 作者 PM | 本文件由 PM 独占写入,所有开发/测试 agent **只读**
---
## 1. 项目定位
`PlantAI-rag-new` 是植物基因家族文献 RAG 知识系统的**新一代工作台**。
它**不重写** RAG 算法、检索引擎与论文抽取流水线——那些是现有 `PlantAI-rag` 工程中经真实论文验证的资产。本项目做两件事:
1. **一层薄适配(BFF, Backend-For-Frontend)**:代理并归一化现有运行中的上游服务,补齐上游缺失的能力(会话持久化、图谱直连、统一错误信封)。
2. **一套全新的响应式前端**:严格实现 `plantai-agent-ui-prototype.html` 的视觉系统,并扩展为支持手机的四档自适应布局。
**红线**:不修改 `PlantAI-rag`、`PlantAI-dev`、`rag-qa-v1_1_73` 的任何源文件;不占用其端口;不切换其流量。
---
## 2. 系统拓扑
```
┌────────── 用户浏览器(桌面 / 平板 / 手机)──────────┐
│ │
│ Vue 3 SPA ← 开发期 Vite dev :5180 │
│ 生产期由 BFF 静态托管 │
└───────────────────────┬──────────────────────────────┘
│ /api/* (同源,JSON + SSE)
┌───────────────────────▼──────────────────────────────┐
│ BFF FastAPI + Uvicorn 127.0.0.1:8090 │
│ ┌────────────┬────────────┬───────────┬──────────┐ │
│ │ sessions │ qa │ kg │extraction│ │
│ │ (SQLite) │ (代理+SSE) │(Bolt直连) │ (代理) │ │
│ └────────────┴─────┬──────┴─────┬─────┴────┬─────┘ │
└────────────────────┬┴────────────┴──────────┴────────┘
│ │ │
┌────────────▼──┐ ┌───────▼─────┐ ┌─▼──────────────┐
│ QA V1.1.73 │ │ Neo4j Bolt │ │ Gene Family │
│ 127.0.0.1:7920│ │ 19788/18788 │ │ RAG :8000 │
│ (只读消费) │ │ /17687 │ │ 回落 :7900 │
└───────────────┘ └─────────────┘ └────────────────┘
```
**BFF 的存在理由**(不是无谓的中间层):
| 职责 | 为什么前端不能直连上游 |
|---|---|
| 会话持久化 | 上游 QA 完全无状态,没有会话概念,UI 的历史列表必须有人存 |
| 别名归一化 | 上游同一语义有多个字段名(`answer`/`response`/`message` 等),归一化放前端会污染每个组件 |
| SSE 阶段映射 | 上游推十余个细粒度 stage,UI 只需五阶段;映射放 BFF,前端不需要知道上游阶段名 |
| 图谱直连 | 上游 `/kg/*` 路由指向已死的 7687 端口,必须由 BFF 用 Bolt 驱动绕过 |
| 统一错误信封 | 三个上游三套错误格式,前端只应处理一种 |
| 跨域与凭据 | 上游只绑回环且 Neo4j 需要密码,凭据不能进浏览器 |
---
## 3. 运行时约定
| 项 | 值 |
|---|---|
| BFF 监听 | `127.0.0.1:8090`(占用则退 8091) |
| Vite dev | `127.0.0.1:5180`(占用则退 5181) |
| BFF conda 环境 | `plantai-bff`(Python 3.11,新建,**不复用** `gfpy312`) |
| SQLite 路径 | `backend/data/plantai.db`(WAL 模式) |
| 前端工具链 | Vite **5.4.x** + @vitejs/plugin-vue **5.2.x** + Vue **3.5.x** + Pinia **2.3.x** |
| Node | v18.19.1(已 EOL)——**禁止升级 Vite 至 7**(需 Node 20+),**禁止用 `npm create vite`** |
生产形态:`npm run build` 产出 `frontend/dist`,BFF 用 `StaticFiles` 挂载,单进程从 `:8090` 提供服务。
---
## 4. 开发工作流:本地 staging + rsync
远端机器是**运行与测试环境**,本地是**编辑环境**。原因:agent 在本地能用完整的读/写/编辑工具迭代,通过 SSH 逐行改远端文件既慢又易错。
| 角色 | 路径 |
|---|---|
| 本地编辑(权威源) | `/Users/jerry/haiguji/PlantAI-rag-new/` |
| 远端运行(部署副本) | `/home/xzq/5090/teamWorkspace/zh/PlantAI-rag-new/` |
同步命令(由 `deploy/sync.sh` 封装):
```bash
rsync -az --delete \
--exclude 'node_modules' --exclude '__pycache__' --exclude '.venv' \
--exclude 'backend/data' --exclude 'frontend/dist' --exclude '.pytest_cache' \
/Users/jerry/haiguji/PlantAI-rag-new/ \
100.109.52.26:/home/xzq/5090/teamWorkspace/zh/PlantAI-rag-new/
```
**规则**:
- 所有 agent **只在本地路径写文件**,绝不用 SSH heredoc 改代码。
- 同步由 **PM 统一执行**,agent 不自行 rsync(避免半成品覆盖远端)。
- `backend/data`(数据库)、`node_modules`、`dist` 不同步,属远端运行时产物。
- 文档最终必须在远端 `docs/` 下可查——每次里程碑结束由 PM 同步一次。
---
## 5. Agent 分工与文件归属
**铁律:文件归属严格互斥。任何 agent 不得写入不属于自己的文件。** 需要跨归属改动时,向 PM 提出,由 PM 单点裁决修改。
| Agent | 职责 | 独占路径 |
|---|---|---|
| **PM** | 契约文档、集成、缺陷派发、验收、rsync | `docs/{PRD,API,UI_SPEC,ARCHITECTURE,TEST_PLAN,ACCEPTANCE,CHANGELOG}.md`、`deploy/**`、`src/i18n/labels.js`、`src/config/modes.js`、`backend/mock/**` |
| **D0 探测** | 上游探测与选型 | `docs/DISCOVERY.md` |
| **A1 后端·骨架** | app 工厂、配置、SQLite、会话与消息 CRUD、归一化器、错误信封 | `backend/pyproject.toml`、`backend/run.sh`、`backend/.env.example`、`backend/app/{main,config,db,schemas,upstream}.py`、`backend/app/routers/{health,sessions}.py`、`backend/app/services/normalize.py` |
| **A2 后端·QA** | QA 代理、SSE 中继、阶段映射、重试与看门狗 | `backend/app/routers/qa.py`、`backend/app/services/sse_relay.py` |
| **A3 后端·图谱与抽取** | Neo4j Bolt 直连、抽取代理与回落 | `backend/app/routers/{kg,extraction}.py`、`backend/app/services/{neo4j,extraction}.py` |
| **B1 前端·基础** | 令牌层、AppShell、四档响应式框架、主题、顶栏/底栏、通用 UI、健康轮询 | `frontend/{package.json,vite.config.js,vitest.config.js,index.html}`、`frontend/src/{main.js,App.vue}`、`frontend/src/styles/**`、`frontend/src/components/{layout,ui}/**`、`frontend/src/stores/{theme,health}.js`、`frontend/src/api/http.js`、`tests/frontend/unit/b1/**` |
| **B2 前端·会话与对话** | 会话列表与抽屉、对话区、作曲器、流水线卡、SSE 客户端 | `frontend/src/components/{session,chat}/**`、`frontend/src/stores/{sessions,chat}.js`、`frontend/src/sse/**`、`frontend/src/api/{qa,sessions}.js`、`tests/frontend/unit/b2/**` |
| **B3 前端·工作台** | 右侧四个 tab:检索过程、文献来源、提取任务、知识图谱 | `frontend/src/components/bench/**`、`frontend/src/stores/bench.js`、`frontend/src/api/{kg,extraction}.js`、`tests/frontend/unit/b3/**` |
| **T 测试** | 用例编写与执行、缺陷报告 | `tests/backend/api/**`、`tests/e2e/**`、`playwright.config.js`、`docs/TEST_CASES.md`、`docs/TEST_REPORT_*.md` |
后端各 agent 的单测放在各自归属子树内(`backend/app/**/test_*.py`,随代码一起交付),运行 `pytest backend/app -q`;T 的 L2 接口测试与 L5 e2e 放在 `tests/`。
### 跨归属的接缝
以下文件被多方读取但只有一个写入方,接缝处必须按契约对齐:
| 接缝 | 写入方 | 读取方 | 契约依据 |
|---|---|---|---|
| BFF 接口形态 | A1/A2/A3 | B1/B2/B3、T | `API.md` |
| 设计令牌 CSS 变量 | B1 (`styles/tokens.css`) | B2、B3 | `UI_SPEC.md` §令牌 |
| 组件类名 | B1 定义基础类,B2/B3 复用 | 互相 | `UI_SPEC.md` §组件 |
| 五阶段标签与颜色 | PM (`i18n/labels.js`) | A2(计算 phase)、B2、B3 | `PRD.md` FR-CHAT-009 |
| 模式参数集 | PM (`config/modes.js`) | B2 | `PRD.md` FR-CHAT-006 |
| 块联合结构 | A1(落库)、B2(渲染) | T | `API.md` §块联合 |
---
## 6. 并行解耦:mock server
前端三个 agent **不等待后端**。M0 阶段 PM 产出 `backend/mock/mock_server.py`:
- FastAPI,监听 `127.0.0.1:8099`
- 实现 `API.md` 中**全部**接口,返回 `backend/mock/fixtures/*.json` 里的固定数据
- 包含一条**可回放的 SSE 事件序列**(按真实时序 sleep),让流式 UI 能在无上游时开发
- 支持 `?scenario=` 查询参数切换场景:`normal` / `empty`(空结果)/ `slow`(慢响应)/ `error`(上游故障)/ `degraded`(部分降级)
B 组开发时把 `vite.config.js` 的 proxy 指向 8099;集成阶段由 PM 切回 8090。
**fixtures 是单一事实源**:mock 与真实 BFF 的单元测试共用同一批 fixture 文件,保证契约不漂移。
---
## 7. 集成节奏
```
M0 契约锁定 ──┬─→ A1 A2 A3(对真实上游)──┐
├─→ B1 B2 B3(对 mock) ─┤→ M2 PM 集成 → M3 补齐 → M4 测试循环 → M5 验收
└─→ T 写用例(对 PRD) ─┘
```
- 每一波结束,agent 向 PM 汇报「完成项 + 未决问题 + 对契约的偏差建议」,**不直接改契约**。
- PM 在集成点统一处理契约偏差,必要时更新 `API.md` 并广播给受影响的 agent。
- 缺陷修复一律**批量派发**,禁止边测边改(见 `TEST_PLAN.md` 轮次纪律)。
---
## 8. 目录结构
```
PlantAI-rag-new/
├── docs/ # 全部文档,见第 5 节归属
├── deploy/
│ ├── sync.sh # 本地 → 远端 rsync
│ └── start-dev.sh # 拉起 BFF + vite,打印隧道命令
├── backend/
│ ├── pyproject.toml run.sh .env.example
│ ├── data/ # plantai.db(不入库、不同步)
│ ├── mock/
│ │ ├── mock_server.py
│ │ └── fixtures/*.json
│ └── app/
│ ├── main.py config.py db.py schemas.py upstream.py
│ ├── routers/ health.py sessions.py qa.py kg.py extraction.py
│ └── services/ normalize.py sse_relay.py neo4j.py extraction.py
├── frontend/
│ ├── package.json vite.config.js index.html vitest.config.js
│ └── src/
│ ├── main.js App.vue
│ ├── styles/ tokens.css base.css animations.css responsive.css
│ ├── i18n/labels.js config/modes.js
│ ├── api/ http.js qa.js sessions.js kg.js extraction.js
│ ├── sse/ parser.js
│ ├── stores/ theme.js health.js sessions.js chat.js bench.js
│ └── components/
│ ├── layout/ AppShell 相关、TopBar、StatusBar、AuroraBg、Drawer
│ ├── ui/ StatusBadge、StatusDot、IconButton、Skeleton、EmptyNotice
│ ├── session/ SessionPanel、SessionItem、SessionRail、SearchBox
│ ├── chat/ ChatPanel、MessageList、Composer、PipelineCard、ToolCard …
│ └── bench/ BenchPanel、RetrievalPane、SourcesPane、ExtractionPane、KgPane …
├── tests/
│ ├── backend/ # pytest
│ ├── frontend/ # vitest
│ └── e2e/ # playwright
└── playwright.config.js
```
---
## 9. 编码约定
**后端**
- 全异步(`async def` + httpx.AsyncClient + aiosqlite);不在请求路径上做阻塞 IO。
- 所有对上游的调用集中在 `services/`,`routers/` 只做参数校验与响应组装。
- 不吞异常:上游故障必须转成带 `code` 的错误信封,并在日志里保留原始异常。
- 类型标注齐全,Pydantic v2 模型定义在 `schemas.py`。
**前端**
- Vue 3 `<script setup>` + Composition API;组件单一职责,超过 ~250 行考虑拆分。
- **样式一律用 `--pa-*` 令牌**,禁止硬编码颜色值(令牌表见 `UI_SPEC.md`)。
- 组件内 `<style scoped>`;跨组件复用的类放 `styles/base.css`(B1 归属)。
- 状态跨组件共享一律走 Pinia store,禁止用 provide/inject 传业务状态。
- 所有用户可见文案走 `i18n/labels.js`,组件内不写死中文字符串(错误提示除外,见 `PRD.md` §全局约定)。
**通用**
- 文件编码 UTF-8,LF 换行。
- 提交粒度:一个 FR 一次提交,提交信息含 FR 编号。
---
## 10. 已知的既存故障(不修,仅规避)
| 现象 | 影响 | 我们的对策 |
|---|---|---|
| 默认端口 7687 无 Neo4j,`:8000/api/v1/kg/*` 与 `/v15-graph-qa/*` 报错 | 上游图谱路由不可用 | BFF 用 Bolt 直连非标准端口实例,见 `DISCOVERY.md` |
| PlantAI-dev `.env` 指向 `7925`(空端口),实际 QA 在 `7920` | PlantAI-dev 知识问答当前是坏的 | 与本项目无关,仅记录;本项目指向 7920 |
| 上游 QA 无 token 级流式,答案在终态一次返回 | 无法做真流式打字 | 客户端揭示动画,视觉一致但不伪造数据流 |
| 上游 `max_rounds: 1`,每个提问独立 | 无多轮上下文 | UI 明确提示「每次提问独立检索」,不假装有上下文记忆 |