业务故事站
P1 AIP

实体抽取:让文档长出可确认的知识图谱

知识库里的口径文档,人脑能看懂,模型却要"靠猜"。V5 的实体抽取把文档按分层树分块交给 LLM,自动抽出实体(人/组织/产品/指标/术语)与语义关系,落成三张表,人审确认/拒绝闭环,再作为 RAG 第五通道把"实体+别名+原文上下文"喂回查数链路。看完这 4 个故事,你就能把一份口径文档变成图谱、再把图谱变成检索精度。

数据工程师 业务分析师 实体抽取 知识图谱 人审 第五通道 共 4 个故事

能 / 不能速览

✅ 这个主题能做
  • 对知识库文档自动做实体/关系抽取(taskName=entity_extract,JSON 限定 schema,坏 JSON 重试 1 次)
  • 任务化抽取:立即返回 queued 运行记录 + 任务,轮询 /extract_runs 到终态
  • 三态人审:auto / confirmed / rejected,确认的保留、拒绝的不再被 RAG 召回
  • 重抽幂等:再次抽取只重建 auto 实体与关系,人工结论永不回退
  • Graph(docID) 输出实体关系图数据(含非实体宾语的 text 虚拟节点),前端力导向图渲染
  • 作为 RAG 第五通道(entity 权重 0.10),五通道权重合计 1.0;旧库四通道自动补种
⛔ 这个主题做不了
  • llm 缺失无规则降级:抽取本质依赖 LLM,未注入直接返回 503
  • 实体匹配是双向包含(子串/分词),不是向量语义召回;单次扫描上限 1000 行
  • 旧扁平文档没有分层树 chunk 时无法抽取,需先经知识库更新一次
  • 实体去重键是名称,同文档同名实体合并;不做跨文档/语义消解(Gotham 图谱域范围)
  • 关系主语必须是已抽取实体,LLM 输出没命中的关系会被丢弃

适用角色

本主题面向两个角色:

  • 数据工程师:知识图谱的"建图人",负责给知识库文档触发抽取、校验实体质量、看运行记录。
  • 业务分析师:图谱的"审核人"与受益者,逐条确认/拒绝实体,随后 NLQ 查数自动带上实体上下文。

页面在 /entity-extract,任意登录用户可操作(protected,无 admin-only 端点)。

能力速览(能做什么)

抽取服务(POST /extract)

按分层树 chunk 逐块调用 LLM 抽取实体与关系,JSON schema 硬约束,坏 JSON 重试 1 次,任务化或同步。

三态人审

confirm 把实体置为 confirmed(保留 + 正常召回);reject 置为 rejected(RAG 不再召回)。

重抽幂等

再抽取删旧关系 + auto 实体,confirmed/rejected 按名保留并按名去重,人审结论不回退。

图数据(Graph docID)

实体为节点、关系为边,非实体宾语生成 text 虚拟节点(txt_<md5[:12]>),ECharts 力导向图直接消费。

RAG 第五通道

entity 通道(权重 0.10)匹配实体名/别名,附带 chunk 原文与 doc:// URI 注入 NLQ 上下文。

运行记录

GET /extract_runs 按 created_at 倒序返回 queued/running/success/failed 与实体/关系计数,前端轮询用。

调整指南(怎么调整)

  • 改实体类型:默认白名单 person/org/product/metric/term/other,业务要 customer/sku/store 需注入 WithEntityTypes 覆写,prompt 与校验同步生效。
  • 改抽取质量:文档标题层级清晰、术语加别名,抽取更准;实体 aliases 写全(英文名/简称都放进去)命中率更高。
  • 改检索权重:entity 通道权重 0.10 是默认值,可在 /rag/channels 调整([0,1] 区间),五通道合计不必恒为 1。
  • 改召回边界:拒绝误抽实体立即从 RAG 实体通道消失;确认实体后再次触发抽取即可让别名/属性参与检索。
  • 改文档结构:抽取依赖 B2-5 分层树 chunk;旧扁平文档先更新一次生成树,否则报"文档无分层树 chunk 节点"。

做得好的场景

实体抽取在"口径类文档 + 人审闭环"上收益最明显:
  • 口径文档图谱化:一张口径说明文档以往要建模师手工建字典(半天),现在 LLM 抽取 + 人审几分钟出初版图谱。
  • 人审结论稳:反复重抽不丢人工结论,拒绝的实体立即从检索消失,错误语义不污染 RAG。
  • 查数更准:问"Sales Amount 口径"这类带别名/实体名的问题,第五通道把别名与原文上下文补进来,NLQ 从"查对列名"提升为"理解口径"。
  • 一张图讲清关系:力导向图给业务同学一张"这篇文档讲了什么"的全景,替代纯文本阅读。

限制与不足

