跳到主要内容

章节解析三层策略与导航决策构建

上一篇讲了三种执行模式的判定,这一篇来深入看 resolveSection 是怎么定位章节的,以及最终的导航决策对象是怎么构建出来的。

resolveSection:章节解析总入口

不管是哪种执行模式,只要需要定位章节,都会走 resolveSection 这个方法:

/**
* 解析问题对应的目标章节。
* 这是“章节解析总入口”,但它自己并不做具体匹配,而是按可靠性从高到低依次尝试三种实现路径:
*
* 1. `resolveBySectionCode(...)`
* 如果问题里显式出现了类似 `1.2.3` 这样的章节编号,就按编号直接精确查找;
* 这是最强、最可靠的结构信号。
*
* 2. `resolveByNavigationIndex(...)`
* 如果没有显式章节编号,就走专门的导航索引检索;
* 这一步适合处理“开户流程属于哪个章节”“异常处理在哪一节”之类的结构语义问题。
*
* 3. `resolveByLocalStructure(...)`
* 如果索引也没有命中,就退回到本地章节树做模糊文本匹配;
* 这一步更像“弱结构兜底”,通过短语和章节标题/路径/正文去估计最可能的章节。
*
* 如果以上三条路径都没有稳定命中,则最后退回到 `graphService.findBestSection(...)` 做 best effort 兜底。
*
* @param documentId 文档 id
* @param originalQuestion 原始问题
* @param rewrittenQuestion 改写问题
* @return 命中的章节;若无法稳定定位,则返回兜底章节或 null
*/
private GraphSection resolveSection(Long documentId, String originalQuestion, String rewrittenQuestion) {
if (documentId == null) {
// 没有文档 id 时,不存在结构图可定位的前提条件。
return null;
}
// 第一层:显式章节编号命中最可靠,因此优先尝试。
GraphSection byCode = resolveBySectionCode(documentId, originalQuestion, rewrittenQuestion);
if (byCode != null) {
return byCode;
}

// 第二层:如果没有显式编号,再尝试走导航索引。
GraphSection indexedMatch = resolveByNavigationIndex(documentId, originalQuestion, rewrittenQuestion);
if (indexedMatch != null) {
return indexedMatch;
}

// 第三层:再退回到本地结构字段模糊匹配。
List<String> phrases = buildSectionPhrases(originalQuestion, rewrittenQuestion);
GraphSection localMatch = resolveByLocalStructure(documentId, phrases);
if (localMatch != null) {
return localMatch;
}
// 最后让 graphService 自己给一个 best effort 的章节兜底结果。
return graphService.findBestSection(documentId, rewrittenQuestion, "");
}

这是一个典型的"可靠性从高到低依次降级"的策略,一共四层:

层级方法适用场景可靠性
第一层resolveBySectionCode问题里有"1.2.3"这类章节编号最高
第二层resolveByNavigationIndex带结构语义但没有显式编号较高
第三层resolveByLocalStructure前两层都没命中一般
兜底graphService.findBestSection全部失败时的最后保障

第一层:显式章节编号精确命中

/**
* 第一种实现:尝试从问题中直接解析章节编号。
* 适合处理类似下面这种问题:
* 1. “1.2.3 章节讲了什么”
* 2. “3.1.4 这一节属于哪个模块”
* 3. “在 2.4 节里提到的步骤是什么”
*
* 这类问题最大的特点是:用户已经显式给出了章节 code,
* 因此不需要再做语义猜测,直接按编号查图结构是最稳的。
*
* @param documentId 文档 id
* @param originalQuestion 原始问题
* @param rewrittenQuestion 改写问题
* @return 命中的章节;若没有显式章节编号则返回 null
*/
private GraphSection resolveBySectionCode(Long documentId, String originalQuestion, String rewrittenQuestion) {
Matcher matcher = SECTION_CODE_PATTERN.matcher((safeText(originalQuestion) + " " + safeText(rewrittenQuestion)).trim());
while (matcher.find()) {
// 只要问题里出现像“1.2.3”这样的章节编号,就直接按编号精确查找。
// 这里同时扫描原问题与改写问题,避免改写阶段补出的结构编号被漏掉。
GraphSection section = graphService.findSectionByCode(documentId, matcher.group(1));
if (section != null) {
return section;
}
}
return null;
}

付费内容提示

该文档的全部内容仅对「JavaUp项目实战&技术讲解」知识星球用户开放

加入星球后,你可以获得:

  • 超级八股文:100万+字的全栈技术知识库,涵盖技术核心、数据库、中间件、分布式等深度剖析的讲解
  • 讲解文档:超级AI智能体、黑马点评Plus、大麦、大麦pro、大麦AI、流量切换、数据中台的从0到1的详细文档
  • 讲解视频:超级AI智能体、黑马点评Plus、大麦、大麦pro、大麦AI、流量切换、数据中台的核心业务详细讲解
  • 1 对 1 解答:可以对我进行1对1的问题提问,而不仅仅只限于项目
  • 针对性服务:有没理解的地方,文档或者视频还没有讲到可以提出,本人会补充
  • 面试与简历指导:提供面试回答技巧,项目怎样写才能在简历中具有独特的亮点
  • 中间件环境:对于项目中需要使用的中间件,可直接替换成我提供的云环境
  • 面试后复盘:小伙伴去面试后,如果哪里被面试官问住了,可以再找我解答
  • 远程的解决:如果在启动项目遇到问题,本人可以帮你远程解决
进入星球后,即可享受上述所有服务,保证不会再有其他隐藏费用。
知识星球二维码

1. 打开微信 -> 扫描左侧二维码 -> 加入「JavaUp项目实战&技术讲解」知识星球

2. 查看星球使用指导,获取完整项目讲解资料索引

👉 点击解锁全部付费内容
🎁优惠