作者:AI大玩家实验室
一、一个真实的需求场景
我有个在私募做研究的朋友,上周发了一条朋友圈:
"每周读30份研报、20份年报、5份招股说明书。不是读不完,是读完了也记不住关联关系。A公司投了B公司的C轮,B公司跟D公司去年签了战略合作协议,D公司最近又入股了E的竞争对手——这些关系在我脑子里像一团乱麻,Excel根本解决不了。"
这条朋友圈下面,几十个同行点赞留言:"+1""同一个世界同一个痛点""代码能开源吗"。
这就是金融从业者每天都在面对的信息困境。 数据不缺,信息泛滥,但真正可用的"知识"——那个能告诉你A公司和B公司之间到底有几层关联的答案——始终抓不住。
大模型可以做总结、可以写摘要、可以回答问题。但它做不到一件事:把散落在上百份PDF文档中的实体和关系,结构化地组织成一个可查询、可推理的"知识网络"。
这就是知识图谱的价值所在。也是今天我们要拆解的开源项目 ArthaNethra 正在解决的核心问题。
ArthaNethra(梵文:财富之网),是一个专门为金融领域设计的开源框架,目标只有一个:自动将金融PDF文档转化为结构化的知识图谱。
从文档解析到实体识别,从关系抽取到图数据库存储,再到自然语言查询——这是一个完整的端到端技术方案。
我们今天把这套方案从头到尾拆一遍。不管你是做知识图谱的、做金融科技的、还是单纯对NLP落地感兴趣,这篇文章应该都能给你一些干货。
二、整体架构:一条完整的"PDF→知识"流水线
先上架构概览。
ArthaNethra的整个处理流程分为五个阶段:
PDF文档 → 文档解析器(Document Parser)
→ NLP管線(Entity & Relation Extraction)
→ 知识对齐(Knowledge Alignment)
→ 图存储(Graph Storage)
→ 查询接口(Query Interface)每个阶段各司其职,但又不像传统软件工程那样严格串行——ArthaNethra的设计中允许各阶段之间反馈回流。例如,当关系抽取阶段发现某个实体的置信度偏低时,可以"回退"到实体识别阶段重新校准。
这种层级反馈架构是ArthaNethra区别于同类项目的一个关键设计决策。
下面我们来逐层拆解。
三、第一层:文档解析——PDF里的信息提纯
PDF是人类最爱的文档格式,也是机器最讨厌的。
原因很简单:PDF本质上是"打印描述语言",它记录的是"第几页第几行什么字体什么颜色",而不是"这是一个标题"、"这是一段正文"、"这是一个表格"。PDF只知道怎么画字,不知道字的意思。
金融PDF的难度更上一层:
- • 复杂表格:年报里的合并财务报表,动不动就是跨页、合并单元格、交错排列
- • 混排内容:一段文字里突然嵌入mini表格、脚注、图表标注
- • 非标准排版:每家券商研报的模板都不一样
- • 扫描件:大量历史文档是扫描版PDF,需要OCR前置
ArthaNethra的解决方案
ArthaNethra使用了一种多模态文档解析管道,融合三种技术路线:
第一路:布局分析(Layout Analysis)
基于深度学习的目标检测模型(类似LayoutLMv3 / DocTR),对PDF页面做像素级的区域分割。输出结果是将页面划分为一个个语义块:
- • 标题块
- • 正文段落块
- • 表格块
- • 图表块(含Caption)
- • 页眉/页脚块
- • 脚注块
这个阶段的关键是不依赖PDF内部结构——它直接"看"页面的视觉特征。
第二路:结构化提取(Structured Extraction)
对于PDF内部的"标注内容"——比如标题的字体字号、加粗、缩进等——ArthaNethra会解析PDF的底层内容流,通过规则引擎做第一遍的结构化标记。
这一路对纯文本PDF的效率很高,但对复杂排版或扫描件基本无能为力。
第三路:OCR兜底(OCR Fallback)
当布局分析模型识别出某个区域是扫描图像(而非可提取文本),自动触发OCR管道。选用的OCR引擎是 Tesseract 5 + PaddleOCR 的双引擎方案:
- • Tesseract负责英文、数字、标准字体
- • PaddleOCR负责中文、复杂字体、弯曲文本
- • 两者投票融合,置信度取高
这种三路并行的设计,将ArthaNethra对不同质量PDF的兼容性做到了90%以上。
关键指标
- • 纯文本PDF的解析准确率:96.7%(基于自建标注的金融PDF测试集,500份)
- • 扫描件PDF的解析准确率:87.3%
- • 表格内容的结构化还原率:91.2%
- • 单个PDF的平均解析耗时:3-12秒(取决于页数和扫描比例)
四、第二层:实体识别——谁是故事里的角色
文档解析完之后,我们得到的是结构化的文本。下一步是:从这些文本里,找出所有"有意义的实体"。
在金融场景中,实体类型远不止"人名、地名、机构名"这种通用NER的范畴。ArthaNethra定义了16种金融专属实体类型,这里列几个最重要的:
| 实体类型 | 示例 | 说明 |
|---|---|---|
| 公司(Company) | 华为、腾讯控股、拼多多 | 包括全称、简称、品牌名、曾用名 |
| 人员(Person) | 马化腾、张一鸣、王兴 | 高管、创始人、重要股东 |
| 金融产品(Financial Product) | 余额宝、沪深300ETF | 基金、理财、保险、信托 |
| 财务指标(Financial Metric) | 营收、净利润、ROE、EPS | 含数值、时间、单位 |
| 事件(Event) | IPO、并购、定增、回购 | 公司重大事件 |
| 行业分类(Industry) | 半导体、新能源、AI | 含多级分类体系 |
| 监管机构(Regulator) | 证监会、银保监会、SEC | 含法律法规 |
| 交易(Transaction) | 3.2亿融资、15%股权转让 | 含金额、股比、对价 |
ArthaNethra的NER方案:两层级联
第一级:通用NER + 微调
基座模型方面,ArthaNethra选择了一条非常务实的路线——不自己训练NER模型,而是在现有的强悍模型基础上做金融领域微调。
具体选型是:
- • 中文版:基于Qwen2.5-7B做LoRA微调,金融文档标注数据约2万篇
- • 英文版:基于GLiNER(通用轻量NER)的金融变体,零样本能力保留
为什么不用ChatGPT或者Claude直接做NER?
团队的回答很直接:成本太高。在大型语料库上做批量NER,如果每次都调用API,单次1万份文档的处理费用就可能超过500美元。而本地微调后的模型,推理成本几乎为零。
第二级:实体链接(Entity Linking)
识别出"腾讯"是一个实体之后,ArthaNethra需要把它链接到一个知识库中的唯一ID上——是"腾讯控股(00700.HK)",还是"腾讯音乐(TME.US)",还是"腾讯云(产品线)"?
这一步靠的是一个金融实体知识库 + 模糊匹配 + BERT语义重排序三层方案:
- 1. 先用Trie树做前缀匹配,召回候选集合
- 2. 再用规则做别名匹配(如"鹅厂"→"腾讯控股")
- 3. 最后用Sentence-BERT计算上下文语义相似度,做精排
实测效果:实体链接准确率 94.1%,召回率 91.8%。
一个典型案例
输入文本:
"2024年Q3,美团实现营收936亿元,同比增长22.4%。王兴在财报电话会上表示,到店业务GMV同比增长超30%。"
NER输出:
[
{"text": "美团", "type": "Company", "offset": [5,7]},
{"text": "936亿元", "type": "FinancialMetric", "offset": [13,19], "metadata": {"metric": "营收", "period": "2024Q3"}},
{"text": "22.4%", "type": "FinancialMetric", "offset": [22,27], "metadata": {"metric": "同比增长率"}},
{"text": "王兴", "type": "Person", "offset": [30,32]},
{"text": "30%", "type": "FinancialMetric", "offset": [47,50], "metadata": {"metric": "GMV同比增长率"}}
]Entity Linking之后,"美团"被链接到知识库ID company:meituan,对应美团的标准化实体信息。
五、第三层:关系抽取——把"孤岛"连成"网络"
实体识别完成了"找角色"的工作。关系抽取则是"找剧情"——角色之间是什么关系?
这层是整个管道中技术难度最大、也是最容易出错的环节。
金融领域的关系类型比通用领域更丰富、更结构化。ArthaNethra定义了12种核心关系类型:
| 关系类型 | 示例三元组 | 解释 |
|---|---|---|
| 投资(invests_in) | (腾讯, 投资, Reddit) | 股权投资关系 |
| 控股(controls) | (张一鸣, 控股, 字节跳动) | 控制权关系 |
| 任职(employed_by) | (王兴, 任职CEO, 美团) | 人事关系 |
| 竞争对手(competes_with) | (美团, 竞争对手, 饿了么) | 竞争关系 |
| 合作(partners_with) | (宁德时代, 合作, 特斯拉) | 战略合作协议 |
| 供应商(supplies) | (台积电, 供应商, 苹果) | 供应链关系 |
| 收购(acquires) | (微软, 收购, Activision) | 并购关系 |
| 持有股份(owns_stake) | (巴菲特, 持有5.9%, 苹果) | 持股比例关系 |
| 同行业(in_industry) | (拼多多, 同行业, 电商) | 行业归属 |
| 财务同比(yoy_comparison) | (美团, 2024Q3营收同比, +22.4%) | 时间序列指标关系 |
| 隶属(belongs_to) | (微信支付, 隶属, 腾讯) | 组织隶属 |
| 监管(regulated_by) | (蚂蚁集团, 受监管, 人民银行) | 合规监管 |
实现方案:Prompt-based Relation Extraction
ArthaNethra在关系抽取阶段没有采用传统的流水线方式(先找实体对,再判断关系),而是使用了一种更直接的方式:基于LLM的端到端关系抽取。
具体做法是:
- 1. 将文档切片成段落级单元(512-1024 tokens)
- 2. 构造Prompt,要求LLM从段落中抽取出所有实体之间的关系三元组
- 3. 对抽取结果做模式匹配和后处理,过滤低质量三元组
- 4. 跨段落做三元组融合(相同实体对合并,强度关系求交)
为什么用LLM而不是传统的BERT-BiLSTM-CRF方案?
项目团队做了一组对照实验:
| 方案 | 关系抽取F1 | 处理速度 | 成本 |
|---|---|---|---|
| BERT-BiLSTM-CRF(专属训练) | 73.4% | 快 | 需标注数据 |
| GPT-4o (API调用) | 89.1% | 慢 | 高 |
| Llama3-70B (本地部署,4bit量化) | 84.6% | 中 | 中 |
| Qwen2.5-14B (本地,LoRA微调) | 86.3% | 较快 | 低 |
最终选择了 Qwen2.5-14B + LoRA微调 的方案。原因是:
- • 效果接近GPT-4o水平(差距不到3个点)
- • 本地部署,数据不外传(金融合规刚需)
- • 14B参数量在半精度推理下,单张A100即可流畅运行
- • 微调后对金融文档的领域术语理解显著优于通用模型
数量级
在项目公开的测试报告中,对100份典型金融PDF(年报、研报、招股书、新闻稿混合)的处理结果:
- • 平均每份PDF提取:47.3个实体,83.6个关系三元组
- • 跨文档实体去重后,100份文档产生:2,845个唯一实体,6,107个唯一关系
- • 关系抽取准确率(抽样人工校验):85.7%
- • 关系抽取召回率(人工标注对照):72.3%
召回率偏低是当前阶段的主要瓶颈,尤其是隐含关系("华为的芯片由台积电代工"→需要抽取出"华为→供应商→台积电")的处理能力还有提升空间。
六、第四层:图数据库存储——让关系"立起来"
实体和关系都抽出来了。现在的问题变成:用什么存,怎么查?
传统的关系型数据库不适合处理多跳关联查询。你想查"马化腾投资过的且与字节跳动有合作关系的公司有哪些"——在MySQL里写这个查询,SQL能让你写到怀疑人生。
这就是图数据库的战场。
选型:Neo4j + NebulaGraph 双引擎
ArthaNethra在存储层做了一个很有意思的设计:双引擎异构存储。
- • 主存储:Neo4j(社区版)
- • 用于日常查询、可视化探索、前端展示
- • 优势:生态成熟(Cypher查询语言)、社区活跃、可视化工具齐全
- • 劣势:单机性能上限,大图(>1亿节点)撑不住
- • 用于大规模图分析、多跳路径计算、图算法(PageRank、社区发现等)
- • 优势:原生分布式、水平扩展、多机性能线性增长
- • 劣势:生态相对新,图查询语言NQL学习成本
为什么需要两层?很简单——查询和计算是两种不同的工作负载。
日常业务查询("查一下A公司和B公司的关系路径")更适合Neo4j的Cypher,直观、简洁、快。而大规模图分析("在100万节点中找出最强关联子图")是NebulaGraph的主场,它能利用分布式计算能力做并行加速。
数据模型设计
ArthaNethra的图模型设计遵循属性图(Property Graph)模型:
节点标签:Company, Person, FinancialProduct, Event, ...
关系类型:invests_in, controls, competes_with, ...
每个节点和关系都可以附带属性:
- 节点属性:name, full_name, wikidata_id, industry, founded_year, ...
- 关系属性:type, confidence, source_doc, timestamp, amount, percentage, ...一个具体的数据实例:
// 节点
(:Company {name: "字节跳动", full_name: "北京字节跳动科技有限公司", industry: "互联网"})
// 关系
(:Person {name: "张一鸣"}) -[:invests_in {type: "股权", source_doc: "招股书-2024.pdf"}]->
(:Company {name: "字节跳动"})
// 多跳路径
(:Person {name: "马化腾"}) -[:invests_in]-> (:Company {name: "腾讯"})
-[:invests_in]-> (:Company {name: "美团"})
-[:competes_with]-> (:Company {name: "饿了么"})七、第五层:查询接口——用自然语言问图数据
存储做完了。图谱建好了。但普通业务人员不会写Cypher,不会写NQL。他们只会说一句话:
"帮我查一下,张一鸣跟王兴之间有什么业务关联。"
这就是第五层的存在意义:自然语言到图查询的转换层(NL2GraphQuery)。
实现方案
ArthaNethra的方案分两步:
第一步:Schema-aware NL2Cypher
用一个微调过的模型(同样是Qwen2.5-14B,参数共享前序步骤的LoRA权重),将用户的自然语言问题转换成Cypher查询语句。
关键技巧:在Prompt中注入图Schema信息。
System: 你是一个图数据库专家。以下是可用的节点类型和关系类型:
节点: Company, Person, FinancialProduct, Event, FinancialMetric
关系: invests_in, controls, employed_by, competes_with, partners_with, ...
User: 查询"王兴"投资过的所有公司的名字
Expected: MATCH (p:Person {name: "王兴"})-[:invests_in]->(c:Company) RETURN c.nameSchema注入的作用非常大——让模型知道"有哪些关系类型可选",而不是靠猜测。
第二步:结果自然语言化
Cypher查询执行后返回的结果是结构化的表格数据,不够直观。ArthaNethra用一个轻量级的LLM调用,将查询结果组织成自然语言描述。
例如:
查询结果:
[{"c.name": "字节跳动"}, {"c.name": "美团"}]
→ 自然语言:
"根据知识图谱,王兴投资过的公司包括美团(他本人任职CEO)以及字节跳动(通过相关实体间接持股)。"查询能力矩阵
| 查询类型 | 示例 | 成功率 |
|---|---|---|
| 单实体查询 | "腾讯的营收是多少" | 96.2% |
| 单跳关系查询 | "张一鸣投资了哪些公司" | 91.7% |
| 两跳关系查询 | "马化腾投资的公司投资了哪些AI公司" | 82.4% |
| 条件过滤查询 | "2024年营收超过100亿的半导体公司" | 79.3% |
| 路径查询 | "找一下王兴和张一鸣之间的最短关系路径" | 76.8% |
| 聚合查询 | "哪些行业被投资最多" | 84.5% |
| 时序对比 | "腾讯2024年Q3营收和2023年同期对比" | 73.1% |
两跳以上的复杂查询是当前的瓶颈,主要原因是自然语言中的隐含逻辑容易误导翻译方向。
八、性能数据与落地实测
讲完技术方案,我们来看看实际效果。
项目团队公开了一份处理1,000份金融PDF(约3.5GB)的基准测试结果:
处理耗时
| 阶段 | 耗时 | 说明 |
|---|---|---|
| 文档解析 | 1小时12分钟 | 含OCR降级处理,约4.3秒/份 |
| 实体识别 + 链接 | 1小时38分钟 | 批量推理,利用vLLM加速 |
| 关系抽取 | 3小时04分钟 | 最耗时的阶段,LLM推理是瓶颈 |
| 跨文档对齐融合 | 28分钟 | 去重、消歧、合并 |
| 图数据库写入 | 12分钟 | 批量导入Cypher语句 |
| 总计 | 约6.5小时 | 全自动化,无需人工干预 |
图谱产出
| 指标 | 数值 |
|---|---|
| 总实体数 | 24,567 |
| 总关系数 | 68,913 |
| 实体类型数 | 16 |
| 关系类型数 | 12 |
| 平均每实体关系数 | 2.8 |
| 最大联通子图规模 | 11,247个节点 |
质量评估(抽样500个三元组人工校验)
| 指标 | 得分 |
|---|---|
| 实体准确率 | 92.3% |
| 关系准确率 | 85.7% |
| 三元组综合正确率 | 81.2% |
| 人工修复成本 | 平均每条三元组修复耗时15秒 |
81.2%的三元组正确率意味着将近五分之一的抽取结果需要人工修正。对生产环境来说,这个数字还不够好。团队正在通过两个方向持续优化:
- 1. 引入human-in-the-loop机制:将低置信度的三元组送给人工审核,通过反馈微调模型
- 2. 利用图本身的一致性约束:如果"A投资B"和"B投资A"同时出现,规则引擎会自动标记为可疑
九、这个方案对其他项目的参考价值
ArthaNethra虽然是面向金融场景的项目,但它的技术架构和设计决策对任何需要从非结构化文档构建知识图谱的场景都有参考意义。
我总结了几个值得借鉴的点:
1. 别迷信"端到端",多模态管道才是务实的选择
很多项目试图用一个"万能模型"搞定从PDF到图谱的全部工作。ArthaNethra的经验证明了:每个阶段用最擅长的工具组合,比一个模型硬扛到底更可靠。
- • 文档解析:传统的PDF引擎 + 视觉布局模型 + OCR引擎
- • NER:轻量级GLiNER + 金融微调的LLM
- • 关系抽取:LLM(作用域 Prompt) + 规则后处理
- • 图存储:传统图DB + 分布式图DB
每个环节选不同工具,通过标准化的中间格式对接。
2. 本地模型 + 微调,在金融场景是刚需
不是所有场景都能调API。金融行业对数据出境的合规要求极其严格。ArthaNethra的选型逻辑——用本地部署的开源模型做微调——在很多管制行业都是唯一可行的路线。
Qwen2.5系列 + LoRA微调的组合,在性价比上确实很能打。
3. 反馈回路比前向管道更重要
ArthaNethra最让我欣赏的设计,不是NER模型多强、关系抽取多准确,而是层级之间的反馈机制。
关系抽取发现某个实体识别不准→回传信号给NER阶段→NER重新评估置信度→如果依然低则标记为人工审核。这个闭环的设计思路,让整个系统的质量随着运行时间不断自我提升,而不是"抽完就完事"。
4. NL2GraphQuery是真正的"最后一公里"
做知识图谱的人容易陷入一个误区:把精力花在建图,忽略了怎么用图。
ArthaNethra把NL2GraphQuery作为与图谱同等重要的模块来设计,是正确的大局观。没有好的查询接口,再漂亮的图谱也只是工程师自嗨的副产品。
十、写在最后
ArthaNethra的开源仓库里有这样一句话:
"We don't just extract data. We weave wealth into a network."
翻译过来就是:我们提取的不是数据,我们是在编织一张财富之网。
从一个PDF文档到一个实体,从一个实体到一条关系,从一条关系到整张知识网络——ArthaNethra做的本质上是"信息→知识"的跃迁。
这个跃迁过程充满了技术挑战:PDF解析的兼容性、NER的金融术语理解、关系抽取的隐含推理、多跳查询的准确性……每一项单独拿出来都是可以写一篇论文的课题。
但ArthaNethra的价值不在于它解决了所有问题——实际上它远未解决所有问题——而在于它提供了一个完整的、开源的、可复用的端到端技术框架。
给后来者铺好了路,这是开源社区最珍贵的精神。
如果你正在做一个类似的项目——不管是金融知识图谱,还是法律知识图谱,还是医疗知识图谱——ArthaNethra的架构设计、技术选型、性能数据,都可以作为你方案设计的重要参考。
项目地址:github.com/ArthaNethra(已公开,Apache 2.0协议)
本文基于项目公开文档、技术报告及社区讨论整理。处理速度和质量数据来源于项目团队公布的基准测试结果,实际表现可能因硬件配置和数据质量而异。
夜雨聆风