知识库元数据标签体系设计 v1.3
前置阅读:
自动标注 Skill:skill_文档自动标注.md
版本记录:
- v1.3 (2026-06-15):L1 业务语义层新增
document_title字段(人类可读文档标题,用于 G-Cite 引用摘要栏显示);明确与document_name(Dify 自动取的处理后 .md 文件名)的语义区分;未填时回退到document_name;同步更新字段映射表、完整示例、附录版本记录- v1.2 (2026-06-12):文档迁移至
40_Chat_Agent_QA/;字段名标准化(target_audience→access_roles);合并 FA 知识库建设备忘录(已删除原文件);business_domain/applicable_equipment_types/doc_nature建议值参考实际运维资料(14 个系统分类)更新;补充动态知识库元数据规划(工单沉淀自动打标);新增独立 Skill 文件- v1.1 (2026-06-12):删除 L3 Chunk 层(本期不做);L1/L2 权限字段合并;去掉
master_agent_id / branch_id / department_id;存储方案改为全 Dify 自定义字段;新增自动标注 Prompt 模板- v1.0 (2026-06-12):初稿
0. 背景与目标
0.1 问题
当前知识库文档导入 Dify 后,只有"物理描述"没有"业务语义",导致:
| 痛点 | 后果 |
|---|---|
| 无法按业务域/设备类型筛选文档 | 检索召回噪声多,LLM 拿到不相关内容 |
| 无法按用户角色控制文档可见性 | 领导/员工/访客看到同样内容,违反数据安全 |
| 无法追踪文档原始来源 | 用户想下载原文找不到路径 |
| 无法感知文档是否过期 | 过时 SOP 被 LLM 当作当前标准引用 |
0.2 目标
在 Dify 已有元数据基础上,补充一套"业务语义标签体系",实现:
- 业务分类 → 检索时按业务域/设备类型/适用 Agent 预过滤
- 权限控制 → 通过系统角色控制文档可见性
- 溯源定位 → 记录原始下载链接
- 运营健康 → 过期检测 + 使用热度统计
1. Dify 已有元数据(底座,不改)
Dify 知识库文档详情面板原生提供的字段,作为标签体系的 L0 层:
1.1 文档基本信息
| Dify 字段 | 类型 | 示例 | 说明 |
|---|---|---|---|
document_name | string | 云巡-巡检端说明书.md | 文档显示名(Dify 自动取文件名) |
uploader | string | sunqiang | 上传者账号 |
upload_date | datetime | 2026-06-11 09:10 | 上传时间 |
last_update_date | datetime | 2026-06-12 10:14 | 最后更新时间 |
source | enum | file_upload | 来源类型:file_upload / notion_import / website_crawl |
1.2 文档详细信息
| Dify 字段 | 类型 | 示例 | 说明 |
|---|---|---|---|
原始文件名称 | string | 云巡-巡检端说明书.md | 上传时的原始文件名 |
原始文件大小 | int | - | 文件大小(字节);部分格式不显示 |
上传日期 | datetime | 2026-06-11 09:10 | 同 upload_date |
最后更新日期 | datetime | 2026-06-11 09:12 | 索引最后更新时间 |
来源 | enum | 文件上传 | 同 source |
1.3 技术参数
| Dify 字段 | 类型 | 示例 | 说明 |
|---|---|---|---|
分段规则 | enum | 通用 | 分段模式:通用/自定义 |
段落长度 | int | 1,024 | 每段最大字符数 |
平均段落长度 | int | 240 characters | 实际平均段长 |
段落数量 | int | 23 paragraphs | 总 chunk 数 |
召回次数 | float | 460.87% (106/23) | 平均每段被召回次数(>100% = 有段被反复召回) |
嵌入时间 | float | 1.71 sec | Embedding 耗时 |
嵌入花费 | int | 7,519 tokens | Embedding Token 消耗 |
⚠️ Dify 的局限:以上字段均为文档级 + 系统自动填充,不支持自定义业务标签,不支持 Chunk 级元数据,不支持权限字段。
2. 我们补充的元数据标签体系
在 Dify L0 基础上,分 3 层补充(全部文档级):
┌─────────────────────────────────────────────────────────────┐
│ L1 业务语义层 业务域 / 主题 / 文档性质 / 适用 Agent / 设备 / 关键词 │ ← 本期核心
├─────────────────────────────────────────────────────────────┤
│ L2 权限控制层 可访问角色(access_roles) │ ← 通过系统角色控制可见性
├─────────────────────────────────────────────────────────────┤
│ L3 运营健康层 过期检测 / 召回热度 / 质量评分 / 留存策略 │ ← 长期运营
└─────────────────────────────────────────────────────────────┘📌 枚举值均为"建议值",非强制穷举。枚举的目的是 LLM 自动标注时有参考锚点、人工打标时有分类一致性。实际值可以自由扩展,后续项目可重新调整枚举集。
2.1 L1 业务语义层(本期核心)
解决的问题:让每篇文档都有"业务身份证",检索时可按业务域/设备类型/适用 Agent 预过滤。
| 字段 | 类型 | 必填 | 建议值(非穷举) | 说明 |
|---|---|---|---|---|
document_title | string | ✅ | 自由文本 | 人类可读的文档标题(用于 G-Cite 引用摘要栏显示)。⚠️ 与 document_name(Dify 自动取的处理后 .md 文件名,如 云巡-巡检端说明书.md)不同:本字段是给终端用户看的"这篇文档叫什么",不是文件名,也不是业务主题分类。命名建议:去掉后缀(.md / .pdf)、去掉内部代号、补全版本/系列号、规范化空格与标点。 回退策略:如未填则前端回退到 document_name 显示。 |
business_domain | string | ✅ | 空间运营 / 设施设备 / 行政服务 / 安全巡更 / 消防系统 / 暖通空调 / 变配电 / 给排水 / 监控系统 / 门禁道闸 / 会议系统 / 音响系统 / 环境监测 / 能源系统 / 企业制度 / 第三方产品 / 其他 | 一级业务域(参考实际运维资料分类) |
business_topic | string | — | 自由文本 | 二级业务主题 |
doc_nature | string | ✅ | 产品说明书 / 用户手册 / 安装说明书 / 操作说明书 / SOP / 制度规范 / 培训材料 / FAQ / 工单案例 / 会议纪要 / 合同 / 图纸 / 研究报告 / 功能使用说明 / 通讯协议 / 保修卡 / 合格证 | 文档性质分类 |
applicable_agents | string[] | ✅ | SA / FA / QA / BI / Master | 这篇文档适用于哪个 Agent(可多选) |
applicable_space_types | string[] | — | 会议室 / 工位 / 机房 / 配电间 / 公共区域 / 整栋 / 跨空间 | 适用的空间类型 |
applicable_equipment_types | string[] | — | 空调 / 新风 / 照明 / 巡更设备 / 门禁 / 消防 / 电梯 / BAS 自控 / 冷水机组 / 精密空调 / UPS 电源 / 配电箱 / 传感器 / 摄像机 / 道闸 / 会议主机 / 调音台 / 音箱 / 话筒 / 水表 / 电表 / 水泵 | 适用的设备类型(参考实际运维资料) |
equipment_brands | string[] | — | 自由文本 | 涉及的品牌/型号 |
keywords | string[] | ✅ | 自由文本 | 关键词标签 |
巡更文档示例:
business_domain: "安全巡更"
business_topic: "巡更设备操作"
doc_nature: "产品说明书"
applicable_agents: ["FA"]
applicable_space_types: ["跨空间"]
applicable_equipment_types: ["巡更设备", "NFC 读卡器"]
equipment_brands: ["兰德华", "云巡 9"]
keywords: ["巡更", "NFC", "GPS", "巡检", "异常上报"]2.2 L2 权限控制层
解决的问题:知识库查询需要结合用户角色权限,不同角色看到不同文档。
设计说明:本层只保留 access_roles 一个字段,同时承担两个角色:
- 语义角色:描述这篇文档的目标读者是谁(给 LLM 做 RAG 路由参考)
- 权限角色:检索后代码层按用户的系统角色过滤不可见文档
不再拆分为 visibility_roles + classification_level + department_id + master_agent_id + branch_id,原因:
- 系统已有角色体系,Agent 检索时按用户角色过滤即可,文档本身不需要感知
master/branch classification_level(密级)与access_roles(谁能看)功能高度重叠- 部门过滤通过系统角色实现,无需文档级
department_id
| 字段 | 类型 | 必填 | 建议值(非穷举) | 说明 |
|---|---|---|---|---|
access_roles | string[] | ✅ | 超级管理员 / 物业经理 / 运维工程师 / 普通员工 / 访客 | 可访问角色(可多选)。检索时按用户系统角色过滤:用户角色 ∩ access_roles ≠ ∅ 则可见 |
权限检索链路:
用户提问 + 用户系统角色 → 向量检索(全量候选)
→ 代码层按 access_roles 过滤(用户角色 ∩ access_roles ≠ ∅)
→ 过滤后候选传给 LLM2.3 L3 运营健康层
解决的问题:文档过期、低质量文档被反复召回、无人维护的"僵尸文档"。
| 字段 | 类型 | 说明 |
|---|---|---|
source_file_url | string | 原始文件下载链接(OSS / 云盘路径),用户点"下载原文"跳这里 |
effective_date | date | 生效日期(如制度文档的生效日) |
expiry_date | date | 失效日期(过期触发告警) |
retention_policy | enum | active / archived / to_delete |
llm_citation_count | int | 被 LLM 引用次数(G-Cite 统计) |
user_query_hit_count | int | 用户问题命中次数 |
过期检测流程:
每日定时任务扫描 expiry_date
→ 距离过期 < 30 天 → 发提醒给 uploaded_by
→ 已过期 → 标记 retention_policy = "archived"
→ 归档文档不参与检索,但保留历史(可搜索)3. 存储方案
3.1 方案:全部存 Dify 自定义字段
选择理由:
- 本期只做文档级元数据,不涉及 Chunk 级,Dify 自定义字段够用
- 管理后台可直接在 Dify UI 查看/编辑,无需额外开发
3.2 Dify 自定义字段的类型限制
Dify 的 custom_metadata 只支持 3 种类型:string / number / time。不支持数组(array)和对象(object)。
数组字段的处理方案:序列化为 JSON string 存储,代码层 json.loads() 读取。
为什么不用逗号分隔?因为值里可能包含逗号、空格、特殊字符。JSON string 是最安全的序列化方式。
3.3 字段类型映射
| 元数据字段 | Schema 类型 | Dify 存储类型 | 存储示例 |
|---|---|---|---|
document_title | string | string | "云巡9巡检端说明书" |
business_domain | string | string | "安全巡更" |
business_topic | string | string | "巡更设备操作" |
doc_nature | string | string | "产品说明书" |
applicable_agents | string[] | string(JSON) | "[\"FA\"]" |
applicable_space_types | string[] | string(JSON) | "[\"跨空间\"]" |
applicable_equipment_types | string[] | string(JSON) | "[\"巡更设备\",\"NFC读卡器\"]" |
equipment_brands | string[] | string(JSON) | "[\"兰德华\",\"云巡9\"]" |
keywords | string[] | string(JSON) | "[\"巡更\",\"NFC\",\"GPS\"]" |
access_roles | string[] | string(JSON) | "[\"超级管理员\",\"运维工程师\"]" |
source_file_url | string | string | "oss://kb-raw/2026/06/巡更.pdf" |
effective_date | date | time | "2026-01-01" |
expiry_date | date | time | "2027-12-31" |
retention_policy | string | string | "active" |
💡 表格中的存储示例是 JSON 值(带引号表示 JSON string),不是你在 UI 里手打的格式。手打格式见下方 §3.5。
3.4 代码层读写示例
import json
# 写入(上传文档时)
custom_metadata = {
"business_domain": "安全巡更",
"applicable_agents": json.dumps(["FA"], ensure_ascii=False),
"access_roles": json.dumps(["超级管理员", "运维工程师"], ensure_ascii=False),
"source_file_url": "oss://kb-raw/2026/06/巡更.pdf",
}
# 读取(检索过滤时)
doc = dify_client.get_document(doc_id)
access_roles = json.loads(doc.custom_metadata.get("access_roles", "[]"))
if user_role not in access_roles:
continue # 用户无权访问,跳过3.5 UI 录入格式(手动填写时)
Dify 自定义字段的 value 永远是 JSON 类型。手填时要注意:
| Schema 类型 | UI 里怎么填 | 示例 |
|---|---|---|
| string | 直接写值,不要加引号 | 安全巡更 ✅ · "安全巡更" ❌ |
| string[](JSON string) | 写完整的 JSON 数组字符串,包括 [] 和 "" | ["运维工程师","物业经理"] ✅ |
为什么:Dify 的 custom_metadata 是一个 JSON 对象,每个 value 的类型必须是 JSON 原生类型(string/number/time)。string 字段的 value 就是裸字符串;数组字段因为没有 array 类型,所以 value 是一个字符串,但内容必须是合法的 JSON 数组。
正确:business_domain = 安全巡更
错误:business_domain = "安全巡更" ← 会多存一层引号
正确:access_roles = ["运维工程师","物业经理"]
错误:access_roles = 运维工程师,物业经理 ← 不是合法 JSON,无法 json.loads()if user_role not in access_roles: continue # 用户无权访问,跳过
---
## 4. 自动标注 Skill
文档上传时,调用 LLM 基于文档标题 + 前 2000 字符自动抽取元数据。
**完整 Prompt 模板(System Prompt + User Prompt + 调用示例 + 预期输出)见独立 Skill 文件**:
> [skill_文档自动标注.md](./skill_文档自动标注.md)
---
## 5. 完整元数据示例(巡更文档)
```yaml
# ===== L0 Dify 原生 =====
document_name: "云巡-巡检端说明书.md"
uploader: "sunqiang"
upload_date: "2026-06-11 09:10"
last_update_date: "2026-06-12 10:14"
source: "file_upload"
原始文件名称:"云巡-巡检端说明书.pdf"
原始文件大小:2048576
分段规则:"通用"
段落长度:1024
段落数量:23
# ===== L1 业务语义 =====
document_title: "云巡9巡检端说明书" # 人类可读标题(G-Cite 摘要栏展示用)
business_domain: "安全巡更"
business_topic: "巡更设备操作"
doc_nature: "产品说明书"
applicable_agents: ["FA"]
applicable_space_types: ["跨空间"]
applicable_equipment_types: ["巡更设备", "NFC 读卡器"]
equipment_brands: ["兰德华", "云巡 9"]
keywords: ["巡更", "NFC", "GPS", "巡检", "异常上报"]
# ===== L2 权限控制 =====
access_roles: ["超级管理员", "物业经理", "运维工程师"]
# ===== L3 运营健康 =====
source_file_url: "oss://kb-raw/2026/06/巡更.pdf"
effective_date: "2026-01-01"
expiry_date: "2027-12-31"
retention_policy: "active"6. 与现有资产的衔接
| 现有资产 | 关系 |
|---|---|
| FA 知识库建设备忘录 | ✅ 已合并并删除原文件。FA 草案的"静态库/动态库"两层架构映射到本设计:L1(静态描述)+ L3(动态运营)+ §7.2 动态知识库元数据(工单沉淀自动打标) |
| 运维相关资料(14 个系统分类,90+ 份文档) | ✅ business_domain / applicable_equipment_types / doc_nature 的建议值已参考实际运维资料分类更新 |
| 多模态 RAG 调研 §3.1 / §3.3 | ✅ L0 对应 §3.1 的"文档级元数据";Chunk 级元数据确认 Dify 不支持 segment 级 custom_metadata,本期不做 |
| 权限体系 5 角色 | ✅ access_roles 复用权限体系的 5 角色,Agent 检索时按用户角色过滤 |
| G-Cite 引用归因 | ⏳ Chunk 级元数据(页码/章节/BBox)等 G-Cite 落地时再纳入 |
7. 后续工作
7.1 本期(v1.2)
- [ ] 管理后台增加元数据打标 UI(L1/L2/L3 字段)
- [ ] 接入自动标注 Skill(文档上传时 LLM 自动抽取)
- [ ] 知识库检索链路增加 access_roles 过滤
- [ ] 运营层定时任务(过期检测)
7.2 下一期(v1.4)
- [ ] 评估 Chunk 级元数据(页码/章节/BBox)——Dify 原生不支持 segment 级 custom_metadata,workaround 代价大,等 G-Cite 落地再决定
- [ ] 动态知识库元数据(工单沉淀自动打标)——对接工单系统,工单闭合后自动提取关键信息入库
- 工单层:工单 ID、建单时间、结单时间、处理时长
- 设备层:设备 ID、设备型号、空间位置
- 故障层:故障码、故障现象(NL 描述)、根因(NL 摘要)
- 方案层:处理方案(NL 摘要)、处理人、处理结果
- [ ] Golden Set 命中率纳入运营看板
- [ ] 多路召回策略(向量检索 + 全文检索 + 混合检索)
文档版本:v1.3创建日期:2026-06-12最后更新:2026-06-15 *作者:强哥 *
