Isolate cloud model access before enabling product RAG workflows
Some checks failed
verify / verify (push) Has been cancelled

The API and ingestion tools now use a fixed internal model gateway while
governed profiles, embedding cache assignments, traceable citations, and
stable API errors establish the boundaries required by later workflows.

Constraint: The current Alibaba Cloud workspace rejects all three live model calls with authentication failures
Rejected: Give the API or seed tools the Bailian key and direct egress | combines database access, cloud credentials, and public network access
Rejected: Mix offline and Bailian vectors in one demo namespace | makes profile activation and retrieval ambiguous
Confidence: high
Scope-risk: moderate
Reversibility: clean
Directive: Keep Bailian credentials and egress exclusive to model-gateway and create a new immutable profile hash for any embedding identity change
Tested: make verify; 121 backend tests; 14 frontend tests; fresh and populated Alembic upgrade-downgrade-upgrade; two idempotent offline seeds; Docker health and HTTP retrieval; isolated provider smoke
Not-tested: Successful live Bailian responses because the supplied workspace credential currently fails authentication
This commit is contained in:
2026-07-13 04:09:06 +08:00
parent 99b7df64ea
commit 75592af33a
28 changed files with 3932 additions and 254 deletions

View File

@@ -4,9 +4,9 @@
|---|---|
| 课题 | 基于 RAG 的地质找矿知识问答系统构建与应用 |
| 学科方向 | 大数据分析 |
| 文档版本 | v1.0-design |
| 状态 | 设计基线,待实现验证 |
| 更新日期 | 2026-07-11 |
| 文档版本 | v1.1-implementation-sync |
| 状态 | 设计基线;安全运行骨架已部分实现,产品主链路未完成 |
| 更新日期 | 2026-07-13 |
| 后端 | Python + FastAPI |
| 前端 | React + TypeScript |
| 向量存储 | PostgreSQL + pgvector |
@@ -22,7 +22,7 @@
1. 仓库、提交历史、镜像层、前端包、测试数据和日志都不能出现真实 Key。
2. 开发环境从未提交的 Docker Secret 文件读取;生产环境从 Secret Manager 或编排平台读取。
3. 前端永远不接触 Key所有百炼请求由后端发起。
3. 前端、入口 Gateway、业务 API、Worker、seed 和 smoke 工具永远不接触百炼 Key所有百炼请求只由独立 `model-gateway` 发起。
4. 日志不得记录 `Authorization` 请求头或完整模型请求体。
5. 工作空间真实域名也只进入本地部署配置;文档统一使用 `<workspace-id>`
6. 每次提交前执行 `make verify`;仓库已配置本地钩子和 Gitea Actions 双重检查Actions 需以远端 runner 首次成功记录作为生效证据。
@@ -33,11 +33,15 @@
```text
React/Nginx
-> 无 Secret 入口 Gateway
-> FastAPI API
-> PostgreSQL + pgvector
-> 阿里云百炼 Embedding / Rerank / Chat
-> FastAPI Worker与 API 共用同一 Python 代码镜像)
-> 内部 model-gateway client
-> Python Worker与 API 共用同一 Python 代码镜像)
-> 文档解析、分块、向量化和持久化任务
-> 内部 model-gateway client
model-gateway唯一持有百炼 Key 和公网出口)
-> 阿里云百炼 Embedding / Rerank / Chat
```
默认模型链:
@@ -51,7 +55,7 @@ deepseek-v4-flash
-> 只依据证据回答并给出页码引用
```
选择 PostgreSQL + pgvector 的原因是当前预计 1 万至 30 万切片,单机 PostgreSQL 已足够,并能把业务状态、元数据、向量和后台任务放在一个事务边界中。这样核心长期运行组件只有 Web、API、Worker、数据库一条 `docker compose up -d --build` 即可启动。达到百万级切片或高吞吐边界后再评估 Qdrant而不是在第一版提前承担双写一致性和第二套备份系统。
选择 PostgreSQL + pgvector 的原因是当前预计 1 万至 30 万切片,单机 PostgreSQL 已足够,并能把业务状态、元数据、向量和后台任务放在一个事务边界中。为把数据库权限与云凭证/公网出口分开,运行拓扑额外保留一个很小的 `model-gateway` 安全边界;它复用后端镜像和领域端口,不把业务拆成多套数据库或分布式事务。达到百万级切片或高吞吐边界后再评估 Qdrant而不是在第一版提前承担双写一致性和第二套备份系统。
## 2. 项目目标和边界
@@ -136,13 +140,14 @@ flowchart LR
G -->|"internal data"| A["FastAPI API"]
A --> D[("PostgreSQL + pgvector")]
A --> F[("文件卷 / OSS 适配器")]
A --> E["text-embedding-v4"]
A --> R["qwen3-rerank"]
A --> L["deepseek-v4-flash"]
A -->|"Bearer + X-RAG-Caller: api"| M["内部 model-gateway"]
M --> E["text-embedding-v4"]
M --> R["qwen3-rerank"]
M --> L["deepseek-v4-flash"]
J["DB 持久化任务"] --> K["Python Worker"]
K --> D
K --> F
K --> E
K -->|"Bearer + X-RAG-Caller: worker"| M
A --> J
```
@@ -154,13 +159,14 @@ flowchart LR
| `gateway` | 固定 API 上游、请求大小/头边界、脱敏错误和流式转发 | 数据库凭证、模型凭证、业务权限判断 |
| `api` | 认证、知识库、检索、问答、评测 API | 长时间 OCR/批量入库 |
| `worker` | 解析、OCR、分块、向量化、索引、评测批任务 | 对外开放端口 |
| `model-gateway` | 唯一读取百炼 Key、统一模型协议/限流/脱敏错误;校验 API/Worker 内部身份 | 数据库、上传卷、外部端口、业务授权 |
| `db` | 元数据、向量、任务、会话、评测事实来源 | 原始大文件长期存储 |
| `storage` | 开发期 Docker volume生产可切换 OSS | 访问控制的最终判定 |
| 百炼适配器 | 统一超时、重试、计量、错误映射 | 记录密钥或正文日志 |
| 百炼适配器 | 仅在 `model-gateway` 进程内统一超时、重试、计量、错误映射 | 记录密钥或正文日志 |
### 4.3 为什么不先拆微服务
解析、检索、生成和评测共享同一领域模型、配置和数据库。拆分微服务会提前引入服务发现、分布式追踪、数据一致性和接口版本问题,却没有对应吞吐收益。第一版使用一个 Python 包,由 `api``worker` 两个进程加载;当某个模块具有独立扩容证据时再拆分
解析、检索、生成和评测共享同一领域模型、配置和数据库。拆分业务微服务会提前引入服务发现、分布式追踪、数据一致性和接口版本问题,却没有对应吞吐收益。第一版使用一个 Python 包,由 `api``worker` `model-gateway` 三种进程加载;其中 `model-gateway` 不是独立业务服务,而是最小权限的凭证/出口隔离边界。详细决策见 [ADR-0005](adr/0005-isolate-model-egress.md)
## 5. 技术选型
@@ -209,7 +215,7 @@ ADR 见 [ADR-0001](adr/0001-use-pgvector.md)。
### 6.1 端点必须分开
业务空间专属域名的 Key、地域和 Base URL 必须匹配;官方推荐生产使用专属域名,见[Base URL 总览](https://help.aliyun.com/zh/model-studio/base-url)。配置只保存以下形态
业务空间专属域名的 Key、地域和 Base URL 必须匹配;官方推荐生产使用专属域名,见[Base URL 总览](https://help.aliyun.com/zh/model-studio/base-url)。这些配置和 Key 只注入 `model-gateway`
```dotenv
BAILIAN_OPENAI_BASE_URL=https://<workspace-id>.cn-beijing.maas.aliyuncs.com/compatible-mode/v1
@@ -227,6 +233,17 @@ DASHSCOPE_API_KEY_FILE=/run/secrets/bailian_api_key
`/apps/anthropic` 是 Anthropic 协议入口,本项目不使用。尤其不能把 `/reranks` 拼到 `/compatible-mode/v1` 后面;官方的 Rerank 路径是独立的 `/compatible-api/v1/reranks`
业务进程不接受任意模型名、供应商 URL 或向量维度,而只调用固定内部接口:
```text
POST /internal/v1/embeddings
POST /internal/v1/rerank
POST /internal/v1/chat/completions
POST /internal/v1/chat/stream
```
调用必须同时携带 `Authorization: Bearer <internal-token>``X-RAG-Caller: api|worker`。服务端以常量时间比较 token 并要求 token 身份与 caller 一致;`api` 只允许查询向量、重排和聊天,文档向量化只允许 `worker` 身份。两个内部 token 相互独立、只通过 Docker Secret 注入。API、seed 和 smoke 工具均经内部客户端访问,既不挂载百炼 Key也不加入公网 `egress` 网络。
### 6.2 Embedding 契约
基线请求:
@@ -365,6 +382,10 @@ UPLOADED
| `evaluation_sets/cases` | 版本化问题、证据和标签 |
| `evaluation_runs/results` | 参数快照、指标、延迟、成本和错误 |
| `audit_logs` | 管理操作和安全审计 |
| `model_profiles` | 模型种类、模型名、API 模式、1024 维约束、端点身份哈希和无凭证配置快照;受控 cache epoch 必须进入不可变 profile hash |
| `embedding_cache` | 以 `profile_hash + embedding_text_sha256` 唯一缓存 1024 维向量和脱敏调用元数据 |
| `chunk_embedding_assignments` | chunk/profile 到缓存项的状态化绑定,保证 READY 只指向同文本同 profile 向量 |
| `model_invocations` | 只记录 trace、caller、模型、用量、耗时、request ID 和脱敏错误,不保存请求/响应正文 |
### 8.2 核心向量表(示意)
@@ -441,6 +462,8 @@ COMMIT;
- 删除先清空 `active_version_id` 并令投影不可检索,再异步物理删除文件和向量;
- 任何回答保留当时的 chunk ID、模型和 Prompt 版本,保证复现。
Alembic `0002_model_profiles` 已实现上述 profile、缓存、assignment 和调用审计表,为 `knowledge_bases` 增加激活 Embedding profile并为 `chunks` 增加稳定唯一的 `citation_id`。迁移只会为可明确识别的单一 synthetic fake profile 做安全回填,绝不从模型别名或 URL 猜测真实供应商身份;同一知识库存在多个候选 profile 时保持未激活,等待显式治理。已在独立全新 volume 验证 `空库 -> 0001 -> 0002 -> 0001 -> 0002`,并在已有 20 条合成向量的数据卷完成相同升降级;重复 seed 后 profile/active KB 为 1/1cache/READY assignment/citation 均为 20且 citation 无重复。
## 9. 在线 RAG 流程
### 9.1 主流程
@@ -527,6 +550,8 @@ Rerank 只能重新排序已召回候选,无法找回初召回遗漏的证据
## 10. 后端 API 设计
FastAPI 入口通过 `create_app()` 应用工厂构建导入阶段不打开数据库、不读取百炼凭证。HTTP 请求由统一中间件生成或透传安全的 trace ID业务错误使用稳定的 Problem JSON 响应并回传 trace未知异常不得泄漏连接串、Secret 路径或供应商正文。`/health/live` 只证明进程存活,`/health/ready` 只验证本地数据库依赖;百炼能力状态由独立模型探测展示,避免供应商短时故障触发 API 重启风暴。
统一前缀 `/api/v1`
```text
@@ -703,6 +728,10 @@ pgvector 官方给出的单精度 `vector` 存储约为 `4 * dimensions + 8` 字
详细指标与阶段完成定义分别见 [01-data-and-evaluation.md](01-data-and-evaluation.md) 和 [03-implementation-plan.md](03-implementation-plan.md)。
### 15.1 2026-07-13 实现边界
当前已实现安全运行骨架、内部 `model-gateway` 及 token 身份、模型内部客户端、应用工厂/Problem/trace 契约和 `0002_model_profiles` 迁移代码;`provider-smoke`、真实 `seed-demo` 和 API 进程不再直接持有百炼 Key。当前工作空间对 `text-embedding-v4``qwen3-rerank``deepseek-v4-flash` 的实际请求仍返回 401因此三能力真实可用性尚未验收。数字文档上传/解析/审核、正式检索 API、grounded chat、Worker 租约恢复、评测运行器和完整产品 UI 仍是后续实现项;不得把本节理解为“整个项目完成”。
## 16. 参考资料
1. [阿里云百炼 Base URL 总览](https://help.aliyun.com/zh/model-studio/base-url)