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
This commit is contained in:
@@ -0,0 +1,112 @@
|
||||
# 正式检索、Worker 与评测运行手册
|
||||
|
||||
本文验证已落地的可运行切片:正式 pgvector 检索与重排接口、PostgreSQL 租约栅栏,
|
||||
以及可复现的 synthetic 检索评测。它不把 synthetic 指标等同于真实地质语料效果,
|
||||
也不表示当前百炼凭据已经通过线上验收。
|
||||
|
||||
## 1. 启动并准备合成知识库
|
||||
|
||||
```bash
|
||||
bash scripts/init-local-secrets.sh
|
||||
docker compose up -d --build web
|
||||
docker compose --profile tools run --rm seed-demo-offline
|
||||
docker compose ps --all
|
||||
```
|
||||
|
||||
`migrate` 是一次性任务,显示 `Exited (0)` 表示 Alembic 已成功升级后正常退出。持续运行的
|
||||
`db/model-gateway/api/gateway/web` 应为 `healthy`,且只有 Web 发布
|
||||
`127.0.0.1:8000`。
|
||||
|
||||
## 2. 验证正式 Retrieval API
|
||||
|
||||
```bash
|
||||
curl -sS -X POST http://127.0.0.1:8000/api/v1/retrieval/search \
|
||||
-H 'Content-Type: application/json' \
|
||||
--data '{
|
||||
"knowledge_base_id":"3acd3785-970b-55f7-a669-9eb4695e27eb",
|
||||
"query":"花岗斑岩铜矿化特征",
|
||||
"vector_top_k":50,
|
||||
"rerank_top_n":10
|
||||
}'
|
||||
```
|
||||
|
||||
成功响应应包含:
|
||||
|
||||
- 服务端生效的 embedding profile hash、模型和 1024 维契约;
|
||||
- `vector_rank/vector_score` 与 `rank/rerank_score`;
|
||||
- 稳定且不透明的 `citation_id`、文档 ID、章节、页码和安全片段;
|
||||
- embedding、数据库、rerank 和总耗时,以及请求 trace ID;
|
||||
- `rerank_status=applied`;重排不可用时则保留初召回并明确标记 `degraded`。
|
||||
|
||||
请求体不能提交 access scope。当前 synthetic 身份由服务器固定映射到 synthetic 知识库;候选
|
||||
SQL 在 `LIMIT` 前同时过滤知识库、授权 scope、当前激活版本、云审批、READY assignment、
|
||||
profile 和 searchable 状态。
|
||||
|
||||
浏览器访问 <http://127.0.0.1:8000/retrieval> 可查看同一正式接口的 React 检索实验室。
|
||||
|
||||
## 3. 运行冻结配置评测
|
||||
|
||||
```bash
|
||||
docker compose --profile tools run --rm seed-demo-offline \
|
||||
python -m app.tools.evaluate_demo \
|
||||
/demo/demo_documents.jsonl /demo/demo_queries.jsonl
|
||||
```
|
||||
|
||||
输出是单行 JSON 工件,至少包含:
|
||||
|
||||
- corpus/query set SHA-256;
|
||||
- active embedding profile hash;
|
||||
- vector/rerank/cutoff 参数和 bootstrap seed;
|
||||
- 完整冻结配置 SHA-256;
|
||||
- 每题排序及 Hit、Recall、MRR、nDCG、CompleteHit、EvidenceGroupRecall;
|
||||
- 聚合指标和固定 seed 的 95% bootstrap 区间。
|
||||
|
||||
公开样例的 9 个可回答问题当前应达到 `Hit@3=1.0`。这是为了验证流水线和指标实现的
|
||||
synthetic 基线,不是论文最终质量结论;正式结论必须使用冻结的开发集/盲测集和真实合法语料。
|
||||
任何 top-k 未判定候选都会使正式评分失败,不能静默按 0 处理。
|
||||
|
||||
## 4. 验证 Worker 并发租约与 fencing
|
||||
|
||||
```bash
|
||||
docker compose --profile tools run --rm worker-smoke
|
||||
```
|
||||
|
||||
预期输出:
|
||||
|
||||
```json
|
||||
{
|
||||
"claim_winners": 1,
|
||||
"expired_lease_rejected": true,
|
||||
"recovery_claim_winners": 1,
|
||||
"recovery_token_rotated": true,
|
||||
"stale_fence_rejected": true,
|
||||
"status": "ok",
|
||||
"terminal_status": "SUCCEEDED"
|
||||
}
|
||||
```
|
||||
|
||||
工具在真实 PostgreSQL 中创建一条随机 capability 的 synthetic 任务,让两个领取者并发竞争,
|
||||
验证只有一个领取成功、伪造 token 和已过期租约均无法心跳、过期任务只能由一个新 Worker
|
||||
使用旋转后的 token 重领并完成,最后删除该验证行。Worker
|
||||
心跳配置被强制限制为不超过租约的三分之一。该工具证明队列运行时和数据库 fence;具体文档
|
||||
解析/向量化 handler 仍需按各自业务验收,不可据此推断完整入库链已经完成。
|
||||
|
||||
## 5. 真实百炼边界
|
||||
|
||||
正式非 synthetic profile 会由 API/Worker 通过内部 token 调用 `model-gateway`,只有后者持有
|
||||
百炼 Key 和公网出口。当前本机凭据对 `text-embedding-v4`、`qwen3-rerank` 和
|
||||
`deepseek-v4-flash` 均返回供应商鉴权失败;离线检索成功不代表线上三模型成功。更换为有效、
|
||||
同一北京工作空间且具备模型权限的新 Key 后,先按
|
||||
[Stage 1 运行手册](05-stage1-runbook.md)执行 `provider-smoke`,三项均成功后才运行真实 seed。
|
||||
|
||||
## 6. 提交前门禁
|
||||
|
||||
```bash
|
||||
make verify
|
||||
docker compose --profile tools run --rm worker-smoke
|
||||
docker compose --profile tools run --rm seed-demo-offline \
|
||||
python -m app.tools.evaluate_demo \
|
||||
/demo/demo_documents.jsonl /demo/demo_queries.jsonl
|
||||
```
|
||||
|
||||
任何输出都不得包含 API Key、内部 token、数据库密码、DSN 或私有文档正文。
|
||||
@@ -0,0 +1,74 @@
|
||||
# Grounded Chat 运行与引用验证手册
|
||||
|
||||
本手册验证“正式检索 → 证据约束回答 → 引用事件”的单轮问答闭环。默认 synthetic profile
|
||||
不会调用百炼;非 synthetic profile 才经内部 `model-gateway` 调用 `deepseek-v4-flash`。
|
||||
|
||||
## 1. 前置服务
|
||||
|
||||
```bash
|
||||
docker compose up -d --build web
|
||||
docker compose --profile tools run --rm seed-demo-offline
|
||||
curl -sS http://127.0.0.1:8000/health/ready
|
||||
```
|
||||
|
||||
浏览器入口为 <http://127.0.0.1:8000/chat>,接口文档位于
|
||||
<http://127.0.0.1:8000/docs>。
|
||||
|
||||
## 2. 直接验证 SSE
|
||||
|
||||
```bash
|
||||
curl -sS -N -X POST http://127.0.0.1:8000/api/v1/chat/completions \
|
||||
-H 'Content-Type: application/json' \
|
||||
--data '{
|
||||
"knowledge_base_id":"3acd3785-970b-55f7-a669-9eb4695e27eb",
|
||||
"question":"花岗斑岩铜矿化有哪些典型蚀变特征?",
|
||||
"vector_top_k":20,
|
||||
"rerank_top_n":5,
|
||||
"max_tokens":512
|
||||
}'
|
||||
```
|
||||
|
||||
成功流必须严格满足:
|
||||
|
||||
```text
|
||||
meta(seq=1)
|
||||
retrieval(seq=2)
|
||||
delta(seq=3..n)
|
||||
citations
|
||||
usage
|
||||
done
|
||||
```
|
||||
|
||||
`done` 或 `error` 必须且只能出现一个。每个事件都是 JSON,`seq` 单调递增。`retrieval`
|
||||
先返回本次授权范围内的证据和检索耗时;`citations` 只允许引用同一轮 source map 中的
|
||||
`[S1]...[Sn]`。页面把回答、文件名和片段全部按纯文本显示,不执行 HTML。
|
||||
|
||||
## 3. 回答模式
|
||||
|
||||
| 模式 | 含义 | 是否调用云模型 |
|
||||
|---|---|---|
|
||||
| `grounded` + `synthetic_extractive` | 用已批准 synthetic 证据生成确定性摘录答案 | 否 |
|
||||
| `grounded` + `cloud_grounded` | 模型回答包含通过校验的本轮引用 | 是 |
|
||||
| `retrieval_only` | 模型没有给出合法引用,服务端退回安全证据摘录 | 云调用可能失败或输出不合格 |
|
||||
| `refused` | 当前授权检索没有足以支持回答的证据 | 否 |
|
||||
| `error` | 生成供应商或流契约失败;事件中不回显供应商正文 | 视失败阶段而定 |
|
||||
|
||||
文档片段在 Prompt 中被序列化为“不可信证据数据”,其中出现的命令、Prompt injection 或
|
||||
凭证索取指令均不能成为系统指令。模型输出会在公开前检查引用;越界、畸形和大小写伪造标签
|
||||
会被删除。引用 ID 合法只证明它来自本轮授权 source map,不自动证明自然语言声明在语义上
|
||||
完全受到该证据支持;后者必须进入人工标注和引用精确率评测。
|
||||
|
||||
## 4. 取消与失败
|
||||
|
||||
React 页面的“停止生成”会中止当前 fetch,后端取消会继续传播到内部模型流。当前接口是单次
|
||||
POST SSE,不保存会话,也不支持 `Last-Event-ID` 重放;持久会话、断线重放和 durable
|
||||
completion 是后续扩展,不得把本切片描述成完整多轮聊天系统。
|
||||
|
||||
生成前的检索/授权错误仍返回 `application/problem+json`。流开始后的错误只能用终态
|
||||
`event: error` 表达,不能再修改 HTTP 状态码。
|
||||
|
||||
## 5. 真实百炼验证
|
||||
|
||||
先运行 `provider-smoke` 并确认 Embedding、Rerank、Chat 三项均成功,再对百炼验证知识库发起
|
||||
问答。synthetic 离线成功不能代替真实模型验收。API 和前端永远不持有百炼 Key;只有隔离的
|
||||
`model-gateway` 持 Key 和公网出口。
|
||||
@@ -0,0 +1,198 @@
|
||||
# 文档上传、审核、向量化与检索运行手册
|
||||
|
||||
本手册用于验证当前已经落地的 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 smoke;401 不自动重试 |
|
||||
|
||||
日志和 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 结果替代。
|
||||
@@ -0,0 +1,56 @@
|
||||
# ADR-0006:采用确定性本地解析、切分与出域清单
|
||||
|
||||
- **状态:** accepted
|
||||
- **日期:** 2026-07-13
|
||||
|
||||
## 背景
|
||||
|
||||
文档进入百炼向量模型前,系统必须能证明“发送了哪段文本、来自哪个版本和页面、由什么配置
|
||||
产生、是否经过明确审批”。如果解析和切分结果随运行变化,embedding cache、引用锚点、审批
|
||||
manifest 和实验结果都会失去可复现性。PDF、DOCX 和扫描图件的能力也不能被模糊成同一种
|
||||
“已解析”状态。
|
||||
|
||||
## 决策
|
||||
|
||||
第一版建立纯本地、无模型调用的确定性入库核心:
|
||||
|
||||
- 严格验证文件大小、扩展名、声明 MIME 与内容签名;用户文件名永不作为存储路径。
|
||||
- TXT 支持严格 UTF-8/UTF-16;Markdown 恢复标题层级;DOCX 仅在 ZIP/XML 安全上限内提取
|
||||
标题、段落和表格行。
|
||||
- 不手写 PDF 文本或空间解析。没有可靠 parser/OCR adapter 时,PDF 明确进入
|
||||
`OCR_REQUIRED`,不产生切片或出域清单。
|
||||
- 规范化文本按结构块优先切分,默认 target 512、hard max 800、overlap 64。Tokenizer
|
||||
将 `ZK1203` 等字母数字地质标识符视为一个 token;块边界只能把窗口扩展到 hard max 内。
|
||||
- `display_text`、`cloud_text`、`embedding_text` 分离;embedding 输入固定为版本化前缀加
|
||||
已审批 cloud text。
|
||||
- parser、normalization、chunk、cloud policy 和 embedding 配置均计算 profile hash;规则变化
|
||||
生成新版本或 cache epoch,不能错误复用旧向量和审批。
|
||||
- chunk ID、source anchor 和 outbound manifest 由输入 hash、配置 hash、ordinal 与源范围
|
||||
确定性派生。每个锚点保留文档版本、页、块、行和字符范围;DOCX 无渲染引擎时物理页为
|
||||
`null`,不能伪造页码。
|
||||
- 任何外发必须在 manifest hash 与审核请求完全匹配后发生。上传/解析阶段无百炼调用。
|
||||
|
||||
## 安全限制
|
||||
|
||||
DOCX adapter 拒绝路径穿越、符号链接、重复条目、加密标志、宏、ActiveX、嵌入对象、DTD、
|
||||
XML entity、超大条目、过多条目和异常压缩比。错误只返回稳定 code,不包含文件名、正文、
|
||||
路径、密钥形态或底层异常。
|
||||
|
||||
凭证形态出现在上传文本时默认 fail closed,以防误把配置文件或日志当知识文档外发。未来如果
|
||||
确有合法语料包含类似字符串,必须新增受审计的本地脱敏策略,不能简单关闭该门禁。
|
||||
|
||||
## 被否决方案
|
||||
|
||||
1. **按固定字符数随意切片:** 中文、地质编号、表格和章节边界不可复现,无法稳定评测。
|
||||
2. **直接把原文同时用于展示、向量和生成:** 无法证明脱敏和出域审批覆盖的准确文本。
|
||||
3. **用标准库或正则手写 PDF 解析:** 不能可靠处理字体映射、多栏顺序、扫描页和加密状态。
|
||||
4. **把 OCR 图例视为空间理解:** OCR 只能识别局部文字,不恢复地图拓扑、比例和几何关系。
|
||||
|
||||
## 后续约束
|
||||
|
||||
- 调整 token 规则、target/max/overlap、前缀、脱敏策略或 parser 行为时,必须修改 profile hash
|
||||
版本并重跑 golden fixture、manifest、向量缓存和引用回归。
|
||||
- 引入 PyMuPDF、OCR 或多模态模型需要独立 adapter、依赖/镜像评审和 ADR;不能替换 raw
|
||||
artifact,必须保留新 revision。
|
||||
- 解析成功不等于可出域;只有 `CLOUD_APPROVED` 且 manifest/profile 绑定一致的 chunk 才能
|
||||
进入 Embedding、Rerank 或 Chat。
|
||||
@@ -0,0 +1,78 @@
|
||||
# ADR-0007:拆分本地解析 Worker 与模型 Worker 的信任边界
|
||||
|
||||
- **状态:** accepted
|
||||
- **日期:** 2026-07-13
|
||||
|
||||
## 背景
|
||||
|
||||
文档入库同时涉及两类高风险资源:原始上传文件,以及访问云模型的能力。若一个通用 Worker
|
||||
同时挂载上传卷、内部模型 Token 和模型网络,那么解析器漏洞、恶意 DOCX/PDF 或任务 payload
|
||||
缺陷可能把未经审批的原文直接发送到云端。仅靠业务代码中的状态判断,无法形成可独立验证的
|
||||
最小权限边界。
|
||||
|
||||
本项目仍采用模块化单体和同一个后端镜像;拆分的是运行身份、capability、网络、卷和 Secret,
|
||||
不是拆成两套业务服务或数据库。两个 Worker 当前仍共用同一个 PostgreSQL 应用角色,因此该拆分
|
||||
只隔离上传卷、模型 Token 和网络能力,不构成数据库授权边界。
|
||||
|
||||
## 决策
|
||||
|
||||
Compose 运行两个互斥能力的长期 Worker:
|
||||
|
||||
| 边界 | `worker-local` | `worker-model` |
|
||||
|---|---|---|
|
||||
| capability | `document_parse` | `embedding` |
|
||||
| 数据库网络 | 有 | 有 |
|
||||
| 内部模型网络 | 无 | 有 |
|
||||
| 公网 egress | 无 | 无;只能访问 internal `model-gateway` |
|
||||
| 上传卷 | 有 | 无 |
|
||||
| 数据库 app Secret | 有 | 有 |
|
||||
| model-gateway Token | 无 | Worker 身份 Token |
|
||||
| 百炼 API Key | 无 | 无 |
|
||||
|
||||
`model-gateway` 是唯一持有百炼 API Key 和普通 egress 网络的进程;它不连接数据库、不挂载上传
|
||||
卷,也不发布宿主机端口。`worker-model` 只能发送数据库中已通过 manifest 审批的 `cloud_text`,
|
||||
不能读取原始文件。`worker-local` 能读取隔离上传卷,但没有模型网络与 Token,即使解析器被恶意
|
||||
文件影响,也缺少直接调用模型的凭据和路由。
|
||||
|
||||
两个 Worker 均以非 root 后端用户运行,根文件系统只读,使用临时 `/tmp`、`no-new-privileges`
|
||||
和 `cap_drop: ALL`,不暴露端口。它们共用 PostgreSQL 任务队列,但领取 SQL 只匹配各自
|
||||
`required_capability`。心跳、失败回写、阶段提交和最终激活必须匹配 `job_id + lease_owner +
|
||||
lease_token` 且租约仍有效,避免旧进程覆盖已重领任务。
|
||||
|
||||
上传卷初始化由一次性 `upload-init` 完成。该容器临时以 root 运行,但 `network_mode: none`、根
|
||||
文件系统只读,只保留初始化目录所需的最小文件能力,成功后正常 `Exited (0)`;长期 API 和
|
||||
`worker-local` 不获得这些额外 capability。
|
||||
|
||||
## 被否决方案
|
||||
|
||||
1. **单一通用 Worker 同时挂卷和模型 Token:** 部署简单,但把未审核原文与云出口放在同一
|
||||
攻击面,违背 manifest 审批门禁。
|
||||
2. **只靠 Python `if review_state == CLOUD_APPROVED`:** 状态判断仍然需要,但不能替代网络、
|
||||
Secret 和文件系统的纵深隔离。
|
||||
3. **每种任务拆成独立代码仓库/数据库:** 当前单机规模没有对应收益,会引入分布式事务、双写、
|
||||
部署和备份复杂度。
|
||||
4. **让 `worker-model` 直接访问百炼公网:** 会把供应商协议、Key 和公网出口扩散到业务进程,
|
||||
无法集中轮换、限流和脱敏错误。
|
||||
|
||||
## 影响
|
||||
|
||||
- 优点:原始文件与云调用能力不能在单个长期 Worker 中汇合;Compose 契约可直接验证卷、网络
|
||||
和 Secret;不同任务可独立扩缩容。
|
||||
- 代价:需要维护两个 Worker 服务和 capability 路由;跨阶段任务只能通过数据库中的已审批
|
||||
工件交接,不能依赖本地临时文件。
|
||||
- 限制:Docker internal network 是重要纵深防线,但不是形式化沙箱;API 与两个 Worker 当前共享
|
||||
数据库应用角色,文档 Actor 也仍是服务器固定的 synthetic 身份。真实多用户/私有数据部署必须
|
||||
先落地认证、对象级 RBAC、分离数据库角色或受控存储过程,并结合主机防火墙、出口 allowlist、
|
||||
集中 Secret Manager、容器运行时策略和审计。
|
||||
|
||||
## 后续约束
|
||||
|
||||
- 新增 OCR Worker 时默认归入本地高风险解析边界,除非独立 ADR 证明它需要模型出口;OCR
|
||||
结果仍须重新生成并审批 outbound manifest。
|
||||
- 任何服务若要同时获得上传卷与模型网络/Token,必须先做威胁建模、更新 ADR 并增加 Compose
|
||||
契约测试,不能通过临时排障静默扩大权限。
|
||||
- 新任务类型必须声明唯一 `required_capability`,并验证错误能力的 Worker 无法领取。
|
||||
- `DOCUMENT_NAMESPACE_MODE` 只能由服务端部署配置选择 `fake` 或 `bailian` synthetic 命名空间;
|
||||
请求不得携带任意 scope 绕过授权。真实多租户上线前必须用认证/RBAC 替代固定 Actor。
|
||||
- 变更 Worker 网络、卷、Secret 或部署拓扑时,必须重跑 document pipeline Docker E2E、租约
|
||||
fencing smoke、Secret 扫描和 Compose 安全契约。
|
||||
@@ -9,3 +9,5 @@ ADR 用于记录会长期影响系统的技术决策。状态使用 `proposed`
|
||||
- [0003-text-first-scope.md](0003-text-first-scope.md):第一版采用文本优先边界,不宣称地质图空间理解。
|
||||
- [0004-secretless-web-ingress.md](0004-secretless-web-ingress.md):用无 Secret 的 Nginx Web 与固定上游 gateway 隔离浏览器、API 和数据库网络。
|
||||
- [0005-isolate-model-egress.md](0005-isolate-model-egress.md):用独立 Model Gateway 隔离百炼 Key、模型出口与数据库感知服务。
|
||||
- [0006-deterministic-local-ingestion.md](0006-deterministic-local-ingestion.md):冻结本地解析、512/800/64 切分、文本分离和 outbound manifest 契约。
|
||||
- [0007-split-local-and-model-workers.md](0007-split-local-and-model-workers.md):拆分本地解析与模型 Worker 的网络、卷、Secret 和 capability 信任边界。
|
||||
|
||||
Reference in New Issue
Block a user