想象一下,你打开一篇中文 NBA 新闻,读到:
「LeBron James 砍下 42 分,帮助洛杉矶湖人险胜金州勇士。Draymond Green
因与对手发生冲突,被判技术犯规。Anthony Davis
因髋部受伤被列为每日观察。」
作为球迷的你,很快就明白了:谁得分、谁冲突、谁受伤。
但如果你希望 数据库也能理解这些信息,并能回答:
- 「最近有哪些球员受伤?」
- 「列出湖人最近险胜的比赛。」
- 「这周有哪些球员领到技术犯规?」
这就是 利用大型语言模型(LLM)的命名实体识别(NER) 的用途。
第一步:我们要提取哪些实体?
NBA 中文新闻中的「实体」不只是球队和球员,还包括:
- 球队:洛杉矶湖人、金州勇士、波士顿凯尔特人。
- 球员:LeBron James、Stephen Curry。
- 比赛事件:湖人对勇士、总决赛第三场。
- 伤病:脚踝扭伤、每日观察、手术。
- 冲突:打架、推搡、禁赛。
- 数据:42 分、12 篮板、三双。
- 犯规与处罚:技术犯规、恶意犯规、驱逐出场。
- 交易与合同:交易传闻、续约。
- 奖项与里程碑:MVP 候选人、破纪录。
- 地点:球馆、城市。
- 时间:比赛日期、伤病恢复期。
第二步:让 LLM 抽取实体
我们给 LLM 一个 清晰的提示词(Prompt),让它输出 JSON:
你是一个信息抽取系统。
请阅读以下 NBA 中文新闻,并抽取结构化的实体。
请只输出有效 JSON,格式如下:
{
"entities": [
{"type":"球员","name":"", "details":{}},
{"type":"球队","name":"", "details":{}},
{"type":"伤病","name":"", "details":{}},
{"type":"犯规","name":"", "details":{}},
{"type":"数据","name":"", "details":{}}
],
"game": {
"date":"",
"arena":"",
"winner":"",
"score":{"TEAM1":0,"TEAM2":0}
}
}
details 示例:
- 球员: {"得分":42, "篮板":12}
- 伤病: {"部位":"脚踝", "状态":"每日观察"}
- 犯规: {"类型":"技术犯规"}
- 数据: {"类别":"助攻", "数值":10}
新闻内容:
"LeBron James 砍下 42 分,湖人险胜勇士。Draymond Green 因冲突被判技术犯规。Anthony Davis 因髋部受伤被列为每日观察。"
LLM 输出示例:
{
"entities": [
{"type":"球员","name":"LeBron James","details":{"得分":42}},
{"type":"球队","name":"洛杉矶湖人"},
{"type":"球队","name":"金州勇士"},
{"type":"球员","name":"Draymond Green","details":{"犯规":"技术犯规"}},
{"type":"球员","name":"Anthony Davis","details":{"伤病":"髋部","状态":"每日观察"}}
],
"game": {
"date":"2025-03-20",
"arena":"Crypto.com Arena",
"winner":"洛杉矶湖人",
"score":{"湖人":112,"勇士":108}
}
}
第三步:存进 PostgreSQL
我们需要三张核心表:
-- 新闻
CREATE TABLE 新闻 (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
标题 TEXT,
内容 TEXT,
来源链接 TEXT,
发布时间 TIMESTAMPTZ,
来源 TEXT
);
-- 实体(球员、球队、比赛、伤病等)
CREATE TYPE 实体类型 AS ENUM (
'球员','球队','比赛','伤病','冲突',
'数据','犯规','交易','奖项','地点','日期'
);
CREATE TABLE 实体 (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
类型 实体类型 NOT NULL,
名称 TEXT NOT NULL,
属性 JSONB DEFAULT '{}'::jsonb
);
-- 实体提及(LLM 输出结果)
CREATE TABLE 实体提及 (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
新闻_id UUID REFERENCES 新闻(id),
实体_id UUID REFERENCES 实体(id),
细节 JSONB DEFAULT '{}'::jsonb, -- 伤病、得分、犯规
来源 TEXT NOT NULL, -- 'llm'
创建时间 TIMESTAMPTZ DEFAULT now()
);
第四步:查询知识
当新闻被结构化之后,就可以像分析师一样发问了。
查询最新伤病:
SELECT e.名称 AS 球员,
em.细节->>'伤病' AS 伤病,
em.细节->>'状态' AS 状态,
n.标题, n.发布时间
FROM 实体 e
JOIN 实体提及 em ON em.实体_id = e.id
JOIN 新闻 n ON n.id = em.新闻_id
WHERE e.类型 = '球员' AND em.细节 ? '伤病'
ORDER BY n.发布时间 DESC;
查询技术犯规:
SELECT e.名称 AS 球员, em.细节->>'犯规' AS 犯规, n.标题
FROM 实体 e
JOIN 实体提及 em ON em.实体_id = e.id
JOIN 新闻 n ON n.id = em.新闻_id
WHERE em.细节->>'犯规' = '技术犯规'
ORDER BY n.发布时间 DESC;
查询湖人比赛结果:
SELECT em.细节->>'score' AS 比分, n.标题, n.发布时间
FROM 实体 e
JOIN 实体提及 em ON em.实体_id = e.id
JOIN 新闻 n ON n.id = em.新闻_id
WHERE e.类型 = '球队' AND e.名称 = '洛杉矶湖人'
ORDER BY n.发布时间 DESC;
为什么 LLM 特别适合中文体育新闻?
中文体育新闻的表达非常灵活,常带有绰号或隐喻:
- 「小皇帝爆发」 → LeBron James 表现火热。
- 「AD 每日观察」 → Anthony Davis 受伤,短期缺阵。
- 「库里投进 12 记三分球」 → Stephen Curry 命中率惊人。
规则方法很容易漏掉,而 LLM 可以理解语境并生成结构化结果。
结论
只需要 PostgreSQL 和 LLM,就能把杂乱的中文 NBA
新闻转化为结构化数据,涵盖球员、球队、伤病、犯规和比赛结果。
流程非常简单:
- 收集新闻。
- 调用 LLM,用 JSON 提示词抽取实体。
- 存进 Postgres。
- 查询并得到洞察。
这样一来,你的数据库就不只是存文章,而是一个可以回答问题的「篮球专家」:
谁受伤?谁状态火热?谁惹上麻烦?
这就是 LLM + 数据设计的力量。