以下是明确的边界,使用前先知道:
  • 抽取依赖 LLM:llm 未注入时 ExtractDocument 明确返回 503(SERVICE_UNAVAILABLE),无规则降级路径——这是与 schemaai 的 rule_only 降级最本质的区别。
  • 匹配非语义:MatchEntityHits 是子串/分词包含匹配(反向:问题含实体名/别名;正向:分词被实体名包含),不是向量语义召回,单次扫描上限 1000 行。
  • 需要分层树:只消费 kdoc_nodes 分层树 chunk,旧扁平文档无法抽取。
  • 去重按名称:同名实体同文档合并(人工结果优先),不做跨文档消解。
  • 关系约束:主语必须命中实体表,否则关系被丢弃;抽取过程暂无事件级审计(可扩展接入)。

场景故事

故事 1 触发抽取:《销售指标口径说明》自动长出实体与关系
背景
陈工是数据工程师,V5 升级后平台多了"实体抽取"能力。他把《销售指标口径说明》写进知识库(markdown 带标题层级,B2-5 分层树自动切成 chunk),登录 http://127.0.0.1:18080(admin / admin1),进入 /entity-extract 页面选中该文档,点"抽取"。系统按 chunk 逐块交给 LLM(taskName=entity_extract),几分钟后一张"知识图谱底料"就落库了。
传统做法对比
以前要把文档变成结构化图谱,得找数据建模师手工建维度/指标字典,一张口径文档要半天;现在 LLM 抽取 + 人审确认,几分钟产出初版图谱。
角色
数据工程师(触发抽取,需文档与 LLM 网关就绪)。
操作步骤
  1. 确认知识库文档已更新(生成分层树 chunk)
  2. 进入 /entity-extract,选择目标文档
  3. 点"抽取",任务化返回后每 2 秒轮询 /extract_runs
  4. 等待 run.status 到 success,查看实体/关系计数
系统响应
触发抽取返回运行记录 + 任务(任务化模式):
{
  "code": 0,
  "data": {
    "run": { "id": "<uuid>", "doc_id": "3", "status": "queued", "entities": 0, "relations": 0, "error": "", "created_at": "..." },
    "task": { "id": "<task_id>", "type": "entity_extract", "status": "queued", "created_by": "u1", "created_at": "..." }
  }
}
轮询 /extract_runs 到终态:
{
  "code": 0,
  "data": {
    "runs": [
      { "id": "<uuid>", "doc_id": "3", "status": "success", "entities": 5, "relations": 3, "error": "", "created_at": "...", "finished_at": "..." }
    ],
    "total": 1
  }
}
结果洞察
实体表出现 销售额(metric)、订单金额(metric)、退款金额(metric)、虚拟订单(term)等 5 条;关系表出现"销售额 等于 订单金额-退款金额"——宾语不是已抽取实体,落成 object_text 原文短语。LLM 输出限定 JSON schema,坏 JSON 自动重试 1 次;单块内容超过 6000 字符会被截断。陈工注意到:抽取依赖 LLM,没有规则降级路径,llm 未注入时端点直接 503。
调整建议
文档把业务术语和别名写清楚(如"销售额 / 销售总额 / Sales"),抽取出的实体 aliases 更全;业务有自定义实体类型时需注入 WithEntityTypes 覆写白名单。
动手试一试
登录:http://127.0.0.1:18080,admin / admin1。页面路径:/entity-extract。操作:选择《销售指标口径说明》点"抽取",轮询 /extract_runs。预期结果:run.status=success,entities/relations 大于 0,实体表格出现 auto 实体(含 type/confidence/aliases/mention)。
限制提示
旧扁平文档没有分层树会直接失败,需先经知识库更新一次;全部 chunk 抽取失败时 run 置为 failed;抽取无事件级审计,但运行记录 /extract_runs 可回溯次数与计数。
故事 2 三态人审:确认口径、拒绝误抽,重抽不丢人工结论
背景
王姐是业务分析师,负责把关图谱质量。抽取出了 5 个实体,其中"金额1000"明显是某段示例文本被误抽的。她在 /entity-extract 实体表格按状态过滤"待确认",逐条核对:确认 销售额、订单金额,拒绝 金额1000。
传统做法对比
过去实体层不存在,口径靠文档全文检索;就算手工建了字典,一旦重新抽取/重建,人工修改全被覆盖,要反复返工。现在三态人审 + 重抽幂等,人审结论永不回退。
角色
业务分析师(逐条核对实体,确认/拒绝)。
操作步骤
  1. GET /entities?doc_id=3&status=auto 拉待确认实体
  2. 确认 销售额 / 订单金额:POST /entities/<id>/confirm
  3. 拒绝 金额1000:POST /entities/<id>/reject
  4. 再次点抽取,验证人审结论保留、计数只统计新增
