拓十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Claude Code 真实案例:用 AI 抽取实体关系并写入 Neo4j 知识图谱

Claude Code 真实案例:用 AI 抽取实体关系并写入 Neo4j 知识图谱

1. 为什么我要用 Claude Code 搭一条知识图谱流水线

非结构化文本里塞满了实体和关系,但真正要回答“谁投资了谁”“哪个技术被哪家公司用了”这类问题,靠关键词搜索基本没戏。我手头有一批行业研报和新闻稿,之前用正则和词典硬抽,召回率惨不忍睹,改一次规则就崩一次。后来换成 Claude Code 驱动整条链路:文本清洗 → 实体抽取 → 关系识别 → 写入 Neo4j → 可视化验证,才算把“从一段话到一张图”跑通。

这篇不讲概念,直接给你可复制的配置骨架和提示词模板。核心思路是:Claude Code 负责编排和生成代码,TaoToken 提供统一的模型调用通道,Neo4j 做图存储。你跟着做,半小时内能跑出一条可复现的图谱构建链路。适合谁?做知识管理、风控情报、技术调研,或者单纯想把一堆文档变成可查询网络的开发者。

2. TaoToken 前置:统一 Key 与 API 通道

Claude Code 本身是编码助手,但它调模型时需要一条稳定的 API 通道。我试过把 Key 散落在各个脚本里,换模型就得改一堆文件,后来统一走 TaoToken。官网入口在 https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,API 基址是 https://taotoken.net/api (这个不加 UTM)。

你需要先拿到一个 Key。登录后进控制台,在 API Keys 页面创建一个,复制出来形如sk-xxxx。这个 Key 同时能用于模型对话和编码场景,不用为每个工具单独申请。如果你主要做长期编码和 Agent 任务,可以看 Coding Plan 页面,额度策略更适合高频调用;只是验证模型效果的话,模型对话入口就够。

配置上,Claude Code 的settings.json里把模型请求指向 TaoToken 的兼容端点。我用的骨架长这样:

{ "model": "claude-sonnet-4-20250514", "apiKey": "sk-你的TaoToken密钥", "baseURL": "https://taotoken.net/api", "maxTokens": 4096, "temperature": 0.1 }

注意baseURL结尾不要带/v1,SDK 会自己拼。temperature设 0.1 是因为实体和关系抽取要的是稳定输出,不是创意。Key 别硬编码进 Git,用环境变量TAOTOKEN_API_KEY注入更安全。

3. 可复制配置:实体抽取与关系识别提示词模板

整条链路我拆成三个可独立测试的模块:extract_entities.py、extract_relations.py、load_neo4j.py。每个模块都能单独跑,出问题好定位。

3.1 实体抽取提示词与调用

实体抽取的关键是让模型输出严格 JSON,别夹带解释。我的提示词模板:

ENTITY_PROMPT = """你是一个信息抽取引擎。从下面的文本中抽取所有命名实体。 实体类型限定为:PERSON(人物)、ORG(组织)、GPE(地点)、DATE(日期)、PRODUCT(产品)、TECH(技术)。 输出纯 JSON 数组,每个元素包含 name 和 type 两个字段,不要输出任何其他文字。 文本: {text} """

调用侧用 OpenAI 兼容的 SDK 指向 TaoToken:

import os, json from openai import OpenAI client = OpenAI( api_key=os.environ["TAOTOKEN_API_KEY"], base_url="https://taotoken.net/api" ) def extract_entities(text: str): resp = client.chat.completions.create( model="claude-sonnet-4-20250514", temperature=0.1, messages=[{"role": "user", "content": ENTITY_PROMPT.format(text=text)}] ) raw = resp.choices[0].message.content.strip() raw = raw.removeprefix("```json").removeprefix("```").removesuffix("```").strip() return json.loads(raw)

removeprefix那几行是防模型手滑加代码围栏,实测能省掉一半解析报错。

3.2 关系识别提示词

关系抽取要带上已抽出的实体列表,让模型在封闭集合里找关系,减少幻觉:

RELATION_PROMPT = """已知实体列表:{entities} 从文本中抽取实体之间的关系。关系类型参考:创立、就职于、位于、投资、收购、开发、使用、上市于。 输出纯 JSON 数组,每个元素包含 head、relation、tail、confidence(0-1) 四个字段。 只抽取文本中明确表述的关系,不要推测。 文本: {text} """

