Files
border-red-post-nalati/docs/INDEX_API.md

7.9 KiB
Raw Permalink Blame History

文本切分与索引版本接口(T05)

本阶段保存可复核的已审核语料切分及发布指针,为后续检索索引提供版本边界。这里的“索引版本”是文本语料快照,不包含 Embedding、pgvector 向量字段/ANN 索引或 RRF。现有 POST /api/retrieval 仍运行原关键词基线,不读取这些 chunks;不能把本阶段称为向量检索已完成。

依赖、迁移与权限

分支 feat/backend-chunk-index-versions 叠加待审 PR #7(认证),并继承 PR #6(关键词)。三项须分别跟踪审阅;本分支不自行合并其他请求。升级时停止旧后端实例,安装依赖、备份并执行 alembic upgrade head,再启动新实例。所有实例必须使用当前版本,才能让目录写入遵守同一发布锁。

新增迁移 0005_chunk_indexes,依赖 0004_auth_sessions,创建:

  • index_versions:切分配置、语料指纹、目录修订号、条目/chunk 数、构建人、构建时间及发布状态。
  • chunks:所属版本、知识 ID、从 0 开始的片序、原文起止偏移、正文、UTF-8 内容 SHA-256 与出处快照。
  • index_state:单例发布指针、发布代数 generation、目录修订号 catalog_revision。
  • index_publications:每次发布的前后版本、代数、操作人和时间;重复发布当前版本不会新增记录。

迁移不改写既有知识、审核、用户、会话或检索记录,不创建初始发布版本。所有 /api/indexes 接口均要求 admin;POST 还必须携带有效 Cookie、明确 Origin 与 X-CSRF-Token,与 认证契约 相同。

构建与原文切分

POST /api/indexes 创建一个 ready 版本,返回 201,不切换当前发布指针。请求可用默认值 {} 或:

{"chunk_size": 500, "overlap": 80}

chunk_size 为 100–2000 个 Python Unicode 字符,默认 500;overlap 默认 80,非负且不得超过切分长度一半。自定义较短长度时也须指定兼容的重叠,例如 {"chunk_size":100,"overlap":20}。未知字段拒绝,不支持按类别单独发布。

固定算法 unicode-window-v1 从知识条目原正文位置 0 开始(不是关联文档或原网页的偏移),以 chunk_size-overlap 前进,末片到正文结束即停止。保留空白、全半角、组合字符和 Emoji,不标准化或截断原文。start_offset 包含起点,end_offset 不包含终点;每片 text == 原文[start_offset:end_offset]。标题放在出处信息中,不拼进正文偏移。客户端若使用 JavaScript 字符串偏移,须注意其 UTF-16 索引与 Python Unicode 字符偏移的区别。

  • 语料条件与关键词基线共用:只读取未删除、审核及采集信息完整的 verified 知识,关联来源/文档须有效、归属及来源网址一致;pending/rejected 不进入构建。
  • 语料指纹与关键词基线使用同一规范 JSON/算法,涵盖实际正文、标题、类别、出处及知识/父记录版本;切分参数独立保存在 config。
  • 出处快照包含 knowledge_id/knowledge_version、可空 document_id/document_version、可空 source_id/source_version/source_name、source_url/title/category。以后原资料修改或失效,不改写旧 chunk 内容及出处。
  • chunk ID 在一个版本内由版本 UUID、知识 ID 与顺序确定;新构建使用新版本 ID。相同资料及参数可重现偏移/正文/内容哈希,但新旧版本的 chunk ID 不相同。
  • 本阶段同步构建,沿用最多 5000 条及标题/正文总计 200 万字符的语料限制,每版本另限 20000 个 chunks。超限返回 503;事务写入失败也完整回滚,不保留半个版本,不改变旧发布指针。
  • 可以构建空语料版本,以便所有依据撤回后发布空快照;这不表示存在可用知识。

发布、过期与并发

先通过 GET /api/indexes/active 读取当前代数,再提交:

POST /api/indexes/{version_id}/publish
{"expected_generation": 0}

发布时锁定 index_state,检查代数、目录修订号及当前有效语料指纹;资料或审核已变化、代数过期均返回 409,要求重新构建或刷新。成功后同一事务把旧版本设为 superseded、新版本设为 published、切换指针、增加代数并追加发布记录。校验失败或写入失败保留此前全部发布状态。持有当前代数重复发布当前版本是无变化的 200;仍有效的历史版本可用最新代数重新发布,发布记录保留每次转换。

知识、来源、文档的应用写入在修改父记录前持有同一状态锁,事务内将 catalog_revision 增加一次;失败写入一起回滚修订号。构建、发布和这些写入串行,避免校验到提交之间发生目录修改。为首批小规模资料选择此保守策略,长构建会暂时阻塞管理写入;尚无后台任务或大规模增量索引。

任一成功目录写入都会使旧快照过期,包括尚未核验的新条目;此规则比只比较有效语料更保守。GET /active 同时核对修订号与实际语料指纹,返回:

{"generation": 1, "catalog_revision": 2, "is_current": true, "active_version": {"id": "<版本 UUID>", "status": "published", "config": {"splitter":"unicode-window-v1","chunk_size":500,"overlap":80}, "...":"其余版本字段见 OpenAPI"}}

初始无发布时 active_version=null/is_current=false。资料变化后指针及历史保留,但 is_current=false;published 只表示仍是发布指针,不表示资料仍有效。历史 chunks 是当时的依据快照,不能绕过 is_current 直接用于后续正式检索。任何结果只描述该事务快照,之后的目录写入仍可让它过期。直接改库不属于支持的维护流程;额外指纹检查可发现绕过修订号的正文/出处变化,不能替代应用写锁。

查询与交接

接口 响应
GET /api/indexes 版本分页,构建时间降序,同时间按 ID
GET /api/indexes/active 发布代数、目录修订号、当前性与当前版本
GET /api/indexes/publications 发布记录分页,代数降序
GET /api/indexes/{version_id} 版本元数据
GET /api/indexes/{version_id}/chunks chunk 分页;可按 knowledge_id 筛选,按知识 ID/片序排列

分页沿用 {items,total,page,page_size},默认 20、最大 100。版本元数据完整字段为 id/status/config/corpus_fingerprint/catalog_revision/item_count/chunk_count/created_by/created_at/published_at;published_at 是最近一次发布的时间,逐次时间保存在发布记录。版本及发布记录时间统一输出 UTC ISO 8601,避免数据库时区让重读结果出现不同表示。chunk 字段为 id/index_version_id/knowledge_id/ordinal/start_offset/end_offset/text/text_sha256/provenance。

前端 src/services/indexes.ts 提供类型和调用封装,写入共用 Cookie/CSRF;未增加页面或假数据。接口新增需由前端按该契约接入、审阅并验收。独立 T05 Issue 因当前缺少 Gitea 写入认证仍待补建,本 PR 和任务板先保留实现与验证记录。

验证及回滚

验证包含真实 PostgreSQL 的原文重建与哈希、审核筛选、重复构建、过期拒绝、来源/文档更新、失败写入、发布历史、数据库锁竞争、失败构建/发布的真实约束回滚、迁移兼容及真实 HTTP 重启后指针/chunks 持久化。这里只使用占位资料,不是论文实验结果。Docker 和浏览器端验收保持待办。

备份后配合旧后端执行 alembic downgrade 0004_auth_sessions,只撤回这四张表,保留用户、会话、知识及检索记录;索引历史与 chunks 恢复依赖备份。旧应用不可使用新版本数据库混跑。

并发实现参考 PostgreSQL 16 行锁说明 与 SQLAlchemy FOR UPDATE。