7.5 KiB
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,回滚时应限制旧历史端点或先以备份保留新模式记录,避免把新记录交给旧模型解析;不要为回滚而无备份删除检索历史。