Files
RAG/docs/09-document-ingestion-indexing-runbook.md
YoVinchen ecdb10c37a
Some checks failed
verify / verify (push) Has been cancelled
Make the governed RAG evidence path executable end to end
Separate local parsing from model indexing, bind review decisions to immutable manifests, persist vectors behind active profiles, and expose retrieval, chat, evaluation, and document workflows through the React workbench.

Constraint: Live Bailian authentication currently fails for all three configured capabilities

Rejected: Direct upload-to-embedding flow | bypasses local review and manifest binding

Confidence: high

Scope-risk: broad

Directive: Keep private-data deployment blocked until authentication, RBAC, and separate database roles land

Tested: make verify; fresh and replay Docker document smoke; worker recovery smoke; frozen synthetic evaluation; migration 0003-0004 roundtrip

Not-tested: Successful live Bailian calls, OCR, real multi-user authorization
2026-07-13 05:58:11 +08:00

199 lines
11 KiB
Markdown
Raw 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.
# 文档上传、审核、向量化与检索运行手册
本手册用于验证当前已经落地的 synthetic 产品主链:浏览器上传公开虚构文档,经本地安全解析、
人工绑定 outbound manifest 审批、异步向量化和原子激活后,能够被正式 Retrieval API 检索。
这条链路已经在 Docker 中连续两次端到端通过;它证明工程状态机、幂等性和向量写库契约可运行,
但不代替真实百炼授权、真实地质语料质量、PDF/OCR 或最终论文盲测验收。
## 1. 最短启动与验证
首次克隆后初始化仅存在于本机、已被 Git 忽略的 Secret 文件:
```bash
make setup-hooks
bash scripts/init-local-secrets.sh
make up
make status
make seed-offline
make smoke-document
```
`make up` 等价于 `docker compose up -d --build`,会启动数据库、两个 Worker、模型隔离网关、
API、入口 Gateway 和 Web并等待迁移与上传卷初始化成功。`make seed-offline` 创建 synthetic
profile 和公开虚构检索基线;`make smoke-document` 使用
`data/samples/public/upload_demo.md` 加入本次运行 nonce 后,自动完成一次全新的上传、解析、审批、
向量化、激活和检索;随后在同一次运行中重放相同声明与内容,验证 ID 不变且不会重复建任务。
smoke 中的 `SYNTHETIC_REVIEW_APPROVED` 是只针对公开 fixture 的自动契约检查;真实资料必须由
有权限的审核人通过 `/documents` 检查 exact cloud text 与 manifest不能自动批准。
成功的 smoke 输出只包含不透明 ID、状态、rank、citation、模型名和重排状态不输出正文或
Secret。历史上曾用固定 fixture 连续两次验证相同 document/document-version ID数据库计数均保持
| 对象 | 幂等计数 |
|---|---:|
| document version | 1 |
| chunk | 1 |
| vector assignment | 1 |
| background job | 2解析 1 + 向量化 1 |
| model invocation | 1 |
当前 smoke 每次命令都会创建带随机 nonce 的新文档,避免历史 READY 数据让当前故障误通过;
同一次命令末尾会重放同一幂等键与内容,并要求 upload、document、parse job、active version 身份
保持不变。输出中的 `replay_confirmed=true` 才表示该双重检查完成。该结果只适用于提交的
synthetic fixture 和当前冻结配置;换文件、切分 profile 或 embedding profile 会生成不同身份。
## 2. `Exited (0)` 不是服务暂停
`docker compose ps -a` 中以下两个容器应当成功运行一次后退出:
- `migrate`:执行 `alembic upgrade head``Exited (0)` 表示迁移已经应用成功。
- `upload-init`:以最小临时权限初始化上传卷属主和目录;`Exited (0)` 表示初始化成功。
它们不是长期服务,不应保持 `Up`,也不应配置 `restart: always`。只有非零退出码才代表启动
失败。长期容器的期望状态如下:
| 服务 | 期望状态 | 说明 |
|---|---|---|
| `db` | `Up (healthy)` | PostgreSQL + pgvector |
| `model-gateway` | `Up (healthy)` | 唯一持百炼 Key 与公网出口 |
| `api` / `gateway` / `web` | `Up (healthy)` | API、固定上游入口、浏览器页面 |
| `worker-local` | `Up` | 只处理 `document_parse`,挂载上传卷,无模型网络/Token |
| `worker-model` | `Up` | 只处理 `embedding`,可访问模型网,不挂载上传卷 |
两个 Worker 的信任边界、Secret、网络和卷约束见
[ADR-0007](adr/0007-split-local-and-model-workers.md)。
排查时使用:
```bash
make status
docker compose logs --tail=200 migrate upload-init api worker-local worker-model
curl -sS http://127.0.0.1:8000/health/ready
```
不要因为 Alembic 日志最后停在 `Will assume transactional DDL` 就判断服务挂起;先查看容器
退出码、API readiness 和依赖服务状态。
## 3. 浏览器操作
访问 <http://127.0.0.1:8000/documents>。页面按以下顺序工作:
1. 在浏览器本地校验扩展名、MIME 和 100 MiB 上限,并计算 SHA-256。
2. 创建带随机 `Idempotency-Key` 的上传声明,随后流式写入隔离上传卷。
3. 完成上传并轮询 `PARSE_DOCUMENT` 任务;本地 Worker 不调用任何云模型。
4. 展示 `display_text`、拟出域的 `cloud_text`、来源锚点和 outbound manifest。
5. 审核人确认 exact manifest 后批准,或选择稳定原因码拒绝。
6. 批准后轮询 `EMBED_DOCUMENT`;向量完整性通过后文档才显示 `READY`
7. 转到 <http://127.0.0.1:8000/retrieval> 检索刚刚激活的内容,或到
<http://127.0.0.1:8000/chat> 验证带引用的单轮问答。
当前页面的“停止”只会停止浏览器上传请求或任务轮询。后台任务一旦提交,不会因关闭页面而
被强制取消Worker 依靠租约、心跳、fencing token 和 reaper 恢复。
## 4. 状态机与不可绕过的门禁
```text
upload CREATED
-> STORED
-> COMPLETED
-> document QUARANTINED_LOCAL_REVIEW
-> PARSE_DOCUMENT / worker-local
-> LOCAL_PARSED_PENDING_CLOUD_REVIEW
-> OCR_REQUIRED 或 FAILED禁止继续
-> 人工审核 exact outbound manifest
-> REJECTED终止
-> CLOUD_APPROVED
-> EMBED_DOCUMENT / worker-model
-> PENDING -> EMBEDDING -> READY
-> version READY + document.active_version_id 原子切换
-> document READY + chunks searchable
-> 正式 Retrieval 候选
```
上传成功、解析成功和审批通过都不等于“已可检索”。正式 Retrieval 的 SQL 在 `LIMIT` 前要求
知识库、服务端授权 scope、active version、`CLOUD_APPROVED`、正确 profile、READY assignment
`searchable=true` 同时成立。审批使用 `review_revision` 乐观并发控制,并绑定 exact outbound
manifest hash旧页面、变化后的正文或变化后的 profile 不能复用审批。
当前 TXT、Markdown、DOCX 走确定性本地解析PDF 会 fail closed 为 `OCR_REQUIRED`。这表示系统
没有把 PDF/OCR 或地质图空间理解伪装成已完成能力。
## 5. API 级手工检查
Swagger 位于 <http://127.0.0.1:8000/docs>。入库主链使用以下接口:
| 方法与路径 | 用途 |
|---|---|
| `POST /api/v1/document-uploads` | 声明文件名、MIME、大小和 SHA-256要求 `Idempotency-Key` |
| `PUT /api/v1/document-uploads/{id}/content` | 上传原始字节,入口上限 100 MiB |
| `POST /api/v1/document-uploads/{id}/complete` | 校验摘要并创建文档/解析任务 |
| `GET /api/v1/document-jobs/{id}` | 轮询解析或向量化任务 |
| `GET /api/v1/documents` | 查看文档状态 |
| `GET /api/v1/documents/{id}/review-bundle` | 分页查看解析结果与 manifest |
| `POST /api/v1/documents/{id}/review-decisions` | 使用 revision + manifest 批准或拒绝 |
| `POST /api/v1/retrieval/search` | 验证 READY 文档可检索 |
推荐用 `make smoke-document` 作为可重复验收;手工拼装请求时不要把文件正文、数据库 DSN、
内部 Token 或百炼 Key 写进 shell 历史、截图或工单。
## 6. 常见错误排查
| 现象/错误 | 含义 | 检查与处理 |
|---|---|---|
| `migrate``upload-init``Exited (0)` | 正常一次性任务终态 | 检查长期服务是否 Up/healthy不要反复重启一次性任务 |
| `UPLOAD_*`、摘要/大小不匹配 | 声明与实际字节不一致或上传状态冲突 | 重新选择原文件;不要修改声明后复用 upload ID |
| `OCR_REQUIRED` | PDF 当前不在可靠解析范围 | 换用 TXT/Markdown/DOCX 验证;等待 OCR/PDF adapter |
| `REVIEW_REVISION_CONFLICT` | revision 或 manifest 已变化 | 刷新 review bundle重新人工复核后提交 |
| 文档停在待审核 | 安全门禁正常工作 | 在 `/documents` 检查 cloud text 和 manifest再批准或拒绝 |
| embedding job `FAILED` | profile、维度、模型调用或完整性校验失败 | 查看脱敏 error code 和 `worker-model` 日志;不要直接改 `searchable` |
| 文档 READY 但检索不到 | scope/profile/active version 或 seed 基线不一致 | 先运行 `make seed-offline`,再检查 retrieval 的生效 profile 与 trace |
| 百炼三能力返回 401 | Key、北京工作空间、端点或模型权限不匹配 | 按第 7 节重新做 provider smoke401 不自动重试 |
日志和 API Problem 必须只包含稳定错误码、trace 和不透明 ID。若发现任何 Secret 或受限正文,
立即停止真实调用、轮换凭证并运行 `make check-secrets`
## 7. 切换到真实阿里云百炼
synthetic E2E 使用 fake embedding/rerank不需要百炼。切换真实模式前必须完成
1. 在百炼北京地域控制台撤销任何曾出现在聊天、日志或截图中的旧 Key。
2. 创建具备 `text-embedding-v4``qwen3-rerank``deepseek-v4-flash` 权限的新 Key。
3. 只把新 Key 写入已忽略的 `secrets/bailian_api_key`;真实工作空间 URL 只写本地部署配置。
4. 确认 Key、专属工作空间域名、北京地域和计费方案属于同一空间。
5. 重启 `model-gateway` 及调用方,运行:
```bash
docker compose restart model-gateway api worker-model
docker compose --profile tools run --rm provider-smoke
```
只有 Embedding、Rerank、Chat 三项最小实际调用全部成功,才能运行真实 `seed-demo` 和真实文档
向量化。当前已验证事实仍是三项请求到达供应商但均返回 401因此真实百炼未验收不能用
synthetic smoke 的成功替代这项外部门禁。
三项探测成功后,先运行 `docker compose --profile tools run --rm seed-demo` 创建独立的百炼
synthetic 知识库及 active profile再在未提交的 `.env` 中设置
`DOCUMENT_NAMESPACE_MODE=bailian`,重建 API 和模型 Worker最后运行 fresh + replay smoke
```bash
docker compose --profile tools run --rm seed-demo
docker compose up -d --force-recreate api worker-model
make smoke-document
```
此时上传声明的知识库/scope 仍由服务端固定选择,请求不能自行越权指定;输出中的
`knowledge_base_id` 应为百炼 synthetic 命名空间,向量由 `text-embedding-v4` 生成并写入
pgvector检索经 `qwen3-rerank`。切回离线回归时把该配置恢复为 `fake` 并重建 API。
在认证、RBAC 和资料出域审批完成前,这个开关只允许公开虚构资料,不能用于私有或真实报告。
API、浏览器、`worker-local`、seed/smoke 工具都不持百炼 Key。`worker-model` 只有内部 Worker
Token真正的百炼 Key 始终只存在于 `model-gateway`。任何真实资料还必须先完成权利、涉密和
云处理审批;有效 Key 不等于有权把资料发送到云端。
## 8. 当前验证基线与剩余范围
截至 2026-07-13提交前全量门禁记录为 296 项后端测试、53 项前端测试,并完成固定身份重放与
fresh + replay 两类 Docker synthetic 产品链 E2E。正式毕业验收仍需完成真实百炼认证、PDF/OCR、真实
授权语料、多租户/RBAC、至少 300 题正式双审盲测、备份恢复、性能/并发压测、论文定稿和答辩
彩排。这些未完成项不能由本手册的 smoke 结果替代。