From dea46402e8f17f14d968c7e44e8fa6fb03ef5593 Mon Sep 17 00:00:00 2001 From: admin Date: Mon, 7 Sep 2026 21:20:35 +0800 Subject: [PATCH] =?UTF-8?q?docs(priv):=20=E9=98=B6=E6=AE=B50=20=E7=A7=81?= =?UTF-8?q?=E6=9C=89=E8=B5=84=E6=96=99=E9=97=AE=E7=AD=94=E6=96=87=E6=A1=A3?= =?UTF-8?q?=E5=85=88=E8=A1=8C=E2=80=94=E2=80=94PRD=20=C2=A717=20FR-PRIV-00?= =?UTF-8?q?1~008=20+=20UI=5FSPEC=20=C2=A79=20+=20=E5=8E=9F=E5=A7=8B?= =?UTF-8?q?=E6=B5=8B=E8=AF=95=E8=AE=A1=E5=88=92=20+=20=E5=9B=A2=E9=98=9F?= =?UTF-8?q?=E4=B8=8A=E4=B8=8B=E6=96=87=20+=20A1/T1=20=E4=BB=BB=E5=8A=A1?= =?UTF-8?q?=E4=B9=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 用户需求:客户上传 PDF 等资料进行向量化并追加进向量库,且问答可检索。 决策:全局检索融合 / BFF 直连 DeepSeek 生成私有回答 / 两路并行展示(命中才显示) / 「我的资料」管理界面挂文档 TAB 空态。改动仅限本分支,QA 金标树与红线零改动。 Co-Authored-By: Claude Fable 5 --- docs/PRD.md | 22 ++++++++++++ docs/PRIVATE_DOCS_TEST_PLAN.md | 57 +++++++++++++++++++++++++++++++ docs/UI_SPEC.md | 28 +++++++++++++++ docs/private_docs_team_context.md | 43 +++++++++++++++++++++++ docs/team/brief_priv_a1.md | 53 ++++++++++++++++++++++++++++ docs/team/brief_priv_t1.md | 24 +++++++++++++ 6 files changed, 227 insertions(+) create mode 100644 docs/PRIVATE_DOCS_TEST_PLAN.md create mode 100644 docs/private_docs_team_context.md create mode 100644 docs/team/brief_priv_a1.md create mode 100644 docs/team/brief_priv_t1.md diff --git a/docs/PRD.md b/docs/PRD.md index ca9f6ec..03aa22f 100644 --- a/docs/PRD.md +++ b/docs/PRD.md @@ -396,3 +396,25 @@ SHELL: 001–007(7)|THEME: 001–006(6)|SESSION: 001–009(9)| **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)。` + diff --git a/docs/PRIVATE_DOCS_TEST_PLAN.md b/docs/PRIVATE_DOCS_TEST_PLAN.md new file mode 100644 index 0000000..5cea16a --- /dev/null +++ b/docs/PRIVATE_DOCS_TEST_PLAN.md @@ -0,0 +1,57 @@ +# 私有资料问答(FR-PRIV)· 原始测试计划 + +> 本文件为本次调整的**原始测试计划**,工程内留存(docs/PRIVATE_DOCS_TEST_PLAN.md)。 +> 对应功能:客户上传 PDF 等资料 → 向量化 → 追加向量库 → 问答可检索(PRD §17 FR-PRIV-001~008)。 +> 用户纪律(2026-09-07):测试用例 100% 覆盖所调整内容;每轮测试均有过程记录(TEST_REPORT_R*.md); +> 每轮完毕后统一修改再开新一轮,直至全部问题解决;TL 终验后把手工验收方法写入「PlantAI-new测试用例一览.xlsx」的知识库 sheet。 + +## 1. 调整内容范围(用例必须覆盖的全部对象) + +| 层 | 内容 | +|---|---| +| vstore(P1) | chunks.py(build_chunks 切块参数/ChunkIndex upsert/remove/search/rebuild_all)、embed.py embed_batch_chunked(批量 16/并发 4/失败降级)、vault.py add/delete 钩子(chunk 失败不阻断 doc 级 + chunk_count/chunk_ok/chunk_error 返回)、api.py search-chunks/rebuild-chunks/health chunk_count、settings.py VSTORE_CHUNK_PATH、scripts/rebuild_chunks.py CLI、存量 210 条重建 | +| BFF(P2) | services/privateqa.py(ask_private:检索→去重→拼上下文→DeepSeek 短答)、routers/private_docs.py(POST /api/qa/private 三态)、schemas.py(PrivateQARequest/PrivateDocItem/PrivateDocBlock 入 Block union)、main.py 路由注册、client.py search_chunks、llm.py complete_text timeout 参数 | +| 前端(P3) | api/private.js askPrivate、chat.js _firePrivate/_appendPrivate/_flushPrivate(ask/search 两模式、retry、竞态 pending/flush、防重入)、AssistantMessage.vue private_doc 块分支、i18n/private.js、PrivateLibraryPane.vue、DocPane 空态挂载、api/docgen.js removeLibrary(204 处理) | +| 回归红线 | docgen 模板检索(doc 级 /search 逐字段一致、上传/generate/导出全链路);QA 金标树 8091 零 diff;红线文件零 diff;BFF 编译白名单未扩大;断流崩溃场景不存在(新模块无 yield) | + +## 2. 测试分层(沿用 TEST_PLAN §1) + +- **L1 单元**:vstore(chunks/embed/vault 钩子)、BFF(privateqa 纯逻辑、schemas union 校验)、前端(chat store 竞态逻辑、http 封装)。 +- **L2 接口**:vstore 端点(search-chunks 三态/rebuild-chunks/health)、BFF /api/qa/private 三态 + docgen library CRUD 回归。 +- **L2s 部署冒烟**:vstore 明文部署重启、chunk 存量重建 CLI、BFF 编译部署重启、前端构建。 +- **L3 前端**:组件测试(private_doc 块渲染、PrivateLibraryPane、竞态场景)。 +- **L4 端到端**:浏览器实测 ask/search 两模式、上传→检索闭环、刷新恢复、删除联动。 + +## 3. 覆盖率判定(100% 的可验证判定) + +1. **功能点追溯**:每个 FR-PRIV-001~008 在用例表至少 1 条用例,双向追溯完整(无孤儿用例)。 +2. **调整对象追溯**:§1 表格中每个调整文件/端点/函数至少 1 条用例(chunk 索引的切块参数边界、批量嵌入失败降级、to_thread 阻塞、pending/flush 竞态、防重入、204 空体、三态信封等均为显式用例)。 +3. **边界与异常**:空库检索、低分阈值(min_score=0.30 两侧)、超 top_k clamp、每文档 200 块上限、同 doc 去重(每 doc≤3 块)、12k 上下文截断、LLM 40s 超时、vstore 宕机、LLM 宕机、上传超长 PDF。 +4. **回归**:docgen doc 级检索响应逐字段、QA 8091 服务零改动(git diff 空)、红线文件 diff 空。 + +## 4. 环境与命令约定 + +- 服务器:ssh -p 26518 xzq@192.168.0.175(内网)或 8.163.92.8;仓库 /home/xzq/5090/teamWorkspace/zh/PlantAI-rag-new(分支 feature/bioflow-conversation-orchestration);部署树 /home/xzq/5090/teamWorkspace/zh/plantai-rag-new-deploy-unified。 +- vstore 测试:venv = /home/xzq/5090/teamWorkspace/zh/plantai-rag-new-venv/bin/python;pytest 跑 vstore/tests/(新增 test_chunks.py);端点 curl http://127.0.0.1:8094。 +- BFF 测试:sdk 测试沿用既有 pytest 布局(services/test_*.py 同目录);端点 curl http://127.0.0.1:8090。 +- 前端测试:ui 下 vitest(npm test;新增 tests/private_docs/)。 +- 部署仅 TL 执行;agent 不 commit 不部署。 + +## 5. 轮次纪律(用户要求,铁律) + +1. T1 按用例表逐轮执行 → 每轮产出 `docs/team/TEST_REPORT_PRIV_R{n}.md`(缺陷编号 BUG-PRIV-R{n}-{序号}:复现步骤/预期/实际/对应用例号)。 +2. 每轮全部问题**统一汇总**交 A1 修复 + 自测 → 交回 T1 复测(新轮次)。 +3. 缺陷分级:阻断(修复前不得进下一轮)/严重/一般/轻微(沿用 TEST_PLAN §4)。 +4. 循环直至某轮缺陷数为 0 → TL 终验。 + +## 6. 验收方式(TL 终验清单) + +1. L1/L2/L3 全绿(pytest/vitest)。 +2. curl 端到端:upload→ingest(chunk_count>0)→search-chunks 命中(snippet/score)→/api/qa/private 三态→delete 联动消失→docgen /search 逐字段回归。 +3. 浏览器:ask+search 两模式私有卡片并行、未命中无感、刷新恢复、管理界面删除闭环。 +4. 回归:docgen 上传/generate/导出全链路;QA 金标树 git diff 空;红线文件 diff 空;白名单未扩大。 +5. 手工验收测试方法写入「PlantAI-new测试用例一览.xlsx」知识库 sheet(本地 docs/ 下)。 + +## 7. 用例规模预估 + +L1 单元 ~25(vstore 12 / BFF 8 / 前端 5);L2 接口 ~18(vstore 9 / BFF 9);L2s 部署冒烟 ~5;L3 前端 ~8;L4 端到端 ~6。合计约 **60 条**(TL 复查以双向追溯完整为准核定精确数)。 diff --git a/docs/UI_SPEC.md b/docs/UI_SPEC.md index 448ae84..8537ea6 100644 --- a/docs/UI_SPEC.md +++ b/docs/UI_SPEC.md @@ -316,3 +316,31 @@ B2/B3 组件必须复用 B1 在 `styles/base.css` 与原型中定义的类名, ### 8.7 生信类名复用约定(在 §7 清单上追加) `flow-path-card`、`flow-form-card`、`flow-file-card`、`bioflow-progress-card`、`flow-archive-card`、`biojobs-pane`、`bioarchive-pane`。其余(按钮/输入/进度条/徽章/面板容器)一律复用 §7 既有类名,不得另起炉灶。 +## 9. 私有资料问答组件规格(FR-PRIV) + +### 9.1 私有资料参考块(`private-doc-block`,对话内) + +| 元素 | 规格 | +|---|---| +| 卡体 | surface-1 + hairline,r-lg,padding 14px 16px,margin 14px 0(同 flow-path-card 卡体) | +| 标题 | 「📚 私有资料参考」12.5px/600/ls .3px | +| 短答区 | markdown 渲染(markdown-it + DOMPurify,同 text 块纪律),12.5px/1.65,最大宽 100% | +| 条目行 | 上 1px hairline 分隔;编号 `[资料n]` 11px mono 相位色 `--pa-t-done` 底白字;资料名 12px ink 超长省略;相关度 11px mono ink-muted | +| 片段区 | snippet 12px ink-muted,max-height 64px 溢出隐藏 + hover 展开 | +| 空答态 | answer 为空时仅显示条目列表(不显示空白答区) | + +### 9.2 我的资料面板(`private-library-pane`,文档 TAB 空态区) + +| 元素 | 规格 | +|---|---| +| 挂载 | DocPane 空态区(`v-if="!docgen.activeArtifact"`)顶部;有活动文档时隐藏 | +| 标题 | 「📁 我的资料」12.5px/600/ls .3px + 右侧条目计数 11px ink-subtle | +| 列表项 | 复用 session-item 形态:名称 13px(超长省略)+ 大小/时间 11px ink-muted;hover 显示删除按钮 | +| 删除按钮 | pane-btn 同款 12px;点击 window.confirm 二次确认后调 DELETE | +| 空态 | 「尚无上传资料。在对话框拖拽 PDF / DOCX / TXT 上传。」12px ink-subtle | +| 加载/错误态 | 加载中 11px ink-subtle;加载失败显示重试按钮(pane-btn) | + +### 9.3 类名复用约定(在 §7 清单上追加) + +`private-doc-block`、`private-library-pane`。其余(按钮/列表项/分隔线/徽章)一律复用 §7 既有类名。 + diff --git a/docs/private_docs_team_context.md b/docs/private_docs_team_context.md new file mode 100644 index 0000000..906c235 --- /dev/null +++ b/docs/private_docs_team_context.md @@ -0,0 +1,43 @@ +# 私有资料问答(FR-PRIV)· 团队共享上下文 + +> docs/private_docs_team_context.md —— A1(编码)与 T1(测试)开工前必读。仿 docgen_team_context.md 纪律。 + +## 1. 目标与范围(只做这些) + +客户上传 PDF/DOCX/TXT → 向量化 → 追加向量库 → **问答可检索**(PRD §17 FR-PRIV-001~008)。四决策已拍板:全局检索融合;私有答案 BFF 直连 DeepSeek;两路并行展示(命中才显示);「我的资料」管理界面挂文档 TAB 空态。 + +## 2. 服务器事实(不可想当然) + +- 仓库 `/home/xzq/5090/teamWorkspace/zh/PlantAI-rag-new`(分支 feature/bioflow-conversation-orchestration,当前 HEAD 759427b);部署树 `/home/xzq/5090/teamWorkspace/zh/plantai-rag-new-deploy-unified`。 +- 端口:BFF 8090(0.0.0.0)、vstore 8094(127.0.0.1 单 worker 明文 Python)、QA 8091(金标树红线)。venv = /home/xzq/5090/teamWorkspace/zh/plantai-rag-new-venv/bin/python(py3.12)。 +- vstore 数据根:deploy-unified/docgen/(vault 210 个 .enc + index/index.json);embed 走 Ollama(127.0.0.1:11434,bge-m3:567m 1024 维,实测单条 3.44s、input 数组 4 条 0.32s)。 +- 上传链路已存在(勿重造):BFF POST /api/docgen/library → vstore /api/vstore/ingest → doc_library 表;前端 uploadLibrary/listLibrary 已有。 +- 生产 .env 已配:VSTORE_BASE_URL、DOCGEN_LLM_*(deepseek-chat)。 + +## 3. 改动文件清单(只动这些;多动一个即违规) + +vstore 侧(明文,不编译):`vstore/chunks.py`(新)、`vstore/embed.py`、`vstore/vault.py`、`vstore/api.py`、`vstore/settings.py`、`vstore/scripts/rebuild_chunks.py`(新)、`vstore/tests/test_chunks.py`(新,T1)。 +BFF 侧:`sdk/app/services/privateqa.py`(新)、`sdk/app/routers/private_docs.py`(新)、`sdk/app/schemas.py`、`sdk/app/main.py`、`sdk/app/services/docgen/client.py`、`sdk/app/services/docgen/llm.py`。 +前端:`ui/src/api/private.js`(新)、`ui/src/stores/chat.js`、`ui/src/components/chat/AssistantMessage.vue`、`ui/src/i18n/private.js`(新)、`ui/src/components/bench/PrivateLibraryPane.vue`(新)、`ui/src/components/bench/DocPane.vue`、`ui/src/api/docgen.js`、`ui/tests/private_docs/*`(新,T1)。 +文档(TL 独占):PRD.md、UI_SPEC.md、PRIVATE_DOCS_TEST_PLAN.md、team/ 下任务书与轮次报告。 + +## 4. 红线(零改动,diff 必须为空) + +- QA 金标树 8091(agent/qa 与部署树 qa 全部文件)。 +- 前端:stores/modes.js、labels.js、bench.js、api/qa.js、sse/parser.js、PipelineCard.vue。 +- vstore:retrieval.py、parsing.py、backend.py、crypto.py(chunk 能力全部新写,不改造既有模块)。 +- sdk:routers/qa.py、services/sse_relay.py、tooling/compile/compile_sdk.sh 白名单(不动)。 +- docgen 既有行为:doc 级 title 检索、模板匹配、生成链路——行为零变化。 + +## 5. 技术纪律 + +- vstore 侧纯明文部署,禁止擅自引入新依赖(只用 faiss/numpy/pymupdf/python-docx/Ollama HTTP 既有件)。 +- BFF 新模块**禁止 yield/SSE**(Cython 协程取消断言会崩 BFF);新模块可编译 .so,不进白名单。 +- 服务层只用 dataclass/dict/stdlib;pydantic 只进 schemas.py。 +- 零硬编码:路径/URL 全部环境变量或设置推导(VSTORE_CHUNK_PATH 等)。 +- 嵌入纪律:批量数组(batch 16 × workers 4);失败重试一次仍失败降级零向量;chunk 失败不阻断 doc 级入库。 +- 同步阻塞:api.py 端点内同步段一律 `asyncio.to_thread`。 + +## 6. 纪律(同 docgen_team_context 十条) + +1. 只动 §3 清单文件;2. 不 commit、不 push、不部署(TL 统一执行);3. 测试 agent 不修改任何产品代码;4. 不自连生产服务做破坏性操作(vstore 重启/重建由 TL 执行);5. 凭据不落文件不打印;6. 改动必须能说明对应哪个 FR-PRIV;7. 每轮测试产出轮次报告;8. 自测通过才交付;9. 有疑问先问 TL 不要自行裁决;10. 完成任务后回报 TL。 diff --git a/docs/team/brief_priv_a1.md b/docs/team/brief_priv_a1.md new file mode 100644 index 0000000..e8da6c9 --- /dev/null +++ b/docs/team/brief_priv_a1.md @@ -0,0 +1,53 @@ +# 任务书 · A1(编码 agent)—— 私有资料问答 P1-P3 实现 + +> docs/team/brief_priv_a1.md | 开工前必读 docs/private_docs_team_context.md(改动清单/红线/纪律是硬约束)。 +> 权威规格:docs/PRD.md §17(FR-PRIV-001~008)、docs/UI_SPEC.md §9、实施方案(TL 已批准)。 + +## 任务 + +按三块实现「客户上传资料 → 问答可检索」闭环。全部代码写在服务器仓库 `/home/xzq/5090/teamWorkspace/zh/PlantAI-rag-new`(feature/bioflow-conversation-orchestration 分支)。**不 commit、不部署、不碰清单外文件。** + +### P1 vstore chunk 级索引(最大块) + +1. 新 `vstore/chunks.py`: + - 切块参数常量:`CHUNK_TARGET_CHARS=600`、`CHUNK_MIN_CHARS=120`、`CHUNK_MAX_PARAS=8`、`CHUNK_OVERLAP_PARAS=1`、`CHUNK_MAX_PER_DOC=200`、`CHUNK_MIN_SCORE=0.30`、`CHUNK_TOP_K_MAX=20`。 + - `build_chunks(title, outline, paragraphs, tables) -> list[Chunk]`:段落顺序聚合(≥600 字或满 8 段封块);相邻块重叠 = 前块最后 1 段作下块前缀;表格矩阵化为 `|a|b|` 行式文本段参与;首块前缀 `# {title}`;seq 从 0 起;截断 200 块。Chunk = dataclass(seq, text),chunk_id = `{doc_id}:{seq}`。 + - `class ChunkIndex`:三文件(`index.json` meta 行 / `index.faiss` faiss 二进制 / `texts.jsonl` 文本);`load()`;`upsert_document()`(替换语义:先移旧行过滤重建 faiss);`remove_document()`;`search(query_vector, top_k, min_score, doc_ids=None, exclude_template=False)` → `{"mode":"vector|empty","items":[{chunk_id,doc_id,seq,snippet,score,doc_name,doc_category,doc_tags,as_template,created_at}]}`(分数低于 min_score 全部丢弃→empty,**无降级档**);`rebuild_all()`;`count()`。单写锁;落盘 tmp + os.replace 原子切换(仿 retrieval.py 纪律)。 +2. `vstore/embed.py` 新增 `embed_batch_chunked(texts, batch_size=16, workers=4, url=None, model=None)`:Ollama /api/embed 的 **input 数组**批量(与现有 embed_batch 的单条循环区分),ThreadPoolExecutor workers 并发;单批失败重试一次仍失败该批降级零向量。保留 embed_batch 不动。测试注入钩子仿 set_embed_fn 模式。 +3. `vstore/vault.py`:`__init__` 加载 ChunkIndex(路径来自 settings);`add()` 在 doc 索引成功后追加:build_chunks → embed_batch_chunked → chunks.upsert_document;**chunk 任何失败只告警不阻断**,返回体追加 `chunk_count/chunk_ok/chunk_error`;`delete()` 连删 chunk 行;新增 `rebuild_chunks()`(遍历密文解密 → build_chunks → 批量嵌入 → rebuild_all,返回 {docs, chunks, duration_ms})。 +4. `vstore/api.py`:`GET /api/vstore/search-chunks?q&top_k&min_score&doc_ids&exclude_template`(top_k clamp 1..20;内部 `asyncio.to_thread` 包同步段;响应 `{ok, mode, query, count, items}`);`POST /api/vstore/rebuild-chunks`;`/health` 加 `chunk_count`。ingest 端点不变(扩展走 vault.add 返回体)。 +5. `vstore/settings.py`:新增 `VSTORE_CHUNK_PATH` env(默认 `{root}/chunks`)+ get_paths 暴露。 +6. 新 `vstore/scripts/rebuild_chunks.py`:CLI 重建(cwd=部署根加载 vstore/.env,用 CLI 参数或环境变量取路径),进度打印 + 汇总;供 TL 后台执行。 +7. 新 `vstore/tests/test_chunks.py` 由 T1 编写——你负责按 T1 用例自测通过。 + +### P2 BFF 私有问答端点 + +1. `sdk/app/schemas.py`(白名单明文):`PrivateQARequest(question, session_id=None, top_k=8, min_score=0.30)`;`PrivateDocItem(doc_id/name/seq/chunk_id/snippet/score/category/tags/created_at/ref,全默认值 extra="allow")`;`PrivateDocBlock(type: Literal["private_doc"], answer="", items=[])`;`PrivateDocBlock` 追加进 `Block = Union[...]` 末尾。 +2. `sdk/app/services/docgen/client.py` 新增 `async def search_chunks(query, *, top_k=8, min_score=0.30, timeout=12.0) -> dict`(GET /api/vstore/search-chunks;失败抛 DocgenUnavailable,同文件既有语义)。 +3. `sdk/app/services/docgen/llm.py`:`complete_text` 增可选 `timeout: float | None = None`(None 走 env 现行为;显式值覆盖),透传 _chat_once;docgen 既有调用零影响。 +4. 新 `sdk/app/services/privateqa.py`(dataclass/dict,无 pydantic,无 yield): + - 常量:`PRIVATE_QA_LLM_TIMEOUT=40.0`、`PRIVATE_QA_MAX_CTX_CHARS=12000`、`PRIVATE_QA_PER_DOC_MAX=3`。 + - `async def ask_private(question, *, top_k=8, min_score=0.30) -> dict`:search_chunks → 空 `{hit:false, mode:"empty", answer:"", items:[]}` → 同 doc 按 score 序去重(每 doc≤3 块)再取 top_k → 上下文拼 `[资料n]《name》(相关度 x.xx)\nsnippet`(12k 预算截断)→ complete_text(system=「只能依据资料回答,不得编造;每个关键论断标注 [资料n];资料不足则答『资料中未找到相关信息』;300 字内简体中文 markdown」, temperature=0.2, max_tokens=800, timeout=40) → `{hit:true, mode:"vector", answer, items:[...带 ref], elapsed_ms}`。异常抛 DocgenUnavailable/Exception 由路由转信封。 +5. 新 `sdk/app/routers/private_docs.py`:`POST /api/qa/private`(body=PrivateQARequest);命中 `{ok:true,hit:true,mode,answer,items,elapsed_ms}`;未命中 `{ok:true,hit:false,mode:"empty",answer:"",items:[]}`;vstore 不可达 503 `UPSTREAM_DOWN`;LLM 失败 502 `UPSTREAM_ERROR`(信封复用 `{ok:false,error:{code,message,degraded}}`)。 +6. `sdk/app/main.py`:+1 行 include_router(放 docgen 之后)。 + +### P3 前端接线 + 管理界面 + +1. 新 `ui/src/api/private.js`:`askPrivate({question, session_id, top_k=8, min_score=0.30})`(POST /api/qa/private,timeout 60s)。 +2. `ui/src/stores/chat.js`:`send()` 内、_runStream/_runSearch 分发前插 `this._firePrivate(q, sessionId, aMsg)`(不 await)。三个新方法按已批准设计:`_firePrivate`(fetch→未命中/异常静默 return;命中→本轮上下文校验→流中暂存 `aMsg._privatePending` / 终态直挂+补 persist)、`_appendPrivate`(防重入:已有 private_doc 块只挂一次)、`_flushPrivate`。flush 挂点:_runStream onComplete 整体替换 blocks 之后、_runSearch blocks 赋值之后、两条 error 路径。 +3. `ui/src/components/chat/AssistantMessage.vue`:新增 `private_doc` 块分支(渲染顺序放 sources 之后):卡体 `private-doc-block`;标题「📚 私有资料参考」(i18n);answer markdown 消毒渲染;items 逐条(编号 `[资料n]`、名称、相关度、片段)。 +4. 新 `ui/src/i18n/private.js`:标题/相关度/管理界面文案(按 UI_SPEC §9.2 文案清单)。 +5. 新 `ui/src/components/bench/PrivateLibraryPane.vue`:组件内局部 state(listLibrary({}) 加载/删除后刷新/错误重试);删除 window.confirm → removeLibrary(id) → 刷新。挂 DocPane 空态区顶部(DocPane.vue `v-if="!docgen.activeArtifact"` 块内第一个子元素,其余空态内容不动)。 +6. `ui/src/api/docgen.js` 新增 `removeLibrary(id)`(DELETE;注意 204 空体——确认 http.js 对 204 的处理,异常时包装返回)。 + +## 自测要求(交付门槛) + +- 本地跑通 vstore pytest(T1 用例全绿);python 语法/import 检查全部新文件。 +- 前端 `npm test` 相关用例全绿(T1 用例);`npm run build` 无错误。 +- BFF:schemas union 校验(用 TypeAdapter/MessageCreate 构造 private_doc 块通过);新模块无 yield(自查);python 语法检查。 +- 交付物:代码改动清单 + 自测结果(粘贴测试输出摘要)回报 TL。**不 commit、不部署。** + +## 参考(只读) + +- 已批准实施方案(TL 提供,含全部签名与 JSON 契约)。 +- 既有纪律与先例:sdk/app/services/sse_relay.py(原子写/帧格式)、vstore/retrieval.py(锁/rebuild 模式)、ui/src/stores/chat.js(sendDocgen 的 meta.library_ids 模式)、AssistantMessage.vue(sources 块分支写法)。 diff --git a/docs/team/brief_priv_t1.md b/docs/team/brief_priv_t1.md new file mode 100644 index 0000000..4be982f --- /dev/null +++ b/docs/team/brief_priv_t1.md @@ -0,0 +1,24 @@ +# 任务书 · T1(测试 agent)—— 私有资料问答测试用例编制与轮次执行 + +> docs/team/brief_priv_t1.md | 开工前必读 docs/private_docs_team_context.md。 +> 权威规格:docs/PRD.md §17(FR-PRIV-001~008)、docs/UI_SPEC.md §9、docs/PRIVATE_DOCS_TEST_PLAN.md(覆盖判据与轮次纪律)。 + +## 阶段 A:测试用例编制(先做,TL 复查达标前不得测试) + +1. 编写 `docs/team/TEST_CASES_PRIVATE.md`:每条含 编号(TC-PRIV-{L层}-{序号})、对应 FR-PRIV 编号、前置条件、步骤、输入、期望输出、判据。 +2. **100% 覆盖判定(硬门禁)**: + - 每个 FR-PRIV-001~008 至少 1 条且双向追溯(附追溯矩阵表); + - 覆盖 §1 调整清单的每个文件/端点/函数:chunks 切块参数边界(600 字/8 段/1 段重叠/200 块上限/表格矩阵化/首块标题前缀)、ChunkIndex 增删改查与原子写、embed_batch_chunked(批量数组/并发/单批重试/降级零向量)、vault 钩子(chunk 失败不阻断 doc 级+返回字段)、search-chunks(三态/低分阈值 0.30 两侧/top_k clamp/doc_ids 过滤/exclude_template)、rebuild-chunks/health chunk_count、存量重建 CLI;BFF ask_private(去重每 doc≤3、12k 截断、LLM 超时 40s、异常抛类型)、/api/qa/private 三态信封(命中/未命中/503/502)、schemas union(private_doc 块校验+extra 未知字段)、llm timeout 参数兼容(None 走 env);前端 _firePrivate(命中/未命中/异常静默)、pending/flush 竞态(流未收口不丢块、onComplete 替换后仍出现)、_appendPrivate 防重入、ask/search 两模式与 retry 覆盖、AssistantMessage 块渲染(answer 空态、markdown 消毒)、PrivateLibraryPane(列表/删除/204/错误重试)。 + - 回归用例:docgen doc 级 /search 响应逐字段、上传/generate/导出;红线 diff 空(QA 8091、modes/labels/bench/api/qa/sse/parser/PipelineCard、retrieval/parsing/backend/crypto、qa.py/sse_relay.py、compile_sdk.sh 白名单)。 +3. 规模目标 ~60 条(TEST_PLAN §7 预估);完成后交 TL 复查,**不达标不进测试**。 + +## 阶段 B:轮次执行(TL 部署后) + +1. 每轮按用例表全量执行(L1 pytest / L2 curl / L3 vitest / L4 浏览器由 TL 配合)。 +2. 每轮产出 `docs/team/TEST_REPORT_PRIV_R{n}.md`:执行摘要、通过/失败统计、缺陷清单(编号 BUG-PRIV-R{n}-{序号}:复现步骤/预期/实际/对应 TC 号)、阻断项。 +3. 每轮缺陷统一汇总交 TL → A1 修复+自测 → 下一轮复测。循环至某轮缺陷为 0。 +4. 轮次报告必须保留过程记录(执行命令、关键输出摘录、统计数字),供 TL 终验引用。 + +## 纪律 + +测试 agent 不修改任何产品代码(测试文件除外:vstore/tests/test_chunks.py、ui/tests/private_docs/ 由你编写);不 commit 不部署;不自连生产做破坏操作;有歧义先问 TL。