confidence字段很有用,后面写 Neo4j 时可以按阈值过滤,低于 0.6 的边先不落库,人工复核后再补。

3.3 写入 Neo4j 的 Cypher

Neo4j 用 MERGE 避免重复节点,关系类型动态拼接时注意反引号包裹:

from neo4j import GraphDatabase driver = GraphDatabase.driver("bolt://localhost:7687", auth=("neo4j", "你的密码")) def load_graph(entities, relations): with driver.session() as session: for e in entities: session.run( f"MERGE (n:`{e['type']}` {{name: $name}})", name=e["name"] ) for r in relations: if r["confidence"] < 0.6: continue rel_type = r["relation"].replace(" ", "_") session.run( f"MATCH (a {{name: $head}}) " f"MATCH (b {{name: $tail}}) " f"MERGE (a)-[rel:`{rel_type}`]->(b) " f"SET rel.confidence = $conf", head=r["head"], tail=r["tail"], conf=r["confidence"] )

节点标签直接用实体类型,查询时MATCH (p:PERSON)就能按类型过滤,比统一标签加属性快得多。

4. 验证请求:从一段文本到一张图

拿一段真实文本跑一遍。我用的是某科技公司融资新闻的节选:

text = """ 星澜科技于2023年完成B轮融资,由远山资本领投,老股东云启创投跟投。 星澜科技总部位于杭州,其核心产品星澜OS是一款面向工业场景的实时操作系统。 远山资本的合伙人林致远表示,工业软件是未来五年的重点赛道。 """

先抽实体,再抽关系,最后落库。跑完在 Neo4j Browser 里执行:

MATCH (a)-[r]->(b) RETURN a, r, b LIMIT 50

你应该能看到星澜科技连着远山资本(投资关系)、连着杭州(位于关系)、连着星澜OS(开发关系)。如果林致远和远山资本之间有就职于的边,说明关系抽取也正常。

再验证一下查询能力:

MATCH (p:PERSON)-[:就职于]->(o:ORG) RETURN p.name, o.name

这条能查出“谁在哪家公司”,就是知识图谱相比关键词搜索的价值所在。实测下来,一段 200 字的文本,实体召回大概 8-12 个,关系 5-8 条,置信度过滤后剩 4-6 条,质量够用。

5. 本篇常见错排查

报错一:json.decoder.JSONDecodeError模型输出带了自然语言前缀。解决:在提示词里强调“不要输出任何其他文字”,解析前用正则re.search(r'\[.*\]', raw, re.DOTALL)兜底提取 JSON 片段。

报错二:Neo4j 连接被拒ServiceUnavailable多半是 Neo4j 没启动,或者 bolt 端口不是 7687。先docker ps看容器状态,再确认NEO4J_AUTH环境变量设了密码。默认密码neo4j/neo4j首次登录会强制改,别用默认的。

报错三:关系类型带空格导致 Cypher 语法错误relation字段里如果有空格或中文标点,直接拼进 Cypher 会炸。统一做replace(" ", "_"),并且用反引号包裹。更稳的做法是维护一个关系类型白名单,不在白名单里的丢弃。

报错四:实体重复,同一家公司出现“星澜科技”和“星澜科技有限公司”这是归一化问题。落库前做一次别名合并:把包含关系或编辑距离小于 2 的名称归到同一个节点。简单做法是维护一个alias_map,复杂点可以再调一次模型做实体消歧。

报错五:TaoToken 返回 401Key 没读到或过期。检查os.environ.get("TAOTOKEN_API_KEY")是否为空,别把 Key 写死在代码里提交到仓库。控制台里可以重新生成 Key,旧 Key 立即失效。

6. 继续往下走

这条链路跑通后,你可以把extract_entities和extract_relations包成一个批处理脚本,遍历整个文档目录,增量写入 Neo4j。我自己的做法是加一个source属性记录每条边来自哪个文件,方便溯源。

如果你想把模型调用统一管理,接入文档里有完整的参数说明和错误码对照;需要验证不同模型在抽取任务上的表现差异,可以直接在模型对话里对比输出;长期跑批处理任务的话,Coding Plan 的额度模型更划算。Key 在 API Keys 页面管理,建议按项目建不同的 Key,方便统计用量和随时吊销。

最后留一个实用技巧:实体抽取和关系抽取分两次调用,比一次性让模型输出“实体+关系”的准确率高不少。模型一次只做一件事,输出结构更稳定,解析代码也更好写。

返回列表