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 的
must、contains 等过滤操作符已就绪
- Filter DSL 表达层(
expr.py)具备 In、Contains、And、Or 等组合能力
当前 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 扩展步骤:
- 从全局搜索命中的结果中收集 tags
- 用这些 tags 做标量过滤,查找未被语义命中但共享相同 tags 的 L0/L1 节点
- 将新发现的节点以较低初始分数加入 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.py、context.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 跳跃只增加一次标量过滤查询(非向量搜索),有上限控制
- 各部分可独立上线:自动提取、用户指定、检索端显式过滤、系统自动扩展四个部分互不依赖,可分步交付
Feature: 利用 Tags 元数据增强跨子树检索能力
背景
当前
HierarchicalRetriever的检索分为三步:全局语义匹配 top-K → 提取 L0/L1 起始节点 → BFS 按PathScope(depth=1)逐层展开子节点。跨子树的发现能力完全依赖第一步的语义匹配能否将起始节点分散到不同子树。如果某个子树的 L0/L1 摘要与 query 语义距离较远,该子树永远不会被探索,即使其 L2 内容高度相关。BFS 展开阶段也没有任何横向跳跃机制。
问题场景
Query: "模型训练的最佳实践"
machine-learning/data-engineering/和devops/的 L0 摘要与 query 语义距离远,不会被发现feature-store/和ml-ops/下的相关内容被永久遗漏根本原因:层级检索只有纵向(父→子)的探索路径,缺少横向(子树→子树)的关联通道。
可行性分析
已具备的基础设施
tags字段已在context_collectionschema 中定义(collection_schemas.py:93)tags已建标量索引,可直接用于过滤查询(collection_schemas.py:111)must、contains等过滤操作符已就绪expr.py)具备In、Contains、And、Or等组合能力当前
tags的使用状况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 节点自动提取主题标签。建议初期以低成本策略为主(关键词提取 + 向下传播),后续按效果迭代。
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 搜索提供一种语义性约束,两者正交互补:做法:在搜索请求(
FindRequest/SearchRequest)中增加可选的tags参数,将其作为额外的 scope filter 注入_build_scope_filter(),在全局搜索阶段就用标量过滤限定范围。2.2 系统自动 tag 扩展
在
retrieve()流程中,在"合并起始节点"与"BFS 递归展开"之间,增加一个 tag 扩展步骤:两个层面的关系
用户显式 tags 和系统自动扩展独立工作、互不依赖:
关键设计约束
涉及改动范围
embedding_utils.py、context.pyresources.py(AddResourceRequest)tags参数search.py(FindRequest / SearchRequest)tags参数viking_vector_index_backend.py_build_scope_filter()支持 tags 过滤;新增按 tags 搜索 L0/L1 节点的方法hierarchical_retriever.py不需要修改底层 VikingDB 引擎、Collection schema 或标量索引定义。
其他注意事项