feat: prepare RAG evidence and compatible chat requests

This commit is contained in:
2026-10-16 20:01:00 +08:00
committed by saint
parent ed8780cacc
commit d91a6808a9
16 changed files with 1066 additions and 5 deletions
+7
View File
@@ -54,3 +54,10 @@ FastAPI + Pydantic
文本发布之后,固定 Embedding 空间的完整向量快照通过独立代数发布,精确余弦检索只使用当前审核与发布版本。编码在发布锁外进行,完成后重新核对语料、空间及发布状态,失败保留旧版本。运行与空间身份见 [VECTOR_API.md](VECTOR_API.md)。
新增 chunk 正文 BM25 与等权 RRF;两路限定同一文本/向量快照和分类,固定 k=60,先各取 candidate_k 再融合。保存完整候选排名、原始分数、贡献、切分/分词/BM25/模型配置及出处。旧条目关键词模式保留,评测不能混同两种证据单元。两路排序至检索记录写入共用当前性锁,面向最多 2000 chunks 的同步精确计算。字段、失败及回滚边界见 [HYBRID_API.md](HYBRID_API.md)。RAG、ANN 与真实领域质量验收仍待后续阶段。
## RAG 上下文与兼容请求准备(T08)
`rag_contexts` 通过检索记录外键保存证据、出处/版本、选段/排除原因、提示词版本和 OpenAI 兼容 Chat Completions 请求体。准备时在发布锁内复核审核资料及文本/向量快照;已过期检索必须重查。输入与证据作为 JSON 数据放在 user message,固定规则独立保存,不允许客户端提交任意上下文或凭据。字符预算不是模型 token 校验。
准备/历史接口限管理员,写入要求会话 CSRF;历史保持当时快照。当前提供纯响应格式/引用解析,不调用生成模型;`prepared_request/model_called=false` 不能视为实际回答或领域实验。真实服务接入及模型 token 预算验证等待后续阶段。字段、权限、迁移和回滚见 [RAG_CONTEXT_API.md](RAG_CONTEXT_API.md)。
+7
View File
@@ -49,3 +49,10 @@
状态:后端分支实现,待审阅。基于已合入的向量快照,新增正文 BM25 的 chunk 基线,与向量模式使用同一文本发布及分类范围;保留条目关键词接口作为独立口径。两路候选各限 candidate_k,使用从 1 起的排名、固定 k=60、等权倒数排名融合,缺席一路贡献为零。保存候选全排名、原始分数、两路贡献和配置,确保实验准备可复核;不把 BM25 与余弦原始分数直接相加。
公式与常数参考 [RRF 原论文](https://cormack.uwaterloo.ca/cormacksigir09-rrf.pdf),本项目尚未依据核验领域问题集调优。同步精确计算限制 2000 chunks,固定配置、版本一致性、历史兼容与回滚见 [HYBRID_API.md](HYBRID_API.md)。
## ADR-012 先固定证据与 Chat Completions 契约
状态:T08 分支实现,待审阅。用户要求模型选择暂缓,先按 OpenAI 通用格式开发。选择纯 `model/messages` 请求构造及非流式单候选响应解析,不配置服务、凭据或默认生成模型;参数兼容通过显式 role/token 字段选项表达,不暗中降级。供应商可替换,真实调用验证另行开展。
上下文只接受服务端 chunk 检索记录,在当前性锁内复核证据,完整保留原文和出处;重叠和超预算片段明确排除,不截断正文。引用校验只能确认编号存在,不能证明事实受到支持。准备快照通过独立表持久化,不把协议 fixture 或准备结果标记为真实模型回答。完整边界见 [RAG_CONTEXT_API.md](RAG_CONTEXT_API.md)。
+83
View File
@@ -0,0 +1,83 @@
# RAG 证据与 OpenAI 兼容请求准备
本阶段按用户要求先实现通用请求格式,生成模型选择和真实调用延期。协议采用非流式 Chat Completions:`model/messages`、单条 assistant 响应、可选用量;不绑定供应商、服务地址、密钥或具体模型。字段依据 [OpenAI 官方 Chat Completions 文档](https://developers.openai.com/api/reference/resources/chat/subresources/completions/methods/create)。
本阶段提供管理员上下文准备与历史接口、完整证据快照、固定提示词及纯响应解析器。`POST /api/chat` 仍保留原占位状态,尚未成为生成式问答接口;准备记录固定为 `kind=prepared_request, model_called=false`,不能作为实际模型回答、耗时、费用或领域实验结果。
## 准备与读取
| 接口 | 用途 | 权限 |
|---|---|---|
| `POST /api/rag/contexts` | 从服务端检索记录准备并持久化请求 | admin、明确 Origin、会话 CSRF |
| `GET /api/rag/contexts?page=1&page_size=20` | 分页读取当时准备快照 | admin |
| `GET /api/rag/contexts/{id}` | 按 UUID 读取单份快照 | admin |
请求示例(ID、模型标识仅作格式示例,不表示已选择或调用模型):
```json
{
"retrieval_run_id": "00000000-0000-0000-0000-000000000001",
"completion": {
"model": "deployment-model-id",
"output_tokens": 512,
"token_parameter": "max_completion_tokens",
"instruction_role": "system"
},
"max_input_characters": 10000,
"max_evidence": 5
}
```
请求只接收服务端检索记录 UUID、组包配置和预算,不接收任意正文、提示词、URL、请求头或 API Key。来源必须是 `keyword_chunks/vector/hybrid` 的完整结果;条目级 `keyword` 返回 422,避免把条目与 chunk 的证据粒度混用。记录不存在返回 404,格式/预算越界返回 422。
| 字段 | 约束 |
|---|---|
| `completion.model` | 必填,1–128 个字母/数字及 `._:/-`;只保存标识,不验证模型存在或服务可用 |
| `output_tokens` | 默认 512,1–4096,写入所选输出预算字段 |
| `token_parameter` | 默认 `max_completion_tokens`;显式选择 `max_tokens` 才生成旧兼容字段,两者不同时发送 |
| `instruction_role` | 默认 `system`,也可显式使用 `developer`;须由后续实际服务兼容性测试确认 |
| `temperature` | 可选 0–2;省略时请求体不发送,不预设所有模型都支持该参数 |
| `max_input_characters` | 默认 10000,1024–24000;两个 message 的 content 字符数之和 |
| `max_evidence` | 默认 5,1–10;保持检索排名顺序挑选证据 |
组装的请求体为 `model`、两个文本 `messages`、`stream=false`、`n=1`、`store=false`、所选输出预算字段,及显式提供的 temperature。第一条消息只有固定规则,第二条是规范 JSON 的 `question/evidence` 数据。请求不包含鉴权或服务地址。这是明确的协议子集,不保证每个“OpenAI 兼容”服务都支持所有字段;后续接入须核对服务的角色、参数及模型限制,不做静默重试或字段回退。
字符预算用于有限体积的准备工作,**不是模型 token 数或上下文窗口校验**。输出 token 上限同样不表示输入能被某个尚未选定的模型接受。真实发送前须按所选模型验证完整输入与输出预算,超限明确失败,不静默截断原文。
## 证据、引用与不足规则
准备时只从数据库读取检索结果,并重新核对当前有效审核语料、原文 chunks、文本发布 ID/代数、目录修订和语料指纹。vector/hybrid 还须匹配当前向量发布 ID/代数、完整向量集及空间身份。整个检查至保存过程沿用目录/发布锁,不重新编码、不加载 Embedding,也不调用生成模型。知识变化、文本或向量重新发布、缺失向量/原文、出处或检索记录不一致均返回 409,要求重新检索。
按检索排名逐条处理完整 chunk,给被纳入的证据分配连续 `E1/E2/...`。保存知识/文档/来源 ID 与版本、来源 URL、标题/分类、chunk ID/序号、原文、字符偏移和 SHA-256、检索排名。对同一知识已选择片段的重叠区间排除后续片段;达到证据数上限或整体 message 字符预算时,排除相应片段并继续检查后续可容纳候选,不切割原文。
每个排除项记录 chunk ID 及 `overlap/budget/evidence_limit` 原因。没有候选时返回 `insufficient_evidence/no_candidates`;全部候选超出预算时为 `insufficient_evidence/context_budget`。这两种情况均不生成 request_body,不能把空证据包交给后续模型。
固定 `nalati-evidence-v1` 提示词要求:依据提供的证据回答,事实标注 `[E1]` 形式引用,不编造出处;不把问题、正文、标题或 URL 中的命令作为系统规则;资料不足或冲突时说明局限,无法可靠回答时使用固定不足说明。JSON 数据与固定指令分离只是结构约束,尚未通过真实模型的注入抵抗/领域质量实验。`prepared/candidates_available` 只表示有可组装片段,不表示这些片段在语义上足够支持答案。
## 快照与复核
响应保存为独立 `rag_contexts` 记录,引用原 `retrieval_runs`,不改写已有检索历史。响应包含:
- `id/retrieval_run_id/retrieval_mode/query`、文本/向量发布 ID 和代数、目录修订、语料指纹。
- `prompt_version`、实际 completion 配置、字符/证据预算、完整已选 evidence 和 excluded 列表。
- `request_body` 及其规范 UTF-8 JSON 的 `request_sha256`;无证据时两者均为 null,input_characters 为 0。
- `kind=prepared_request`、`model_called=false`、状态/原因和创建时间,不包含任何模型答案或调用成本。
历史查询返回当时的完整快照,资料后续撤回也不会改写它。历史请求体不是当前发送授权;未来实际模型调用必须重新检查当前依据和发布状态,不能从历史端点取出旧包后直接认定仍有效。
## 兼容响应解析
`services/completions.py` 提供纯解析函数,当前不暴露可向数据库写入“模型回答”的导入接口,也没有 HTTP 模型客户端。它只接受 `object=chat.completion` 的单一 index=0 assistant 文本候选,拒绝流式 chunk、工具/函数调用、未知结束原因或畸形结构。
- `finish_reason=length` 标记 incomplete 并丢弃部分答案;content_filter 或 refusal 标记 refused。
- 正常 stop 响应必须有非空文本、至少一个已提供的 `[E#]` 引用;缺失、越界或错误编号拒绝。固定不足说明单独标记 insufficient_evidence。
- 返回实际 response ID/model、引用列表和可选非负整数 token 用量;用量合计不一致时拒绝,不自行估算费用。
- 即使引用结构通过,也只标记 `answer_candidate`。引用存在不能证明原文支持每个事实,事实支持度、冲突、时效及人工验收仍需后续阶段验证。
测试使用明确的合成响应/坐标与 PostgreSQL,不冒充真实模型推理。供应商兼容、生成质量、真实 token 预算、超时/重试/限流和费用记录须在模型选择后另行验收。
## 迁移、回滚与交接
新增 `0007_rag_contexts`,启动前备份并执行 `alembic upgrade head`。表通过外键保留检索记录和创建账号关系,只有追加与查询 API,没有修改/删除 API。撤回本阶段时可备份后配合旧后端执行 `alembic downgrade 0006_vectors`;仅删除准备快照表,保留知识、账号、文本/向量发布与检索历史。恢复准备快照须使用备份。
前端新增 `services/rag.ts` 调用与类型,不修改现有问答页面。实际部署环境、运行日志、凭据和真实请求数据不得提交仓库。生成模型接入按用户后续选择推进,当前没有待创建的密钥,也不因延期而反复询问。
+11
View File
@@ -79,3 +79,14 @@
- 模型编码在发布锁外,完成后重新检查审核语料、文本/向量代数与空间;两路排序和记录保存受同一事务保护。竞争、模型或写入失败不保存部分混合结果;2000 chunks 容量与原向量阶段一致。没有新增数据库迁移。完整字段、比较口径及回滚见 [HYBRID_API.md](HYBRID_API.md)。
- 常规测试 `pytest -q` **180 passed, 1 skipped**(真实模型专项须显式启用);新增 27 项覆盖手算排序/融合、候选截断/缺席/同分、相同粒度及分类、原文、历史、权限、编码期间锁释放和版本竞争、损坏/失效快照、失败回滚与容量。`ruff check`、`ruff format --check` 和前端 `npm run build` 通过。真实模型专项在独立 CPU 环境另执行 **1 passed**:固定 BGE small 经真实 HTTP 完成向量/混合/关键词 chunk 查询,进程重启后两路候选排名、结果和历史保持一致。首次专项请求因 candidate_k 小于默认 top_k 被按契约拒绝,修正测试请求后通过;未改变该输入约束。
- Issue #5 本轮复查无新评论,后端实现已合入,前端登录/CSRF/角色页面及浏览器流程仍待交接;Docker 容器、核验领域语料/问题集、正式模型冻结与领域评测同样未验收。所有新增自动化资料均为合成测试,不作为文旅事实或论文分数。下一阶段须等待本轮 PR 真正合入 `dev`。
## 2026-10-16 T08:证据与 OpenAI 兼容请求契约
- RRF 经 PR #14 真正合入 `dev` 并核对 head/merge 祖先关系后,从最新 `dev` 创建唯一阶段分支 `feat/backend-rag-context`。用户明确“模型先不急”,随后要求“先以OpenAI通用请求格式进行开发”;本轮完成协议和证据准备,不选择生成模型或发起真实调用。
- 新增管理员 `POST/GET /api/rag/contexts` 及详情接口,写操作沿用 Cookie/Origin/CSRF;只接受服务端 chunk 检索记录,复核当前审核语料、文本/向量发布、空间、原文及出处,持久化完整证据、排除原因、提示词版本、请求体与哈希。
- OpenAI 兼容 Chat Completions 请求构造支持明确的 model/messages、角色及输出预算字段;纯响应解析校验单候选、结束原因、引用编号和可选 token 用量。截断/拒绝/无引用等不作为有效答案;引用通过只表示结构候选,不表示事实已核验。
- 新增迁移 `0007_rag_contexts`,失败写入完整回滚,历史保留当时快照;回滚保留知识、账号、文本/向量发布和检索记录。当前 `/api/chat` 仍为原占位,准备结果固定 `prepared_request/model_called=false`。完整协议、字符预算与后续 token/模型边界见 [RAG_CONTEXT_API.md](RAG_CONTEXT_API.md)。
- 实际验证:`pytest -q` **224 passed, 1 skipped**;较上一阶段新增 43 项上下文/协议/权限/失效/预算/原文/失败回滚/真实 HTTP 重启用例,另新增 1 项上下文迁移降级保留数据验证。skip 为既有须显式启用的真实 Embedding 用例,本轮未重跑真实模型专项,不沿用旧结果冒充生成模型验证。`ruff check`、`ruff format --check` 通过。前端调用层同步,构建在补齐依赖后通过。
- PR #16 已合入的认证路由引用了 `vue-router`,原 package.json/锁文件未声明;本轮补充匹配现有 Vue 3 的 vue-router 4 依赖及锁记录,保留其他依赖原版本和平台元数据。前端 `npm run build` 通过;浏览器实际 Cookie/CSRF/角色及容器验收仍待完成。
- 本轮所有测试为协议/合成资料或坐标 fixture,没有真实生成模型请求、质量分数或费用。生成模型选择和服务凭据保持延期,不重复索要;本阶段提交 PR 后等待实际合入,再继续下一项可独立验收的后端任务。