# 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 路径 | `sdk/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` 产出 `ui/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 'sdk/data' --exclude 'ui/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(避免半成品覆盖远端)。 - `sdk/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`、`sdk/mock/**` | | **D0 探测** | 上游探测与选型 | `docs/DISCOVERY.md` | | **A1 后端·骨架** | app 工厂、配置、SQLite、会话与消息 CRUD、归一化器、错误信封 | `sdk/pyproject.toml`、`sdk/run.sh`、`sdk/.env.example`、`sdk/app/{main,config,db,schemas,upstream}.py`、`sdk/app/routers/{health,sessions}.py`、`sdk/app/services/normalize.py` | | **A2 后端·QA** | QA 代理、SSE 中继、阶段映射、重试与看门狗 | `sdk/app/routers/qa.py`、`sdk/app/services/sse_relay.py` | | **A3 后端·图谱与抽取** | Neo4j Bolt 直连、抽取代理与回落 | `sdk/app/routers/{kg,extraction}.py`、`sdk/app/services/{neo4j,extraction}.py` | | **B1 前端·基础** | 令牌层、AppShell、四档响应式框架、主题、顶栏/底栏、通用 UI、健康轮询 | `ui/{package.json,vite.config.js,vitest.config.js,index.html}`、`ui/src/{main.js,App.vue}`、`ui/src/styles/**`、`ui/src/components/{layout,ui}/**`、`ui/src/stores/{theme,health}.js`、`ui/src/api/http.js`、`tests/ui/unit/b1/**` | | **B2 前端·会话与对话** | 会话列表与抽屉、对话区、作曲器、流水线卡、SSE 客户端 | `ui/src/components/{session,chat}/**`、`ui/src/stores/{sessions,chat}.js`、`ui/src/sse/**`、`ui/src/api/{qa,sessions}.js`、`tests/ui/unit/b2/**` | | **B3 前端·工作台** | 右侧四个 tab:检索过程、文献来源、提取任务、知识图谱 | `ui/src/components/bench/**`、`ui/src/stores/bench.js`、`ui/src/api/{kg,extraction}.js`、`tests/ui/unit/b3/**` | | **T 测试** | 用例编写与执行、缺陷报告 | `tests/sdk/api/**`、`tests/e2e/**`、`playwright.config.js`、`docs/TEST_CASES.md`、`docs/TEST_REPORT_*.md` | 后端各 agent 的单测放在各自归属子树内(`sdk/app/**/test_*.py`,随代码一起交付),运行 `pytest sdk/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 产出 `sdk/mock/mock_server.py`: - FastAPI,监听 `127.0.0.1:8099` - 实现 `API.md` 中**全部**接口,返回 `sdk/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,打印隧道命令 ├── sdk/ │ ├── 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 ├── ui/ │ ├── 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/ │ ├── sdk/ # pytest │ ├── ui/ # vitest │ └── e2e/ # playwright └── playwright.config.js ``` --- ## 9. 编码约定 **后端** - 全异步(`async def` + httpx.AsyncClient + aiosqlite);不在请求路径上做阻塞 IO。 - 所有对上游的调用集中在 `services/`,`routers/` 只做参数校验与响应组装。 - 不吞异常:上游故障必须转成带 `code` 的错误信封,并在日志里保留原始异常。 - 类型标注齐全,Pydantic v2 模型定义在 `schemas.py`。 **前端** - Vue 3 `