如果你还没听说过 Semantica:它是一个 Python 库,专门解决一件事——把非结构化文本变成结构化、可查询、可推理的知识图谱。你丢给它一段文字,它帮你识别出里面的人物、地点、机构,再找出这些实体之间的关系(谁创立了谁、谁位于哪里),最后拼成一张图。这种"图谱化"的上下文,正是现在很多 AI Agent 项目缺的那块拼图——纯向量检索能找到"语义相关"的文本片段,却回答不了"A 和 B 之间是什么关系"这种结构化问题。

这篇文章不讲概念,直接带你跑代码。跑完之后,你会得到一段能直接复用的最小闭环脚本,后面几篇文章都是在这个骨架上往深了讲。

环境准备

Semantica 已经发布到 PyPI,装起来很简单:

pip install semantica

实体识别默认走本地的 spaCy 模型,所以还需要补装一个英文模型(本文示例用英文文本,原因后面会讲):

python -m spacy download en_core_web_sm

装完这两步,不需要任何 API Key、不需要连数据库,就可以跑完整套流程。这也是本文特意选择"本地 ML 方法"而不是"LLM 方法"的原因——先让你无门槛地看到完整链路跑起来,LLM 方式的配置细节留到下一篇专门讲。

流程长什么样

我们要串起来的 6 步是这样的:

每一步对应 Semantica 的一个模块,模块之间用普通的 Python 对象(列表、字典)传递数据,不存在什么"黑箱魔法"。下面我们一步步把它敲出来。

准备一段示例文本

先说清楚:下面这段文本只是我编的一个中性示例(根据莱特兄弟的公开历史资料改写),跟任何具体商业项目无关。你完全可以在跟着敲完代码后,换成你自己感兴趣的文本重新跑一遍。

新建一个目录,写入示例文本:

import osos.makedirs("sample_data", exist_ok=True)with open("sample_data/wright_brothers.txt", "w", encoding="utf-8") as f:    f.write(        "Wilbur Wright was born in Millville, Indiana. "        "His younger brother Orville Wright was born in Dayton, Ohio.\n"        "In 1892, Orville Wright founded the Wright Cycle Company. "        "The Wright Cycle Company is located in Dayton, Ohio.\n"        "On December 17, 1903, Wilbur Wright and Orville Wright achieved "        "the first powered flight near Kitty Hawk, North Carolina.\n"    )

之所以用英文文本,是因为本文用的本地 ML 方法(spaCy 英文模型 + 内置的英文正则关系模式)是针对英文设计的。跑通中文文本需要额外配置,我们放到后面的文章再展开。

第 1 步:ingest —— 把文件读进来

from semantica.ingest import FileIngestorfiles = FileIngestor().ingest("sample_data")print(f"共摄取 {len(files)} 个文件:", [f.name for f in files])

FileIngestor().ingest(...) 接受一个目录或文件路径,自动扫描目录下的文件,返回一组 FileObject(每个对象带 pathnamecontentmetadata 等属性)。这一步只负责"把文件读进内存",还没有做任何文本层面的解析——它甚至不关心文件格式是 PDF 还是纯文本。

预期输出:

共摄取 1 个文件: ['wright_brothers.txt']

第 2 步:parse —— 把文件解析成文档对象

from semantica.parse import DocumentParserparsed_docs = DocumentParser().parse(files)print("解析出的文档字段:", list(parsed_docs[0].keys()))

DocumentParser().parse(...) 接收上一步 ingest 产出的 FileObject 列表,根据文件后缀自动路由到对应的解析器(PDF、DOCX、HTML、纯文本……),返回一组字典。这里有个你需要记住的小细节:纯文本文件解析出来的字典,正文内容存在 text 这个 key 里(而不是 content),下一步要用到。

预期输出:

解析出的文档字段: ['text', 'metadata', 'full_text']

第 3 步:normalize —— 文本归一化

from semantica.normalize import TextNormalizertexts = [doc["text"] for doc in parsed_docs]normalized_texts = TextNormalizer().normalize(texts)

注意这里我们先用 [doc["text"] for doc in parsed_docs] 把上一步的字典列表转换成了纯字符串列表,再传给 normalize()。这不是画蛇添足——TextNormalizer 内部会做 Unicode 规整、空白字符清理这类操作,它期望接收的是"纯文本"或者带 content 键的字典,而不是 parse 产出的那种带 text/full_text 键的字典。先转成字符串列表,是最稳妥、最不会踩坑的传参方式。

第 4 步:semantic_extract —— 抽取实体和关系

这是整条链路里信息量最大的一步,拆成实体识别和关系抽取两个子步骤:

from semantica.semantic_extract import NERExtractor, RelationExtractortext = normalized_texts[0]ner = NERExtractor()  # 默认 method="ml",本地 spaCyentities = ner.extract(text)print(f"识别到 {len(entities)} 个实体,例如:", entities[0])relation_extractor = RelationExtractor()  # 默认 method="pattern"relations = relation_extractor.extract(text, entities)print(f"抽取到 {len(relations)} 条关系:")for r in relations:    print(f"  {r.subject.text} --{r.predicate}--> {r.object.text}")

NERExtractor 默认用本地 spaCy 模型识别人物(PERSON)、地点(GPE)、机构(ORG)、日期(DATE)等实体类型;RelationExtractor 默认用内置的英文正则模式(founded_bylocated_inworks_forborn_in 等)在实体之间找关系。两者都返回的是 Python 对象(EntityRelation),不是字典——这一点在下一步会很关键。

在我本机实际跑出来的结果是:

识别到 18 个实体,例如: Entity(text='Wilbur Wright', label='PERSON', ...)抽取到 4 条关系:  The Wright Cycle Company --founded_by--> Orville Wright  The Wright Cycle Company --located_in--> Dayton  Wilbur Wright --born_in--> Millville  Orville Wright --born_in--> Dayton

你可能会注意到,18 个实体里有一个是把 “Kitty Hawk” 识别成了 PERSON 而不是地名 GPE——这是本地 ML 模型的正常局限(spaCy 认识"人名+人名"的组合模式,容易把不熟悉的地名当成人名)。这不是 bug,而是"本地 ML 方法"这条路线本身的准确率上限。下一篇我们会换成 LLM 方法跑同一段文本,对比一下两种方式在这类边界案例上的表现差异。

第 5 步:kg.GraphBuilder —— 构建知识图谱

from semantica.kg import GraphBuilderkg = GraphBuilder().build(entities, relations)print("返回值类型:", type(kg))print("顶层字段:", list(kg.keys()))print("实体数:", len(kg["entities"]), "关系数:", len(kg["relationships"]))

这里是本文最重要的正确性提醒:GraphBuilder().build() 返回的是一个普通 Python dict,形如 {"entities": [...], "relationships": [...], "metadata": {...}},不是 networkx 图对象。如果你之前用过 networkx,请不要下意识地写 kg.nodes()kg.edges()——这里只能老老实实按字典的 key 去取值,比如 kg["entities"]

同时注意,进到这一步之后,实体和关系的"形状"也变了:上一步 NERExtractor/RelationExtractor 产出的是 Entity/Relation 对象,而 GraphBuilder 内部会自动把它们转换成字典——实体变成 {"id", "name", "type", "confidence", "metadata"},关系变成 {"source", "target", "type", "confidence", "metadata"}。这是两种不同的数据形状,后面写代码时要分清楚自己手上拿的是对象还是字典。

预期输出:

返回值类型: <class 'dict'>顶层字段: ['entities', 'relationships', 'metadata']实体数: 18 关系数: 4

第 6 步:export —— 导出成 JSON

from semantica.export import GraphExporteros.makedirs("output", exist_ok=True)GraphExporter().export(kg, file_path="output/knowledge_graph.json", format="json")print("已导出到 output/knowledge_graph.json")

GraphExporter().export(...) 把上一步的 kg 字典原样落盘成 JSON 文件。因为 kg 本身已经是普通字典,这一步不需要任何格式转换,打开生成的 knowledge_graph.json 你会看到跟上一步打印出来的结构完全一致的内容,只是多了缩进格式化。

完整代码

把上面 6 步串起来,就是一个完整、可以直接复制运行的脚本:

import osfrom semantica.ingest import FileIngestorfrom semantica.parse import DocumentParserfrom semantica.normalize import TextNormalizerfrom semantica.semantic_extract import NERExtractor, RelationExtractorfrom semantica.kg import GraphBuilderfrom semantica.export import GraphExporter# 0. 准备示例文本os.makedirs("sample_data", exist_ok=True)with open("sample_data/wright_brothers.txt", "w", encoding="utf-8") as f:    f.write(        "Wilbur Wright was born in Millville, Indiana. "        "His younger brother Orville Wright was born in Dayton, Ohio.\n"        "In 1892, Orville Wright founded the Wright Cycle Company. "        "The Wright Cycle Company is located in Dayton, Ohio.\n"        "On December 17, 1903, Wilbur Wright and Orville Wright achieved "        "the first powered flight near Kitty Hawk, North Carolina.\n"    )# 1. ingestfiles = FileIngestor().ingest("sample_data")# 2. parseparsed_docs = DocumentParser().parse(files)# 3. normalizetexts = [doc["text"] for doc in parsed_docs]normalized_texts = TextNormalizer().normalize(texts)text = normalized_texts[0]# 4. semantic_extractentities = NERExtractor().extract(text)relations = RelationExtractor().extract(text, entities)# 5. kg.GraphBuilderkg = GraphBuilder().build(entities, relations)print(f"知识图谱构建完成:{len(kg['entities'])} 个实体,{len(kg['relationships'])} 条关系")# 6. exportos.makedirs("output", exist_ok=True)GraphExporter().export(kg, file_path="output/knowledge_graph.json", format="json")print("已导出到 output/knowledge_graph.json")

跑一遍,确认你看到类似 知识图谱构建完成:18 个实体,4 条关系 这样的输出,再打开 output/knowledge_graph.json 看看里面的结构——恭喜,你已经跑通了从文本到知识图谱的最小闭环。

小结

这篇文章里,你用不到 30 行核心代码,走完了 ingest → parse → normalize → semantic_extract → kg.GraphBuilder → export 六步,拿到了一份结构化的知识图谱 JSON。整个过程只依赖本地 spaCy 模型,不需要任何外部 API。

但你可能也发现了两个"不完美"的地方:一是 NERExtractor 把 “Kitty Hawk” 错认成了人名;二是关系抽取只找到了 4 条,而文本里其实还藏着别的隐含关系(比如"Wilbur Wright 和 Orville Wright 是兄弟"就完全没被抽出来)。这些不是本文的疏漏,而是"本地 ML 方法"这条路线的真实能力边界。

学AI大模型的正确顺序,千万不要搞错了

🤔2026年AI风口已来!各行各业的AI渗透肉眼可见,超多公司要么转型做AI相关产品,要么高薪挖AI技术人才,机遇直接摆在眼前!

有往AI方向发展,或者本身有后端编程基础的朋友,直接冲AI大模型应用开发转岗超合适!

就算暂时不打算转岗,了解大模型、RAG、Prompt、Agent这些热门概念,能上手做简单项目,也绝对是求职加分王🔋

在这里插入图片描述

📝给大家整理了超全最新的AI大模型应用开发学习清单和资料,手把手帮你快速入门!👇👇

学习路线:

✅大模型基础认知—大模型核心原理、发展历程、主流模型(GPT、文心一言等)特点解析
✅核心技术模块—RAG检索增强生成、Prompt工程实战、Agent智能体开发逻辑
✅开发基础能力—Python进阶、API接口调用、大模型开发框架(LangChain等)实操
✅应用场景开发—智能问答系统、企业知识库、AIGC内容生成工具、行业定制化大模型应用
✅项目落地流程—需求拆解、技术选型、模型调优、测试上线、运维迭代
✅面试求职冲刺—岗位JD解析、简历AI项目包装、高频面试题汇总、模拟面经

以上6大模块,看似清晰好上手,实则每个部分都有扎实的核心内容需要吃透!

我把大模型的学习全流程已经整理📚好了!抓住AI时代风口,轻松解锁职业新可能,希望大家都能把握机遇,实现薪资/职业跃迁~

这份完整版的大模型 AI 学习资料已经上传CSDN,朋友们如果需要可以微信扫描下方CSDN官方认证二维码免费领取【保证100%免费

在这里插入图片描述

Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