merge: synchronize retrieval export to main
This commit is contained in:
@@ -37,6 +37,8 @@ FastAPI + Pydantic
|
||||
|
||||
新增 `retrieval_runs` 保存查询、模式、语料指纹和完整结果快照,支持分页读取及按 UUID 复核。零命中如实记录;当前依据修改后失效不会改写历史,但后续检索必须重新检查审核与父记录状态。响应耗时是本次真实读取、分词、排序及片段生成耗时,不含写日志或模型调用。
|
||||
|
||||
管理员 `POST /api/retrieval/exports` 按明确编号导出四种模式的原保存响应,并可包含关联准备上下文;以一个 SQL 语句快照读取,分别标记条目/chunk 粒度。JSONL 首行记录版本、选择范围、条数和后续记录字节摘要;输出前校验、限制容量,不写新历史、不依赖当前发布或模型,不导出认证数据。生成用量和费用为未采集,完整规则见 [导出契约](RETRIEVAL_EXPORT_API.md)。
|
||||
|
||||
关键词基线仍按知识条目检索,显示片段最长 240 字符;独立的持久化 chunks 和文本索引版本见下方。该接口不读取 chunks;chunk 与向量/RRF 的独立接口见后文。全语料计算明确限制条目数及总字符数,超限报错,适用于首批小规模资料与后续对照实验准备,不能当作生产检索性能结论。
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
# 检索与上下文历史导出
|
||||
|
||||
本接口为后续实验保留当时的检索证据与配置,不重新检索、不加载 Embedding 或调用生成模型,也不生成质量分数。四种模式仍保持各自证据粒度,不能把条目与 chunk 混为同一评测样本。
|
||||
|
||||
## 请求与权限
|
||||
|
||||
`POST /api/retrieval/exports`,管理员会话、允许的 Origin 和 `X-CSRF-Token` 均为必需。普通用户 403,匿名/失效会话 401;错误 Origin 或 CSRF 为 403。继承 [认证契约](AUTH_API.md),不会返回用户、会话或凭据字段。
|
||||
|
||||
```json
|
||||
{
|
||||
"run_ids": ["00000000-0000-4000-8000-000000000001"],
|
||||
"include_contexts": false
|
||||
}
|
||||
```
|
||||
|
||||
编号仅为格式示例,实际应先从管理员 `GET /api/retrieval/runs` 选择已存在的记录。`run_ids` 必须为 1–100 个不重复 UUID,大小写规范化后仍重复也拒绝;`include_contexts` 为严格布尔值,默认 false。额外字段、空列表和非法编号为 422。
|
||||
|
||||
明确选择避免导出无边界的全库记录。任一检索编号不存在即 404,不生成部分文件。可选上下文是这些检索记录关联的**全部已保存准备记录**,不含其他检索的上下文。
|
||||
|
||||
## 下载与 JSONL 契约
|
||||
|
||||
- 成功为 200,`Content-Type: application/x-ndjson; charset=utf-8`,`Content-Disposition: attachment; filename="nalati-retrieval-<export UUID>.jsonl"`。
|
||||
- 响应固定 `Cache-Control: no-store` 和 `X-Content-Type-Options: nosniff`;`X-Records-SHA256` 等于文件首行的 `records_sha256`。
|
||||
- UTF-8 编码,每行一个 JSON 对象,每行(包括末行)以 LF 结束;内容中的换行由 JSON 转义。不是 CSV,也不是一个 JSON 数组。
|
||||
- 首行为 `record_type=manifest`。其余先为检索记录,再为上下文记录,各组按数据库 `created_at,id` 升序。请求 UUID 列表的顺序不影响记录区顺序。
|
||||
|
||||
首行字段:
|
||||
|
||||
| 字段 | 含义 |
|
||||
|---|---|
|
||||
| `schema_version` | 固定 `nalati-retrieval-export-v1` |
|
||||
| `export_id`、`exported_at` | 本次导出 UUID 与带时区 UTC 时间 |
|
||||
| `selection` | 排序后的检索 UUID 及是否包含上下文 |
|
||||
| `counts` | 实际 `retrieval_runs` 与 `rag_contexts` 条数,不含首行 |
|
||||
| `records_sha256` | **首行之后全部原始 UTF-8 字节**的 SHA-256,含每条末尾 LF |
|
||||
| `snapshot_semantics` | 固定 `stored_history_not_current_corpus` |
|
||||
| `generation_measurements` | `status=not_called`,`usage/cost/currency=null`;没有生成调用或费用测量,不能解释为零成本 |
|
||||
|
||||
检索行有 `record_type=retrieval_run`、`id`、`ranking_unit` 和 `payload`。`payload` 保留原已保存响应,不重新计算或修改分数、耗时、文本、来源及版本:
|
||||
|
||||
| `payload.mode` | `ranking_unit` | 保留的配置与证据 |
|
||||
|---|---|---|
|
||||
| `keyword` | `knowledge_item` | 条目 BM25、中文分词/标题权重、片段与来源版本 |
|
||||
| `keyword_chunks` | `chunk` | 文本发布/切分/语料版本、正文 BM25、chunk 原文出处 |
|
||||
| `vector` | `chunk` | 文本/向量发布、模型完整 revision、空间/维度与精确余弦配置 |
|
||||
| `hybrid` | `chunk` | 同一 chunk 粒度、两路完整候选/排名/原始分数/贡献、RRF 与模型空间配置 |
|
||||
|
||||
上下文行有 `record_type=rag_context`、`id` 和原保存的 `payload`;保留准备参数、证据、排除原因、提示词版本、请求体及请求摘要。`model_called=false`,即使 `status=prepared` 也不是模型回答。原字段详见 [RAG_CONTEXT_API.md](RAG_CONTEXT_API.md)。不导出数据库 `created_by` 或认证表数据。
|
||||
|
||||
## 快照、容量与失败
|
||||
|
||||
- 检索与可选上下文通过**一个 PostgreSQL SQL 语句快照**读取,没有跨页读取造成的漂移;执行后新增的上下文留到下一次导出。
|
||||
- 已修改、删除或重新发布的知识不会改写历史。导出成功只说明当时记录可读取,不说明它仍是当前有效发布或来源仍受支持。
|
||||
- 每次最多 100 条检索、100 条关联上下文,完整 UTF-8 文件(含 manifest)最多 8 MiB。超量为 413,不静默截断;某个检索自身关联超过 100 条准备记录时,本版可先不含上下文导出,不能借此声称已导出全部准备记录。
|
||||
- 输出前完成结构、编号/关联、数据库元数据和准备请求摘要校验;未知/缺失字段、非有限数值或关联异常为 409。它是历史格式一致性检查,不是来源真实性或事实正确性证明。
|
||||
- 服务端先完整验证并组装文件,再返回下载响应;校验、容量或数据库失败不会先发出部分 JSONL。此接口不写入新的检索/上下文记录,不重建索引。
|
||||
- 同一历史选择的记录区字节和摘要可重复;manifest 的导出 UUID/时间每次变化,因此不要比较整个文件的摘要。若新增上下文后再次启用 `include_contexts`,记录区也会变化。SHA-256 仅供字节完整性核对,不是审阅签名或质量指标。
|
||||
|
||||
`elapsed_ms` 沿用各模式原始计时范围,不是生成模型或完整浏览器交互耗时。模型、语料与参数不一致的记录不能只凭分数直接做实验对比。
|
||||
|
||||
## 调用与核对
|
||||
|
||||
已有管理员登录并按认证契约准备 Cookie 文件和当前 CSRF 后,可在仓库根目录调用。Cookie 文件、真实请求、JSONL 和实际日志均留在 Git 元数据目录或仓库外;下面的编号仍需替换成真实已保存的检索编号。
|
||||
|
||||
```bash
|
||||
mkdir -p .git/exports
|
||||
curl --fail-with-body --request POST http://127.0.0.1:8000/api/retrieval/exports \
|
||||
--cookie "$COOKIE_JAR" \
|
||||
--header 'Origin: http://localhost:5173' \
|
||||
--header "X-CSRF-Token: $CSRF_TOKEN" \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{"run_ids":["00000000-0000-4000-8000-000000000001"],"include_contexts":true}' \
|
||||
--output .git/exports/history.jsonl
|
||||
```
|
||||
|
||||
仅在 HTTP 200 后按 JSONL 读取文件。核对摘要时不应重新序列化记录:
|
||||
|
||||
```python
|
||||
import hashlib
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
header, records = Path(".git/exports/history.jsonl").read_bytes().split(b"\n", 1)
|
||||
manifest = json.loads(header)
|
||||
assert manifest["schema_version"] == "nalati-retrieval-export-v1"
|
||||
assert hashlib.sha256(records).hexdigest() == manifest["records_sha256"]
|
||||
rows = [json.loads(line) for line in records.rstrip(b"\n").split(b"\n")]
|
||||
assert len(rows) == sum(manifest["counts"].values())
|
||||
```
|
||||
|
||||
前端调用层 `services/exports.ts` 的 `exportRetrievalHistory` 返回 Blob、受限文件名和记录摘要,沿用同一 Cookie/CSRF、401 撤销及错误处理,不放入 localStorage。此阶段没有新增下载页面,页面入口由后续前端集成;调用层测试是模拟网络的契约测试,不冒充浏览器下载验收。
|
||||
|
||||
## 验证与撤回
|
||||
|
||||
新增专项覆盖混合模式原始快照、可选准备记录、无命中、软删除后历史、摘要稳定性、权限/CSRF、未知字段/关联损坏拒绝、字节/条数上限、并发新增上下文以及真实 HTTP 后端重启后的同一记录区。坐标 fixture 仅用于导出协议,不构成真实领域模型实验。专项命令 `pytest -q backend/tests/test_exports.py`:**30 passed**;完整 `pytest -q backend/tests`:**254 passed, 1 skipped**,skip 为显式启用的真实 Embedding 用例,本阶段没有重跑模型。`ruff check backend`、`ruff format --check backend` 通过;前端在 `frontend/` 执行 `npm run test:session`:**3 passed**,`npm run build` 通过。
|
||||
|
||||
数据库不新增迁移,继续为 `0007_rag_contexts`。撤回导出路由、服务、调用层即可停止新下载;原检索/上下文历史与已保存文件不变,不为回滚删除数据库记录。
|
||||
@@ -99,3 +99,11 @@
|
||||
- 修正页面联调发现的采集时间缺失、审核/文档导航入口和过期功能说明;后端审核/授权不放宽,不猜测历史资料采集时间。具体路径、HTTP 与浏览器证据分工及合成截图见 [BROWSER_ACCEPTANCE.md](BROWSER_ACCEPTANCE.md)。
|
||||
- 独立 HTTP 检查确认匿名401、普通用户八类管理资源403、缺失CSRF/错误Origin403、退出后401;目录写入使旧发布409,重新发布后恢复真实检索;准备上下文仍为 model_called=false。合成数据和本机运行环境、原始记录、凭据不入库。
|
||||
- 该轮验收收尾了T02的真实浏览器API连接、T05知识录入/审核与文本/向量页面核心流程、Issue #5登录/Cookie/CSRF/角色的主要浏览器路径。Docker、HTTPS、多浏览器、浏览器改密及历史元数据修订页面仍待验收;生成模型和领域评测继续延期。本轮 `pytest -q backend/tests` **224 passed, 1 skipped**,`ruff check`、`ruff format --check` 和前端 `npm run build` 均通过;常规 skip 为须显式启用的真实模型专项,本轮另外通过浏览器与独立 HTTP 实际运行了固定 BGE small。
|
||||
|
||||
## 2026-10-16 T07/T10:可复现历史导出
|
||||
|
||||
- 浏览器联调与录入修正经 [PR #29](https://gitea.kronecker.cc/saint/border-red-post-nalati/pulls/29) 真正合入 dev,已核对 head/merge 都在最新 dev 祖先链;再从最新 dev 创建唯一阶段分支 `feat/backend-retrieval-export`。
|
||||
- 新增管理员/Origin/CSRF 保护的 `POST /api/retrieval/exports`,按明确 UUID 导出 JSONL,保留四模式原保存的查询、命中、原始分数、两路候选、模型/语料/切分参数、来源和耗时;可选择关联的准备上下文。条目与 chunk 粒度分开,生成模型使用量和费用为未采集,不伪造生成调用或实验分数。
|
||||
- 一个 SQL 语句读取历史快照,保留旧版本而不重新检索;导出前完整校验,最多100条检索/100条上下文/8MiB,不静默截断,不返回部分文件。不新增数据库迁移或环境配置。
|
||||
- 前端仅增加下载类型与调用层,复用 Cookie/CSRF/错误处理,不新增页面;真实浏览器下载与实验展示留给后续集成。完整字段、摘要核对、复现与回滚见 [RETRIEVAL_EXPORT_API.md](RETRIEVAL_EXPORT_API.md)。
|
||||
- 专项测试 **30 passed**,包括真实 PostgreSQL 并发快照和 HTTP 重启导出;完整回归 **254 passed, 1 skipped**(skip 为须显式启用的真实 Embedding 专项),`ruff check` 与 `ruff format --check` 通过。前端会话下载契约 **3 passed**,类型与生产构建通过。测试使用合成资料与坐标 fixture,未重跑模型推理或调用生成模型。
|
||||
|
||||
Reference in New Issue
Block a user