FA 一键诊断 知识库 Metadata 过滤限制与 Access Roles 方案
背景:本文记录 Dify Retrieve API 的 metadata_filtering_conditions 能力边界分析,以及 FA 一键诊断知识库在
category+access_roles双层过滤需求下的方案选型决策。
1. 场景描述
知识库中有一篇文档:
category="暖通"access_roles= 希望"超级管理员"和"运维工程师"都能看到
用户角色(从用户身份解析得到):
用户拥有的角色 = ["运维工程师", "后勤主管"]期望的过滤条件(伪代码表达):
(category == "暖通")
AND
(文档角色的集合 inner join 用户角色的集合 结果非空)即:
(category == "暖通")
AND
(access_roles["超级管理员", "运维工程师"] inner join 用户角色["运维工程师", "后勤主管"])
↓
命中 "运维工程师" ✅inner join 的结果只要有交集就说明该文档对该用户可见。
2. Dify API 能力边界
2.1 metadata_filtering_conditions 的 schema
根据 Dify 官方 API 文档(RetrievalModel schema),metadata_filtering_conditions 的结构如下:
{
"logical_operator": "and" | "or",
"conditions": [
{
"name": "字段名",
"comparison_operator": "contains / == / in / ...",
"value": "值"
}
]
}2.2 关键限制
不支持嵌套条件。每个 condition 只有三个字段:name、comparison_operator、value,没有子 conditions 字段,也没有内层 logical_operator。
这意味着只能全体 AND 或 全体 OR,无法表达:
(category == X) AND (role == A OR role == B OR role == C)这种内外层逻辑混合的场景。
⚠️ 说明:以上对 Dify API 的理解基于 Context7 索引的官方文档,不保证为最新版本。开发同学需自行查阅最新 Dify API 文档确认当前版本是否仍为此限制,或已支持嵌套条件。参考链接:docs.dify.ai
3. 方案分析
3.1 方案 A:自建文档管理与元数据过滤(应用层方案)
思路:不在 Dify 层面做 metadata 过滤,自己搭建一套文档管理系统(管理后台),自行管理文档元数据、自行实现复杂的过滤逻辑。
优点:
- 过滤逻辑完全自主可控,嵌套 AND/OR 没有限制
- 不依赖 Dify 的 API 能力
缺点:
- 需要开发管理后台,管理文档和元数据的 CRUD
- 需要自行对接 Dify 的检索结果再做一次过滤(二次开发)
- 维护成本高,与"在 Dify 里一站式搭建"的初衷矛盾
3.2 方案 B(选定方案):约束 access_roles 为单值
核心思想:不在 API 层面解决 OR 问题,而是在文档元数据设计层面规避。
| 约束 | 说明 |
|---|---|
access_roles 必须是单值字符串 | 不能是数组。一篇文档只能绑定一个角色 |
| 如果一个文档需要被多个角色看到 | 复制文档,分别标不同角色 |
检索时用 in 操作符 | access_roles in [用户拥有的角色列表 + "public"] |
选定理由:改动最小,不引入额外的系统依赖,不增加开发工作量。代价是文档复用需要复制,对于 FA 诊断知识库(设备手册为主,文档量不大)可以接受。
4. 方案 B 实现思路
4.1 元数据存储
# Dify 自定义字段
category: "暖通" # string,精确匹配
access_roles: "运维工程师" # string,单值(不能是数组)如果需要同时被 超级管理员 和 运维工程师 看到,就导入两条文档:
| 文档 | category | access_roles |
|---|---|---|
| 暖通空调操作手册 v1 | 暖通 | 运维工程师 |
| 暖通空调操作手册 v2 | 暖通 | 超级管理员 |
4.2 检索时的过滤条件构造
输入:用户角色列表 ["运维工程师", "后勤主管"]
构造逻辑:
{
"logical_operator": "and",
"conditions": [
{
"name": "category",
"comparison_operator": "==",
"value": "暖通"
},
{
"name": "access_roles",
"comparison_operator": "in",
"value": ["运维工程师", "后勤主管", "public"]
}
]
}注意
"public"始终追加到in列表中,确保没有角色限制的公开文档也能被检索到。
4.3 完整的数据流
用户角色: ["运维工程师", "后勤主管"]
│
▼
Build Filter Node (Python)
- 入参: query, user_roles, category
- 逻辑:
1. 解析 user_roles → ["运维工程师", "后勤主管"]
2. 追加 "public" → ["运维工程师", "后勤主管", "public"]
3. 构造 AND 条件:
- category == 入参category
- access_roles in ["运维工程师", "后勤主管", "public"]
- 输出: retrieve_body (含 metadata_filtering_conditions)
│
▼
Dataset Retrieve (HTTP Request)
- POST /datasets/{id}/retrieve
- Body: retrieve_body
│
▼
返回结果: 只有 category 匹配 且 角色匹配的文档4.4 注意事项
- 文档准入时机:文档上传/导入知识库时,
access_roles字段由标注流程(LLM 自动标注 + 人工审核)填写,严格控制单值约束 public角色:始终追加到in列表末尾,确保公开文档不被遗漏- 用户角色解析:由上游流程(工单打标/用户身份解析)产出,传入该工作流节点
5. 补充说明:public 角色设计
public 是一个特殊的角色标记:
文档侧:如果一篇文档对所有人可见,设置 access_roles = "public"。该文档会出现在所有用户的检索结果中(无论用户拥有什么角色)。
检索侧:public 始终被追加到 in 的 value 列表中,作为保底匹配。即使某个用户没有任何已知角色,["public"] 也能检索到公开文档。
这样可以区分三类文档:
| access_roles | 可见范围 |
|---|---|
"public" | 所有人 |
"运维工程师" | 仅运维工程师 |
"超级管理员" | 仅超级管理员 |