系统响应
确认与拒绝均返回更新后的完整实体记录:
// POST /entities/<id>/confirm
{ "code": 0, "data": { "id": "<uuid>", "doc_id": "3", "name": "销售额", "type": "metric", "status": "confirmed", "confidence": 0.9, "uri": "doc://aip/3?chunk=1" } }

// POST /entities/<id>/reject
{ "code": 0, "data": { "id": "<uuid>", "doc_id": "3", "name": "金额1000", "type": "other", "status": "rejected", "confidence": 0.6 } }
结果洞察
三态语义:auto 待确认 / confirmed 人工确认(重抽保留、RAG 正常召回)/ rejected 人工拒绝(RAG 实体通道 MatchEntityHits 直接过滤,不再召回)。王姐再次触发抽取后发现:旧关系全删、auto 实体重建,confirmed/rejected 按名保留并按名去重,新抽取遇到同名实体直接跳过——实体计数只统计新增,人审结论一个都没丢。
调整建议
按 confidence 降序核对,高分优先;被拒绝的实体如果想恢复召回,先确认再重抽即可;把高频错误类型的误抽反馈给文档维护者,从源头减少误抽。
动手试一试
登录:admin / admin1。页面路径:/entity-extract → 实体表格 → 状态过滤"待确认"。操作:确认 销售额,拒绝 金额1000,再触发一次抽取。预期结果:confirm/reject 后 status 立即变化;重抽后已确认/已拒绝实体原样保留。
限制提示
实体去重键是名称(小写不区分大小写),同文档同名实体合并;跨文档同名/别名消解不在本模块(P4 Gotham 图谱域范围);关系只认已抽取实体作主语,LLM 输出的主语没命中的关系会被静默丢弃。
故事 3 RAG 第五通道:问"Sales Amount 口径"命中实体别名与原文
背景
王姐在 /chat 输入"查一下华东区的 sales_amount 口径"。sales_amount 的别名是"Sales Amount",英文别名以前 metadata 通道常匹配不上。陈工已对口径文档完成抽取并确认了 sales_amount 实体(别名 Sales Amount/Sales),第五通道能否把这段强语义线索补进 Text2SQL 上下文,是这次要验证的。
传统做法对比
RAG 四通道(metadata/knowledge/history/fewshot)时,问题稍口语或带别名就 miss,只能靠列名硬猜;第五通道 entity 把"实体+别名+属性+原文 chunk+doc:// URI"整段补进检索上下文,检索精度显著提升。
角色
数据工程师(前置抽取与确认)+ 业务分析师(NLQ 验证结果)。
操作步骤
  1. 前置:在 /entity-extract 完成抽取并确认 sales_amount 实体
  2. 在 /chat 输入含实体名/别名的问题并回车
  3. 查看响应 rag_channels_used 是否含 entity
  4. 或 POST /rag/retrieve 直接验证实体通道命中
系统响应
NLQ 响应 rag_channels_used 出现第五通道:
{
  "intent": "data_query",
  "confidence": 0.8,
  "rag_channels_used": ["metadata", "knowledge", "entity"],
  "sql_query": "SELECT c.customer_name, SUM(o.sales_amount) FROM orders o JOIN customers c ON o.customer_id = c.customer_id WHERE c.city = '华东区' GROUP BY c.customer_name",
  "sql_explanation": "按销售额口径统计华东区客户的销售金额"
}
实体通道命中片段(/rag/retrieve 可查):
实体: sales_amount(类型: metric) 别名: Sales Amount/Sales
属性: {"口径":"订单实付金额"}
上下文: 本月销售额=订单金额-退款金额,指已成交订单的实付金额
来源: doc://aip/3?chunk=2
结果洞察
V5 把 RAG 调整为五通道,权重 metadata 0.35 / knowledge 0.25 / history 0.15 / fewshot 0.15 / entity 0.10(合计 1.0)。存量库没有 entity 通道配置时会自动补种默认值,老数据不用手工改。实体命中后按通道权重 0.10 参与融合重排,附带 chunk 原文与 doc:// URI——NLQ 从"查对列名"提升为"查对列名 + 理解口径"。
调整建议
entity 通道权重可在 /rag/channels 调整([0,1] 区间);实体别名写全(英文名、缩写、业务叫法)命中率更高;确认的实体越多,第五通道覆盖越广。
动手试一试
登录:admin / admin1。操作:先抽取确认 sales_amount 实体,再到 /chat 输入"查一下华东区的 sales_amount 口径"。预期结果:响应 rag_channels_used 含 entity;POST /rag/retrieve 返回实体片段与 doc:// URI。
限制提示
实体匹配是双向包含(问题含实体名/别名,或分词被实体名包含),不是向量语义召回,单次扫描上限 1000 行;rejected 实体不参与召回;实体通道未注入/无数据时该路静默为空,不影响其余四路。
故事 4 实体关系图:一张图讲清"这篇文档讲了什么"
背景
陈工要在评审会上给业务讲"《销售指标口径说明》到底讲了哪些关系"。他打开 /entity-extract 页面右侧的力导向图,节点按实体类型/状态着色,边带谓词标签,业务同学一眼看明白销售额和订单金额、退款金额之间的关系。
传统做法对比
以前让业务读整篇文档自行理解,十几分钟还不一定抓全关系;现在一张图替代纯文本阅读,鼠标拖拽即可浏览,hover 节点还能看到 doc:// URI 溯源。
角色
数据工程师 / 业务分析师(看图辅助理解,只读)。
操作步骤
  1. 在 /entity-extract 左侧选择目标文档
  2. GET /knowledge/documents/:id/graph 取图数据
  3. 前端 ECharts 力导向图渲染节点与边
  4. 拖拽浏览,hover 节点看溯源 URI,点击回文档核对原文
