Skip to content

检索问答全流程

检索问答(RAG)是Molore链路最深、打磨最多的一条链:它的目标不是「向量搜到几段就答」,而是在个人语料上做到数字可考、表格可枚举、出处可指、没货敢说没货。整条链分六段——候选召回、结构化通道、融合、装配、生成、证据边界——外加意图分轨决定是否动用图谱。代码集中在 app/api/v1/endpoints/llm/retrieval/ 子包(core/fusion/guarantee/rows/anchors/assembly/keywords/rewrite)与 chat.py_grounding.py

全流程总览

阶段模块关键机制代码位置
查询理解关键词/意图分词与 n-gram 抽取、_relation_intent 关系意图双闸retrieval/keywords.py
向量召回向量通道sqlite-vec KNN,深池 top_k*12 → 每文档 ≤3 块 → 总量 top_k*4retrieval/core.py
全文召回FTS 通道FTS5 BM25,命中集上限 500,与向量 weighted 融合retrieval/core.pyservices/fts_index.py
结构化通道表格/数字/枚举row:: 行条目、SQL 保底、数字普查、枚举直拉、整表补全、全文直扫retrieval/guarantee.pyretrieval/rows.py
融合融合排序相对水位门 + MIN_VECTOR_SIMILARITY 绝对兜底,kw_cov 豁免retrieval/fusion.py
装配上下文装配锚窗密度打分 + 质心校正,容量自适应,保底源独立预算retrieval/anchors.pyretrieval/assembly.py
生成对话链路温度 0.3,参考资料标记内是数据不是指令chat.pyservices/llm_service/routing.py
证据边界grounding数字核对、零命中声明、诚实线、推测标注_grounding.pychat.py
图谱扩展图谱分轨仅关系意图触发,一跳 + 传递谓词 BFS 2 跳retrieval/keywords.pyservices/graphify_service.py

候选召回

向量通道core.py):查询向量走 sqlite-vec KNN,但不直接取 top_k——先拉 top_k×12 的深池,再经 _diversify_chunk_hits 做文档级多样化(每文档 ≤3 块、总量 top_k×4),防止头部大文档的同义块挤爆候选。嵌入统一 bge-m3 1024 维。

全文通道core.py + fts_index.py):FTS5 BM25 倒排,候选即命中集(上限 500 在 fts_search 收口),不再 ILIKE 预过滤。

两路融合:向量分与 BM25 分按 weighted 加权合成融合分;BM25 归一在库小时会塌缩,门槛设计按「垫数百篇让 IDF 生效」的库规模校准。

结构化通道(表格与数字的直通车)

纯向量对「某数字出现在哪」「把整张表列出来」类问题天然弱,因此检索层在双通道之外挂了一组 SQL 直查通道,全部独立分账、不吃主预算:

  • 表格行结构化索引(row::):表格行以 row:: 前缀行条目进索引,带章节前缀与列名,四类内容共用,500 行限帽;XLSX/DOCX 表格经管道表抽取后同样产 row:: 行。
  • 编号/稀有数字 SQL 保底召回guarantee.py):题面里的编号、稀有数字独立份额前置直查。配套机制:单位表(MPa/℃/户/万户 等);纯小数收;完整日期 token_GUARANTEE_DATE_RE,裸年份/年月不收);token 空格变体双向_like_variants,题面「42 户」带空格能命中档内「42户」,保底与普查共用)。
  • 数字索引普查 _numeric_censusguarantee.py):对题面每个数字逐数字全库 ILIKE 普查,每数字独立帽(8 载体,超帽记 N+),与保底分工——保底管 sources 注入,普查管全库计数与载体清单。出现次数口径:同篇多处分别计数(变体取 max 防重复),区块报「N 处命中(M 篇内容)」。普查结果以独立预算区块进 prompt,并进站内证据面。它根治的事故形态是:保底全局帽被噪声载体吃光后,模型把「没召回」说成「库内仅一处」。
  • 稀有实体保底位:≥4 字稀有长词全库 ≤2 篇命中时末位区注入;承载全灭时放宽直查;变体合并后再兜底;框架词伪稀有由 _RARE_FRAME_STOP 抑制。
  • 数词概念保底锚:整词零命中且词素 ≥3 时,按纯计数密度峰补簇。
  • 跨语豁免带:关键词真零信号且 vec ≥ 0.48 放行,治跨语相关对死在「绝对下限与相对水位折算之间」的死区。
  • 枚举意图结构化通道rows.py):识别「列出/有哪些/几条/多少/统计/合计/均值/平均/占比」等枚举与计数聚合线索词,SQL 直拉全部 row:: 行,预算分级 8000 / 12000 / 16000ENUM_ROWS_BUDGET ~ ENUM_ROWS_BUDGET_MAX,随装配总预算自适应);题面点名的表/工作表整组排前_prefer_named_sheets),预算截断不再砍目标表。
  • 整表补全通道 table_completion_sectionrows.py):row:: 命中行按「章节前缀 + 列名集合」签名在线重建整表,SQL 直取注入(零索引变更、零重嵌),独立预算 3000 / 6000,帽 2 张表;枚举已覆盖时跳过。
  • 全文匹配行直取通道 find_all_lines_sectionrows.py):穷尽线索词 × 字面短语(引文优先、键=值兜底)双闸,全文直扫 + 精确计数 + 重复块去重双报,独立预算 6000。这是非表文件(TXT 日志等)的全文穷尽题通道——有它在场,全文件口径的计数断言才合法。
  • 时间线日期行通道:全文扫描年份行,服务「时间线/按年梳理」类题。

