把文档变成可查询的知识图谱:Semantica 完整实践指南
【免费下载链接】semanticaGraph-Native Infrastructure for Context and Accountable AI Systems项目地址: https://gitcode.com/GitHub_Trending/sema/semantica
如果你手上有一批非结构化文档(报告、邮件、网页文本),想要让 LLM 应用或 Agent 基于"实体-关系"结构来回答问题,并且每条结论都能追溯到来源——Semantica 就是干这件事的 Python 框架。它把文本解析、实体关系抽取、建图、持久化、时序查询、决策记忆串成一条流水线,核心能力是:构建带溯源的知识图谱,并让 Agent 有据可依。有 Python 基础、正在给 RAG 或 Agent 项目找"图原生的上下文层"的开发者,最该读这篇。
环境准备:三步装好,零 Key 先跑通
Semantica 当前版本 0.8.0,要求 Python 3.11–3.13(3.10 已停止支持,装到 3.10 会解析到旧的 0.7.x)。安装基础包即可运行本文全部示例,不需要任何 LLM API Key 和模型下载:
pip install semantica python -c "import semantica; print(semantica.__version__)"可视化是可选能力,用到时再补装:
pip install 'semantica[viz]' # 补上 Plotly、pyvis 等画图依赖第一次跑通:一句话文本,立刻看到实体和边
先用一行文本验证整条抽取链路,这是全文所有代码的最小原型。不下载模型、不配 Key 的关键是method="pattern":它走内置正则规则,而不是默认的ml(spaCy,需装en_core_web_sm模型)。
from semantica.semantic_extract import NERExtractor, RelationExtractor text = "Apple Inc. was founded by Steve Jobs, Steve Wozniak, and " \ "Ronald Wayne in 1976 in Cupertino, California." entities = NERExtractor(method="pattern").extract(text) rels = RelationExtractor(method="pattern").extract(text, entities=entities) for e in entities: print(e.text, e.label, e.confidence) for r in rels: print(r.subject.text, "->", r.predicate, "->", r.object.text)运行后你会看到类似这样的真实输出:
Apple Inc. ORG 0.9 Steve Jobs PERSON 0.9 ... Apple Inc. -> founded_by -> Steve Jobs Apple Inc. -> founded_by -> Steve Wozniak三个值得记住的点:
Entity是 dataclass,字段固定为text/label/start_char/end_char/confidence/metadata;confidence可能为None,表示后端不给分,不是"没把握"。RelationExtractor.extract的entities=参数是必须传的——模板只对已识别实体做匹配,不会自己再跑一遍 NER。RelationExtractor默认方法就是"pattern",内置founded_by、works_for、born_in等句式模板(见 relation_extractor.py);还支持cooccurrence(同现窗口)、dependency(依赖句法,需 spaCy)、llm(传provider走 LLM)三种方法。精度不够时把method从"pattern"换成"llm"并传provider,两种方法返回的是同一种Relation结构,可以直接替换。
核心工作流:一份文档如何变成图上的有向边
把上面的演示换成真实文件,链路是:读文件 → 解析出纯文本 → 抽实体和关系 → 构建成图 dict。四个环节各只有一个入口函数,下面按数据流动的顺序走一遍。
读文件。这一环节解决"目录里几十上百个文件,我要拿到一个统一句柄列表"的问题。ingest()是统一入口:传文件路径走单文件逻辑,传目录路径走目录逻辑(默认递归)。
from semantica.ingest import FileIngestor sources = FileIngestor().ingest("data/reports/") # 文件或目录均可 print(len(sources), "files") # 每项是 FileObject,取路径:sources[0].path解析。拿到路径后交给DocumentParser,它按扩展名自动路由到对应的解析器(txt、md、html、csv、docx 等),返回值恒含full_text和metadata两个键——后面的代码只需要关心前者:
from semantica.parse import DocumentParser parser = DocumentParser() text = parser.parse(sources[0].path)["full_text"]抽取。用上一节的NERExtractor+RelationExtractor,把text换成刚解析出来的内容即可。两个方法参数不变。
建图。抽取产出的实体和关系列表,直接打包成 dict 交给GraphBuilder:
from semantica.kg import GraphBuilder ents = NERExtractor(method="pattern").extract(text) rels = RelationExtractor(method="pattern").extract(text, entities=ents) graph = GraphBuilder().build({"entities": ents, "relationships": rels}) print(graph["metadata"]["num_entities"]) # 实体数build()返回的 dict 含entities、relationships、metadata三个键,是整个项目的通用图结构——后面可视化、导出、落库全部吃这个 dict,不需要任何格式转换。这一步没有开任何归并开关,同一实体的不同写法会各占一个节点,这是刻意的:默认行为最保守,归并策略放到下一节按数据规模再开。
从"能跑"到"能用":批量文档与重复实体归并
文档量上来后,真正的问题不再是"能不能建图",而是:三份报告里 "Apple"、"Apple Inc."、"AAPL" 到底是不是同一家公司?不处理的话每个变体各成一个节点,"这家公司有几条边"这类查询必然失真。
把上一节的循环扩到整个目录,并在GraphBuilder上打开归并:
sources = FileIngestor().ingest("data/reports/") ents, rels = [], [] for src in sources: text = parser.parse(src.path)["full_text"] e = ner.extract(text) # 上节构造过的 NERExtractor ents.extend(e) rels.extend(rel.extract(text, entities=e)) graph = GraphBuilder( merge_entities=True, # 关键开关:默认是 False entity_resolution_strategy="fuzzy", # 默认 fuzzy,想更保守改 "exact" resolve_conflicts=True, # 默认 True,检测抽取结果冲突 ).build({"entities": ents, "relationships": rels})关键在merge_entities=:它默认False,不显式传True,构建期根本不会实例化EntityResolver(见 graph_builder.py)。打开后,默认的fuzzy策略会把 "Apple" / "Apple Inc." 归并成同一节点;entity_resolution_strategy改成"exact"则只做完全匹配,宁可漏并也不错并,适合实体名高度规范的语料(实现见 entity_resolver.py)。
另一个规模取舍是内存:ingest()会把目录里所有文件内容读进FileObject列表,大目录建议改用scan_directory(),它只返回path/name/size/extension/modified五字段的元信息 dict,逐文档处理时再解析:
for info in FileIngestor().scan_directory("data/reports/", recursive=True): if info["extension"] in ("md", "txt"): # 顺带过滤无关扩展名 text = parser.parse(info["path"])["full_text"] ents.extend(ner.extract(text))scan_directory的recursive默认True,还支持extensions、min_size、pattern等过滤参数,等于把文件筛选前移到了读文件之前。
输出与集成:看图、导出 RDF、落图数据库
图建好之后,常见的三个去向:肉眼检查、交换格式、持久化。
可视化。检查图的质量最直接的方式是画出来:
from semantica.visualization import KGVisualizer KGVisualizer().visualize_network( graph, output="html", file_path="graph.html", node_color_by="type" )output可选interactive/html/png/svg;node_color_by="type"是默认值,让不同实体类型着不同色,一眼能看出哪类节点连边最密。底层是 Plotly,基础包不带画图依赖,报缺包时补pip install 'semantica[viz]'。项目还自带一个 Web 版 Knowledge Explorer,交互式浏览整张图谱(setup 见 explorer-setup.md):
导出。同一张图 dict 交给不同导出器即可,格式按需选择:
from semantica.export import RDFExporter, ParquetExporter rdf = RDFExporter() rdf.export(graph, file_path="graph.ttl", format="turtle") # 对接 RDF 生态 rdf.export(graph, file_path="graph.jsonld", format="jsonld") # 换 JSON-LD ParquetExporter().export(graph, file_path="output/graph") # 喂 Spark两个参数行为值得注意:RDFExporter的format只接受turtle/rdfxml/jsonld/ntriples/n3五种序列化格式,传别的直接报错;ParquetExporter遇到 dict 输入会按 key 拆成多个文件(graph_entities.parquet、graph_relationships.parquet),file_path是基础路径而不是单文件。CSV、JSON、YAML、OWL、Arrow 等其余导出器都在 semantica/export/ 下,接口形态一致。
持久化。前面所有示例的图只活在进程内存里,进程退出即丢。要落库,把存储后端实例传给GraphBuilder,构建期边建边写,而不是建完再整体搬:
from semantica.graph_store import GraphStore store = GraphStore( backend="neo4j", # 目前支持 "neo4j" / "falkordb" uri="bolt://localhost:7687", user="neo4j", password="password", ) graph = GraphBuilder(merge_entities=True, graph_store=store).build(payload)GraphStore其余配置通过 kwargs 透传给具体后端;不传backend时默认走配置里的neo4j。后端选型对比见 storage-backends.md。
进阶:时序查询与 Agent 决策记忆
时序知识图谱。当关系本身带"有效期"——比如某人 2018 年起任 CEO、2022 年卸任——你需要能问"某个时间点这条边还成立吗"。做法是建图时给边加valid_from/valid_until,查询时用TemporalGraphQuery:
from semantica.kg import GraphBuilder, TemporalGraphQuery kg = GraphBuilder().build({ "entities": [ {"id": "alice", "type": "Person"}, {"id": "acme", "type": "Organization"}, ], "relationships": [ {"source": "alice", "target": "acme", "type": "ceo_of", "valid_from": "2018-01-01", "valid_until": "2022-06-01"}, {"source": "alice", "target": "acme", "type": "advisor_of", "valid_from": "2022-07-01"}, ], }) tq = TemporalGraphQuery(temporal_granularity="day") print(tq.query_at_time(kg, query="", at_time="2020-06-15")["num_relationships"]) # 1 print(tq.query_at_time(kg, query="", at_time="2023-01-01")["num_relationships"]) # 1query_at_time按valid_from/valid_until区间过滤边(见 temporal_query.py):2020 年返回ceo_of,2023 年只剩advisor_of。返回 dict 里num_relationships是过滤后的边数,所以两次查询都是 1,但成立的是不同的边。整条管道要时序能力时,构建侧传GraphBuilder(enable_temporal=True, temporal_granularity="month"),粒度支持从second到year七档。
Agent 决策记忆。多 Agent 长程任务常"前后矛盾":上周选了 A 模型,这周换 B 模型,没有任何机制阻止。AgentContext把事实记忆、图谱记忆和决策记录放在同一层,决策是一等对象而不是日志:
from semantica.context import AgentContext, ContextGraph from semantica.vector_store import VectorStore ctx = AgentContext( vector_store=VectorStore(backend="faiss", dimension=768), # 必传 knowledge_graph=ContextGraph(), # 决策记录落在这里 decision_tracking=True, # 不开则 record_decision 报错 ) ctx.store("GPT-4 outperforms GPT-3.5 on reasoning benchmarks by 40%") did = ctx.record_decision( category="model_selection", scenario="Choose LLM for production reasoning pipeline", reasoning="Benchmark advantage justifies 3x cost", outcome="selected_gpt4", confidence=0.91, ) print(ctx.find_precedents("model selection", limit=5)) # 相关历史决策 print(ctx.analyze_decision_influence(did)) # 该决策的因果影响链硬性前置有两条,违反都会直接抛错(见 agent_context.py):vector_store必传,不传抛ValueError;record_decision依赖decision_tracking=True且决策后端可用(上面的knowledge_graph),否则抛RuntimeError。其余参数都有默认值:retention_days=30(记忆保留天数)、max_memories=10000、hybrid_alpha=0.5(0 偏向量检索、1 偏图遍历)。faiss 后端需另装pip install 'semantica[vectorstore-faiss]'。
常见坑与排查
现象:PDF 解析后抽不到任何实体。原因:扫描件只有图片层、没有机器可读文本,DocumentParser检测到无文本层只打警告,抽取自然为空。 解法:换带 OCR 的解析器,pip install semantica[parse-docling]后:
from semantica.parse import DoclingParser parsed = DoclingParser().parse("data/scan.pdf")现象:大目录建图时内存溢出。原因:默认图只存进程内存,且ingest()会把整个目录读进内存。 解法:用scan_directory()逐文档处理 + 把GraphStore传给GraphBuilder(graph_store=...)边构建边落库。
现象:record_decision抛RuntimeError: Decision tracking is not enabled。原因:构造AgentContext时没开decision_tracking,或没传knowledge_graph。 解法:补上decision_tracking=True并传knowledge_graph=ContextGraph(),二者缺一不可。
现象:visualize_network报缺少 Plotly。原因:基础包不含可视化依赖。 解法:pip install 'semantica[viz]',一次装齐。
现象:抽取结果是空的但没报错。原因:method="pattern"的模板只覆盖有限句式,没命中就静默返回空列表。 解法:换method="ml"(先pip install 'semantica[nlp-spacy]'并下载 spaCy 小模型)或method="llm",两者返回结构与 pattern 完全一致。
进一步阅读
- getting-started.md:官方快速入门,覆盖环境搭建到第一个图
- quickstart.md:五分钟速查,最小可复现路径
- docs/reference/:逐模块 API 参考(semantic_extract、kg、context 等)
- cookbook/introduction/:26 篇入门教程 Notebook,从数据摄入到时序图谱
- cookbook/advanced/:时序知识图谱、Datalog 风格推理等进阶主题
- docs/storage-backends.md:图存储后端选型对比(Neo4j / FalkorDB / Neptune 等)
- integrations/:LangChain / CrewAI / Agno / Google ADK 框架封装
- semantica_mcp/:MCP 服务端,把图谱能力挂给任意 MCP 客户端
【免费下载链接】semanticaGraph-Native Infrastructure for Context and Accountable AI Systems项目地址: https://gitcode.com/GitHub_Trending/sema/semantica
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考