- prompts/superpowers_{review,answer_quality,minimal_code}.md + prompt_pack.py
- agentic_controller/answer_composer Superpowers 注入点(env 总闸,关态逐字节一致)
- compile_qa.sh: prompts/ 目录自动拷贝
- humanizer: CJK 逗号规则 + 引用保护收窄(只护标记本身,带引用句可润色)
- docs: README 能力矩阵+env 表 / ARCHITECTURE Gateway 章 / API §6 / PRD:17 /
TEST_CASES:126 多轮语义 / CHANGELOG
- tooling/cap/final_acceptance.sh 终验矩阵
- 终验: 金标 16/20 regressions=0, 多轮指代 PASS, 热加载 PID 不变 PASS,
Humanizer 开态「此外」消除且 22 引用保留, 五上游全绿, 7913 已释放
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
16 KiB
ARCHITECTURE.md —— PlantAI-rag-new 架构与协作约定
版本 1.0 | 作者 PM | 本文件由 PM 独占写入,所有开发/测试 agent 只读
1. 项目定位
PlantAI-rag-new 是植物基因家族文献 RAG 知识系统的新一代工作台。
它不重写 RAG 算法、检索引擎与论文抽取流水线——那些是现有 PlantAI-rag 工程中经真实论文验证的资产。本项目做两件事:
- 一层薄适配(BFF, Backend-For-Frontend):代理并归一化现有运行中的上游服务,补齐上游缺失的能力(会话持久化、图谱直连、统一错误信封)。
- 一套全新的响应式前端:严格实现
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 封装):
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
<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.5 Capability Gateway(2026-09-02 起)
三层机制
- 注册层:
agent/qa/configs/capabilities.yaml(明文、可热加载)。每个能力一段 manifest(provider/permission/schema/默认开关);坏条目跳过并记 load_warnings, 一条坏配置不影响其余能力,更不影响 QA 主体启动。 - 调用层:
capabilities/{manifests,registry,providers,install_manager}.py+tool_runner.py(编译 .so)。四 Provider:HttpToolProvider(HTTP JSON 工具)、 McpToolProvider(官方 mcp SDK,stdio 子进程 + streamable-http;空闲回收、 崩溃限次重启 3 次熔断)、PythonToolProvider(module:function)、 PromptPackProvider(SKILL.md 注入)。 - 安装管理层:
install_manager.py(白名单校验、单写锁 + 原子替换、操作审计 JSONL)+ QA loopback/api/v1/qa-v1-1-73/capabilities/install+ BFF 透传POST /api/capabilities/install。热加载 = 重解析 → registry 差分刷新 → MCP 子进程按 diff 启停(已加载进程不重启;QA 冷启动 ~167s 是热加载硬要求的动因)。
会话级开关链
设置面板 PATCH sessions.meta.capabilities(BFF 白名单 sanitize)→ BFF 读 meta →
注入 body.history(memory on)/ body.capabilities(web_search 双闸:会话开 AND
env 总闸)→ QA 按 capabilities 决策。humanize 在 BFF complete 帧归一化后、
图片注入后执行(纯 BFF 层)。
首批四项能力落点
| 能力 | 落点 |
|---|---|
| 多轮记忆 | SDK services/history.py(3 轮/16k 预算)→ QA answer_composer Dialogue Context 段(检索零回归) |
| 联网搜索 | QA web_search.py(PubMed esearch+esummary → Crossref 兜底;中文问题经 build_query 转英文)→ pipeline 证据采纳后确定性门控(去重公开引用数 < min_evidence) |
| Humanizer | SDK services/humanizer.py(9 类规则纯函数;块级保护) |
| Superpowers | QA prompts/*.md + prompt_pack.py 注入 answer_composer SYSTEM / agentic REVIEW |
金标防抖(LLM 方差实证)
金标 compare 对基线 PASS 而批次 FAIL 的题复跑一次;GI-005(拟南芥 WRKY 成员数)
经配对采样实证为上游 deepseek-chat 随时间漂移(同题同端口多次采样 key_hit
0.2~0.8 横跨阈值),标记 flaky 不计回归。见 qa-golden/S0_RECORD.md。
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 明确提示「每次提问独立检索」,不假装有上下文记忆 |