融合(fusion.py)

  • 绝对下限MIN_VECTOR_SIMILARITY = 0.55(按 nomic 校准;换 bge-m3 后无关对在 0.31–0.40 区间,仍拦得住)。
  • 相对水位门:候选融合分相对当轮最高分成比例过滤;豁免按去重覆盖度 kw_cov 而非命中计数——同一个通用词在标题+正文各中一次不该算多关键词证据。
  • rerank 永久关闭HYBRID_RERANK_ENABLED 默认 false;实测 rerank 与强模型 rerank 复评均不增收,小模型门卫不用于判断题,省钱闸门只用规则与阈值。

装配(anchors.py + assembly.py)

  • 锚窗密度打分 + 质心校正:不以切块边界为准,而以关键词锚点的密度峰选址窗口,质心校正防窗口偏移;锚窗总额上限 2500、向量父块保底 1500,互不挤死。
  • 容量自适应:多锚点题放宽锚窗/单源帽(_ANCHOR_PARTS_CAP_MULTI = 3500_PER_SOURCE_CAP_MULTI = 6000);装配总预算分级 RAG_FULL_BUDGET_LOCAL = 12000 / RAG_FULL_BUDGET_CLOUD = 24000 / RAG_FULL_BUDGET_CLOUD_MULTI = 32000
  • 保底源装配独立预算guaranteed_extra / rare_slot 来源不吃主预算——_guaranteed_reserve 预留、_guaranteed_body 按锚点定心窗给正文(cap 1600),自排承载预算够时全量照给。独立预算是兜底不是上限:检索层给保底源发了入场券,装配层就必须让它被读到,保底机制全链记账,只保检索侧等于没保。

生成(chat.py + routing.py)

  • 生成温度 0.3(llm_service/routing.py 的 chat 档),配 lite 模式。
  • prompt 信任层:参考资料标记内的内容按数据对待、不作为指令执行;产品指南与用户内容分区,防注入。
  • 生成失败落 [Error: ...] 文本契约,端点侧 502 原文透传,不落兜底假答案(见后端设计的错误处理纪律)。

证据边界(_grounding.py + chat.py)

回答的可信度由一组系统侧机制保证,不赌模型自觉:

  • 数字 grounding:回答中的数字逐一对召回证据核对;未命中先收紧重答一次,仍未命中则标注(SSE replace)。衍生数字豁免:系词族(是/为/等于/≈/相差/之差/低估/高估/均值/平均/±、算式分句、markdown 强调符)、「误差」收窄只认「误差约/为/±」、小数↔百分数确定换算(0.753675 ↔ 75.4% 同值豁免),双侧精度容差。
  • 零命中确定性声明:召回为空时,「库中暂无相关内容」由系统侧在流首 chunk 前置直发——产品承诺类行为用确定性实现,不靠小模型的指令服从。
  • 召回诚实线:无数字索引普查依据时禁说「库内仅一处」类全库绝对句;截断窗口内禁说「全文件唯一/全部」类文档内全量绝对句——只能说「当前召回结果/片段中仅见」。全文匹配通道在场时,全文件计数合法。原则一句话:召回缺口 ≠ 事实不存在,全库/全文判断必须有索引级依据。
  • 推测标注:模型自认的通用知识段、未落地数字所在句,在流末以 ⚠️ 标注随正文落库;开关 RAG_SPECULATION_CHECKapp/core/config.py,默认开)。规则派实现,零额外模型调用。
  • 引用 [n]:编号只认原文层(当轮 sources 位置编号);图谱推理路径等衍生内容不占 [n] 源位;跨轮编号稳定未承诺。

意图分轨与图谱扩展

  • _relation_intentkeywords.py):规则族(关系/归属/影响/区别/包含模式)+ 内容锚点双闸识别关系意图。仅关系意图跑图谱扩展,非关系意图整段跳过——实测砍后退化 0、每题省 1~2 次 SQL,主价值是消除 junk 邻居注入风险面。
  • 关系意图且扩展非空时渲染**「图谱推理路径」区块**:先分步推导再给结论,via 带关系语义与 evidence 证据句;[n] 引用仍只认原文层。
  • 图谱扩展本身:一跳任意边 + 传递谓词(is_a / part_of)BFS 2 跳、每跳 ×0.7 衰减;AMBIGUOUS 边默认在隔离区不进扩展。

下一步

基于 AGPL-3.0 协议发布。