Files
border-red-post-nalati/docs/HYBRID_API.md
T

7.5 KiB
Raw Blame History

Chunk 关键词与 RRF 混合检索

本阶段基于已合入 dev 的文本/向量发布,增加可独立调用的 chunk BM25 和 RRF。既有条目关键词接口继续保留。三个 chunk 模式使用相同的当前文本版本、原文 chunk 与分类范围;比较时应固定发布版本、分类和 top_k,同时记录混合检索的 candidate_k。

接口 请求 mode 排序/证据单元 前置条件
POST /api/retrieval keyword 知识条目,标题权重 2、正文权重 1 当前有效审核语料
POST /api/retrieval/chunks keyword_chunks chunk 正文 BM25,标题不参与打分 当前有效文本发布;无需模型或向量发布
POST /api/retrieval/vector vector chunk 精确余弦 当前有效文本/向量发布,运行模型空间匹配
POST /api/retrieval/hybrid hybrid 同一批 chunk 的两路 RRF 与向量模式相同

mode 可省略,由接口固定;传入其他模式或未知字段返回 422。公开检索无需登录;历史分页及详情继续限管理员。问答接口仍为占位,当前响应不生成 LLM 答案。

请求与排名

{"query":"合成资料中的交通查询入口","mode":"hybrid","top_k":5,"candidate_k":50,"category":"交通"}
  • query 去除首尾空白后 1–500 字符,top_k 默认 5、范围 1–20,category 可省略。chunk 关键词请求不接收 candidate_k。
  • 混合检索每路最多取 candidate_k 条候选,默认 50、范围 1–200,并须不小于 top_k。这是固定排名后的候选截断,不会改变文本或输入 token。最终从候选并集返回最多 top_k 条。
  • chunk BM25 只对正文分词。沿用固定 Jieba 0.42.1 搜索分词、NFKC/casefold、HMM 关闭、项目词与 zh-small-v1 停用词表;查询词去重,k1=1.2、b=0.75,正值平滑 IDF。语料长度和词频在分类过滤后的全部 chunks 上计算,只将正分候选纳入关键词一路。标题仅作为出处字段。
  • 向量一路复用精确余弦排序,同样先按分类筛选,不设置相似度阈值。负分和零分仍可能进入候选,分数不是事实置信度,status=ok 只表示存在排序结果。
  • 两路先按各自原始分数排序;同分按 knowledge_id, chunk_ordinal, chunk_id 升序,排名从 1 开始。RRF 使用等权、固定 k=60:score(c) = sum(1 / (60 + rank_leg(c)))。不在某路候选列表中的 chunk,该路贡献为 0。原始 BM25 和余弦分数不直接相加;融合分数同分沿用相同 ID 次序。排序与日志使用未预先四舍五入的分数。
  • 融合以 chunk ID 去重。同一知识的不同片段(含重叠片段)保留各自排名和原文偏移,不做条目折叠或多样性重排。条目基线与 chunk 模式的分数、命中数、语料数及评测单元不同,不能直接混算指标。

RRF 方法与常数参考 Cormack、Clarke、Büttcher,SIGIR 2009 原论文。这里把 k=60 作为首版可复现配置,并未通过本项目领域问题集调优。

响应与复核

全部模式保留原文、字符偏移(左闭右开)、知识/文档/来源 ID 及版本、URL、排名与耗时。chunk 关键词和混合响应另含:

字段 含义
mode keyword_chunks 或 hybrid,可用于历史类型判别
text_index_version_id, text_generation, catalog_revision 本次文本发布和目录修订
corpus_fingerprint 整份已审核语料的指纹,分类筛选不改变它
corpus_size 分类范围内参与两路打分的 chunk 数
total_hits 关键词模式为全部正分 chunk 数;混合模式为两路截断后的候选并集大小,可能大于返回条数
query_terms, config.lexical, config.chunking, config.tie_break 实际分词、BM25 参数、切分配置、稳定排序规则
elapsed_ms 语料检查、模型编码(混合模式)、排序及响应组装耗时;不含记录写入及网络时间

混合模式还保存:

  • vector_version_id、generation:向量发布版本与代数;config.space 包含完整模型 revision、维度、token 预算、指令、实现/池化/归一化与权重格式身份;与向量契约一致。
  • config.rrf_k/weights/candidate_k/vector_scorer:融合与候选预算,不允许请求覆盖固定算法参数。
  • lexical_candidates/vector_candidates:两路完整的截断候选列表,每条为 chunk_id/rank/score,即使未进最终前 top_k 也保留。
  • lexical_total_hits/vector_total_hits:截断前正分关键词候选数、分类范围内向量候选总数。
  • 命中的 lexical_rank/lexical_score/vector_rank/vector_score 和两路 *_contribution:缺席的排名/原始分数为 null、贡献为 0。score 等于两路贡献之和。

结果完整写入现有 retrieval_runs,历史接口联合类型支持四种 mode,不需要新增迁移,当前 Schema 仍为 0006_vectors。修改依据后历史保持原样,只代表当时快照;重查须通过当前性验证。原始查询和运行记录属于数据库数据,不应提交到仓库。

一致性、失败与容量

混合检索先检查发布版本与模型空间,然后提交只读事务释放发布锁,再真实编码查询;编码后重新获取目录/向量状态锁,复核目录修订、文本/向量代数、审核指纹、实际 chunks、完整向量集及空间身份。变化时 409,且不保存部分结果。两路排名及写入检索历史在同一个受锁保护的事务内完成。

关键词一路没有命中或没有分词时,混合模式明确保留空候选列表,可仅有向量一路;纯关键词模式没有可检索词则 422。分类无资料或空的有效发布返回 no_evidence。模型 token 超限/输出校验失败返回 422,运行环境不可用或编码忙返回 503;模型失败不降级成一份声称成功的混合结果。

两种新接口均沿用向量阶段最多 2000 chunks、5000 条知识/200 万字符的整体核验预算,超限明确 503。只在分类范围内打分,但当前性及容量检查针对整份发布。文本快照允许的 20000 chunks 不等同于本接口容量。同步精确排序及 BM25 在锁内运行,会短暂串行化目录写入;当前没有 ANN、后台队列或生产吞吐量结论。

运行依赖、固定缓存、默认关闭的本地模型、构建/发布步骤见 VECTOR_API.md。前端新增 services/hybrid.ts 两个调用及历史类型;模式切换页面、Docker/浏览器端和真实领域验收仍待交接。

验证与回滚

轻量测试使用明确的合成正文及坐标 fixture,覆盖手算 BM25/RRF、原始分数与融合的独立性、候选边界、两路缺席、稳定排序、chunk 偏移、分类、权限、历史、失效/破坏快照、编码期间版本竞争、无部分日志及容量拒绝。真实模型专项沿用 test_vector_runtime.py 的显式 opt-in,增加固定 BGE small 的 HTTP 混合检索和进程重启后的两路排名/历史复核。运行命令见 VECTOR_API.md。这些用例验证实际模型/数据库/API 贯通,合成问题不代表领域质量或论文实验结论。

撤回本功能代码即可撤回两个新增接口;数据库无需降级。旧后端历史响应模型不认识 keyword_chunks/hybrid,回滚时应限制旧历史端点或先以备份保留新模式记录,避免把新记录交给旧模型解析;不要为回滚而无备份删除检索历史。