系统响应
图数据接口返回 nodes + edges:
{
  "code": 0,
  "data": {
    "nodes": [
      { "id": "<ent_id>", "label": "销售额", "type": "metric", "status": "confirmed" },
      { "id": "<ent_id>", "label": "订单金额", "type": "metric", "status": "confirmed" },
      { "id": "txt_<md5[:12]>", "label": "订单金额-退款金额…", "type": "text" }
    ],
    "edges": [
      { "source": "<ent_id>", "target": "txt_<md5[:12]>", "label": "等于", "confidence": 0.9 }
    ]
  }
}
结果洞察
实体为节点(type 标注实体类型、status 标注人审状态);非实体宾语(object_text)自动生成 text 虚拟节点(确定性 ID txt_<md5[:12]>,Label 截断 24 rune),保证每条关系边两端都有节点可渲染。这样"销售额 等于 订单金额-退款金额"这种宾语不是实体的关系也能完整画出来。
调整建议
关系多、文档复杂时配合实体/关系表格一起核对;图是只读聚合接口,要在图上改关系需回到人审表格操作;把图数据接入 PPT 时可用 edges[].confidence 做粗细映射。
动手试一试
登录:admin / admin1。页面路径:/entity-extract 选《销售指标口径说明》。预期结果:右侧渲染力导向图,出现"销售额 等于 订单金额-退款金额"等边,text 虚拟节点灰色,hover 显示 doc://aip/3?chunk=1 等溯源 URI。
限制提示
graph 只聚合单文档的实体与关系,跨文档图谱要等 Gotham 图谱域;虚拟节点 Label 截断 24 rune;抽取前若文档无实体数据,图返回空 nodes/edges。

常见问题

实体抽取一定要 LLM 吗?

一定。抽取本质是把文档语义结构化,没有规则降级路径:llm 未注入时 POST /extract 直接返回 503(SERVICE_UNAVAILABLE)。这与 schemaai 的 rule_only 降级相反——标注是半自动辅助可接受规则结果,抽取是核心产出无 LLM 无法工作。

重抽会把人审结论冲掉吗?

不会。重抽幂等设计:再次抽取删旧关系 + auto 实体,confirmed/rejected 实体按名保留并按名去重;新抽取遇到同名实体直接跳过。人审结论永不回退。

第五通道对查数有什么实际帮助?

问题含实体名/别名时,entity 通道把"实体+别名+属性+原文 chunk+doc:// URI"整段注入 Text2SQL 上下文,权重 0.10 参与融合排序。典型收益:问"Sales Amount 口径"时别名不再 miss,NLQ 从"查对列名"提升为"理解口径"。

为什么图里有些节点是灰色的?

那是 text 虚拟节点:关系宾语不是已抽取实体时,系统用确定性 ID txt_<md5[:12]> 生成一个文本节点,保证每条边两端都有节点可渲染。它不代表真实实体。

实体匹配是语义匹配吗?

不是。当前是双向包含匹配:反向(问题文本包含实体名或任一别名)+ 正向(问题分词被实体名/别名包含),单次扫描上限 1000 行。Batch 5 之后计划升级为实体向量化 + 余弦检索的语义召回。

它和 Gotham 的实体消解是什么关系?

本模块只做单文档抽取与名称去重;跨文档同名/别名实体合并、语义消解在 P4 Gotham 图谱域范围。本模块的表可被本体建模、知识问答等消费,Graph 数据也可接入 Gotham 图谱域。

主题小结

一句话:实体抽取 = 分块 → LLM 抽取 → 三态人审 → 图数据 → RAG 第五通道,把知识文档变成可检索、可确认的知识图谱。记住四个边界:抽取依赖 LLM 无规则降级、匹配是包含匹配非语义、旧扁平文档需先重建树、去重按名称不跨文档。口径类文档收益最大——抽一次、审一遍、图一张、查数更准。