5.7.md 4.2 KB

5.7 一跳与多跳图检索

店长知道草莓缺货。现在要从草莓反向找:哪些东西直接用它,哪些东西又需要这些东西?先沿着图走一遍,再看代码。

{{figure:07-hop|图 5-10 草莓到奶昔经过 1 条关系,到套餐经过 3 条;技术上分别叫 1 跳和 3 跳。}}

一、每经过一条关系,就是一跳

草莓奶昔直接需要新鲜草莓,是 1 跳。草莓酱也直接需要草莓,但它是半成品。招牌蛋糕需要草莓酱,再到草莓是 2 跳。双人下午茶包含招牌蛋糕,再经过果酱到草莓,是 3 跳。

这里两种关系共同组成路径:套餐包含蛋糕;蛋糕需要果酱;果酱需要草莓。 查询可以同时沿 包含 和 需要 走,展示时仍保留“包含”和“需要”的区别。

二、图怎么存,与从哪里开始查,是两件事

图存“套餐 → 包含 → 蛋糕”“蛋糕 → 需要 → 果酱”,从商品指向组成或原料。查缺货影响时,从草莓反向走,不用额外再存一份相反箭头。

MATCH (草莓:食材 {名称: '新鲜草莓', 知识域: '甜品店'})
MATCH 路径 = (草莓)<-[:需要|包含*1..3]-(出售的东西)
WHERE 出售的东西.类型 IN ['商品', '套餐']
  AND all(节点 IN nodes(路径) WHERE 节点.知识域 = '甜品店')
RETURN 出售的东西.名称, [节点 IN nodes(路径) | 节点.名称] AS 影响路径
ORDER BY 出售的东西.名称

第一行用中文名称定位草莓。第二行画出“草莓 ← 需要或包含 ← 出售的东西”这个图形,<- 表示沿已存箭头反向查找;需要|包含 表示允许这两种关系;*1..3 表示经过 1 到 3 条关系。

出售的东西 是查询里临时起的名字,因为最终结果可能是商品,也可能是套餐,所以在下一行用 类型 IN ['商品', '套餐'] 限定。果酱可以出现在中间路径里,但不会被单独计为出售商品。

nodes(路径) 取出路径经过的全部节点;all(...) 检查它们是否都属于甜品店。这两个是数据库函数,保留原写法。[节点 IN nodes(路径) | 节点.名称] 把节点依次换成便于阅读的名称,AS 影响路径 给这一列起名字,ORDER BY 按名称排序。

返回列“影响路径”从草莓向商品展开,表示本次反向查找的顺序。解释配方时仍按“套餐包含蛋糕、蛋糕需要果酱、果酱需要草莓”读,不能把显示顺序误当作存储箭头方向。

三、把范围和方向写进项目查询

{{snippet:dependency-paths}}

最大跳数先转换并检查,再构造有限查询模板。对象编号、知识域与结果数量用参数传入。代码内部根据问法选择查询方向:问“套餐包含什么”沿出边看组成;问“草莓缺货影响什么”沿入边看使用它的东西。

这是图检索器内部的查询规划,不是决定图通道要不要执行的路由。当前规则只覆盖配套表达,不能说已理解所有自然语言;更复杂问法可以在保持相同接口的前提下扩展。

四、找到套餐以后,再查“通知谁”

{{snippet:attach-responsibility}}

如果问题问通知谁,再查 由谁负责;问在哪里售卖,再查 在哪里售卖。小林负责制作蛋糕,小陈负责套餐上架,所以不能只因路径经过蛋糕,就把套餐通知对象猜成小林。

负责关系和通知位置也要有原文。小陈的通知位置是收银台,数据没有提供电话号码,就不能自行补一个号码。

五、亲手比较 1 跳和 3 跳

GRAPH_MAX_HOPS=1 python -m graph_qa retrieve \
  --question "草莓缺货,哪些甜品和套餐可能受影响?" \
  --mode graph --scope 甜品店

GRAPH_MAX_HOPS=3 python -m graph_qa retrieve \
  --question "草莓缺货,哪些甜品和套餐可能受影响?" \
  --mode graph --scope 甜品店

1 跳能够找到奶昔,却不能走到需要 3 条关系连接的套餐。3 跳范围覆盖配套四项出售对象;最终是否完整保留,还要检查候选数量、重排和上下文预算。

下一节把刚才查到的三条关系与三份配方放在一起,让模型能够解释、学生能够核对。