Files
PlantAI-rag-new/docs/PRD.md
adminandClaude Fable 5 dea46402e8 docs(priv): 阶段0 私有资料问答文档先行——PRD §17 FR-PRIV-001~008 + UI_SPEC §9 + 原始测试计划 + 团队上下文 + A1/T1 任务书
用户需求:客户上传 PDF 等资料进行向量化并追加进向量库,且问答可检索。
决策:全局检索融合 / BFF 直连 DeepSeek 生成私有回答 / 两路并行展示(命中才显示) /
「我的资料」管理界面挂文档 TAB 空态。改动仅限本分支,QA 金标树与红线零改动。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-07 21:20:35 +08:00

421 lines
41 KiB
Markdown
Raw Permalink 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.
# PRD.md —— PlantAI-rag-new 产品需求文档
> 版本 1.0 | 作者 PM | 本文件由 PM 独占写入,所有开发/测试 agent **只读**
>
> **本文件是全项目需求的唯一来源。** 开发 agent 按此实现,测试 agent 按此写用例。
> 每个功能点有唯一编号 `FR-<模块>-<序号>`,测试用例必须在追溯矩阵中与之一一对应。
> 需求有歧义时,向 PM 提出,**不要自行解释**。
---
## 0. 产品定位
面向植物生物信息学研究者的**文献知识问答工作台**。用户提出关于植物基因家族的科学问题,系统从已构建的文献知识库(约 100 篇论文、44,595 条关系断言)中检索证据,生成有引用锚定的答案,并展示完整的检索过程、证据来源与知识图谱。同时提供论文抽取任务的管理入口。
**使用场景**:桌面深度研究为主,**手机端随时查询为重要补充**(实验室、会议、田间)。
**不做什么**:默认支持同会话上下文记忆(可在设置中关闭)(上游 `max_rounds:1`,每次提问独立检索);不做文献全文阅读器;(2026-09-05 起:生信流程执行经对话式编排进入,见 FR-BIO)。
---
## 1. 术语表
| 术语 | 含义 |
|---|---|
| 会话 Session | 一组问答的容器,有标题、状态、时间。**注意:会话是组织单位,不是上下文单位**——同一会话内的多次提问互相独立 |
| 消息 Message | 会话内的一条记录,`role` 为 `user` 或 `assistant` |
| 块 Block | 助手消息的内容单元,判别式联合类型(text/table/code/stream/pipeline/tool/sources/graph) |
| 来源 Source | 一条支撑答案的文献证据,含标题、片段、分数、PMID/DOI 等 |
| 证据链 Chain | 知识图谱上的一条多跳推理路径,由若干 hop 组成 |
| 阶段 Stage | 上游 QA 服务推送的细粒度检索步骤(十余个) |
| 相位 Phase | 前端展示用的五阶段归并(规划/检索/证据/生成/完成) |
| 上游 Upstream | 现有的三个后端服务:QA(7920)、抽取(8000/7900)、Neo4j |
| BFF | 本项目的适配层后端,前端唯一直连对象 |
---
## 2. 全局约定
### 2.1 状态枚举
**会话状态** `idle | running | done | error`
- `idle` 排队中/新建未提问 —— 空心点,ink-subtle
- `running` 有进行中的问答 —— 青色 `#00D4FF` 脉冲点
- `done` 最近一次问答成功 —— success `#2ED3A0` 实心点
- `error` 最近一次问答失败或中断 —— error `#FF5C7A` 实心点
**消息状态** `streaming | done | error`
**相位状态** `waiting | running | done`(对应字形 ◌ / ● / ✓)
### 2.2 错误处理原则
- BFF 一切错误返回统一信封 `{"ok":false,"error":{"code","message","degraded"}}`,HTTP 状态码语义正确。
- 前端**不显示原始异常堆栈**,显示 `error.message` 的中文可读文案。
- `degraded:true` 表示「上游不可用」而非「请求有误」,UI 用降级提示(横幅/离线态)而非错误弹窗。
- **失败必须可见**:任何静默吞掉错误的实现视为缺陷。
### 2.3 文案约定
用户可见文案统一走 `src/i18n/labels.js`。例外:后端返回的 `error.message` 直接展示(后端已中文化)。
### 2.4 数据不伪造原则
UI 只展示后端真实返回的数据。**禁止**:占位假数据流入生产路径、把空结果渲染成看起来有内容的样子、用动画暗示实际不存在的流式过程(客户端揭示动画是明示的视觉设计,允许,但不得声称是 token 流)。
---
## 3. 模块与编号
| 模块 | 前缀 | 范围 |
|---|---|---|
| 应用外壳 | `FR-SHELL` | 三栏布局、顶栏、状态栏、折叠、背景 |
| 主题与无障碍 | `FR-THEME` | 深浅主题、动效降级、焦点、对比度 |
| 会话管理 | `FR-SESSION` | 列表、分组、增删改查、搜索 |
| 对话与问答 | `FR-CHAT` | 消息渲染、作曲器、流式、流水线、持久化 |
| 检索过程 | `FR-RETRIEVAL` | 工作台 Tab1 |
| 文献来源 | `FR-SOURCE` | 工作台 Tab2 |
| 提取任务 | `FR-EXTRACT` | 工作台 Tab3 |
| 知识图谱 | `FR-KG` | 工作台 Tab4 |
| 响应式 | `FR-RESP` | 四档断点与移动端专项 |
| 健康与降级 | `FR-HEALTH` | 上游探活与降级行为 |
| 生信流程编排 | `FR-BIO` | 对话式生信编排(七项引导 / 面板 / 三形态 / 独立服务) |
---
## 4. FR-SHELL 应用外壳
| 编号 | 功能点 | 业务逻辑与边界 | 验收要点 |
|---|---|---|---|
| **FR-SHELL-001** | 桌面三栏网格 | `grid-template-columns: 260px minmax(0,1fr) 380px`;`grid-template-rows: 48px minmax(0,1fr) 28px`;区域 `"top top top" / "side chat bench" / "status status status"`。页面本身不滚动(`body{overflow:hidden}`),滚动发生在各栏内部 | 1440×900 下三栏宽度精确;缩放窗口不出现横向滚动条 |
| **FR-SHELL-002** | 顶栏 | 高 48px。左:9px 渐变发光圆点 + 「海古纪 PlantAI」渐变文字(`background-clip:text`)。中:当前会话标题(超长省略)+ 状态徽章。右:主题切换按钮。背景 `color-mix(in srgb, var(--pa-canvas) 88%, transparent)` + `backdrop-filter: blur(14px)`,底部 1px hairline | 无会话时中部显示「就绪」徽章;标题超长省略而非换行 |
| **FR-SHELL-003** | 底部状态栏 | 高 28px。左:4 个 7px agent 状态点(规划/检索/证据/生成),与流水线相位联动。右:`PlantAI RAG v0.1` + mono 的 QA 版本号 + 会话数 + 上游健康标记 | 问答进行中四点按相位依次点亮;QA 版本号来自 `/api/qa/info` |
| **FR-SHELL-004** | 左栏折叠 | 折叠后宽 64px 图标轨,显示 3 个 34×34 图标按钮(会话/图谱/设置),当前项左侧 2px 渐变竖条。折叠状态写入 localStorage `pa-side-collapsed` | 折叠/展开过渡 300ms `ease-out`;刷新后保持 |
| **FR-SHELL-005** | 右栏折叠 | 折叠后保留 28px 手柄条(**折叠 ≠ 隐藏**),点击手柄重新展开。状态写入 localStorage `pa-bench-collapsed` | 折叠后手柄可见可点;刷新后保持 |
| **FR-SHELL-006** | Aurora 背景 | 固定层,`z-index:0`,`pointer-events:none`。两个 560/620px 渐变光斑,`blur(120px)`,`aurora-drift` 16s 往复;叠加 48px 点阵,顶部渐隐遮罩。浅色主题下 `--pa-aurora-opacity:.08` | 不遮挡任何交互;浅色下明显衰减 |
| **FR-SHELL-007** | 首屏初始化 | 顺序:读 localStorage 主题并写 `documentElement.dataset.theme`(**必须在挂载前**,避免闪白)→ 挂载应用 → 并行请求会话列表与 `/api/health`、`/api/qa/info` → 无会话则显示欢迎空态 | 深色主题下刷新无白屏闪烁;接口失败不阻塞渲染 |
---
## 5. FR-THEME 主题与无障碍
| 编号 | 功能点 | 业务逻辑与边界 | 验收要点 |
|---|---|---|---|
| **FR-THEME-001** | 双主题等价 | 深色为默认。浅色**不是降级稿**,是同一套语义的另一组令牌值。所有组件必须在两套主题下都正确 | 逐组件走查两套主题无失色、无对比度不足 |
| **FR-THEME-002** | 切换与持久化 | 顶栏按钮切换,写 `localStorage['pa-theme']`,同步 `documentElement.dataset.theme`。图标日/月互换并 rotate 180°(400ms) | 切换即时生效;刷新保持 |
| **FR-THEME-003** | 浅色 glow 衰减 | 浅色下所有辉光乘 `--pa-glow-opacity:.35`——浅底上不衰减会糊 | 浅色下按钮悬停辉光明显弱于深色 |
| **FR-THEME-004** | 动效降级 | `@media (prefers-reduced-motion: reduce)` 下关闭全部 animation/transition;状态改由静态符号表达(实心/空心/✓/✕),不依赖运动传达信息 | 开启系统「减少动态效果」后无任何动画,且状态仍可辨认 |
| **FR-THEME-005** | 焦点与键盘 | `:focus-visible` 输出 2px `rgba(94,106,210,.5)` 焦点环,offset 1px。所有交互元素键盘可达,Tab 顺序符合视觉顺序 | 纯键盘可完成:新建会话→输入→发送→切换 tab |
| **FR-THEME-006** | 对比度 | 深色 ink/canvas ≈15:1,ink-muted/canvas ≥4.5:1;浅色同标准 | 抽检主要文本对比度达标 |
---
## 6. FR-SESSION 会话管理
| 编号 | 功能点 | 业务逻辑与边界 | 验收要点 |
|---|---|---|---|
| **FR-SESSION-001** | 列表与分组 | 按 `updated_at` 倒序,分「今天 / 昨天 / 更早」三组,组标签 11px/500/字距0.6px。空列表显示引导文案 | 跨零点后分组正确迁移 |
| **FR-SESSION-002** | 状态点四态 | 见 §2.1。`running` 脉冲动画,其余静态 | 四种状态视觉可区分,且不仅靠颜色(形状:空心/实心 + 动/静) |
| **FR-SESSION-003** | 新建会话 | 顶部渐变按钮(40px 高)。行为:调 `POST /api/sessions` 创建 → 切为活动会话 → 对话区显示欢迎空态。**不预先落一条空消息** | 连点两次产生两个独立会话,不重复 |
| **FR-SESSION-004** | 选择与加载 | 点击会话 → `GET /api/sessions/{id}` 拉取消息 → 渲染历史。加载中显示骨架屏。切换会话时若有进行中的流,**中断该流**并把原会话置 `error`(标注「已中断」) | 历史消息含表格/图谱块能正确重渲染 |
| **FR-SESSION-005** | 搜索过滤 | 搜索框对 `title` 做不区分大小写的子串匹配,实时过滤(前端过滤,不发请求)。无匹配显示「无匹配会话」 | 输入中文/英文均可过滤;清空恢复全量 |
| **FR-SESSION-006** | 重命名 | 双击标题或右键菜单进入编辑,回车提交 `PATCH /api/sessions/{id}`,Esc 取消。空标题不允许提交 | 重命名后列表与顶栏标题同步更新 |
| **FR-SESSION-007** | 删除 | 需二次确认。确认后 `DELETE /api/sessions/{id}`(级联删消息)。若删的是当前会话,切到列表中最新一个;列表空则显示欢迎空态 | 删除后刷新不再出现;消息一并清除 |
| **FR-SESSION-008** | 标题自动生成 | 新会话首次提问后,用问题前 30 字符(超出加省略号)作为标题,调 `PATCH` 落库。用户手动改过标题的会话(`meta.title_manual=true`)不再自动覆盖 | 首问后标题自动变化;手动改名后再提问不被覆盖 |
| **FR-SESSION-009** | 活动态高亮 | 左侧 2px 渐变竖条 + `color-mix(in srgb, var(--pa-g-to) 8%, transparent)` 底 + 22% 内描边 | 活动项一眼可辨,与悬停态区分 |
---
## 7. FR-CHAT 对话与问答(核心模块)
### 7.1 基础渲染
| 编号 | 功能点 | 业务逻辑与边界 | 验收要点 |
|---|---|---|---|
| **FR-CHAT-001** | 欢迎空态 | 消息为空时显示:46px 渐变菱形(✧)+ 渐变标题「海古纪 PlantAI 知识工作台」+ 副标题 + **3 张建议卡**(示例问题)。点击建议卡直接以该文本提问 | 卡片悬停上浮 2px + 青色微光;点击即发起真实提问 |
| **FR-CHAT-002** | 用户消息 | 右对齐,最大宽 70%,渐变底(深色 `135deg,#2A2A3E,#1C1C2A`;浅色 `#E8ECFF,#F4FBFF`)+ hairline-strong 边,r12,padding 10/15px | 长文本正确换行不溢出 |
| **FR-CHAT-003** | 助手消息 | **无底色全文**(Linear 式),左缘 2px 透明竖线悬停变渐变。头部:24px 渐变头像(✦)+ 「PlantAI 智能体」+ 时间。内容按块联合顺序渲染 | 多块混排顺序正确 |
| **FR-CHAT-004** | Markdown 渲染 | `markdown-it` 解析 → DOMPurify 消毒 → `v-html`。表格加 `md-table` 类:thead 用 2px 渐变下边框,数据格 mono(表头为「说明」的列除外,保持比例字体);代码块 code-bg + hairline + mono 12.5px | LLM 输出的表格/列表/加粗正确渲染;**注入脚本被消毒** |
| **FR-CHAT-014** | 自动滚动 | 新消息或流式更新时滚到底。**例外**:用户手动向上滚动后暂停自动滚动,并显示「回到最新」按钮;点击或用户滚回底部后恢复 | 阅读历史时不被强制拽到底 |
| **FR-CHAT-015** | 内联引用 | 答案文本中的引用标记渲染为可点 chip,点击滚动定位到工作台「文献来源」中对应条目并高亮 2s | 引用 chip 与来源列表编号一致 |
### 7.2 作曲器
| 编号 | 功能点 | 业务逻辑与边界 | 验收要点 |
|---|---|---|---|
| **FR-CHAT-005** | 输入框 | r16 surface-1 卡片,`:focus-within` 渐变描边 + 青色辉光。textarea 无边框透明,14px/1.6,min-height 46px,**max-height 160px 后内部滚动**。占位符提示示例问题 | 多行输入自动增高至上限后滚动 |
| **FR-CHAT-006** | 三种模式 | pill 切换,参数集定义在 `config/modes.js`:<br>**问答** `{use_llm:true}` → `/api/qa/chat/stream`<br>**方案** `{use_llm:true, native_chain_enabled:true, native_chain_max_hops:3, native_chain_top_k:5}` → 同上,深证据链<br>**检索** `{use_llm:false}` → `/api/qa/search`,只出来源不出答案<br>激活态渐变底 + 白字 + 青光 | 三种模式发出的请求体字段正确;检索模式不产生答案块 |
| **FR-CHAT-007** | 发送与快捷键 | `Enter` 换行,`⌘/Ctrl+Enter` 发送。发送按钮渐变 pill,空输入或忙碌时 `disabled`(opacity .45)。底部提示文案说明快捷键 | 快捷键在中英文输入法下均生效;空输入不可发送 |
| **FR-CHAT-011** | 停止/中断 | 流式进行中,发送按钮变为「停止」。点击 → `AbortController.abort()` → 关闭 SSE → 当前消息置 `error` 并标注「已停止」→ 已收到的块保留不丢 | 停止后 BFF 侧上游连接也关闭(不留悬挂请求) |
### 7.3 流式问答(最复杂的部分,详细说明)
**FR-CHAT-008 流式问答主流程**
业务逻辑,按时序:
1. 用户发送 → 前端立即在界面追加用户消息气泡 → `POST /api/sessions/{id}/messages` 落库(`role:user`)
2. 会话状态置 `running`,左栏状态点开始脉冲,底栏 agent 点复位
3. 前端追加一条助手消息骨架(`status:streaming`),内含一个 `pipeline` 块,五相位全为 `waiting`
4. `fetch` + `ReadableStream` 打开 `POST /api/qa/chat/stream`(**不能用 EventSource**——请求是带 JSON body 的 POST)
5. 逐帧消费:
- `event: progress` → 取 `phase` 字段(**BFF 已计算好,前端不解析上游 stage 名**)→ 更新 `pipeline` 块对应相位为 `running`,此前相位置 `done`;同时把该事件的 `details` 追加为工作台「检索过程」的一张阶段卡
- `event: complete` → payload 即完整归一化响应 → 用 `answer` 生成 `text` 块(走揭示动画)、`sources` 生成 `sources` 块、图谱数据生成 `graph` 块;`pipeline` 全部置 `done`
- `event: error` → 消息置 `error`,展示 `error.message`,保留已收到的块
- `event: timeout` → 同 error,文案为「检索超时」
- `event: raw` → 记入 `raw`,不渲染
- `:` 注释帧(keepalive)→ 忽略
6. 每 ~3s 调 `PATCH /api/sessions/{id}/messages/{mid}` 做检查点持久化
7. 终态:消息置 `done`/`error` 落库,会话状态同步,底栏 agent 点定格
边界与异常:
- **上游最长 240s**:期间必须持续有视觉反馈(相位卡 + shimmer + 已耗时计数),不得让用户面对静止界面
- **网络中断**(移动端切网常见):`fetch` reject → 置 `error`,文案「连接中断」,提供「重试」按钮。**不自动重连**——每次 LLM 调用昂贵,自动重试会放大成本
- **页面刷新中途**:重载后显示最后检查点内容 + 「已中断」标注,会话状态 `error`
- **同一会话并发提问**:忙碌时发送按钮 disabled,从交互上杜绝
**FR-CHAT-009 五阶段流水线卡**
| 相位 | 颜色令牌 | 上游 stage(由 BFF 归并) |
|---|---|---|
| 规划 | `--pa-t-planning` `#C0A8DD` | `understanding_started`、`entities_identified`、`related_search_started` |
| 检索 | `--pa-t-execution` `#9FBBE0` | `concepts_found`、`subquestion_retrieved`、`agent_review_started`、`subquestion_retry_started` |
| 证据 | `--pa-t-knowledge` `#9FC9A2` | `evidence_accepted`、`evidence_review_completed` |
| 生成 | `--pa-t-validation` `#DFA88F` | `answer_generation_started` |
| 完成 | `--pa-t-done` `#C08532` | `completed` |
渲染:纵向时间线,每相位 = 16px 状态点(✓ done / ● running / ◌ waiting)+ 2px 连接线 + 相位名 + 描述(取自最近一次该相位事件的摘要)+ 右侧状态文字;running 相位额外显示 11px 旋转 spinner。卡片头部「⚙ 检索流水线」+ 整体状态徽章(语义色)。
**关键约束**:相位色只回答「哪一步在做什么」,语义色只回答「整体成败」,两者不得混用。
**FR-CHAT-010 答案揭示动画**
上游**不做 token 级流式**,完整答案在 `complete` 一次到达。前端以 30ms/字符的速度揭示,尾部显示 8×15px 青色闪烁光标(`blink` 1s),下方显示 3px shimmer 进度线。揭示期间用户可点击「跳过」立即显示全文。揭示结束替换为最终 `text` 块。
边界:答案超过 2000 字符时,揭示速度自动加快(总时长上限 8s),避免长答案等待过久。
**FR-CHAT-012 错误与重试**
- 上游返回 HTTP≥400 且 `detail.error_code == "invalid_llm_citation"`:BFF 已自动重试一次;若仍失败,前端展示「引用校验失败,请重试」并提供重试按钮
- 其他错误:展示 `error.message`,提供重试按钮
- 重试 = 用相同问题与模式重新发起,**在同一条助手消息位置替换**,不新增用户消息
**FR-CHAT-013 消息持久化与恢复**
- 块联合完整落库(`stream` 块为瞬态,**不落库**,落库时已转为 `text`)
- `raw` 字段保存完整归一化响应,供排查与重渲染
- 刷新后从 `GET /api/sessions/{id}` 恢复全部块,包括表格、来源、图谱
---
## 8. FR-RETRIEVAL 检索过程(工作台 Tab1)
| 编号 | 功能点 | 业务逻辑与边界 | 验收要点 |
|---|---|---|---|
| **FR-RETRIEVAL-001** | 阶段卡列表 | 每个 SSE `progress` 事件生成一张卡,沿用原型「工具调用卡」视觉:折叠头(相位色图标 + 阶段中文名 + 状态点 + 耗时 mono)+ 展开体(`details` 以 mono 参数块呈现 + 摘要行) | 一次问答后卡片数量与收到的 progress 事件数一致 |
| **FR-RETRIEVAL-002** | 相位筛选 | chip:全部/规划/检索/证据/生成。激活态实心 `#5E6AD2` 底 + 白字 | 筛选后仅显示对应相位卡片 |
| **FR-RETRIEVAL-003** | 展开折叠 | 点击头部切换,chevron 旋转 90°,展开体 `fade-slide` 200ms。**运行中的卡默认展开**,完成后自动折叠 | 展开状态互相独立 |
| **FR-RETRIEVAL-004** | 运行中效果 | 运行中卡片头部 shimmer 扫光 1.8s 循环 + 状态点脉冲 | 完成后动效停止 |
| **FR-RETRIEVAL-005** | 检索计划 | 若响应含 `retrieval_plan`,在列表顶部以只读信息块展示;另展示 `/api/qa/info` 的 12 段 pipeline 描述作为静态说明 | 无 plan 时该区域不显示(不留空框) |
| **FR-RETRIEVAL-006** | 空态 | 未提问时显示「发起提问后,这里会实时展示检索过程」 | 空态不显示假卡片 |
---
## 9. FR-SOURCE 文献来源(工作台 Tab2)
| 编号 | 功能点 | 业务逻辑与边界 | 验收要点 |
|---|---|---|---|
| **FR-SOURCE-001** | 来源列表 | 沿用原型「产物文件」的 mono 列表视觉。每条:序号 + 标题(超长省略)+ mono 元信息行(`PMID:xxx` / `DOI:xxx` / 分数)+ 证据片段(默认 2 行截断,点击展开) | 条目数与 `source_count` 一致 |
| **FR-SOURCE-002** | 字段展示 | 展示归一化后的 `title / snippet / score / pmid / pmcid / doi / section / support_count`。缺失字段不显示占位符,**不显示 `undefined`/`null`** | 各字段缺失组合下渲染正常 |
| **FR-SOURCE-003** | 外链跳转 | 链接优先级:`url` > `doi`(拼 `https://doi.org/{doi}`)> `pmid`(拼 PubMed)> `pmcid`(拼 PMC)。均无则不显示链接图标。新标签页打开,带 `rel="noopener"` | 点击真实跳转到正确文献页 |
| **FR-SOURCE-004** | 证据链 | `chains` 按 `chain_id` 分组,每组可展开为逐跳列表:`源实体 —[关系]→ 目标实体`,每跳附 paper_id/PMID。跳数以徽章显示 | 多跳链条展开顺序正确 |
| **FR-SOURCE-005** | 会话级聚合 | 顶部切换「本次回答 / 本会话全部」。后者调 `GET /api/sessions/{id}/sources`,按 `paper_id` 去重并累计出现次数 | 去重正确,计数准确 |
| **FR-SOURCE-006** | 空态 | 无来源时显示「本次检索未命中文献」,**不显示空列表框** | 空结果不误导为加载中 |
---
## 10. FR-EXTRACT 提取任务(工作台 Tab3)
| 编号 | 功能点 | 业务逻辑与边界 | 验收要点 |
|---|---|---|---|
| **FR-EXTRACT-001** | 任务列表 | `GET /api/extraction/jobs`。每条沿用原型进度条视觉:任务名 + mono 百分比 + 6px 轨道 + 渐变填充 + sweep 扫光;100% 转为纯 success 色且停止扫光。**上游无 progress 数值**,百分比由 BFF 按 `stage` 派生(queued=5 / mineru=35 / llm=75 / completed=100,failed 显示失败态不显示进度条) | 列表与上游一致;派生数值正确;failed 任务呈现为错误态 |
| **FR-EXTRACT-002** | 任务详情 | 点击展开 `GET /api/extraction/jobs/{id}`,展示状态、message(中文进度描述)、统计字段(stage/species/figure_table 计数)、plan(仅 completed 有,未完成置 null 不显示) | 详情字段与上游一致;未完成任务不显示 plan 且不报错 |
| **FR-EXTRACT-003** | 运行日志 | `GET /api/extraction/run-log`,mono 等宽展示,最大高度内滚动,自动贴底 | 长日志不撑破布局 |
| **FR-EXTRACT-004** | 论文上传 | 作曲器区域附件按钮 → 选 PDF → `POST /api/extraction/upload`(multipart)→ 显示上传进度 → 成功返回 job_id 并刷新列表。**限制**:仅 PDF,单文件 ≤50MB。上游当前环境 MinerU 二进制路径缺失会导致新任务 failed(历史 27 个 failed 任务同因)——若发生,展示上游 message 的明确错误文案 | 非 PDF 被拒;超限被拒;错误文案可读 |
| **FR-EXTRACT-005** | 轮询刷新 | 存在 `running` 状态任务时每 5s 轮询列表;全部终态则停止轮询。切到其他 tab 时暂停轮询 | 无进行中任务时不空转 |
| **FR-EXTRACT-006** | 离线降级 | 抽取上游不可用时显示离线提示与重试按钮,不影响其他 tab | 8000/7900 全停时该 tab 优雅降级 |
---
## 11. FR-KG 知识图谱(工作台 Tab4)
**双数据源策略**(重要):
- **答案图谱**:来自当前消息的 `graph` 块(由 QA 响应的 `knowledge_graph`/`native_graph_chains` 归一化而来)。永远可用,Neo4j 挂了也不受影响。
- **自由浏览**:来自 `GET /api/kg/*`,BFF 用 Bolt 直连 Neo4j。上游 `/api/v1/kg/*` 已损坏(指向未运行的 7687),不可依赖。
| 编号 | 功能点 | 业务逻辑与边界 | 验收要点 |
|---|---|---|---|
| **FR-KG-001** | 答案图谱渲染 | 手写 SVG 力导向布局。节点圆点 + 标签,边为渐变线(`#5E6AD2→#00D4FF`)带关系名。节点数 >50 时自动降级为列表视图并提示 | 图谱与答案引用的实体一致 |
| **FR-KG-002** | 节点着色与图例 | 按实体 `type` 着色,用品牌色 + 辅助紫 + 五相位色构成调色板,底部图例。**同一 type 在会话内颜色稳定** | 图例与节点颜色对应 |
| **FR-KG-003** | 节点详情 | 点击节点 → 侧滑详情面板,展示实体属性;若 Neo4j 可用,额外调 `GET /api/kg/entities/{name}` 拉全量属性 | Neo4j 不可用时仍能显示消息内已有属性 |
| **FR-KG-004** | 实体搜索 | 搜索框 → `GET /api/kg/entities?q=` → 按 `name_norm` 精确/前缀匹配(走 name_norm 索引,0.1s 内返回)→ 选中渲染其邻域。**注意上游无全文索引**,搜索是精确/前缀匹配,非模糊全文检索 | 中英文实体名(精确或前缀)均可搜到;响应 <1s |
| **FR-KG-005** | 邻域展开 | `GET /api/kg/entities/{name}/neighborhood?depth=&limit=`。深度选择 1/2,默认 1(**两跳实测约 13s**,需提示较慢)。节点上限 200,每跳按 paper_count/support_count 降序截断防 hub 吃满预算 | 深度切换生效;超限有提示不卡死 |
| **FR-KG-006** | 缩放与平移 | 滚轮/双指缩放(0.3×–3×),拖拽平移,双击复位。**触摸端必须支持双指缩放与单指拖拽** | 手机上可流畅缩放平移 |
| **FR-KG-007** | 离线降级 | Neo4j 不可用时:搜索与邻域功能禁用并提示离线;**答案图谱照常渲染**(这是双数据源的意义) | 断开 Neo4j 后 tab 不白屏 |
| **FR-KG-008** | 空态 | 本次回答无图谱数据时提示「本次检索未产生图谱关系」,并引导使用实体搜索 | 空态不显示空画布 |
---
## 12. FR-RESP 响应式(手机为一等公民)
### 12.1 断点
| 编号 | 档位 | 宽度 | 布局规则 |
|---|---|---|---|
| **FR-RESP-001** | Desktop | ≥1280px | 完整三栏 260/1fr/380 |
| **FR-RESP-002** | Laptop | 1024–1279px | 三栏保留;左栏**自动**折叠为 64px 轨;右栏收窄至 340px |
| **FR-RESP-003** | Tablet | 768–1023px | 对话单栏铺满;左栏改**左抽屉**(scrim 遮罩 + 滑入),顶栏加汉堡按钮;右栏改**右抽屉**,顶栏加工作台按钮并显示来源数角标 |
| **FR-RESP-004** | Mobile | <768px | 单栏;顶栏 = 汉堡 + 标题 + 工作台按钮;对话铺满;作曲器吸底;工作台为**全屏覆盖层 + 顶部 tab 条**;底部状态栏隐藏,四个 agent 点合并为顶栏一枚聚合状态点 |
抽屉与覆盖层动效沿用 `ease-out` 300ms;打开时 scrim 淡入,点击 scrim 或按 Esc 关闭。
### 12.2 移动端专项
| 编号 | 功能点 | 业务逻辑与边界 | 验收要点 |
|---|---|---|---|
| **FR-RESP-005** | viewport 与安全区 | `<meta viewport ... viewport-fit=cover>`;作曲器 `padding-bottom: env(safe-area-inset-bottom)`;顶栏 `env(safe-area-inset-top)` | iPhone 刘海/Home 条不遮挡内容 |
| **FR-RESP-006** | iOS 输入放大规避 | <768px 时 textarea `font-size:16px`(低于 16px 必触发 iOS 自动缩放)。**禁止**用 `maximum-scale=1` 规避——伤害无障碍 | iPhone Safari 聚焦输入框页面不放大 |
| **FR-RESP-007** | 悬停态触摸等价 | 所有 hover 样式包在 `@media (hover: hover)` 内。触摸设备下:来源外链图标常驻可见、助手消息左缘线常驻、会话条目去掉悬停底色改用点击反馈 | 手机上无「需要悬停才出现」的功能 |
| **FR-RESP-008** | 触控目标 | <768px 下会话条目、bench tab、模式 pill、图标按钮 `min-height:44px`(iOS HIG 标准,高于 DESIGN.md 的 36px) | 手机上不误触 |
| **FR-RESP-009** | 视口高度 | 用 `100dvh`,保留 `100vh` 回退,规避移动浏览器地址栏伸缩导致的高度跳变 | 滚动时布局不跳动 |
| **FR-RESP-010** | 虚拟键盘 | 监听 `visualViewport` 的 resize:键盘弹出时压缩消息区高度并保持贴底,作曲器始终可见 | 键盘弹出不遮挡输入框 |
| **FR-RESP-011** | 表格横向滚动 | <768px 下答案内 `md-table` 包一层横向滚动容器,右侧加渐隐提示。**不压缩字号**(可读性优先) | 宽表格可横滑查看完整内容 |
| **FR-RESP-012** | 移动端性能降级 | <768px 下 aurora 模糊半径降低或 `--pa-aurora-opacity:0`;图谱节点数上限降至 30 | 中端手机滚动流畅无卡顿 |
| **FR-RESP-013** | 手势(可选) | 左缘右滑开会话抽屉,右滑关工作台覆盖层。**不作为验收门槛** | 实现则可用,不实现不算缺陷 |
---
## 13. FR-HEALTH 健康与降级
| 编号 | 功能点 | 业务逻辑与边界 | 验收要点 |
|---|---|---|---|
| **FR-HEALTH-001** | 健康轮询 | 挂载后请求 `GET /api/health`,此后每 30s 一次。结果写入 store,驱动状态栏标记与各降级态。轮询失败不弹窗,仅标记 | 上游恢复后 30s 内自动解禁 |
| **FR-HEALTH-002** | QA 不可用 | `/api/qa/*` 返 503 degraded:作曲器禁用并显示「知识问答服务当前不可用」,历史会话仍可浏览 | 停掉 7920 后 UI 明确提示而非静默失败 |
| **FR-HEALTH-003** | Neo4j 不可用 | 图谱 tab 搜索/邻域禁用并提示离线;答案图谱不受影响 | 见 FR-KG-007 |
| **FR-HEALTH-004** | 抽取不可用 | 见 FR-EXTRACT-006 | 仅该 tab 降级 |
| **FR-HEALTH-005** | 网络中断 | 流式中断时提示「连接中断」+ 重试按钮;**不自动重连**(避免重复触发昂贵 LLM 调用) | 手机切换网络后有明确提示 |
---
## 14. 非功能要求
| 项 | 要求 |
|---|---|
| 首屏可交互 | 桌面 <2s(本地网络),手机 <3s |
| 长任务反馈 | QA 最长 240s,期间必须持续有视觉反馈,无静止超过 5s 的状态 |
| 数据安全 | Neo4j 凭据只存在于 BFF 配置,**绝不下发到浏览器**;LLM 输出必须经 DOMPurify 消毒后才 `v-html` |
| 离线容忍 | 前端零外网请求(无 Google Fonts、无 CDN),符合 DESIGN.md 禁令 |
| 浏览器支持 | Safari 16+(iOS)、Chrome 110+、Firefox 110+ |
---
## 15. FR 索引(供追溯矩阵使用)
SHELL: 001–007(7)|THEME: 001–006(6)|SESSION: 001–009(9)|CHAT: 001–015(15)|RETRIEVAL: 001–006(6)|SOURCE: 001–006(6)|EXTRACT: 001–006(6)|KG: 001–008(8)|RESP: 001–013(13)|HEALTH: 001–005(5)
**合计 81 个功能点。** 测试用例追溯矩阵必须覆盖全部 81 项(FR-RESP-013 标注为可选,仍需有用例但允许标记为 N/A)。
---
## 16. FR-BIO 生信流程编排(对话式)
模块范围:在对话界面用自然语言驱动 73 个生信分析阶段(基因家族 21 / RNA-seq 14 / WGS 14 / 群体 5 / GWAS 9+报告)的路径规划、数据文件引导、参数确认、执行过程展示、报告生成调整、任务存档与存档复用;新增「生信任务」「生信档案」两个工作台面板;流程文档知识库问答并入生信对话;三形态部署(只 rag / 只生信 / 同部署)。
**FR-BIO-001 意图分流**:**仅当用户明确下达分析指令时**才走生信流——「用 xxx 进行分析」「帮我分析 xxxxx」「帮我跑 xxx 鉴定」类指令(任务动词+家族/对象、前缀分析句式、分析链句式、存档复用句式四通道,词典与用例表见 docs/team/cases_bioflow_intent.md v1.1);其余一律走正常问答流程——「水稻 SPL 基因家族的最新研究进展有哪些?」等问答句不进生信流(疑问引导词与问答主题词两道拦截,漏判安全优先)。(验收:明确分析指令句进入生信流;问答句照旧走 qa 检索回答,不弹出生信路径卡)
**FR-BIO-002 分析路径引导**:系统先回复可用分析步骤路径候选(2-3 条,依据阶段目录+流程知识库生成),渲染为可点选路径卡;用户可点选或自然语言描述修改。(验收:SPL 询问 → 回复含 2-3 条候选路径卡,每条含阶段序列与用途)
**FR-BIO-003 路径校验与前置补全**:LLM 候选路径经阶段目录硬校验,未知阶段直接拒绝;缺失的前置阶段自动补全并在回复中明示。(验收:伪造阶段名被拒;跳过中间阶段时前置被补全且可见)
**FR-BIO-004 路径确认卡**:最终阶段链以确认卡展示(逐阶段名称/用途/预计耗时),用户确认后创建任务。(验收:确认卡逐阶段可读;确认后生信任务面板出现新任务)
**FR-BIO-005 数据文件引导**:阶段执行前检查输入文件;缺失时自然语言告知「请将 xxx 数据文件拖入窗口上传至服务器,或在聊天窗口中指定数据文件的具体路径」,附文件引导卡。(验收:起始阶段缺 fasta → 出现该提示与文件引导卡)
**FR-BIO-006 拖拽上传**:文件引导卡支持拖拽/点击上传;XHR 上传实时进度(百分比);支持大文件(≥456M 基因组)。(验收:拖入文件 → 进度条推进至 100%;大文件不断流)
**FR-BIO-007 服务器路径登记**:用户可直接在聊天里输入服务器文件路径字符串登记为阶段输入。(验收:输入合法路径 → 输入检查通过;非法路径 → 明确报错)
**FR-BIO-008 参数确认表单**:阶段执行前对话告知「XXXX 步骤,可调整参数如下,可按需调整,调整完毕后继续」,并显示可编辑参数表单卡(默认值带来源标注)。(验收:每个阶段执行前出表单卡;默认值=契约文档/参数预填值且来源可见)
**FR-BIO-009 继续执行**:用户编辑参数后点「继续」按钮,该阶段按表单值执行。(验收:改一个参数 → 执行命令使用新值)
**FR-BIO-010 自动连续执行开关**:开启后按推荐参数自动连续跑完全程;可随时暂停切回手动。(验收:开启后阶段间不等待表单确认自动推进;暂停后停在下一阶段前)
**FR-BIO-011 阶段进度卡**:阶段链确定后生成进度卡(与现有流水线卡同款视觉);随执行逐阶段流转(等待/运行/完成/失败/跳过);运行中阶段显示实时日志尾部。(验收:执行中进度卡逐阶段点亮、日志尾滚动更新)
**FR-BIO-012 失败三选一**:失败阶段标红,卡片内提供「重跑/跳过/改参数重跑」三按钮。(验收:注入失败阶段 → 标红 + 三按钮出现;三种处置各自生效)
**FR-BIO-013 长链折叠**:阶段超过 10 个自动折叠为分组视图,可展开。(验收:>10 阶段链显示分组折叠;展开可见全部)
**FR-BIO-014 断线回放**:SSE 断线重连后按任务快照整体回放进度卡状态。(验收:断网 30s 重连 → 卡片恢复当前状态,无进度丢失)
**FR-BIO-015 报告生成**:任务最后阶段运行报告链,生成图文并茂单文件 HTML(图 base64 内嵌 + 各阶段 manifest 事实数据)。(验收:报告含图与阶段结果汇总,浏览器可打开)
**FR-BIO-016 报告预览**:报告登记进文档工作台,DocPane 预览(白底)。(验收:任务完成后文档 TAB 出现报告,预览白底图文正常)
**FR-BIO-017 报告编辑**:可像公文一样就地编辑文字、用 prompt 调整排版(走现有编辑链路)。(验收:改文字落盘;prompt 排版指令生效)
**FR-BIO-018 报告导出**:导出 PDF/DOCX。(验收:两种格式下载成功且文件可打开)
**FR-BIO-019 图表调整**:图表类 prompt(如「xxx 图表颜色换成红色」)解析为绘图参数 → 重跑产生该图的阶段 → 新图回填报告并刷新预览;纯文字排版指令走 docgen 编辑。(验收:换色指令后报告图更新为新颜色)
**FR-BIO-020 任务存档提示**:任务完成后对话提示「本次生信任务已完成,是否存档?可按你的要求命名并描述」;用户可放弃。(验收:完成即出存档提示;放弃不存档无副作用)
**FR-BIO-021 存档执行与状态回复**:按用户要求存档(过程记录、中间文件、结果文件、报告),完成后回复存档状态(名称/位置/大小)。(验收:存档后回复含上述三要素;磁盘档案目录存在)
**FR-BIO-022 档案回看**:「生信档案」面板可找出历次任务回看(详情/产物/报告)。(验收:存档任务出现在档案列表,详情可看)
**FR-BIO-023 存档方法复用**:按名称匹配存档(精确优先,模糊给候选)→ 装载阶段链与参数 → 表单预填存档值 → 按确认参数发起新任务。(验收:「用 XXX 存档任务的方法帮我跑 yyy」→ 表单预填 XXX 参数 → 新任务按此执行)
**FR-BIO-024 生信任务面板**:「生信任务」面板:任务列表/阶段 DAG/进度/manifest 与日志浏览/重跑。(验收:多任务浏览流畅;manifest 与日志可读;重跑成功)
**FR-BIO-025 探活显隐**:启动时探测 /api/bioflow/health;不可达则隐藏生信 TAB 与块分支(只部署 rag 的前端自动退化为无生信界面)。(验收:停 8098 刷新 → 生信入口消失;恢复后出现)
**FR-BIO-026 流程知识库问答**:生信对话内流程咨询(如「identify 需要什么输入」),答案含引用原文,UI 可跳转原文。(验收:咨询回复含引用块且可跳转;评估集通过率 ≥90%)
**FR-BIO-027 三形态部署**:只部署 rag(BIOFLOW_ENABLED 默认关 → 反代不注册、前端无生信痕迹);只部署生信(8098 直连不依赖 BFF,报告降级为下载 HTML);同部署(反代启用+前端显形)。(验收:三形态各自冒烟通过)
**FR-BIO-028 独立服务与数据**:生信服务独立进程 8098 + 独立 SQLite bioflow.db;BFF 仅流式反代 /api/bioflow/*(BIOFLOW_ENABLED 开关)+ 报告登记端点。(验收:停 BFF 生信服务仍可直连工作;BFF 重启不影响生信任务)
**FR-BIO-029 Docker 执行**:每阶段以 docker run --rm 拉起对应镜像执行,--cpus/--memory 资源限制,--user 宿主机 uid,BIOFLOW_DATA 只读挂载。(验收:执行日志含资源参数;容器退出即清理)
**FR-BIO-030 内核等价性**:金标 SPL identify 输出 24 成员 ID 与 dev 预期输出集合全等。(验收:diff 命令输出为空)
**FR-BIO-031 并发与恢复**:全局并发信号量上限 3(可配);生信服务重启后从 DB 恢复 in-flight 任务并续跑。(验收:第 4 个并发任务排队;重启服务后任务续跑不重跑已完成阶段)
**FR-BIO-032 发布与编译**:生信服务 .so 编译流水线(compile_bioflow.sh)+ 内核 4 镜像构建(build_all.sh)+ rag 侧 BIOFLOW_ENABLED 装配开关。(验收:两条流水线各自产出;SHA256SUMS 与泄露门禁通过)
BIO: 001–032(32)|上述 81 + BIO 32 = **合计 113 个功能点**。生信用例追溯必须覆盖全部 32 项 FR-BIO(规模与门禁见 TEST_PLAN §10)。
## 17. FR-PRIV 私有资料问答(客户上传资料 → 向量化 → 追加向量库 → 问答可检索)
模块范围:客户通过对话输入框上传 PDF/DOCX/TXT 资料,资料向量化后追加进向量库(复用既有 docgen/vstore 上传链路);**任何问答提问同时并行检索私有资料**(全局融合),命中时在原 QA 答案后追加独立「私有资料参考」块(片段+相关度+带引用短答,两路并行不混排);未命中或服务故障用户无感;提供「我的资料」管理界面(列表+删除)。QA 8091 金标树零改动;私有资料回答由 BFF 新端点直连 DeepSeek 生成。
**FR-PRIV-001 上传入库(回归确认)**:客户拖拽 PDF/DOCX/TXT 到输入框 → `POST /api/docgen/library` → vstore `/api/vstore/ingest` 向量化加密入库 → BFF 落 doc_library。(验收:上传成功返回条目;「我的资料」列表可见;原有限制已解除——无大小/数量限制、有进度条)
**FR-PRIV-002 chunk 级索引**:入库时正文按段落聚合切块(目标 600 字/单块上限 8 段/相邻重叠 1 段/每文档上限 200 块,表格矩阵化参与),经 input 数组批量嵌入(batch 16 × 并发 4)追加进独立 chunk 索引(与原 doc 级 title 索引并存、互不影响)。(验收:ingest 响应含 chunk_count>0 且 chunk_ok=true;正文片段可被 search-chunks 命中;doc 级 /search 响应与改动前逐字段一致)
**FR-PRIV-003 全局并行检索**:ask/search 两模式提问同时触发私有资料检索(fire-and-forget,失败静默)。(验收:命中出块;未命中/服务故障零痕迹——不出现任何块与提示,原 QA 答案不受影响)
**FR-PRIV-004 私有资料回答**:BFF 新端点 `POST /api/qa/private` 检索 chunk 索引,命中则直连 DeepSeek 生成 300 字内带 `[资料n]` 引用的 markdown 短答(system 约束:只能依据资料回答、不得编造、不足则答「资料中未找到相关信息」)。(验收:answer 引用编号与 items 一一对应;未命中返回 hit:false;vstore/LLM 故障返回错误信封且前端静默)
**FR-PRIV-005 私有资料参考块**:命中时助手消息追加 `private_doc` 块(标题「私有资料参考」+ 短答 + 条目列表[编号、资料名、相关度、片段]);与 QA 答案两路并行不混排;终态持久化、刷新后完整恢复;流未收口时私有结果不被 complete 覆盖(pending+flush)。(验收:ask/search 两模式卡片并行展示;刷新恢复;竞态用例不丢块)
**FR-PRIV-006 我的资料管理**:文档 TAB 空态区展示「我的资料」列表(名称/大小/时间)与删除(二次确认;BFF doc_library 行删除 + vstore 密文与 chunk 联动删除)。(验收:列表加载正常;删除后列表刷新,且该资料不再被检索命中)
**FR-PRIV-007 存量重建与健康**:存量资料(现 210 条)经后台 CLI 重建 chunk 索引(不进 HTTP,避免超时);`/health` 暴露 chunk_count。(验收:重建完成后 chunk_count>0;重建期间 /health /search 保持可服务)
**FR-PRIV-008 降级与隔离**:私有检索/LLM 故障不影响原 QA 问答;docgen 模板检索(doc 级索引)行为零变化;QA 金标树(8091)与红线文件零改动。(验收:停 vstore 后 QA 问答照常;docgen 全链路回归通过;红线 diff 为空)
索引行追加:`PRIV: 001–008(8)|上述 113 + PRIV 8 = 合计 121 个功能点。私有资料用例追溯必须覆盖全部 8 项 FR-PRIV(用例表与门禁见 docs/PRIVATE_DOCS_TEST_PLAN.md)。`