首页 > 教程攻略 > ai资讯 >本体论语义建设新思路,另类RAG来解决检索问题

本体论语义建设新思路,另类RAG来解决检索问题

来源:互联网 时间:2026-06-30 14:58:23
有没有想过一个问题,本体论里一半的篇幅都在讲怎么定义标准数据、怎么描述数据之间的关系。之所以要这么较真,是因为所有的分析和Action都需要一个精准的上下文。从这个角度看,它本质上是一个高维度的RAG问题。 只不过普通RAG搜索的目标大多是大量文本对象,而Ontology操作的目标更偏向数据库对象。这意味着,我们可以借鉴为RAG设计的系统框架来设计Ontology的数据层。比如,这次要聊的SAG(Structured Aggregated Graph),就是一个很有参考价值的方案。 在回答复杂问题时,单靠向量匹配搜索出来的chunk往往不够用。因为很多隐含条件并没有体现在字面上,需要借助关系来查找字面上没有出现的实体。SAG的思路是把关系与向量结合起来做召回和重排序,在多跳问答数据集上的验证结果相当亮眼。核心在于:它不维护重型知识图谱,而是建立三种轻量索引(`chunk → event`、`event ↔ entities`、`chunk → entities`),用“双存储协同 + 多跳扩展”来弥补纯向量检索在多跳场景下的不足。 ## 索引方式 SAG把一个chunk拆成了三个部分:事件、实体和关系。事件是对chunk的摘要,实体是从事件中提取出的主体,关系则是事件与实体之间建立的连接。 对于每一个chunk,让LLM提取事件和实体,并建立关联。 这就像是一个图:两个事件之间如果存在相同的实体,它们就产生了关联。 整个索引流程是一个五步流水线: `chunks → processor(LLM调用) → filter(过滤) → parser(解析) → sa ver(持久化)` 每个chunk经过一次LLM调用,融合成**恰好一个**自包含的事件,再加上若干实体。这和传统的“一句一三元组”是完全不同的思路。对chunk的提取会产出两大类成果:结构化数据和向量化数据。 MySQL负责存储结构化数据,通过event和entity的id来关联,实现精确的关系遍历,用于Step3的通道1(entity→event)、Step5的多跳扩展以及Step8的chunk回溯。 ES则存储向量化数据,负责模糊语义召回和打分,用于Step2的实体召回、Step3的通道2以及Step6的粗排。 结构化数据在MySQL中通过id记录event/entity之间的关系,可以通过entity_id进行精确的关联查询: ``` stmt = select(EventEntity.event_id).where(EventEntity.entity_id.in_(entity_ids) # 精确 JOIN ).join(SourceEvent...).where(source_config_id.in_(...)) ``` 向量化数据存放在ES中,供向量搜索使用: | ES 索引 | 向量来源 | 用途 | | --- | --- | --- | | `event_vectors` | 事件标题、`title+content` 分别 embed | 事件语义召回 | | `entity_vectors` | `entity.name` embed | 实体向量召回(NER命中后找相似实体) | | `event_entity_vectors` | `EventEntity.description` embed | 关联关系检索 | ## 检索:8步流水线的逐层职责 | 步骤 | 职责 | 存储 | 关键参数 | | --- | --- | --- | --- | | **Step1** NER | query → 实体名 | LLM(multi)/ BM25(multi_es) | — | | **Step2** 实体召回 | 实体名 → entity_ids | ES `entity_vectors` | top_k=20, 阈值 0.9 | | **Step3** 双通道召回 | 召回初始事件 | MySQL JOIN + ES kNN | **k=20(入口窄)** | | **Step4** 事件详情 | 取 content + 关联 entities | MySQL / ES | — | | **Step5** 多跳扩展 | 沿实体图遍历补全桥梁 doc | MySQL JOIN / ES 反查 | max_hops=1(默认) | | **Step6** 粗排 | 向量相似度去噪打分 | ES kNN | **max_events=100(5倍冗余)** | | **Step7** LLM 精选 | 多跳推理选 top_k | LLM | top_k=5/10,**不看分数** | | **Step8** chunk 回溯 | event → 原始 chunk | MySQL | chunk_id 去重 | ### 多跳扩展:解决“语义断裂”问题 在多跳问答场景里,答案所在的doc可能与query在语义上并不相关(query里没有答案实体的字面信息)。纯向量检索无法召回这类doc。**Step5的多跳扩展沿着 entity↔event 的关系图进行遍历,把那些“图可达但语义远”的doc拉进候选池**。 基于真实MuSiQue 4跳样本的验证结果如下: | hop | gold doc 的 query 语义相关性 | 召回方式 | | --- | --- | --- | | hop1(query含实体) | 高 | Step3 向量直接召回 | | **hop2(中间桥梁)** | **极低(主题域不交叉)** | **只能靠 Step5 图遍历** | | hop3-4 | 中-高 | 向量 + 图遍历互补 | ### Step3(k=20)与 Step6(max=100)的5倍冗余 ``` Step3 入口窄(k=20,严苛语义筛选) ↓ Step5 多跳注入(绕过相似度,图可达性注入) ↓ Step6 缓冲池宽(max=100,5倍冗余给注入doc留存活空间) ↓ Step7 LLM 不看分数(候选池内一律平等,靠推理选) ``` 这实际上是说,在做向量搜索时用K=20限制了向量召回的数量,把一部分空间留给了用MySQL做精确关联的event。然后将双搜索召回的event放到一起做重排序。有意思的是,这里的重排序用的是LLM,而不是简单的reranker。 ### Step7 用 LLM 而非 reranker:任务定义不同 | 方面 | 传统 reranker | SAG Step7 | | --- | --- | --- | | 任务 | query-doc 语义匹配度 | doc对**多跳推理链**的贡献度 | | 能力 | 相似度打分 | 理解 “First find X, then find Y” | | 成本 | 毫秒级 | 秒级(万 token 量级) | Reranker无法识别那种“跟query不像但却是推理链必经桥梁”的doc,而LLM可以。简单说,就是把召回的100条event依次让LLM重新判断,看哪个event对回答问题更有帮助。当然,它也提供了**fast 模式(multi_es)用数值公式替代 LLM**,用来节约时间和成本。 ## 在RAG上存在的问题 ### 文档格式强依赖 首先,文档格式依赖非常强。SAG的Load模块**只认markdown**,而且heading_strict切分方式强制依赖ATX风格的标题(`#`号)来定义chunk边界。如果你的文档没有标题、不是markdown格式(比如PDF、Word、HTML),这套流程基本就凉了。换句话说,SAG的Load只能处理结构清晰的数据,否则很容易出问题。 benchmark数据集里的corpus都很干净(title/text齐全),完美避开了生产环境中那些乱七八糟的格式预处理问题。真实部署的话,你不得不在前端加一层格式转换。 ### 图遍历与向量打分的固有张力 多跳扩展靠图可达性来召回,Step6又靠向量相似度来排序——这两者之间可能根本不相关。深跳(3-4跳)的答案在向量上可能几乎不相关,因此在Step6被100名的截断淘汰的可能性很大。这是SAG架构的固有代价,也是MuSiQue(其中48%是3-4跳的样本)比HotpotQA更难的根本原因。 ### 成本,还是成本 在整个抽取和检索过程中都需要调用LLM,产生的成本是普通RAG的数倍。 | 阶段 | 每次 input token 量级 | | --- | --- | | **抽取** | 每个 chunk ~500-2000 token + system prompt + few-shot | | **检索** | NER 较小;rerank 100 候选 × ~200 token = ~20000 token | ## 基于SAG的语义层? 如果要用图数据库来定义本体之间的关系,常见的做法是把两张表定义为两个本体,然后用某种关系连接起来。但问题在于,两个本体之间可能存在多种关联关系。 从数据层面看,可能有外键关联;从其他维度看,可能有某些维度字段关联,比如城市、商品类目。通常情况下,图数据库建模都不建议在两个节点之间直接定义多种关系,要么造一个中间节点来处理,要么通过专门的查询条件来避免笛卡尔积。 如果参考SAG的构建方式,把每条数据看作一个chunk/event,把关联字段看作SAG中的实体,就可以自然地建立多种关系。 但要注意,不能直接用LLM来处理数仓中的每一行数据,那token的费用可能比整个数据团队的工资还高。 经过反复取舍和测试,业内逐渐形成了一种结合wiki和cube的多层结构混合存储与检索方案,大致思路如下: 1. 为每张表建立一个wiki,详细描述表的内容、业务含义、适用场景、可能的关联关系等; 2. 把这个wiki作为一个chunk,提取它的event和entity,存入MySQL和ES; 3. 按照cube的标准,定义关联字段、视图等; 4. 使用SAG的检索流程,进行相关表的检索; 5. 综合表、wiki和cube的定义,生成一个或多个SQL语句,进行查询和聚合,并最终生成答案。 简化来看,就是这样一个流程: ``` 用户 query ↓ 查询意图分类(LLM) ├── 明细查询 → SAG 检索(召回行) ├── 聚合查询 → CubeSQL(生成 SQL) └── 混合查询 → SAG 召回 + SQL 聚合 ``` 但在工程实践中,还有很多落地的细节需要处理,比如多个表之间的同义entity如何保证一致性,如何分解query需求,在解答用户或其他系统的问题时是否要采用ReAct模型进行多步检索等等。这些问题,都值得继续深入探讨。

相关下载