Skip to content

[Feature]: 利用 Tags 元数据增强跨子树检索能力 #1147

@ParlamLiao

Description

@ParlamLiao

Feature: 利用 Tags 元数据增强跨子树检索能力

背景

当前 HierarchicalRetriever 的检索分为三步:全局语义匹配 top-K → 提取 L0/L1 起始节点 → BFS 按 PathScope(depth=1) 逐层展开子节点。

跨子树的发现能力完全依赖第一步的语义匹配能否将起始节点分散到不同子树。如果某个子树的 L0/L1 摘要与 query 语义距离较远,该子树永远不会被探索,即使其 L2 内容高度相关。BFS 展开阶段也没有任何横向跳跃机制。

问题场景

viking://resources/
├── machine-learning/        ← L0 摘要: "模型训练与算法"
│   └── transformers/
├── data-engineering/        ← L0 摘要: "数据管道与ETL"
│   └── feature-store/       ← 内容涉及 "特征工程用于模型训练"
└── devops/                  ← L0 摘要: "部署运维"
    └── ml-ops/              ← 内容涉及 "模型训练流水线"

Query: "模型训练的最佳实践"

  • 语义匹配大概率只命中 machine-learning/
  • data-engineering/devops/ 的 L0 摘要与 query 语义距离远,不会被发现
  • feature-store/ml-ops/ 下的相关内容被永久遗漏

根本原因:层级检索只有纵向(父→子)的探索路径,缺少横向(子树→子树)的关联通道。

可行性分析

已具备的基础设施

  • tags 字段已在 context_collection schema 中定义(collection_schemas.py:93
  • tags 已建标量索引,可直接用于过滤查询(collection_schemas.py:111
  • 底层 VikingDB 的 mustcontains 等过滤操作符已就绪
  • Filter DSL 表达层(expr.py)具备 InContainsAndOr 等组合能力

当前 tags 的使用状况

  • 仅 skill 有填充skill_processor.py),资源和记忆的 vectorize_directory_meta()vectorize_file() 在创建 Context 时均未传入 tags
  • 检索阶段完全未使用_build_scope_filter()_tenant_filter() 中没有 tags 相关逻辑
  • 字段和索引已就绪但处于空置状态

方案思路

核心理念:将 tags 作为跨子树的主题关联桥梁,从写入端填充和检索端利用两个方向打通。


一、写入端:Tags 填充

Tags 的来源分为系统自动提取和用户主动指定两部分,二者是补充而非替代关系。

1.1 系统自动提取

在向量化阶段(embedding_utils.py),为 L0/L1/L2 节点自动提取主题标签。

策略 适用场景 权衡
从 abstract/overview 提取关键词(TF-IDF / TextRank) 所有节点 零额外成本,质量中等
L0 的 tags 向下传播给 L2 子节点 继承性标签 简单有效,粒度较粗
LLM 提取语义标签 高价值内容 质量最高,有调用成本

建议初期以低成本策略为主(关键词提取 + 向下传播),后续按效果迭代。

1.2 用户主动指定

自动提取能覆盖文本表面的关键词,但无法推断出跨领域的隐含关联。例如一份数据库设计文档,自动提取可能得到 "schema, table, index",但用户知道它与 "性能优化"、"微服务架构" 相关——这种关联只有用户自己清楚。

做法:在 AddResourceRequest 中增加一个可选的 tags 参数。当前该请求设置了 extra="forbid" 拒绝任意扩展字段,但 tags 不同于开放式自定义 metadata——它是 schema 中已存在的、有明确语义的单一字段,开放它不需要重新设计 schema 扩展机制,只需新增一个可选参数并透传到 Context。

用户指定的 tags 与系统自动提取的 tags 合并存储即可。

Tags 格式约定

使用分号分隔的字符串(如 "machine-learning;model-training;pytorch"),与现有 list<string> 的分号分隔惯例一致。


二、检索端:Tags 利用

检索端对 tags 的利用分为两个层面:用户显式指定和系统自动扩展。

2.1 用户显式指定 tags 搜索

当前用户限定搜索范围的唯一方式是 target_directories——按目录结构过滤。这是一种结构性约束,要求用户了解目录结构。Tags 搜索提供一种语义性约束,两者正交互补:

维度 target_directories tags
约束方式 结构性(在哪棵子树下找) 语义性(找哪个主题的)
用户心智 "在 docs/architecture 下搜" "搜关于微服务的内容"
跨子树能力 弱,需要用户知道目录结构 强,天然跨子树

做法:在搜索请求(FindRequest / SearchRequest)中增加可选的 tags 参数,将其作为额外的 scope filter 注入 _build_scope_filter(),在全局搜索阶段就用标量过滤限定范围。

2.2 系统自动 tag 扩展

retrieve() 流程中,在"合并起始节点"与"BFS 递归展开"之间,增加一个 tag 扩展步骤

  1. 从全局搜索命中的结果中收集 tags
  2. 用这些 tags 做标量过滤,查找未被语义命中但共享相同 tags 的 L0/L1 节点
  3. 将新发现的节点以较低初始分数加入 BFS 队列
         query
           │
  ┌────────┼────────┐
  ▼        ▼        ▼
子树A    子树B    子树C       ← 全局语义匹配命中
[ML]     [ML]    [Python]    ← 提取 tags
           │
    tags="ML" ──────────┐
                         ▼
                       子树D    ← 语义未命中,但 tag 匹配 → 横向跳跃发现
                       [ML]
两个层面的关系

用户显式 tags 和系统自动扩展独立工作、互不依赖

  • 用户显式 tags 是主动缩放范围——"我只关心这些领域"
  • 系统自动扩展是被动发现关联——"你可能还需要这些子树"
  • 两者可以同时生效:在用户指定的 tags 范围内,系统进一步做横向扩展

关键设计约束

  • 扩展需有上限:限制 tag 跳跃引入的新子树数量,避免搜索范围爆炸
  • 自动扩展的分数应降权:tag 匹配的节点使用独立的、较低的初始分数,避免喧宾夺主
  • 按需触发:可考虑仅在初始结果过于集中(如 top-K 全落在同一子树)时才启用自动 tag 扩展
  • 配置可控:通过配置项控制是否开启自动扩展,便于灰度和 A/B 测试

涉及改动范围

层次 涉及模块 改动内容
写入端 embedding_utils.pycontext.py 向量化时自动填充 tags
写入端 新增 tag 提取模块 Tags 自动提取逻辑
写入端 resources.py(AddResourceRequest) 开放可选 tags 参数
检索端 search.py(FindRequest / SearchRequest) 开放可选 tags 参数
检索端 viking_vector_index_backend.py _build_scope_filter() 支持 tags 过滤;新增按 tags 搜索 L0/L1 节点的方法
检索端 hierarchical_retriever.py BFS 前增加 tag 横向跳跃步骤

不需要修改底层 VikingDB 引擎、Collection schema 或标量索引定义。

其他注意事项

  • 存量数据:上线后需对已有数据重新填充 tags(backfill),或在下次重新向量化时自然覆盖
  • Tag 质量是核心风险:低质量 tags 会引入噪声导致结果发散,初期应选择保守策略,优先保证 precision
  • 可观测性:记录 tag expansion 的命中率和最终贡献,用于持续评估效果
  • 性能影响可控:tag 跳跃只增加一次标量过滤查询(非向量搜索),有上限控制
  • 各部分可独立上线:自动提取、用户指定、检索端显式过滤、系统自动扩展四个部分互不依赖,可分步交付

Metadata

Metadata

Assignees

Labels

enhancementNew feature or request

Type

No type

Projects

Status

Done

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions